Mini Shell
<?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;
}
}
}
Zerion Mini Shell 1.0