/
opt
/
alt
/
php-xray
/
php
/
profiler
/
classes
/
Upload Filee
HOME
<?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' => $val, ); } } elseif (is_string($attr) && !empty($attr)) { $attrs[] = array( 'type' => 'string', 'key' => '', 'val' => $attr, ); } return $attrs; } /** * @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; } } }