diff --git a/CHANGELOG.md b/CHANGELOG.md index 198bc84..8613f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +0.5.0 / 2025-12-08 + - Refactored `ppd` function for improved CLI test handling with conditional exit strategy + - Added support for automatic object-to-array conversion in `PrettyPrint` (`asArray()`/`toArray()`) + - Added namespace declaration to global helper functions + 0.4.3 / 2025-12-05 - Default precision increased from 2 to 4 - Refactor `PrettyPrint` for modularity: extract formatting logic into dedicated private methods diff --git a/README.md b/README.md index 4f01428..af37473 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,20 @@ composer require apphp/pretty-print Note: When used in web (non-CLI) environments, output is automatically wrapped in `
` to preserve spacing. In CLI, no wrapping is applied. When you request a string to be returned (see `return` option), no auto-wrapping is applied.
+### Import functions
+
+All examples below assume you have imported the helper functions from the `Apphp\PrettyPrint` namespace, for example:
+
+```php
+use function Apphp\PrettyPrint\pprint;
+use function Apphp\PrettyPrint\pp;
+use function Apphp\PrettyPrint\ppd;
+```
+or simply
+```php
+use function Apphp\PrettyPrint\{pprint, pp, ppd};
+```
+
### Global helper functions
Print scalars/strings
@@ -153,6 +167,31 @@ $pp($tensor3d, headB: 2, tailB: 1, headRows: 1, tailRows: 1, headCols: 1, tailCo
$pp('Metrics:', [[0.91, 0.02], [0.03, 0.88]]);
```
+### Objects with `asArray()` / `toArray()`
+
+If you pass an object that exposes an `asArray()` or `toArray()` method, `pprint` / `PrettyPrint` will automatically convert it to an array before formatting:
+
+```php
+class UserCollection
+{
+ public function asArray(): array
+ {
+ return [
+ ['id' => 1, 'name' => 'Alice'],
+ ['id' => 2, 'name' => 'Bob'],
+ ];
+ }
+}
+
+$users = new UserCollection();
+
+pprint($users);
+// [[1, 'Alice'],
+// [2, 'Bob']]
+```
+
+If `asArray()` is not present but `toArray()` is, `toArray()` will be used instead.
+
## Running tests
```bash
diff --git a/src/PrettyPrint.php b/src/PrettyPrint.php
index ff41711..db3c28b 100644
--- a/src/PrettyPrint.php
+++ b/src/PrettyPrint.php
@@ -249,6 +249,18 @@ private function normalizeArgs(array $args): array
}
}
$args = array_values($args);
+
+ // Convert objects to arrays if possible
+ foreach ($args as $i => $value) {
+ if (is_object($value)) {
+ if (is_callable([$value, 'asArray'])) {
+ $args[$i] = $value->asArray();
+ } elseif (is_callable([$value, 'toArray'])) {
+ $args[$i] = $value->toArray();
+ }
+ }
+ }
+
if (count($args) > self::MAX_ARGS) {
$args = array_slice($args, 0, self::MAX_ARGS);
}
@@ -263,8 +275,7 @@ private function normalizeArgs(array $args): array
* @param array $fmt
* @param string $start
* @param string $end
- * @param int $prevPrecision
- * @return bool True if handled, false otherwise
+ * @return string|null True if handled, false otherwise
*/
private function tryLabel3D(array $args, array $fmt, string $start, string $end): ?string
{
@@ -292,8 +303,7 @@ private function tryLabel3D(array $args, array $fmt, string $start, string $end)
* @param array $args
* @param string $start
* @param string $end
- * @param int $prevPrecision
- * @return bool True if handled, false otherwise
+ * @return string|null True if handled, false otherwise
*/
private function tryLabel2D(array $args, string $start, string $end): ?string
{
diff --git a/src/functions.php b/src/functions.php
index bcd953d..10cd2f2 100644
--- a/src/functions.php
+++ b/src/functions.php
@@ -2,43 +2,47 @@
declare(strict_types=1);
-namespace {
-
- use Apphp\PrettyPrint\PrettyPrint;
-
- /**
- * Convenience wrapper around PrettyPrint's callable interface.
- * @param ...$args
- * @return string
- */
- function pprint(...$args): string
- {
- return (new PrettyPrint())(...$args);
- }
-
- /**
- * Alias for pprint
- * @param ...$args
- * @return string
- */
- function pp(...$args): string
- {
- return pprint(...$args);
- }
+namespace Apphp\PrettyPrint;
+
+use Apphp\PrettyPrint\Env;
+use Apphp\PrettyPrint\PrettyPrint;
+
+/**
+ * Convenience wrapper around PrettyPrint's callable interface.
+ * @param ...$args
+ * @return string
+ */
+function pprint(...$args): string
+{
+ return (new PrettyPrint())(...$args);
+}
- /**
- * Alias for pprint with exit after printing
- * @param ...$args
- * @return string
- */
- function ppd(...$args): string
- {
- return pprint(...$args);
+/**
+ * Alias for pprint
+ * @param ...$args
+ * @return string
+ */
+function pp(...$args): string
+{
+ return pprint(...$args);
+}
- if (PHP_SAPI === 'cli' && getenv('APP_ENV') === 'test') {
- return '';
- }
+/**
+ * Alias for pprint with exit after printing
+ * @param ...$args
+ */
+function ppd(...$args)
+{
+ $exiter = null;
- exit;
+ if (Env::isCli() && getenv('APP_ENV') === 'test') {
+ $exiter = fn() => null;
}
+
+ $exiter ??= fn() => exit;
+
+ // Execute behavior
+ pprint(...$args);
+ $exiter();
}
+
diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php
index 146b5e6..ee89131 100644
--- a/tests/FunctionsTest.php
+++ b/tests/FunctionsTest.php
@@ -11,11 +11,12 @@
use PHPUnit\Framework\Attributes\CoversFunction;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
+use function Apphp\PrettyPrint\{pprint, pp, ppd};
-#[Group('prettyprint')]
-#[CoversFunction('pprint')]
-#[CoversFunction('pp')]
-#[CoversFunction('ppd')]
+#[Group('PrettyPrint')]
+#[CoversFunction('Apphp\\PrettyPrint\\pprint')]
+#[CoversFunction('Apphp\\PrettyPrint\\pp')]
+#[CoversFunction('Apphp\\PrettyPrint\\ppd')]
final class FunctionsTest extends TestCase
{
#[Test]
@@ -47,4 +48,40 @@ public function ppdPrintsThenExits(): void
$this->expectOutputString("hello\n");
ppd('hello');
}
+
+ #[Test]
+ #[TestDox('pprint uses asArray() when object provides it')]
+ public function pprintUsesAsArrayOnObject(): void
+ {
+ $obj = new class {
+ public function asArray(): array
+ {
+ return [1, 2, 3];
+ }
+ };
+
+ ob_start();
+ pprint($obj);
+ $out = ob_get_clean();
+
+ self::assertSame("[1, 2, 3]\n", $out);
+ }
+
+ #[Test]
+ #[TestDox('pprint falls back to toArray() when object has no asArray()')]
+ public function pprintUsesToArrayOnObject(): void
+ {
+ $obj = new class {
+ public function toArray(): array
+ {
+ return ['a' => 1, 'b' => 2];
+ }
+ };
+
+ ob_start();
+ pprint($obj);
+ $out = ob_get_clean();
+
+ self::assertSame("[1, 2]\n", $out);
+ }
}
diff --git a/tests/PrettyPrintTest.php b/tests/PrettyPrintTest.php
index 689facc..c5f3acb 100644
--- a/tests/PrettyPrintTest.php
+++ b/tests/PrettyPrintTest.php
@@ -514,4 +514,44 @@ public function defaultFormatterUsesFormatForArrayFor1D(): void
$out = ob_get_clean();
self::assertSame("[1, 2, 3]\n", $out);
}
+
+ #[Test]
+ #[TestDox('converts objects with asArray() to arrays before formatting')]
+ public function convertsObjectWithAsArrayToArray(): void
+ {
+ $pp = new PrettyPrint();
+
+ $obj = new class {
+ public function asArray(): array
+ {
+ return [10, 20, 30];
+ }
+ };
+
+ ob_start();
+ $pp($obj);
+ $out = ob_get_clean();
+
+ self::assertSame("[10, 20, 30]\n", $out);
+ }
+
+ #[Test]
+ #[TestDox('converts objects with toArray() to arrays when asArray() is not available')]
+ public function convertsObjectWithToArrayToArray(): void
+ {
+ $pp = new PrettyPrint();
+
+ $obj = new class {
+ public function toArray(): array
+ {
+ return ['x' => 5, 'y' => 6];
+ }
+ };
+
+ ob_start();
+ $pp($obj);
+ $out = ob_get_clean();
+
+ self::assertSame("[5, 6]\n", $out);
+ }
}