Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ocramius/c56a8e8ff25a8e0bd96800c41edab02a to your computer and use it in GitHub Desktop.
Save Ocramius/c56a8e8ff25a8e0bd96800c41edab02a to your computer and use it in GitHub Desktop.
`compact()` function in PHP, and why it is problematic due to its magic behavior

compact() is a problematic construct that breaks the direct code link between variable definition and variable consumption.

$someVar = 123;

return compact('someVar');

This is equivalent to

$someVar = 123;

return ['someVar' => $someVar];

Practically, this started working in PHPStorm and PHPStan because it's used extensively in the Laravel community, but it requires non-trivial hackery. From a code review and code analysis perspective, especially for any tools that didn't implement the necessary hackery, it is problematic.

At lower level, there's obviously additional work happening when using compact().

This is what an array declaration looks like: https://3v4l.org/XXsL2/vld

Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/XXsL2
function name:  (null)
number of ops:  4
compiled vars:  !0 = $someVar
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    3     0  E >   ASSIGN                                                   !0, 123
    5     1        INIT_ARRAY                                       ~2      !0, 'someVar'
          2      > RETURN                                                   ~2
          3*     > RETURN                                                   1

This is what a compact call looks like instead: https://3v4l.org/HDbuh/vld

Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/HDbuh
function name:  (null)
number of ops:  6
compiled vars:  !0 = $someVar
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    3     0  E >   ASSIGN                                                   !0, 123
    5     1        INIT_FCALL                                               'compact'
          2        SEND_VAL                                                 'someVar'
          3        DO_ICALL                                         $2      
          4      > RETURN                                                   $2
          5*     > RETURN                                                   1

The code for compact then emulates part of the PHP engine to try and behave as if it was PHP code: https://github.com/php/php-src/blob/d78b3fe94338b10981a71083d0d515194705ae13/ext/standard/array.c#L2480-L2517

In the above code, the function implementation will access the current stack frame, extract information from it, then produce an array structure from it.

This means that compact() does not really behave like a function, since its inputs are contextual, and not given explicitly: that makes it harder to spot problems in its usages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment