private function interceptMySQLiConstructConnect()

in agent/php/ElasticApm/Impl/AutoInstrument/MySQLiAutoInstrumentation.php [123:258]


    private function interceptMySQLiConstructConnect(RegistrationContextInterface $ctx): void
    {
        /**
         * @param ?string $className
         * @param string  $funcName
         * @param ?object $interceptedCallThis
         * @param array   $interceptedCallArgs
         *
         * @return null|callable(int, bool, mixed): void
         */
        $preHook = function (
            ?string $className,
            string $funcName,
            ?object $interceptedCallThis,
            array $interceptedCallArgs
        ): ?callable {
            // function mysqli_connect(
            //      $host = null,         // <- $interceptedCallArgs[0]
            //      $username = null,     // <- $interceptedCallArgs[1]
            //      $password = null,     // <- $interceptedCallArgs[2]
            //      $database = null,     // <- $interceptedCallArgs[3]
            //      $port = null,         // <- $interceptedCallArgs[4]
            //      $socket = null        // <- $interceptedCallArgs[5]
            // );
            //
            // public function __construct (
            //      $host = null,         // <- $interceptedCallArgs[0]
            //      $username = null,     // <- $interceptedCallArgs[1]
            //      $passwd = null,       // <- $interceptedCallArgs[2]
            //      $database = null,     // <- $interceptedCallArgs[3]
            //      $port = null,         // <- $interceptedCallArgs[4]
            //      $socket = null        // <- $interceptedCallArgs[5]
            //  );

            /** @var ?mysqli $mysqliObj */
            $mysqliObj = null;

            if ($interceptedCallThis !== null) {
                if (!$this->util->verifyInstanceOf(mysqli::class, $interceptedCallThis)) {
                    return null;
                }
                /** @var mysqli $interceptedCallThis */
                $mysqliObj = $interceptedCallThis;
            }

            /** @var ?string $dbName */
            $dbName = null;
            if (count($interceptedCallArgs) >= 4) {
                $fourthArg = $interceptedCallArgs[3];
                if ($fourthArg !== null) {
                    if (is_string($fourthArg)) {
                        $dbName = $fourthArg;
                    } else {
                        ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__))
                        && $loggerProxy->log(
                            'Expected 4th argument to be database name but it is not a string.',
                            [
                                'className' => $className,
                                'funcName' => $funcName,
                                '4th argument type' => DbgUtil::getType($fourthArg),
                                '4th argument' => $this->logger->possiblySecuritySensitive($fourthArg),
                                'interceptedCallArgs' => $this->logger->possiblySecuritySensitive($interceptedCallArgs),
                            ]
                        );
                    }
                }
            }

            return AutoInstrumentationUtil::createInternalFuncPostHookFromEndSpan(
                self::beginSpan($className, $funcName, $dbName, /* statement: */ null),
                /**
                 * doBeforeSpanEnd
                 *
                 * @param bool  $hasExitedByException
                 * @param mixed $returnValueOrThrown
                 */
                function (bool $hasExitedByException, $returnValueOrThrown) use ($mysqliObj, $dbName): void {
                    if ($hasExitedByException) {
                        return;
                    }
                    if ($mysqliObj == null) {
                        if (!$this->util->verifyInstanceOf(mysqli::class, $returnValueOrThrown)) {
                            return;
                        }
                        /** @var mysqli $returnValueOrThrown */
                        $mysqliObj = $returnValueOrThrown;
                    }
                    $this->mapPerObject->set(
                        $mysqliObj,
                        DbAutoInstrumentationUtil::PER_OBJECT_KEY_DB_NAME,
                        $dbName
                    );
                }
            );
        };

        $funcName = 'mysqli_connect';
        $ctx->interceptCallsToInternalFunction(
            $funcName,
            /**
             * @param mixed[] $interceptedCallArgs
             *
             * @return null|callable(int, bool, mixed): mixed
             */
            function (array $interceptedCallArgs) use ($preHook, $funcName): ?callable {
                return $preHook(
                    null /* <- className */,
                    $funcName,
                    null /* <- interceptedCallThis */,
                    $interceptedCallArgs
                );
            }
        );

        $className = self::MYSQLI_CLASS_NAME;
        $methodName = '__construct';
        $ctx->interceptCallsToInternalMethod(
            $className,
            $methodName,
            function (
                ?object $interceptedCallThis,
                array $interceptedCallArgs
            ) use (
                $preHook,
                $className,
                $methodName
            ): ?callable {
                return $preHook(
                    $className,
                    $methodName,
                    $interceptedCallThis,
                    $interceptedCallArgs
                );
            }
        );
    }