Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ composer require apphp/pretty-print

Note: When used in web (non-CLI) environments, output is automatically wrapped in `<pre>` 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
Expand Down Expand Up @@ -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
Expand Down
18 changes: 14 additions & 4 deletions src/PrettyPrint.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
{
Expand Down Expand Up @@ -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
{
Expand Down
74 changes: 39 additions & 35 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

45 changes: 41 additions & 4 deletions tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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);
}
}
40 changes: 40 additions & 0 deletions tests/PrettyPrintTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}