protected function applyFix()

in src/Fixers/ClientUpgradeFixer/ClientUpgradeFixer.php [48:204]


    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
    {
        if (!class_exists('Google\Auth\OAuth2')) {
            throw new \LogicException(
                'In order for Google\Cloud\NewSurfaceFixer to work, you must install the '
                . 'google/cloud client library and include its autoloader in .php-cs-fixer.dist.php'
            );
        }

        $clients = [];
        $useDeclarations = UseStatement::getUseDeclarations($tokens);
        foreach (UseStatement::getImportedClients($useDeclarations) as $clientClass => $useDeclaration) {
            $newClientName = ClientVar::getNewClassFromClassname($clientClass);
            if (class_exists($newClientName)) {
                // Rename old clients to new namespaces
                $tokens->overrideRange(
                    $useDeclaration->getStartIndex(),
                    $useDeclaration->getEndIndex(),
                    UseStatement::getTokensFromClassName($newClientName)
                );
                $clients[] = $clientClass;
            }
        }

        // Get variable names for all clients
        $clientShortNames = [];
        foreach ($clients as $clientClass) {
            // Save the client shortnames so we can search for them below
            $parts = explode('\\', $clientClass);
            $shortName = array_pop($parts);
            $clientShortNames[$clientClass] = $shortName;
        }
        $clientVars = array_merge(
            ClientVar::getClientVarsFromConfiguration($this->configuration),
            ClientVar::getClientVarsFromNewKeyword($tokens, $clientShortNames),
            ClientVar::getClientVarsFromVarTypehint($tokens, $clientShortNames),
            ClientVar::getClientVarsFromTypehint($tokens, $clientShortNames),
        );

        // Find the RPC methods being called on the clients
        $classesToImport = [];
        $counter = new RequestVariableCounter();
        $importStart = $this->getImportStart($tokens);
        $insertStart = null;
        for ($index = 0; $index < count($tokens); $index++) {
            $foundClientVar = false;
            foreach ($clientVars as $clientVar) {
                if ($foundClientVar = $clientVar->isDeclaredAt($tokens, $index)) {
                    break;
                }
            }

            if (!$foundClientVar) {
                // The token is not a client var
                continue;
            }

            $operatorIndex = $tokens->getNextMeaningfulToken($index);
            if (!$tokens[$operatorIndex]->isGivenKind(T_OBJECT_OPERATOR)) {
                // The client var is not calling a method
                continue;
            }

            // The method being called by the client variable
            $methodIndex = $tokens->getNextMeaningfulToken($operatorIndex);
            if (!$rpcMethod = $clientVar->getRpcMethod($tokens[$methodIndex]->getContent())) {
                // The method doesn't exist, or is not an RPC call
                continue;
            }

            // Get the arguments being passed to the RPC method
            [$arguments, $firstIndex, $lastIndex] = RpcParameter::getRpcCallParameters($tokens, $methodIndex);

            // determine where to insert the new tokens
            $lineStart = $clientVar->getLineStart($tokens);

            // Handle differently when we are dealing with inline PHP
            $isInlinePhpCall = $tokens[$lineStart]->getId() === T_OPEN_TAG;

            $indent = '';
            if (!$isInlinePhpCall) {
                $indent = str_replace("\n", '', $tokens[$lineStart]->getContent());
            }

            $requestClass = $rpcMethod->getRequestClass();
            $requestVarName = $counter->getNextVariableName($requestClass->getShortName());

            // Tokens for the setters called on the new request object
            $requestSetterTokens = $rpcMethod->getRequestSetterTokens($tokens, $arguments, $indent);

            // Tokens for initializing the new request variable
            $newRequestTokens = $requestClass->getInitTokens(
                $requestVarName,
                count($requestSetterTokens) > 0
            );

            // Add them together
            $newRequestTokens = array_merge(
                [new Token([T_WHITESPACE,  PHP_EOL . $indent])],
                $newRequestTokens,
                $requestSetterTokens,
                [new Token(';')]
            );

            // When inserting for inline PHP, add a newline before the first request variable
            if ($isInlinePhpCall && $counter->isFirstVar()) {
                array_unshift($newRequestTokens, new Token([T_WHITESPACE, PHP_EOL]));
            }

            // Determine where the request variable tokens should be inserted
            if ($isInlinePhpCall) {
                // If we are inline, insert right before the first closing PHP tag
                if (is_null($insertStart)) {
                    $insertStart = $tokens->getNextTokenOfKind($importStart, ['?>', [T_CLOSE_TAG]]) - 1;
                }
            } else {
                // else, insert at beginning of the line of the original RPC call
                $insertStart = $lineStart;
            }

            // insert the request variable tokens
            $tokens->insertAt($insertStart, $newRequestTokens);

            // Replace the original RPC call arguments with the new request variable
            $tokens->overrideRange(
                $firstIndex + 1 + count($newRequestTokens),
                $lastIndex - 1 + count($newRequestTokens),
                [new Token([T_VARIABLE, $requestVarName])]
            );

            // Increment the current $index and $insertStart
            $index = $firstIndex + 1 + count($newRequestTokens);
            if ($isInlinePhpCall) {
                $insertStart = $insertStart + count($newRequestTokens);
            }

            // Add the request class to be imported later
            $classesToImport[$requestClass->getName()] = $requestClass;
        }

        // Import the new request classes
        if ($classesToImport) {
            $importedClasses = array_map(fn ($useDeclaration) => $useDeclaration->getFullName(), $useDeclarations);
            $classesToImport = array_filter(
                $classesToImport,
                fn ($requestClass) => !isset($importedClasses[$requestClass->getName()])
            );
            $requestClassImportTokens = array_map(
                fn ($requestClass) => $requestClass->getImportTokens(),
                array_values($classesToImport)
            );
            $tokens->insertAt($importStart, array_merge(...$requestClassImportTokens));
            // Ensure new imports are in the correct order
            $orderFixer = new OrderedImportsFixer();
            $orderFixer->fix($file, $tokens);
        }
    }