Heray-Was-Here
Server : Apache
System : Linux dal-shared-66.hostwindsdns.com 4.18.0-513.24.1.lve.1.el8.x86_64 #1 SMP Thu May 9 15:10:09 UTC 2024 x86_64
User : krnuyqrm ( 1183)
PHP Version : 7.4.33
Disable Function : NONE
Directory :  /opt/alt/php-xray/php/profiler/classes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //opt/alt/php-xray/php/profiler/classes/xray-profiler-collector-shortcodes.php
<?php

/**
 * Copyright (с) Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2022 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * https://www.cloudlinux.com/legal/
 */

namespace XrayProfiler;

use Closure;
use Exception;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionObject;

if (!class_exists('\XrayProfiler\CollectorShortcode')) {
    class CollectorShortcode extends Collector
    {
        /**
         * @var int
         */
        private $next_id = 0;

        /**
         * @var array<string,array<int>>
         */
        public $tag_id = array();

        /**
         * @var array<string,array<string>>
         */
        private $parsed_handlers = array();

        /**
         * @var self|null
         */
        private static $instance = null;

        private function __construct()
        {
        }

        private function __clone()
        {
        }

        /**
         * @return self
         */
        public static function instance()
        {
            if (is_null(self::$instance)) {
                self::$instance = new self();
                self::$instance->clean();
            }

            return self::$instance;
        }

        /**
         * @return int
         */
        public function getNextId()
        {
            return $this->next_id;
        }

        /**
         * @return int
         */
        public function incrNextId()
        {
            return $this->next_id++;
        }

        /**
         * @param string $tag
         * @param int $id
         * @return void
         */
        public function setTagId($tag, $id)
        {
            $this->tag_id[$tag][] = $id;
        }

        /**
         * @param string $tag
         * @param bool $replace
         * @return int
         */
        public function popTagId($tag, $replace = true)
        {
            $id = 0;

            if (array_key_exists($tag, $this->tag_id)) {
                $ids = $this->tag_id[$tag];

                if (!is_null($last = array_pop($ids))) {
                    $id = $last;
                }

                if ($replace === true) {
                    $this->tag_id[$tag] = $ids;
                }
            }

            return $id;
        }

        /**
         * @param string $tag
         *
         * @return array<string,string>|null
         */
        public function getParsedHandlers($tag)
        {
            if (array_key_exists($tag, $this->parsed_handlers)) {
                return $this->parsed_handlers[$tag];
            }

            return null;
        }

        /**
         * @param string $tag
         * @param array<string,string> $data
         *
         * @return void
         */
        public function setParsedHandlers($tag, $data)
        {
            $this->parsed_handlers[$tag] = $data;
        }

        /**
         * @param string $tag
         * @param int $id
         *
         * @return array<string,float|int|string|null>|null
         * [
         *     'shortcode_id' => (int)
         *     'handler' => (string)
         *     'name' => (string)
         *     'plugin' => (string)
         *     'duration' => (int)
         *     'attrs_json' => (string)
         *     'timer_start' => (float)
         *]
         */
        public function getShortcode($tag, $id)
        {
            $data = $this->getData();
            if (array_key_exists($tag, $data) && array_key_exists($id, $data[$tag])) {
                return $data[$tag][$id];
            }

            return null;
        }

        /**
         * @param string $tag
         * @param int $id
         * @param array<string,float|int|string|null> $data
         *
         * @return void
         */
        public function setShortcode($tag, $id, $data)
        {
            $shortcodes = $this->getData();
            if (!array_key_exists($tag, $shortcodes)) {
                $shortcodes[$tag] = array();
            }

            $shortcodes[$tag][$id] = $data;

            $this->setData($shortcodes);
        }

        /**
         * @param array|string $attr
         *
         * @return array<int, array<string|mixed>>
         */
        public function prepareAttributes($attr)
        {
            $attrs = array();
            if (is_array($attr) && !empty($attr)) {
                foreach ($attr as $key => $val) {
                    $attrs[] = array(
                        'type' => 'key',
                        'key' => $key,
                        'val' => $this->redactAttributeValue($key, $val),
                    );
                }
            } elseif (is_string($attr) && !empty($attr)) {
                $attrs[] = array(
                    'type' => 'string',
                    'key' => '',
                    'val' => $this->redactAttributeValue('', $attr),
                );
            }

            return $attrs;
        }

        /**
         * Benign attribute names that are NEVER redacted, regardless of any
         * secret-looking substring they may contain (e.g. "author" contains
         * "auth"). Checked first so the secret-token substring match below can
         * be unanchored without re-introducing collisions on presentational
         * attributes.
         *
         * @var array<int,string>
         */
        private static $benign_attr_keys = array(
            'author', 'id', 'ids', 'src', 'url', 'href', 'link', 'class',
            'align', 'size', 'width', 'height', 'title', 'alt', 'name', 'slug',
            'type', 'color', 'style', 'target', 'rel', 'caption', 'label',
            'tag', 'category', 'date',
            // Common presentational keys that collide with a secret-token
            // substring (keyword/keywords -> "key") -- exempt so the substring
            // match below does not over-redact ordinary shortcode telemetry.
            'keyword', 'keywords',
        );

        /**
         * Secret-looking key tokens. Matched as case-insensitive SUBSTRINGS of
         * the (non-allowlisted) attribute name, so glued/camelCase secret keys
         * with no separators (secretkey, accesskey, apitoken, authtoken,
         * passphrase, passkey, userpass, consumerkey, encryptionkey, hmacsig,
         * SecretAccessKey, clientsecret, privatekey, ...) are caught alongside
         * separated variants (api_key, auth_token). Substring matching is
         * deliberate: under-redacting a glued secret name (data exposure) is
         * worse than over-redacting a rare presentational name. The benign
         * allowlist above exempts the common presentational keys that would
         * otherwise collide (author, keyword, ...).
         *
         * @var array<int,string>
         */
        private static $secret_key_tokens = array(
            'secret', 'password', 'passwd', 'pass', 'token', 'apikey',
            'api_key', 'api', 'key', 'auth', 'credential', 'bearer', 'dsn',
            'privatekey', 'accesskey', 'signature', 'sig',
        );

        /**
         * Redact secret-looking shortcode attribute values before they are
         * serialized into attrs_json and exported off-box to telemetry.
         * Attribute names and overall structure are preserved; only values
         * flagged by a non-allowlisted secret-looking key name (substring
         * match), an email address, or a credentialed URL are replaced with a
         * placeholder. Allowlisted presentational keys (id/src/url/author/...)
         * are always kept. Retained values are capped at 256 chars as a
         * backstop; non-scalars are left to the JSON encoder.
         *
         * @param int|string $key
         * @param mixed $val
         *
         * @return mixed
         */
        private function redactAttributeValue($key, $val)
        {
            if (!is_string($val)) {
                return $val;
            }

            $key_str = is_string($key) ? $key : '';
            $key_lc = strtolower($key_str);

            // Benign-key allowlist: presentational attributes are never
            // redacted, which resolves the author/auth substring collision
            // without anchoring the secret-token match below.
            $is_benign = $key_lc !== '' && in_array($key_lc, self::$benign_attr_keys, true);

            if (!$is_benign && $key_lc !== '') {
                // Secret-looking key names: substring match so glued/camelCase
                // keys (secretkey, accesskey, apitoken, passphrase, passkey,
                // userpass, SecretAccessKey, ...) are caught, not just separated
                // variants. The benign allowlist above exempts the common
                // presentational keys that would otherwise collide (keyword/...).
                foreach (self::$secret_key_tokens as $token) {
                    if (strpos($key_lc, $token) !== false) {
                        return '[REDACTED]';
                    }
                }
            }

            // Value-shape rules apply even to benign-but-non-allowlisted keys,
            // catching secrets hiding under an innocuous attribute name.

            // Email address.
            if (preg_match('/[^\s@]+@[^\s@]+\.[^\s@]+/', $val)) {
                return '[REDACTED]';
            }

            // URL embedding credentials (scheme://user:pass@host).
            if (preg_match('#[a-z][a-z0-9+.-]*://[^/\s:@]+:[^/\s@]+@#i', $val)) {
                return '[REDACTED]';
            }

            // No bare value-length/entropy redaction: it masks public asset
            // paths, slug ids and long URLs. Glued secret keys are already
            // covered by the substring key match above, so a length heuristic
            // would only re-introduce over-redaction of public URLs/paths.

            // Backstop: cap retained value length to limit accidental leakage.
            if (strlen($val) > 256) {
                return substr($val, 0, 256) . '...[truncated]';
            }

            return $val;
        }

        /**
         * @param string $tag
         * @param object|callable $fn
         *
         * @return array<string,string>
         */
        public function parseHandler($tag, $fn)
        {
            if ($cache = $this->getParsedHandlers($tag)) {
                return $cache;
            }

            $handler = '';
            $plugin = '';

            if (
                class_exists('ReflectionClass') &&
                class_exists('ReflectionObject') &&
                class_exists('ReflectionFunction')
            ) {
                try {
                    $parse = array(
                        'path' => '',
                        'handler' => '',
                    );

                    if (is_array($fn)) {
                        // Class::method
                        $parse = $this->parseHandlerArray($fn);
                    } elseif (is_object($fn)) {
                        // Object/Closure/Invoke
                        $parse = $this->parseHandlerObject($fn);
                    } elseif (is_string($fn)) {
                        // Function string
                        $parse = $this->parseHandlerString($fn);
                    }

                    $path = $parse['path'];
                    $handler = $parse['handler'];

                    if (!empty($path)) {
                        $plugin = $this->pluginOrThemeName($path);
                    }

                    if (empty($handler)) {
                        xray_profiler_log(
                            E_USER_NOTICE,
                            "Can't parse handler: " . print_r($fn, true),
                            __FILE__,
                            __LINE__
                        );
                    }
                } catch (Exception $e) {
                    $message = "Catch Reflection error Exception: "
                               . $e->getMessage()
                               . ', with handler: '
                               . print_r($fn, true);
                    xray_profiler_log(E_USER_WARNING, $message, $e->getFile(), $e->getLine());
                }
            } else {
                xray_profiler_log(E_USER_NOTICE, "Can't parse handler, Reflection doesn't exists", __FILE__, __LINE__);
            }

            $result = array(
                'handler' => $handler,
                'plugin' => $plugin,
            );

            $this->setParsedHandlers($tag, $result);

            return $result;
        }

        /**
         * @param  array<mixed>  $fn
         *
         * @return array<string,string>
         * @throws ReflectionException
         */
        public function parseHandlerArray($fn)
        {
            $path = '';
            $handler = '';
            $fn = array_values($fn);

            if (!empty($fn)) {
                if (is_object($fn[0])) {
                    $parse = $this->parseHandlerObject($fn[0]);
                } elseif (is_string($fn[0])) {
                    $parse = $this->parseHandlerString($fn[0]);
                }

                if (!empty($parse['handler'])) {
                    $path = $parse['path'];
                    $handler = $parse['handler'];

                    if (array_key_exists(1, $fn) && is_string($fn[1])) {
                        $handler .= '::' . $fn[1];
                    }
                }
            }

            return array(
                'path' => $path,
                'handler' => $handler,
            );
        }

        /**
         * @param object $fn
         *
         * @return array<string,string>
         * @throws ReflectionException
         */
        public function parseHandlerObject($fn)
        {
            $ref = new ReflectionObject($fn);
            $name = $ref->getName();
            $path = '';
            $handler = '';

            if (!empty($name)) {
                $handler = $name;
                $file = $ref->getFileName();
                if (!empty($file)) {
                    $path = $file;
                }

                if ($name == 'Closure' && $fn instanceof Closure) {
                    $ref = new ReflectionFunction($fn);
                    $name = $ref->getName();
                    if (!empty($name)) {
                        $file = $ref->getFileName();
                        if (!empty($file)) {
                            $path = $file;
                        }
                    }
                }
            }

            return array(
                'path' => $path,
                'handler' => $handler,
            );
        }

        /**
         * @param string $fn
         *
         * @return array<string,string>
         */
        public function parseHandlerString($fn)
        {
            $path = '';
            $handler = '';
            $class = $fn;
            $ref = null;

            if (strpos($fn, '::') !== false) {
                $parts = explode('::', $fn);
                $class = array_shift($parts);
            }

            if (class_exists($class)) {
                $ref = new ReflectionClass($class);
            } elseif (function_exists($class)) {
                $ref = new ReflectionFunction($class);
            }

            if ($ref) {
                $handler = $fn;
                $name = $ref->getName();
                if (! empty($name)) {
                    $file = $ref->getFileName();
                    if (! empty($file)) {
                        $path = $file;
                    }
                }
            }

            return array(
                'path' => $path,
                'handler' => $handler,
            );
        }

        /**
         * @param string $path
         *
         * @return string
         */
        public function pluginOrThemeName($path)
        {
            $name = '';
            $pattern = '/wp-content\/(mu-plugins\/|plugins\/|themes\/)(.*?)(\/|.php)/is';
            preg_match($pattern, $path, $matches);
            if (!empty($matches) && array_key_exists(2, $matches)) {
                if (strpos($matches[1], 'themes') === 0) {
                    $name = 'Theme: ';
                }

                $name .= $matches[2];
            }

            return $name;
        }

        /**
         * @param false|string $return
         * @param string $tag
         * @param array|string $attr
         * @param array $m
         *
         * @return false|string
         */
        public function preDoShortcodeTagEarly($return, $tag, $attr, $m)
        {
            $this->incrNextId();

            $id = $this->getNextId();
            $this->setTagId($tag, $id);

            $attrs = $this->prepareAttributes($attr);
            $parseHandler = $this->parseHandler($tag, $GLOBALS['shortcode_tags'][$tag]);
            $handler = $parseHandler['handler'];
            $plugin = $parseHandler['plugin'];

            if (empty($attrs) || !($attrs_json = json_encode($attrs))) {
                $attrs_json = 'null';
            }

            $data = [
                'shortcode_id' => $id,
                'handler' => empty($handler) ? 'Unknown' : $handler,
                'name' => $tag,
                'plugin' => empty($plugin) ? 'Unknown' : $plugin,
                'duration' => 0,
                'attrs_json' => $attrs_json,
                'timer_start' => microtime(true),
            ];

            $this->setShortcode($tag, $id, $data);

            return $return;
        }

        /**
         * @param false|string $return
         * @param string $tag
         * @param array|string $attr
         * @param array $m
         *
         * @return false|string
         */
        public function preDoShortcodeTagLate($return, $tag, $attr, $m)
        {
            if ($return !== false) {
                $this->popTagId($tag);
            }

            return $return;
        }

        /**
         * @param false|string $output
         * @param string $tag
         * @param array|string $attr
         * @param array $m
         *
         * @return false|string
         */
        public function doShortcodeTagLate($output, $tag, $attr, $m)
        {
            $timer_end = microtime(true);

            $id = $this->popTagId($tag);
            $shortcode = $this->getShortcode($tag, $id);

            if (empty($shortcode)) {
                xray_profiler_log(
                    E_USER_NOTICE,
                    'Can\'t find shortcode ' . $tag . ' id: ' . $id . ' in ' . __METHOD__,
                    __FILE__,
                    __LINE__
                );
            } else {
                $timer_start = floatval($shortcode['timer_start']);
                $diff = $timer_end - $timer_start;
                $duration = number_format($diff * 1000000, 0, '', '');
                $shortcode['duration'] = $duration;

                unset($shortcode['timer_start']);

                $tag = $shortcode['name'] . '';

                $this->setShortcode($tag, $id, $shortcode);
            }

            return $output;
        }

        /**
         * @return array
         */
        public function getXrayData()
        {
            $shortcodes = $this->data;
            $items = array();
            foreach ($shortcodes as $iterations) {
                $items = array_merge($items, $iterations);
            }

            usort($items, function ($a, $b) {
                if ($a['duration'] == $b['duration']) {
                    return 0;
                }

                return ($a['duration'] < $b['duration']) ? 1 : -1;
            });

            foreach ($items as &$item) {
                if (array_key_exists('timer_start', $item)) {
                    unset($item['timer_start']);
                }
            }

            return array_slice($items, 0, 20);
        }

        /**
         * @return $this
         */
        public function clean()
        {
            $this->data = array();
            $this->next_id = 0;
            $this->tag_id = array();
            $this->parsed_handlers = array();

            return $this;
        }
    }
}

Hry