Eloquent\Phony\Matcher\EqualToMatcher::matches PHP Method

matches() public method

Returns true if $value matches this matcher's criteria.
public matches ( mixed $value ) : boolean
$value mixed The value to check.
return boolean True if the value matches.
    public function matches($value)
    {
        $left = $this->value;
        $right = $value;
        if ($this->useSubstitution) {
            if ($left instanceof IterableSpy) {
                $left = $left->iterable();
            } elseif ($left instanceof Generator && isset($left->_phonySubject)) {
                $left = $left->_phonySubject;
            } elseif ($left instanceof InstanceHandle) {
                $left = $left->get();
            }
            if ($right instanceof IterableSpy) {
                $right = $right->iterable();
            } elseif ($right instanceof Generator && isset($right->_phonySubject)) {
                $right = $right->_phonySubject;
            } elseif ($right instanceof InstanceHandle) {
                $right = $right->get();
            }
        }
        /*
         * @var array<string,bool> The set of object comparisons that have been made.
         *
         * Keys are of the format "<left spl hash>:<right spl hash>"; values are
         * always TRUE. This set is used to detect comparisons that have already
         * been made in order to avoid infinite recursion when comparing cyclic
         * data structures.
         */
        $visitedObjects = array();
        /*
         * @var array<string,bool> The set of array comparisons that have been made.
         *
         * Keys are of the format "<left id>:<right id>"; values are always
         * TRUE. This set is used to detect comparisons that have already been
         * made in order to avoid infinite recursion when comparing cyclic data
         * structures.
         */
        $visitedArrays = array();
        /*
         * @var array<&array> Arrays that have been marked with an internal ID.
         *
         * In order to detect cyclic arrays we need to mark them with an ID.
         * This ID must be removed upon completion of the comparison.
         */
        $markedArrays = array();
        /*
         * @var array<&array> Stacks of values that are currently being compared.
         *
         * We maintain our own stack in order to:
         *
         *  - make fewer function calls (much faster in PHP 5.*)
         *  - avoid stack-depth limits
         *  - allow for 'continuations'
         *  - avoid unwinding when comparison fails
         *
         * Separate stacks are used for left/right to avoid construction of
         * temporary arrays.
         */
        $leftStack = array();
        $rightStack = array();
        /*
         * @var int The number of elements on the stacks.
         */
        $stackSize = 0;
        // ---------------------------------------------------------
        // This is the main entry point of the comparison algorithm.
        // ---------------------------------------------------------
        compare:
        if (is_array($left) && is_array($right)) {
            // Fetch or generate a unique ID for the left-hand-side.
            if (isset($left[self::ARRAY_ID_KEY])) {
                $leftId = $left[self::ARRAY_ID_KEY];
            } else {
                reset($left);
                $leftId = count($markedArrays) + 1;
                $left[self::ARRAY_ID_KEY] = $leftId;
                $markedArrays[] =& $left;
            }
            // Fetch or generate a unique ID for the right-hand-side.
            if (isset($right[self::ARRAY_ID_KEY])) {
                $rightId = $right[self::ARRAY_ID_KEY];
            } else {
                reset($right);
                $rightId = count($markedArrays) + 1;
                $right[self::ARRAY_ID_KEY] = $rightId;
                $markedArrays[] =& $right;
            }
            // Left and right are references to the same array.
            if ($leftId === $rightId) {
                goto pass;
            }
            $comparisonId = $leftId . ':' . $rightId;
            // These two arrays have already been compared.
            if (isset($visitedArrays[$comparisonId])) {
                goto pass;
            }
            // Record the comparison.
            $visitedArrays[$comparisonId] = true;
            // ---------------------------------
            // Compare the next array key/value.
            // ---------------------------------
            compareNextArrayElement:
            // Get the current key for each array, skipping our internal ID
            // value.
            $leftKey = key($left);
            next($left);
            if ($leftKey === self::ARRAY_ID_KEY) {
                $leftKey = key($left);
                next($left);
            }
            $rightKey = key($right);
            next($right);
            if ($rightKey === self::ARRAY_ID_KEY) {
                $rightKey = key($right);
                next($right);
            }
            // Keys can only be string|int (or null, if end of array).
            // Compare them using regular PHP comparison.
            if ($leftKey !== $rightKey) {
                return false;
                // Both keys are null, which means that both array are the same
                // length.
            } elseif (null === $leftKey) {
                goto pass;
            }
            // Push the arrays we're comparing on to the stack and start
            // comparing the values of this element.
            $leftStack[$stackSize] =& $left;
            $rightStack[$stackSize] =& $right;
            ++$stackSize;
            $left =& $left[$leftKey];
            $right =& $right[$rightKey];
            goto compare;
        }
        // Objects and other non-arrays can be compared with ===, as it will
        // not recurse in either case.
        if ($left === $right) {
            goto pass;
        }
        // Non-objects are not identical.
        if (!is_object($left) || !is_object($right)) {
            return false;
        }
        $leftClass = get_class($left);
        $rightClass = get_class($right);
        // The class names do not match.
        if ($leftClass !== $rightClass) {
            return false;
        }
        $comparisonId = spl_object_hash($left) . ':' . spl_object_hash($right);
        // These two objects have already been compared.
        if (isset($visitedObjects[$comparisonId])) {
            goto pass;
        }
        // Record the comparison.
        $visitedObjects[$comparisonId] = true;
        /*
         * Cast the objects as arrays and start comparing them.
         *
         * Importantly, the array cast operator maintains private and protected
         * properties, as well as arbitrary properties added to the object after
         * construction.
         *
         * Some special properties are removed for the purposes of comparison.
         *
         * @see https://github.com/php/php-src/commit/5721132
         */
        $leftIsMock = $left instanceof Mock;
        $leftIsException = $left instanceof Throwable || $left instanceof Exception;
        $left = (array) $left;
        unset($left["gcdata"]);
        if ($leftIsMock) {
            $handleProperty = "" . $leftClass . "_handle";
            if ($left[$handleProperty]) {
                $left['phony.label'] = $left[$handleProperty]->label();
            }
            unset($left[$handleProperty]);
        }
        if ($leftIsException) {
            unset($left["*file"], $left["*line"], $left["Exceptiontrace"], $left["Exceptionstring"], $left['xdebug_message']);
        }
        $rightIsMock = $right instanceof Mock;
        $rightIsException = $right instanceof Throwable || $right instanceof Exception;
        $right = (array) $right;
        unset($right["gcdata"]);
        if ($rightIsMock) {
            $handleProperty = "" . $rightClass . "_handle";
            if ($right[$handleProperty]) {
                $right['phony.label'] = $right[$handleProperty]->label();
            }
            unset($right[$handleProperty]);
        }
        if ($rightIsException) {
            unset($right["*file"], $right["*line"], $right["Exceptiontrace"], $right["Exceptionstring"], $right['xdebug_message']);
        }
        goto compareNextArrayElement;
        // -----------------------------
        // The current values are equal!
        // -----------------------------
        pass:
        // The stack is not empty, pop some values and compare again.
        if ($stackSize--) {
            $left =& $leftStack[$stackSize];
            $right =& $rightStack[$stackSize];
            goto compareNextArrayElement;
        }
        // Stack is empty, there's nothing left to compare. Clean up the
        // injected array IDs and return
        foreach ($markedArrays as &$array) {
            unset($array[self::ARRAY_ID_KEY]);
        }
        return true;
    }