Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. Ocramius created this gist Mar 28, 2023.
    69 changes: 69 additions & 0 deletions compact-in-php-a-problematic-construct.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,69 @@
    `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.