agent/php/ElasticApm/Impl/AutoInstrument/Util/DbConnectionStringParser.php (199 lines of code) (raw):

<?php /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ declare(strict_types=1); namespace Elastic\Apm\Impl\AutoInstrument\Util; use Elastic\Apm\Impl\Constants; use Elastic\Apm\Impl\Log\LogCategory; use Elastic\Apm\Impl\Log\LoggableInterface; use Elastic\Apm\Impl\Log\LoggableTrait; use Elastic\Apm\Impl\Log\Logger; use Elastic\Apm\Impl\Log\LoggerFactory; /** * Code in this file is part of implementation internals and thus it is not covered by the backward compatibility. * * @internal */ final class DbConnectionStringParser implements LoggableInterface { use LoggableTrait; /** @var Logger */ private $logger; public function __construct(LoggerFactory $loggerFactory) { $this->logger = $loggerFactory->loggerForClass( LogCategory::AUTO_INSTRUMENTATION, __NAMESPACE__, __CLASS__, __FILE__ )->addContext('this', $this); } public function parse(string $dbConnectionString, ?string &$dbType, ?string &$dbName): void { $localLogger = $this->logger->inherit()->addContext( 'dbConnectionString', $this->logger->possiblySecuritySensitive($dbConnectionString) ); $dbType = null; $dbName = null; $dbTypePrefix = ''; $posAfterDbTypePrefix = 0; $isDbTypePrefixFound = self::extractDbTypePrefix( $dbConnectionString, /* ref */ $dbTypePrefix, /* ref */ $posAfterDbTypePrefix, $localLogger ); if (!$isDbTypePrefixFound) { ($loggerProxy = $localLogger->ifWarningLevelEnabled(__LINE__, __FUNCTION__)) && $loggerProxy->log('DB type prefix not found in connection string'); return; } $localLogger = $localLogger->addContext( 'dbTypePrefix', $this->logger->possiblySecuritySensitive($dbTypePrefix) ); $dbNameKey = 'dbname'; /** @var ?string $dsnKey */ $dsnKey = null; switch ($dbTypePrefix) { case 'cubrid': $dbType = Constants::SPAN_SUBTYPE_CUBRID; break; case 'dblib': case 'mssql': $dbType = Constants::SPAN_SUBTYPE_MSSQL; break; case 'firebird': $dbType = Constants::SPAN_SUBTYPE_FIREBIRD; break; case 'ibm': $dbType = Constants::SPAN_SUBTYPE_IBM_DB2; $dbNameKey = 'database'; $dsnKey = 'DSN'; break; case 'informix': $dbType = Constants::SPAN_SUBTYPE_INFORMIX; $dbNameKey = 'database'; $dsnKey = 'DSN'; break; case 'mysql': $dbType = Constants::SPAN_SUBTYPE_MYSQL; break; case 'oci': $dbType = Constants::SPAN_SUBTYPE_ORACLE; break; case 'odbc': $dbType = Constants::SPAN_SUBTYPE_ODBC; self::extractDbNameODBC($dbConnectionString, $posAfterDbTypePrefix, /* ref */ $dbName); return; case 'pgsql': $dbType = Constants::SPAN_SUBTYPE_POSTGRESQL; break; case 'sqlite': $dbType = Constants::SPAN_SUBTYPE_SQLITE; self::extractDbNameSQLite($dbConnectionString, $posAfterDbTypePrefix, /* ref */ $dbName); return; case 'sqlsrv': $dbType = Constants::SPAN_SUBTYPE_MSSQL; $dbNameKey = 'database'; break; default: ($loggerProxy = $localLogger->ifWarningLevelEnabled(__LINE__, __FUNCTION__)) && $loggerProxy->log( 'Unknown DB type in connection string prefix', ['dbTypePrefix' => $dbTypePrefix] ); return; } $localLogger->addContext('dbType', $dbType); self::extractDbName( $dbConnectionString, $posAfterDbTypePrefix, $dbNameKey, $dsnKey, $dbName /* <- ref */, $localLogger ); } private static function extractDbTypePrefix( string $dbConnectionString, /* ref */ string &$dbTypePrefix, /* ref */ int &$posAfterDbTypePrefix, Logger $localLogger ): bool { $colonPos = strpos($dbConnectionString, ':'); if ($colonPos === false) { ($loggerProxy = $localLogger->ifWarningLevelEnabled(__LINE__, __FUNCTION__)) && $loggerProxy->log('Colon (\':\') not found in DB connection string'); return false; } $dbTypePrefix = substr($dbConnectionString, 0, $colonPos); $posAfterDbTypePrefix = $colonPos + 1; return true; } /** * @param string $list * @param int $startIndex * @param string $separator * * @return iterable<string> */ private static function iterateList(string $list, int $startIndex, string $separator): iterable { $nextPos = $startIndex; $listLen = strlen($list); while ($nextPos < $listLen) { $currentPos = $nextPos; $sepPos = strpos($list, $separator, $currentPos); $nextPos = (($sepPos === false) ? $listLen : $sepPos) + 1; yield substr($list, $currentPos, $nextPos - $currentPos - 1); } } private static function splitKeyValuePair( string $keyValuePair, string &$key, string &$value, Logger $localLogger ): bool { $sepPos = strpos($keyValuePair, '='); if ($sepPos === false) { ($loggerProxy = $localLogger->ifTraceLevelEnabled(__LINE__, __FUNCTION__)) && $loggerProxy->log('Key-value pair separator not found', ['keyValuePair' => $keyValuePair]); return false; } $key = trim(substr($keyValuePair, /* offset: */ 0, $sepPos)); $value = trim(substr($keyValuePair, /* offset: */ $sepPos + 1)); return true; } private static function extractDbName( string $dbConnectionString, int $startIndex, string $dbNameKey, ?string $dsnKey, ?string &$dbName, Logger $localLogger ): void { $localLogger->addContext('dbNameKey', $dbNameKey); foreach (self::iterateList($dbConnectionString, $startIndex, /* separator: */ ';') as $keyValuePair) { $key = ''; $value = ''; if (!self::splitKeyValuePair($keyValuePair, /* ref */ $key, /* ref */ $value, $localLogger)) { continue; } if (strcasecmp($key, $dbNameKey) == 0) { $dbName = $value; break; } elseif (($dsnKey !== null) && (strcasecmp($key, $dsnKey) == 0)) { $dbName = $dsnKey . '=' . $value; break; } } if ($dbName === null) { ($loggerProxy = $localLogger->ifTraceLevelEnabled(__LINE__, __FUNCTION__)) && $loggerProxy->log( 'Key-value pair with DB name key not found' ); } } private static function extractDbNameSQLite(string $dbConnectionString, int $startIndex, ?string &$dbName): void { if ($startIndex === strlen($dbConnectionString)) { $dbName = Constants::SQLITE_TEMP_DB; return; } if ($dbConnectionString === 'sqlite::memory:') { $dbName = 'memory'; return; } $dbName = substr($dbConnectionString, $startIndex); } private static function extractDbNameODBC(string $dbConnectionString, int $startIndex, ?string &$dbName): void { $dbName = 'DSN=' . substr($dbConnectionString, $startIndex); } }