![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/codeception/codeception/src/Codeception/ |
<?php declare(strict_types=1); namespace Codeception; use Closure; use Codeception\Lib\ModuleContainer; use Codeception\Step\Argument\FormattedOutput; use Codeception\Step\Meta as MetaStep; use Codeception\Util\Locator; use Exception; use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\MockObject\MockObject; use RuntimeException; use Stringable; abstract class Step implements Stringable { /** * @var int */ public const DEFAULT_MAX_LENGTH = 200; /** * @var int */ public const STACK_POSITION = 3; public bool $executed = false; protected string|int|null $line = null; protected ?string $file = null; protected string $prefix = 'I'; protected ?MetaStep $metaStep = null; protected bool $failed = false; protected bool $isTry = false; public function __construct(protected string $action, protected array $arguments = []) { } public function saveTrace(): void { $stack = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); if (count($stack) <= self::STACK_POSITION) { return; } $traceLine = $stack[self::STACK_POSITION - 1]; if (!isset($traceLine['file'])) { return; } $this->file = $traceLine['file']; $this->line = $traceLine['line']; $this->addMetaStep($traceLine, $stack); } private function isTestFile(string $file) { return preg_match('#[^\\' . DIRECTORY_SEPARATOR . '](Cest|Cept|Test).php$#', $file); } public function getName(): string { $class = explode('\\', __CLASS__); return end($class); } public function getAction(): string { return $this->action; } public function getFilePath(): ?string { if ($this->file) { return codecept_relative_path($this->file); } return null; } public function getLineNumber(): ?int { if ($this->line) { return $this->line; } return null; } public function hasFailed(): bool { return $this->failed; } public function getArguments(): array { return $this->arguments; } public function getArgumentsAsString(int $maxLength = self::DEFAULT_MAX_LENGTH): string { $arguments = $this->arguments; $argumentCount = count($arguments); $totalLength = $argumentCount - 1; // count separators before adding length of individual arguments foreach ($arguments as $key => $argument) { $stringifiedArgument = $this->stringifyArgument($argument); $arguments[$key] = $stringifiedArgument; $totalLength += mb_strlen($stringifiedArgument, 'utf-8'); } if ($totalLength > $maxLength && $maxLength > 0) { //sort arguments from shortest to longest uasort($arguments, function ($arg1, $arg2): int { $length1 = mb_strlen($arg1, 'utf-8'); $length2 = mb_strlen($arg2, 'utf-8'); if ($length1 === $length2) { return 0; } return ($length1 < $length2) ? -1 : 1; }); $allowedLength = floor(($maxLength - $argumentCount + 1) / $argumentCount); $lengthRemaining = $maxLength; $argumentsRemaining = $argumentCount; foreach ($arguments as $key => $argument) { --$argumentsRemaining; if (mb_strlen($argument, 'utf-8') > $allowedLength) { $arguments[$key] = mb_substr($argument, 0, (int)$allowedLength - 4, 'utf-8') . '...' . mb_substr($argument, -1, 1, 'utf-8'); $lengthRemaining -= ($allowedLength + 1); } else { $lengthRemaining -= (mb_strlen($argument, 'utf-8') + 1); //recalculate allowed length because this argument was short if ($argumentsRemaining > 0) { $allowedLength = floor(($lengthRemaining - $argumentsRemaining + 1) / $argumentsRemaining); } } } //restore original order of arguments ksort($arguments); } return implode(',', $arguments); } protected function stringifyArgument(mixed $argument): string { if (is_string($argument)) { return '"' . strtr($argument, ["\n" => '\n', "\r" => '\r', "\t" => ' ']) . '"'; } elseif (is_resource($argument)) { $argument = (string)$argument; } elseif (is_array($argument)) { foreach ($argument as $key => $value) { if (is_object($value)) { $argument[$key] = $this->getClassName($value); } } } elseif (is_object($argument)) { if ($argument instanceof FormattedOutput) { $argument = $argument->getOutput(); } elseif (method_exists($argument, '__toString')) { $argument = (string)$argument; } elseif ($argument::class === 'Facebook\WebDriver\WebDriverBy') { $argument = Locator::humanReadableString($argument); } elseif ($argument instanceof Constraint) { $argument = $argument->toString(); } else { $argument = $this->getClassName($argument); } } $arg_str = json_encode($argument, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); return str_replace('\"', '"', $arg_str); } protected function getClassName(object $argument): string { if ($argument instanceof Closure) { return Closure::class; } elseif ($argument instanceof MockObject) { $parentClass = get_parent_class($argument); $reflection = new \ReflectionClass($argument); if ($parentClass !== false) { return $this->formatClassName($parentClass); } $interfaces = $reflection->getInterfaceNames(); foreach ($interfaces as $interface) { if (str_starts_with($interface, 'PHPUnit\\')) { continue; } if (str_starts_with($interface, 'Codeception\\')) { continue; } return $this->formatClassName($interface); } } return $this->formatClassName($argument::class); } protected function formatClassName(string $classname): string { return trim($classname, "\\"); } public function getPhpCode(int $maxLength): string { $result = "\${$this->prefix}->" . $this->getAction() . '('; $maxLength = $maxLength - mb_strlen($result, 'utf-8') - 1; return $result . ($this->getHumanizedArguments($maxLength) . ')'); } public function getMetaStep(): ?MetaStep { return $this->metaStep; } public function __toString(): string { $humanizedAction = $this->humanize($this->getAction()); return $humanizedAction . ' ' . $this->getHumanizedArguments(); } public function toString(int $maxLength): string { $humanizedAction = $this->humanize($this->getAction()); $maxLength = $maxLength - mb_strlen($humanizedAction, 'utf-8') - 1; return $humanizedAction . ' ' . $this->getHumanizedArguments($maxLength); } public function getHtml(string $highlightColor = '#732E81'): string { if (empty($this->arguments)) { return sprintf('%s %s', ucfirst($this->prefix), $this->humanize($this->getAction())); } return sprintf('%s %s <span style="color: %s">%s</span>', ucfirst($this->prefix), htmlspecialchars($this->humanize($this->getAction()), ENT_QUOTES | ENT_SUBSTITUTE), $highlightColor, htmlspecialchars($this->getHumanizedArguments(0), ENT_QUOTES | ENT_SUBSTITUTE)); } public function getHumanizedActionWithoutArguments(): string { return $this->humanize($this->getAction()); } public function getHumanizedArguments(int $maxLength = self::DEFAULT_MAX_LENGTH): string { return $this->getArgumentsAsString($maxLength); } protected function clean(string $text): string { return str_replace('\/', '', $text); } protected function humanize(string $text): string { $text = preg_replace('#([A-Z]+)([A-Z][a-z])#', '\\1 \\2', $text); $text = preg_replace('#([a-z\d])([A-Z])#', '\\1 \\2', $text); $text = preg_replace('#\bdont\b#', "don't", $text); return mb_strtolower($text, 'UTF-8'); } /** * @return mixed */ public function run(ModuleContainer $container = null) { $this->executed = true; if ($container === null) { return null; } $activeModule = $container->moduleForAction($this->action); if (!is_callable([$activeModule, $this->action])) { throw new RuntimeException("Action '{$this->action}' can't be called"); } try { $res = call_user_func_array([$activeModule, $this->action], $this->arguments); } catch (Exception $e) { if ($this->isTry) { throw $e; } $this->failed = true; $this->getMetaStep()?->setFailed(true); throw $e; } return $res; } /** * If steps are combined into one method they can be reproduced as meta-step. * We are using stack trace to analyze if steps were called from test, if not - they were called from meta-step. */ protected function addMetaStep(array $step, array $stack): void { if (($this->isTestFile($this->file)) || ($step['class'] == Scenario::class)) { return; } $i = count($stack) - self::STACK_POSITION - 1; // get into test file and retrieve its actual call while (isset($stack[$i])) { $step = $stack[$i]; --$i; if (!isset($step['file']) || !isset($step['function']) || !isset($step['class'])) { continue; } if (!$this->isTestFile($step['file'])) { continue; } // in case arguments were passed by reference, copy args array to ensure dereference. array_values() does not dereference values $this->metaStep = new Step\Meta($step['function'], array_map(fn ($i) => $i, array_values($step['args']))); $this->metaStep->setTraceInfo($step['file'], $step['line']); // page objects or other classes should not be included with "I" if (!in_array(Actor::class, class_parents($step['class']))) { if (isset($step['object'])) { $this->metaStep->setPrefix($step['object']::class . ':'); return; } $this->metaStep->setPrefix($step['class'] . ':'); } return; } } public function setMetaStep(?MetaStep $metaStep): void { $this->metaStep = $metaStep; } public function getPrefix(): string { return $this->prefix . ' '; } }