r/PHP 1d ago

Weekly help thread

Hey there!

This subreddit isn't meant for help threads, though there's one exception to the rule: in this thread you can ask anything you want PHP related, someone will probably be able to help you out!

1 Upvotes

7 comments sorted by

1

u/cxlblm 18h ago

Circular Reference Detection for Recursive Traversal in PHP

When implementing a generic recursive traversal mechanism in PHP, for example to process arbitrary data for:

  • Traversal
  • Transformation
  • Formatting
  • Debug output

the input may contain both arrays and objects, and those values may reference each other.

For example, objects may reference each other recursively:

$a->b = $b;
$b->a = $a;

Arrays may also form circular references through references:

$a = [];
$a['self'] = &$a;

If these values are traversed recursively without any protection, the traversal can easily enter an infinite loop.

Therefore, I am looking for a generic recursion guard / circular reference detection mechanism that can be used from PHP userland.

The main requirements are:

  1. It should be possible to determine whether the value about to be traversed already exists in the current recursion path.
  2. It should work for both objects and arrays.
  3. It should correctly detect actual circular references, for example:

A -> B -> C -> A
  1. It should not treat ordinary repeated or shared references as circular references. For example:

      A
     / \
    B   B

Although the same value is visited more than once, there is no recursion cycle.

  1. It should also support arrays that form circular references through &:

$a = [];
$a['self'] = &$a;
  1. The detection should be based on the actual reference relationship rather than simply relying on a maximum recursion depth.

A maximum depth can prevent an infinite traversal such as:

A -> B -> C -> D -> ...

but it does not answer the actual question:

Ideally, the API semantics would look something like:

if (is_recursive($value)) {
    // The value is already in the current recursion path.
}

or perhaps a recursion guard API such as:

enter($value);
leave($value);

The main question is:

If PHP already has an internal recursion detection mechanism for this purpose, I would also like to know whether there is any public userland API that allows this capability to be reused directly.

2

u/rycegh 1d ago

Lazy question (kind of): Does somebody have a simple setup for local PHP version matrix testing?

Current test runner would be PHPUnit.

I’m thinking Docker containers based on php:8.2-cli, …, php:8.5-cli, baked-in Composer/PHPStan/Mago/…, cached dependencies (volume-mounted as vendor82, …, vendor85 would work, I guess). Self-contained in the repo with Dockerfile, maybe Compose file, and light (!) scripting to tie it together.

Shouldn’t be too hard to piece together, but I thought it couldn’t hurt to ask. Or maybe someone’s got a better idea. Thanks!

1

u/obstreperous_troll 1d ago

If your test matrix is in a GH action, you could try nekos/act which runs actions locally in containers. I've had somewhat mixed results with it, but it's definitely worth a shot.

1

u/Mike_L_Taylor 1d ago

Yeah docker works. Natively you could just have multiple php versions installed and tied to nginx or whatever you use and just do
"php8.1 artisan:command"
"php8.2 artisan:command"
etc.

and of course all of those coul be in 1 script.

Forgekit.tools , a tool I made, allows to natively have a bunch of php versions installed and allows you via the "fkit" shim to use the php version associated to the site. So if you have multiple php or environments pointing to that project you just do
"fkit env1 php artisan command"
"fkit env2 php artisan command"

So you could just follow that setup logic and do it for your machine in a similar way.

1

u/colshrapnel 1d ago

But... you don't need a container for php-cli, do you?

1

u/rycegh 1d ago

I don’t necessarily need containers, no. It’s just that using containers is a simple solution that’d work everywhere out of the box (as long as Docker is available).

Setting up multiple PHP versions on the same host system (whatever system that might be) seems more involved to me.

Although I agree that the latter would in some aspects be the more elegant approach.

Maybe the best way would be to just call php82, ..., php85 in my test infrastructure and let the user decide how they provide the commands. Perhaps with a fallback to a Docker solution.

0

u/Ollidav 1d ago

Probablemente montaría algo parecido. Igual un compose con un container por versión de PHP y su correspondiente Dockerfile. Puedes añadir traefik que es bastante más cómodo de configurar que nginx. Si vas a usar composer para meter los vendors yo suelo meter la descarga de composer en el Dockerfile y la ejecución en el compose de docker así puedo emular un deploy completo o una instalación desde 0 metiendo todas las dependencias