HEX
Server: nginx
System: Linux pool195-106-36.bur.atomicsites.net 6.12.57+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1~bpo12+1 (2025-11-17) x86_64
User: (0)
PHP: 8.3.32
Disabled: pcntl_fork
Upload Files
File: //usr/local/share/elastic_apm/latest/ElasticApm/Impl/AutoInstrument/MemcacheAutoInstrumentation.php
<?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.
 */

/** @noinspection PhpComposerExtensionStubsInspection */

declare(strict_types=1);

namespace Elastic\Apm\Impl\AutoInstrument;

use Elastic\Apm\Impl\AutoInstrument\Util\AutoInstrumentationUtil;
use Elastic\Apm\Impl\Log\LogCategory;
use Elastic\Apm\Impl\Log\Logger;
use Elastic\Apm\Impl\Tracer;
use Memcache;
/**
 * Code in this file is part of implementation internals and thus it is not covered by the backward compatibility.
 *
 * @internal
 */
final class MemcacheAutoInstrumentation extends AutoInstrumentationBase {
    private const CLASS_NAME = 'Memcache';

    /** @var Logger */
    private $logger;

    /** @var AutoInstrumentationUtil */
    private $util;

	private $flush_number = null;

    public function __construct(Tracer $tracer) {
        parent::__construct($tracer);
        $this->logger = $tracer->loggerFactory()->loggerForClass(
            LogCategory::AUTO_INSTRUMENTATION,
            __NAMESPACE__,
            __CLASS__,
            __FILE__
		)->addContext('this', $this);
        $this->util = new AutoInstrumentationUtil($tracer->loggerFactory());
    }

    public function requiresAttachContextToExternalObjects(): bool {
        return true;
    }

    public function name(): string {
        return 'Memcache';
    }

    public function keywords(): array {
        return [ 'Memcache' ];
    }

    /** @inheritDoc */
    public function register(RegistrationContextInterface $ctx): void {
		if ( ! extension_loaded( 'memcache' ) ) {
            return;
        }

        $this->interceptMemcacheMethodToSpan( $ctx, 'get' );
        $this->interceptMemcacheMethodToSpan( $ctx, 'set' );
        $this->interceptMemcacheMethodToSpan( $ctx, 'replace' );
        $this->interceptMemcacheMethodToSpan( $ctx, 'add' );
		$this->interceptMemcacheMethodToSpan( $ctx, 'delete');
		$this->interceptMemcacheMethodToSpan( $ctx, 'increment' );
		$this->interceptMemcacheMethodToSpan( $ctx, 'decrement' );
		$this->interceptMemcacheMethodToSpan( $ctx, 'flush' );
		$this->interceptMemcacheMethodToSpan( $ctx, 'connect' );
		$this->interceptMemcacheMethodToSpan( $ctx, 'close' );
		$this->interceptMemcacheMethodToSpan( $ctx, 'pconnect' );
    }

	private function interceptMemcacheMethodToSpan(RegistrationContextInterface $ctx, string $methodName ): void {
        $ctx->interceptCallsToInternalMethod(
            'Memcache',
            $methodName,
            /**
             * @param ?object $interceptedCallThis
             * @param mixed[] $interceptedCallArgs
             *
             * @return callable
             */
			function ( ?object $interceptedCallThis, array $interceptedCallArgs ) use ($methodName): ?callable {
                if ( ! $this->util->verifyInstanceOf( 'Memcache', $interceptedCallThis ) ) {
                    return null;
				}

				global $wp_object_cache;
				if ( count( $interceptedCallArgs ) > 0 ) {
					$key = $interceptedCallArgs[0];

					if ( defined( 'WP_CACHE_KEY_SALT' ) && WP_CACHE_KEY_SALT ) {
						if ( 0 === strpos( $key, WP_CACHE_KEY_SALT ) ) {
							$key = substr( $key, strlen( WP_CACHE_KEY_SALT ) + 1 );
						}
					}

					if ( ! empty( $wp_object_cache ) && is_object( $wp_object_cache ) && 'WP_Object_Cache' === get_class( $wp_object_cache ) ) {
						if ( null === $this->flush_number && false !== strpos( $key, 'WP_Object_Cache:flush_number_v' ) ) {
							$this->flush_number = false;
						} elseif ( false === $this->flush_number ) {
							$this->flush_number = sprintf( '%d', $wp_object_cache->get_blog_flush_number() );
							$key = substr( $key, strlen( $this->flush_number ) + 1 );
						} else if ( 0 === strpos( $key, $this->flush_number ) ) {
							$key = substr( $key, strlen( $this->flush_number ) + 1 );
						}
					}
					
					if ( defined( 'WP_CACHE_KEY_SALT' ) && WP_CACHE_KEY_SALT ) {
						global $table_prefix;
						if ( 0 === strpos( $key, '_:' ) ) {
							$key = substr( $key, 2 );
						}
						if ( strlen( $table_prefix ) > 0 && 0 === strpos( $key, $table_prefix ) ) {
							$key = substr( $key, strlen( $table_prefix) + 1 );
						}
					}
					
					$span = AutoInstrumentationUtil::beginCurrentSpan(
						sprintf( 'memcache::%s %s', $methodName, $key ),
						'Memcache',
						$methodName,
						sprintf( '%s (%s)', $key, $interceptedCallArgs[0] )
					);
				} else {
					$span = AutoInstrumentationUtil::beginCurrentSpan(
						sprintf( 'memcache::%s', $methodName ),
						'Memcache',
						$methodName
					);
				}
				return AutoInstrumentationUtil::createInternalFuncPostHookFromEndSpan( $span );
			}
        );
    }
}