One Hat Cyber Team
Your IP:
216.73.216.30
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
self
/
root
/
proc
/
thread-self
/
cwd
/
Edit File:
Util.tar
UrlEncoder.php 0000644 00000005073 15107376177 0007342 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; use League\CommonMark\Exception\UnexpectedEncodingException; /** * @psalm-immutable */ final class UrlEncoder { private const ENCODE_CACHE = ['%00', '%01', '%02', '%03', '%04', '%05', '%06', '%07', '%08', '%09', '%0A', '%0B', '%0C', '%0D', '%0E', '%0F', '%10', '%11', '%12', '%13', '%14', '%15', '%16', '%17', '%18', '%19', '%1A', '%1B', '%1C', '%1D', '%1E', '%1F', '%20', '!', '%22', '#', '$', '%25', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '%3C', '=', '%3E', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '%5B', '%5C', '%5D', '%5E', '_', '%60', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '%7B', '%7C', '%7D', '~', '%7F']; /** * @throws UnexpectedEncodingException if a non-UTF-8-compatible encoding is used * * @psalm-pure */ public static function unescapeAndEncode(string $uri): string { // Optimization: if the URL only includes characters we know will be kept as-is, then just return the URL as-is. if (\preg_match('/^[A-Za-z0-9~!@#$&*()\-_=+;:,.\/?]+$/', $uri)) { return $uri; } if (! \mb_check_encoding($uri, 'UTF-8')) { throw new UnexpectedEncodingException('Unexpected encoding - UTF-8 or ASCII was expected'); } $result = ''; $chars = \mb_str_split($uri, 1, 'UTF-8'); $l = \count($chars); for ($i = 0; $i < $l; $i++) { $code = $chars[$i]; if ($code === '%' && $i + 2 < $l) { if (\preg_match('/^[0-9a-f]{2}$/i', $chars[$i + 1] . $chars[$i + 2]) === 1) { $result .= '%' . $chars[$i + 1] . $chars[$i + 2]; $i += 2; continue; } } if (\ord($code) < 128) { $result .= self::ENCODE_CACHE[\ord($code)]; continue; } $result .= \rawurlencode($code); } return $result; } } Html5EntityDecoder.php 0000644 00000003427 15107376177 0010755 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; /** * @psalm-immutable */ final class Html5EntityDecoder { /** * @psalm-pure */ public static function decode(string $entity): string { if (\substr($entity, -1) !== ';') { return $entity; } if (\substr($entity, 0, 2) === '&#') { if (\strtolower(\substr($entity, 2, 1)) === 'x') { return self::fromHex(\substr($entity, 3, -1)); } return self::fromDecimal(\substr($entity, 2, -1)); } return \html_entity_decode($entity, \ENT_QUOTES | \ENT_HTML5, 'UTF-8'); } /** * @param mixed $number * * @psalm-pure */ private static function fromDecimal($number): string { // Only convert code points within planes 0-2, excluding NULL // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found if (empty($number) || $number > 0x2FFFF) { return self::fromHex('fffd'); } $entity = '&#' . $number . ';'; $converted = \mb_decode_numericentity($entity, [0x0, 0x2FFFF, 0, 0xFFFF], 'UTF-8'); if ($converted === $entity) { return self::fromHex('fffd'); } return $converted; } /** * @psalm-pure */ private static function fromHex(string $hexChars): string { return self::fromDecimal(\hexdec($hexChars)); } } RegexHelper.php 0000644 00000024024 15107376177 0007507 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; use League\CommonMark\Exception\InvalidArgumentException; use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock; /** * Provides regular expressions and utilities for parsing Markdown * * All of the PARTIAL_ regex constants assume that they'll be used in case-insensitive searches * All other complete regexes provided by this class (either via constants or methods) will have case-insensitivity enabled. * * @phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found * * @psalm-immutable */ final class RegexHelper { // Partial regular expressions (wrap with `/` on each side and add the case-insensitive `i` flag before use) public const PARTIAL_ENTITY = '&(?:#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});'; public const PARTIAL_ESCAPABLE = '[!"#$%&\'()*+,.\/:;<=>?@[\\\\\]^_`{|}~-]'; public const PARTIAL_ESCAPED_CHAR = '\\\\' . self::PARTIAL_ESCAPABLE; public const PARTIAL_IN_DOUBLE_QUOTES = '"(' . self::PARTIAL_ESCAPED_CHAR . '|[^"\x00])*"'; public const PARTIAL_IN_SINGLE_QUOTES = '\'(' . self::PARTIAL_ESCAPED_CHAR . '|[^\'\x00])*\''; public const PARTIAL_IN_PARENS = '\\((' . self::PARTIAL_ESCAPED_CHAR . '|[^)\x00])*\\)'; public const PARTIAL_REG_CHAR = '[^\\\\()\x00-\x20]'; public const PARTIAL_IN_PARENS_NOSP = '\((' . self::PARTIAL_REG_CHAR . '|' . self::PARTIAL_ESCAPED_CHAR . '|\\\\)*\)'; public const PARTIAL_TAGNAME = '[a-z][a-z0-9-]*'; public const PARTIAL_BLOCKTAGNAME = '(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)'; public const PARTIAL_ATTRIBUTENAME = '[a-z_:][a-z0-9:._-]*'; public const PARTIAL_UNQUOTEDVALUE = '[^"\'=<>`\x00-\x20]+'; public const PARTIAL_SINGLEQUOTEDVALUE = '\'[^\']*\''; public const PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]*"'; public const PARTIAL_ATTRIBUTEVALUE = '(?:' . self::PARTIAL_UNQUOTEDVALUE . '|' . self::PARTIAL_SINGLEQUOTEDVALUE . '|' . self::PARTIAL_DOUBLEQUOTEDVALUE . ')'; public const PARTIAL_ATTRIBUTEVALUESPEC = '(?:' . '\s*=' . '\s*' . self::PARTIAL_ATTRIBUTEVALUE . ')'; public const PARTIAL_ATTRIBUTE = '(?:' . '\s+' . self::PARTIAL_ATTRIBUTENAME . self::PARTIAL_ATTRIBUTEVALUESPEC . '?)'; public const PARTIAL_OPENTAG = '<' . self::PARTIAL_TAGNAME . self::PARTIAL_ATTRIBUTE . '*' . '\s*\/?>'; public const PARTIAL_CLOSETAG = '<\/' . self::PARTIAL_TAGNAME . '\s*[>]'; public const PARTIAL_OPENBLOCKTAG = '<' . self::PARTIAL_BLOCKTAGNAME . self::PARTIAL_ATTRIBUTE . '*' . '\s*\/?>'; public const PARTIAL_CLOSEBLOCKTAG = '<\/' . self::PARTIAL_BLOCKTAGNAME . '\s*[>]'; public const PARTIAL_HTMLCOMMENT = '<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->'; public const PARTIAL_PROCESSINGINSTRUCTION = '[<][?][\s\S]*?[?][>]'; public const PARTIAL_DECLARATION = '<![A-Z]+' . '\s+[^>]*>'; public const PARTIAL_CDATA = '<!\[CDATA\[[\s\S]*?]\]>'; public const PARTIAL_HTMLTAG = '(?:' . self::PARTIAL_OPENTAG . '|' . self::PARTIAL_CLOSETAG . '|' . self::PARTIAL_HTMLCOMMENT . '|' . self::PARTIAL_PROCESSINGINSTRUCTION . '|' . self::PARTIAL_DECLARATION . '|' . self::PARTIAL_CDATA . ')'; public const PARTIAL_HTMLBLOCKOPEN = '<(?:' . self::PARTIAL_BLOCKTAGNAME . '(?:[\s\/>]|$)' . '|' . '\/' . self::PARTIAL_BLOCKTAGNAME . '(?:[\s>]|$)' . '|' . '[?!])'; public const PARTIAL_LINK_TITLE = '^(?:"(' . self::PARTIAL_ESCAPED_CHAR . '|[^"\x00])*"' . '|' . '\'(' . self::PARTIAL_ESCAPED_CHAR . '|[^\'\x00])*\'' . '|' . '\((' . self::PARTIAL_ESCAPED_CHAR . '|[^()\x00])*\))'; public const REGEX_PUNCTUATION = '/^[\x{2000}-\x{206F}\x{2E00}-\x{2E7F}\p{Pc}\p{Pd}\p{Pe}\p{Pf}\p{Pi}\p{Po}\p{Ps}\\\\\'!"#\$%&\(\)\*\+,\-\.\\/:;<=>\?@\[\]\^_`\{\|\}~]/u'; public const REGEX_UNSAFE_PROTOCOL = '/^javascript:|vbscript:|file:|data:/i'; public const REGEX_SAFE_DATA_PROTOCOL = '/^data:image\/(?:png|gif|jpeg|webp)/i'; public const REGEX_NON_SPACE = '/[^ \t\f\v\r\n]/'; public const REGEX_WHITESPACE_CHAR = '/^[ \t\n\x0b\x0c\x0d]/'; public const REGEX_UNICODE_WHITESPACE_CHAR = '/^\pZ|\s/u'; public const REGEX_THEMATIC_BREAK = '/^(?:\*[ \t]*){3,}$|^(?:_[ \t]*){3,}$|^(?:-[ \t]*){3,}$/'; public const REGEX_LINK_DESTINATION_BRACES = '/^(?:<(?:[^<>\\n\\\\\\x00]|\\\\.)*>)/'; /** * @psalm-pure */ public static function isEscapable(string $character): bool { return \preg_match('/' . self::PARTIAL_ESCAPABLE . '/', $character) === 1; } /** * @psalm-pure */ public static function isLetter(?string $character): bool { if ($character === null) { return false; } return \preg_match('/[\pL]/u', $character) === 1; } /** * Attempt to match a regex in string s at offset offset * * @psalm-param non-empty-string $regex * * @return int|null Index of match, or null * * @psalm-pure */ public static function matchAt(string $regex, string $string, int $offset = 0): ?int { $matches = []; $string = \mb_substr($string, $offset, null, 'UTF-8'); if (! \preg_match($regex, $string, $matches, \PREG_OFFSET_CAPTURE)) { return null; } // PREG_OFFSET_CAPTURE always returns the byte offset, not the char offset, which is annoying $charPos = \mb_strlen(\mb_strcut($string, 0, $matches[0][1], 'UTF-8'), 'UTF-8'); return $offset + $charPos; } /** * Functional wrapper around preg_match_all which only returns the first set of matches * * @psalm-param non-empty-string $pattern * * @return string[]|null * * @psalm-pure */ public static function matchFirst(string $pattern, string $subject, int $offset = 0): ?array { if ($offset !== 0) { $subject = \substr($subject, $offset); } \preg_match_all($pattern, $subject, $matches, \PREG_SET_ORDER); if ($matches === []) { return null; } return $matches[0] ?: null; } /** * Replace backslash escapes with literal characters * * @psalm-pure */ public static function unescape(string $string): string { $allEscapedChar = '/\\\\(' . self::PARTIAL_ESCAPABLE . ')/'; $escaped = \preg_replace($allEscapedChar, '$1', $string); \assert(\is_string($escaped)); return \preg_replace_callback('/' . self::PARTIAL_ENTITY . '/i', static fn ($e) => Html5EntityDecoder::decode($e[0]), $escaped); } /** * @internal * * @param int $type HTML block type * * @psalm-param HtmlBlock::TYPE_* $type * * @phpstan-param HtmlBlock::TYPE_* $type * * @psalm-return non-empty-string * * @throws InvalidArgumentException if an invalid type is given * * @psalm-pure */ public static function getHtmlBlockOpenRegex(int $type): string { switch ($type) { case HtmlBlock::TYPE_1_CODE_CONTAINER: return '/^<(?:script|pre|textarea|style)(?:\s|>|$)/i'; case HtmlBlock::TYPE_2_COMMENT: return '/^<!--/'; case HtmlBlock::TYPE_3: return '/^<[?]/'; case HtmlBlock::TYPE_4: return '/^<![A-Z]/i'; case HtmlBlock::TYPE_5_CDATA: return '/^<!\[CDATA\[/i'; case HtmlBlock::TYPE_6_BLOCK_ELEMENT: return '%^<[/]?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[123456]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?:\s|[/]?[>]|$)%i'; case HtmlBlock::TYPE_7_MISC_ELEMENT: return '/^(?:' . self::PARTIAL_OPENTAG . '|' . self::PARTIAL_CLOSETAG . ')\\s*$/i'; default: throw new InvalidArgumentException('Invalid HTML block type'); } } /** * @internal * * @param int $type HTML block type * * @psalm-param HtmlBlock::TYPE_* $type * * @phpstan-param HtmlBlock::TYPE_* $type * * @psalm-return non-empty-string * * @throws InvalidArgumentException if an invalid type is given * * @psalm-pure */ public static function getHtmlBlockCloseRegex(int $type): string { switch ($type) { case HtmlBlock::TYPE_1_CODE_CONTAINER: return '%<\/(?:script|pre|textarea|style)>%i'; case HtmlBlock::TYPE_2_COMMENT: return '/-->/'; case HtmlBlock::TYPE_3: return '/\?>/'; case HtmlBlock::TYPE_4: return '/>/'; case HtmlBlock::TYPE_5_CDATA: return '/\]\]>/'; default: throw new InvalidArgumentException('Invalid HTML block type'); } } /** * @psalm-pure */ public static function isLinkPotentiallyUnsafe(string $url): bool { return \preg_match(self::REGEX_UNSAFE_PROTOCOL, $url) !== 0 && \preg_match(self::REGEX_SAFE_DATA_PROTOCOL, $url) === 0; } } ArrayCollection.php 0000644 00000006624 15107376177 0010375 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; /** * Array collection * * Provides a wrapper around a standard PHP array. * * @internal * * @phpstan-template T * @phpstan-implements \IteratorAggregate<int, T> * @phpstan-implements \ArrayAccess<int, T> */ final class ArrayCollection implements \IteratorAggregate, \Countable, \ArrayAccess { /** * @var array<int, mixed> * @phpstan-var array<int, T> */ private array $elements; /** * Constructor * * @param array<int|string, mixed> $elements * * @phpstan-param array<int, T> $elements */ public function __construct(array $elements = []) { $this->elements = $elements; } /** * @return mixed|false * * @phpstan-return T|false */ public function first() { return \reset($this->elements); } /** * @return mixed|false * * @phpstan-return T|false */ public function last() { return \end($this->elements); } /** * Retrieve an external iterator * * @return \ArrayIterator<int, mixed> * * @phpstan-return \ArrayIterator<int, T> */ #[\ReturnTypeWillChange] public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->elements); } /** * Count elements of an object * * @return int The count as an integer. */ public function count(): int { return \count($this->elements); } /** * Whether an offset exists * * {@inheritDoc} * * @phpstan-param int $offset */ public function offsetExists($offset): bool { return \array_key_exists($offset, $this->elements); } /** * Offset to retrieve * * {@inheritDoc} * * @phpstan-param int $offset * * @phpstan-return T|null */ #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->elements[$offset] ?? null; } /** * Offset to set * * {@inheritDoc} * * @phpstan-param int|null $offset * @phpstan-param T $value */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value): void { if ($offset === null) { $this->elements[] = $value; } else { $this->elements[$offset] = $value; } } /** * Offset to unset * * {@inheritDoc} * * @phpstan-param int $offset */ #[\ReturnTypeWillChange] public function offsetUnset($offset): void { if (! \array_key_exists($offset, $this->elements)) { return; } unset($this->elements[$offset]); } /** * Returns a subset of the array * * @return array<int, mixed> * * @phpstan-return array<int, T> */ public function slice(int $offset, ?int $length = null): array { return \array_slice($this->elements, $offset, $length, true); } /** * @return array<int, mixed> * * @phpstan-return array<int, T> */ public function toArray(): array { return $this->elements; } } LinkParserHelper.php 0000644 00000007652 15107376177 0010517 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; use League\CommonMark\Parser\Cursor; /** * @psalm-immutable */ final class LinkParserHelper { /** * Attempt to parse link destination * * @return string|null The string, or null if no match */ public static function parseLinkDestination(Cursor $cursor): ?string { if ($res = $cursor->match(RegexHelper::REGEX_LINK_DESTINATION_BRACES)) { // Chop off surrounding <..>: return UrlEncoder::unescapeAndEncode( RegexHelper::unescape(\substr($res, 1, -1)) ); } if ($cursor->getCurrentCharacter() === '<') { return null; } $destination = self::manuallyParseLinkDestination($cursor); if ($destination === null) { return null; } return UrlEncoder::unescapeAndEncode( RegexHelper::unescape($destination) ); } public static function parseLinkLabel(Cursor $cursor): int { $match = $cursor->match('/^\[(?:[^\\\\\[\]]|\\\\.){0,1000}\]/'); if ($match === null) { return 0; } $length = \mb_strlen($match, 'UTF-8'); if ($length > 1001) { return 0; } return $length; } public static function parsePartialLinkLabel(Cursor $cursor): ?string { return $cursor->match('/^(?:[^\\\\\[\]]+|\\\\.?)*/'); } /** * Attempt to parse link title (sans quotes) * * @return string|null The string, or null if no match */ public static function parseLinkTitle(Cursor $cursor): ?string { if ($title = $cursor->match('/' . RegexHelper::PARTIAL_LINK_TITLE . '/')) { // Chop off quotes from title and unescape return RegexHelper::unescape(\substr($title, 1, -1)); } return null; } public static function parsePartialLinkTitle(Cursor $cursor, string $endDelimiter): ?string { $endDelimiter = \preg_quote($endDelimiter, '/'); $regex = \sprintf('/(%s|[^%s\x00])*(?:%s)?/', RegexHelper::PARTIAL_ESCAPED_CHAR, $endDelimiter, $endDelimiter); if (($partialTitle = $cursor->match($regex)) === null) { return null; } return RegexHelper::unescape($partialTitle); } private static function manuallyParseLinkDestination(Cursor $cursor): ?string { $oldPosition = $cursor->getPosition(); $oldState = $cursor->saveState(); $openParens = 0; while (($c = $cursor->getCurrentCharacter()) !== null) { if ($c === '\\' && ($peek = $cursor->peek()) !== null && RegexHelper::isEscapable($peek)) { $cursor->advanceBy(2); } elseif ($c === '(') { $cursor->advanceBy(1); $openParens++; } elseif ($c === ')') { if ($openParens < 1) { break; } $cursor->advanceBy(1); $openParens--; } elseif (\preg_match(RegexHelper::REGEX_WHITESPACE_CHAR, $c)) { break; } else { $cursor->advanceBy(1); } } if ($openParens !== 0) { return null; } if ($cursor->getPosition() === $oldPosition && (! isset($c) || $c !== ')')) { return null; } $newPos = $cursor->getPosition(); $cursor->restoreState($oldState); $cursor->advanceBy($newPos - $cursor->getPosition()); return $cursor->getPreviousText(); } } HtmlFilter.php 0000644 00000002674 15107376177 0007356 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; use League\CommonMark\Exception\InvalidArgumentException; /** * @psalm-immutable */ final class HtmlFilter { // Return the entire string as-is public const ALLOW = 'allow'; // Escape the entire string so any HTML/JS won't be interpreted as such public const ESCAPE = 'escape'; // Return an empty string public const STRIP = 'strip'; /** * Runs the given HTML through the given filter * * @param string $html HTML input to be filtered * @param string $filter One of the HtmlFilter constants * * @return string Filtered HTML * * @throws InvalidArgumentException when an invalid $filter is given * * @psalm-pure */ public static function filter(string $html, string $filter): string { switch ($filter) { case self::STRIP: return ''; case self::ESCAPE: return \htmlspecialchars($html, \ENT_NOQUOTES); case self::ALLOW: return $html; default: throw new InvalidArgumentException(\sprintf('Invalid filter provided: "%s"', $filter)); } } } HtmlElement.php 0000644 00000010054 15107376177 0007511 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; final class HtmlElement implements \Stringable { /** @psalm-readonly */ private string $tagName; /** @var array<string, string|bool> */ private array $attributes = []; /** @var \Stringable|\Stringable[]|string */ private $contents; /** @psalm-readonly */ private bool $selfClosing; /** * @param string $tagName Name of the HTML tag * @param array<string, string|string[]|bool> $attributes Array of attributes (values should be unescaped) * @param \Stringable|\Stringable[]|string|null $contents Inner contents, pre-escaped if needed * @param bool $selfClosing Whether the tag is self-closing */ public function __construct(string $tagName, array $attributes = [], $contents = '', bool $selfClosing = false) { $this->tagName = $tagName; $this->selfClosing = $selfClosing; foreach ($attributes as $name => $value) { $this->setAttribute($name, $value); } $this->setContents($contents ?? ''); } /** @psalm-immutable */ public function getTagName(): string { return $this->tagName; } /** * @return array<string, string|bool> * * @psalm-immutable */ public function getAllAttributes(): array { return $this->attributes; } /** * @return string|bool|null * * @psalm-immutable */ public function getAttribute(string $key) { return $this->attributes[$key] ?? null; } /** * @param string|string[]|bool $value */ public function setAttribute(string $key, $value = true): self { if (\is_array($value)) { $this->attributes[$key] = \implode(' ', \array_unique($value)); } else { $this->attributes[$key] = $value; } return $this; } /** * @return \Stringable|\Stringable[]|string * * @psalm-immutable */ public function getContents(bool $asString = true) { if (! $asString) { return $this->contents; } return $this->getContentsAsString(); } /** * Sets the inner contents of the tag (must be pre-escaped if needed) * * @param \Stringable|\Stringable[]|string $contents * * @return $this */ public function setContents($contents): self { $this->contents = $contents ?? ''; // @phpstan-ignore-line return $this; } /** @psalm-immutable */ public function __toString(): string { $result = '<' . $this->tagName; foreach ($this->attributes as $key => $value) { if ($value === true) { $result .= ' ' . $key; } elseif ($value === false) { continue; } else { $result .= ' ' . $key . '="' . Xml::escape($value) . '"'; } } if ($this->contents !== '') { $result .= '>' . $this->getContentsAsString() . '</' . $this->tagName . '>'; } elseif ($this->selfClosing && $this->tagName === 'input') { $result .= '>'; } elseif ($this->selfClosing) { $result .= ' />'; } else { $result .= '></' . $this->tagName . '>'; } return $result; } /** @psalm-immutable */ private function getContentsAsString(): string { if (\is_string($this->contents)) { return $this->contents; } if (\is_array($this->contents)) { return \implode('', $this->contents); } return (string) $this->contents; } } PrioritizedList.php 0000644 00000003151 15107376177 0010433 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; /** * @internal * * @phpstan-template T * @phpstan-implements \IteratorAggregate<T> */ final class PrioritizedList implements \IteratorAggregate { /** * @var array<int, array<mixed>> * @phpstan-var array<int, array<T>> */ private array $list = []; /** * @var \Traversable<mixed>|null * @phpstan-var \Traversable<T>|null */ private ?\Traversable $optimized = null; /** * @param mixed $item * * @phpstan-param T $item */ public function add($item, int $priority): void { $this->list[$priority][] = $item; $this->optimized = null; } /** * @return \Traversable<int, mixed> * * @phpstan-return \Traversable<int, T> */ #[\ReturnTypeWillChange] public function getIterator(): \Traversable { if ($this->optimized === null) { \krsort($this->list); $sorted = []; foreach ($this->list as $group) { foreach ($group as $item) { $sorted[] = $item; } } $this->optimized = new \ArrayIterator($sorted); } return $this->optimized; } } SpecReader.php 0000644 00000004200 15107376200 0007267 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; use League\CommonMark\Exception\IOException; /** * Reads in a CommonMark spec document and extracts the input/output examples for testing against them */ final class SpecReader { private function __construct() { } /** * @return iterable<string, array{input: string, output: string, type: string, section: string, number: int}> */ public static function read(string $data): iterable { // Normalize newlines for platform independence $data = \preg_replace('/\r\n?/', "\n", $data); \assert($data !== null); $data = \preg_replace('/<!-- END TESTS -->.*$/', '', $data); \assert($data !== null); \preg_match_all('/^`{32} (example ?\w*)\n([\s\S]*?)^\.\n([\s\S]*?)^`{32}$|^#{1,6} *(.*)$/m', $data, $matches, PREG_SET_ORDER); $currentSection = 'Example'; $exampleNumber = 0; foreach ($matches as $match) { if (isset($match[4])) { $currentSection = $match[4]; continue; } yield \trim($currentSection . ' #' . $exampleNumber) => [ 'input' => \str_replace('→', "\t", $match[2]), 'output' => \str_replace('→', "\t", $match[3]), 'type' => $match[1], 'section' => $currentSection, 'number' => $exampleNumber++, ]; } } /** * @return iterable<string, array{input: string, output: string, type: string, section: string, number: int}> * * @throws IOException if the file cannot be loaded */ public static function readFile(string $filename): iterable { if (($data = \file_get_contents($filename)) === false) { throw new IOException(\sprintf('Failed to load spec from %s', $filename)); } return self::read($data); } } Xml.php 0000644 00000001342 15107376200 0006016 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of the league/commonmark package. * * (c) Colin O'Dell <colinodell@gmail.com> * * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) * - (c) John MacFarlane * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Util; /** * Utility class for handling/generating XML and HTML * * @psalm-immutable */ final class Xml { /** * @psalm-pure */ public static function escape(string $string): string { return \str_replace(['&', '<', '>', '"'], ['&', '<', '>', '"'], $string); } } Misc.php 0000644 00000003703 15107406315 0006155 0 ustar 00 <?php /** * Whoops - php errors for cool kids * @author Filipe Dobreira <http://github.com/filp> */ namespace Whoops\Util; class Misc { /** * Can we at this point in time send HTTP headers? * * Currently this checks if we are even serving an HTTP request, * as opposed to running from a command line. * * If we are serving an HTTP request, we check if it's not too late. * * @return bool */ public static function canSendHeaders() { return isset($_SERVER["REQUEST_URI"]) && !headers_sent(); } public static function isAjaxRequest() { return ( !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); } /** * Check, if possible, that this execution was triggered by a command line. * @return bool */ public static function isCommandLine() { return PHP_SAPI == 'cli'; } /** * Translate ErrorException code into the represented constant. * * @param int $error_code * @return string */ public static function translateErrorCode($error_code) { $constants = get_defined_constants(true); if (array_key_exists('Core', $constants)) { foreach ($constants['Core'] as $constant => $value) { if (substr($constant, 0, 2) == 'E_' && $value == $error_code) { return $constant; } } } return "E_UNKNOWN"; } /** * Determine if an error level is fatal (halts execution) * * @param int $level * @return bool */ public static function isLevelFatal($level) { $errors = E_ERROR; $errors |= E_PARSE; $errors |= E_CORE_ERROR; $errors |= E_CORE_WARNING; $errors |= E_COMPILE_ERROR; $errors |= E_COMPILE_WARNING; return ($level & $errors) > 0; } } SystemFacade.php 0000644 00000005217 15107406315 0007634 0 ustar 00 <?php /** * Whoops - php errors for cool kids * @author Filipe Dobreira <http://github.com/filp> */ namespace Whoops\Util; class SystemFacade { /** * Turns on output buffering. * * @return bool */ public function startOutputBuffering() { return ob_start(); } /** * @param callable $handler * @param int $types * * @return callable|null */ public function setErrorHandler(callable $handler, $types = 'use-php-defaults') { // Since PHP 5.4 the constant E_ALL contains all errors (even E_STRICT) if ($types === 'use-php-defaults') { $types = E_ALL; } return set_error_handler($handler, $types); } /** * @param callable $handler * * @return callable|null */ public function setExceptionHandler(callable $handler) { return set_exception_handler($handler); } /** * @return void */ public function restoreExceptionHandler() { restore_exception_handler(); } /** * @return void */ public function restoreErrorHandler() { restore_error_handler(); } /** * @param callable $function * * @return void */ public function registerShutdownFunction(callable $function) { register_shutdown_function($function); } /** * @return string|false */ public function cleanOutputBuffer() { return ob_get_clean(); } /** * @return int */ public function getOutputBufferLevel() { return ob_get_level(); } /** * @return bool */ public function endOutputBuffering() { return ob_end_clean(); } /** * @return void */ public function flushOutputBuffer() { flush(); } /** * @return int */ public function getErrorReportingLevel() { return error_reporting(); } /** * @return array|null */ public function getLastError() { return error_get_last(); } /** * @param int $httpCode * * @return int */ public function setHttpResponseCode($httpCode) { if (!headers_sent()) { // Ensure that no 'location' header is present as otherwise this // will override the HTTP code being set here, and mask the // expected error page. header_remove('location'); } return http_response_code($httpCode); } /** * @param int $exitStatus */ public function stopExecution($exitStatus) { exit($exitStatus); } } TemplateHelper.php 0000644 00000022403 15107406315 0010173 0 ustar 00 <?php /** * Whoops - php errors for cool kids * @author Filipe Dobreira <http://github.com/filp> */ namespace Whoops\Util; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Cloner\AbstractCloner; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Whoops\Exception\Frame; /** * Exposes useful tools for working with/in templates */ class TemplateHelper { /** * An array of variables to be passed to all templates * @var array */ private $variables = []; /** * @var HtmlDumper */ private $htmlDumper; /** * @var HtmlDumperOutput */ private $htmlDumperOutput; /** * @var AbstractCloner */ private $cloner; /** * @var string */ private $applicationRootPath; public function __construct() { // root path for ordinary composer projects $this->applicationRootPath = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__)))))); } /** * Escapes a string for output in an HTML document * * @param string $raw * @return string */ public function escape($raw) { $flags = ENT_QUOTES; // HHVM has all constants defined, but only ENT_IGNORE // works at the moment if (defined("ENT_SUBSTITUTE") && !defined("HHVM_VERSION")) { $flags |= ENT_SUBSTITUTE; } else { // This is for 5.3. // The documentation warns of a potential security issue, // but it seems it does not apply in our case, because // we do not blacklist anything anywhere. $flags |= ENT_IGNORE; } $raw = str_replace(chr(9), ' ', $raw); return htmlspecialchars($raw, $flags, "UTF-8"); } /** * Escapes a string for output in an HTML document, but preserves * URIs within it, and converts them to clickable anchor elements. * * @param string $raw * @return string */ public function escapeButPreserveUris($raw) { $escaped = $this->escape($raw); return preg_replace( "@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@", "<a href=\"$1\" target=\"_blank\" rel=\"noreferrer noopener\">$1</a>", $escaped ); } /** * Makes sure that the given string breaks on the delimiter. * * @param string $delimiter * @param string $s * @return string */ public function breakOnDelimiter($delimiter, $s) { $parts = explode($delimiter, $s); foreach ($parts as &$part) { $part = '<span class="delimiter">' . $part . '</span>'; } return implode($delimiter, $parts); } /** * Replace the part of the path that all files have in common. * * @param string $path * @return string */ public function shorten($path) { if ($this->applicationRootPath != "/") { $path = str_replace($this->applicationRootPath, '…', $path); } return $path; } private function getDumper() { if (!$this->htmlDumper && class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) { $this->htmlDumperOutput = new HtmlDumperOutput(); // re-use the same var-dumper instance, so it won't re-render the global styles/scripts on each dump. $this->htmlDumper = new HtmlDumper($this->htmlDumperOutput); $styles = [ 'default' => 'color:#FFFFFF; line-height:normal; font:12px "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace !important; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: normal', 'num' => 'color:#BCD42A', 'const' => 'color: #4bb1b1;', 'str' => 'color:#BCD42A', 'note' => 'color:#ef7c61', 'ref' => 'color:#A0A0A0', 'public' => 'color:#FFFFFF', 'protected' => 'color:#FFFFFF', 'private' => 'color:#FFFFFF', 'meta' => 'color:#FFFFFF', 'key' => 'color:#BCD42A', 'index' => 'color:#ef7c61', ]; $this->htmlDumper->setStyles($styles); } return $this->htmlDumper; } /** * Format the given value into a human readable string. * * @param mixed $value * @return string */ public function dump($value) { $dumper = $this->getDumper(); if ($dumper) { // re-use the same DumpOutput instance, so it won't re-render the global styles/scripts on each dump. // exclude verbose information (e.g. exception stack traces) if (class_exists('Symfony\Component\VarDumper\Caster\Caster')) { $cloneVar = $this->getCloner()->cloneVar($value, Caster::EXCLUDE_VERBOSE); // Symfony VarDumper 2.6 Caster class dont exist. } else { $cloneVar = $this->getCloner()->cloneVar($value); } $dumper->dump( $cloneVar, $this->htmlDumperOutput ); $output = $this->htmlDumperOutput->getOutput(); $this->htmlDumperOutput->clear(); return $output; } return htmlspecialchars(print_r($value, true)); } /** * Format the args of the given Frame as a human readable html string * * @param Frame $frame * @return string the rendered html */ public function dumpArgs(Frame $frame) { // we support frame args only when the optional dumper is available if (!$this->getDumper()) { return ''; } $html = ''; $numFrames = count($frame->getArgs()); if ($numFrames > 0) { $html = '<ol class="linenums">'; foreach ($frame->getArgs() as $j => $frameArg) { $html .= '<li>'. $this->dump($frameArg) .'</li>'; } $html .= '</ol>'; } return $html; } /** * Convert a string to a slug version of itself * * @param string $original * @return string */ public function slug($original) { $slug = str_replace(" ", "-", $original); $slug = preg_replace('/[^\w\d\-\_]/i', '', $slug); return strtolower($slug); } /** * Given a template path, render it within its own scope. This * method also accepts an array of additional variables to be * passed to the template. * * @param string $template */ public function render($template, array $additionalVariables = null) { $variables = $this->getVariables(); // Pass the helper to the template: $variables["tpl"] = $this; if ($additionalVariables !== null) { $variables = array_replace($variables, $additionalVariables); } call_user_func(function () { extract(func_get_arg(1)); require func_get_arg(0); }, $template, $variables); } /** * Sets the variables to be passed to all templates rendered * by this template helper. */ public function setVariables(array $variables) { $this->variables = $variables; } /** * Sets a single template variable, by its name: * * @param string $variableName * @param mixed $variableValue */ public function setVariable($variableName, $variableValue) { $this->variables[$variableName] = $variableValue; } /** * Gets a single template variable, by its name, or * $defaultValue if the variable does not exist * * @param string $variableName * @param mixed $defaultValue * @return mixed */ public function getVariable($variableName, $defaultValue = null) { return isset($this->variables[$variableName]) ? $this->variables[$variableName] : $defaultValue; } /** * Unsets a single template variable, by its name * * @param string $variableName */ public function delVariable($variableName) { unset($this->variables[$variableName]); } /** * Returns all variables for this helper * * @return array */ public function getVariables() { return $this->variables; } /** * Set the cloner used for dumping variables. * * @param AbstractCloner $cloner */ public function setCloner($cloner) { $this->cloner = $cloner; } /** * Get the cloner used for dumping variables. * * @return AbstractCloner */ public function getCloner() { if (!$this->cloner) { $this->cloner = new VarCloner(); } return $this->cloner; } /** * Set the application root path. * * @param string $applicationRootPath */ public function setApplicationRootPath($applicationRootPath) { $this->applicationRootPath = $applicationRootPath; } /** * Return the application root path. * * @return string */ public function getApplicationRootPath() { return $this->applicationRootPath; } } HtmlDumperOutput.php 0000644 00000001316 15107406315 0010562 0 ustar 00 <?php /** * Whoops - php errors for cool kids * @author Filipe Dobreira <http://github.com/filp> */ namespace Whoops\Util; /** * Used as output callable for Symfony\Component\VarDumper\Dumper\HtmlDumper::dump() * * @see TemplateHelper::dump() */ class HtmlDumperOutput { private $output; public function __invoke($line, $depth) { // A negative depth means "end of dump" if ($depth >= 0) { // Adds a two spaces indentation to the line $this->output .= str_repeat(' ', $depth) . $line . "\n"; } } public function getOutput() { return $this->output; } public function clear() { $this->output = null; } } DefaultLogger.php 0000644 00000001431 15107420237 0010001 0 ustar 00 <?php namespace Stripe\Util; /** * A very basic implementation of LoggerInterface that has just enough * functionality that it can be the default for this library. */ class DefaultLogger implements LoggerInterface { /** @var int */ public $messageType = 0; /** @var null|string */ public $destination; public function error($message, array $context = []) { if (\count($context) > 0) { throw new \Stripe\Exception\BadMethodCallException('DefaultLogger does not currently implement context. Please implement if you need it.'); } if (null === $this->destination) { \error_log($message, $this->messageType); } else { \error_log($message, $this->messageType, $this->destination); } } } CaseInsensitiveArray.php 0000644 00000004310 15107420237 0011347 0 ustar 00 <?php namespace Stripe\Util; /** * CaseInsensitiveArray is an array-like class that ignores case for keys. * * It is used to store HTTP headers. Per RFC 2616, section 4.2: * Each header field consists of a name followed by a colon (":") and the field value. Field names * are case-insensitive. * * In the context of stripe-php, this is useful because the API will return headers with different * case depending on whether HTTP/2 is used or not (with HTTP/2, headers are always in lowercase). */ class CaseInsensitiveArray implements \ArrayAccess, \Countable, \IteratorAggregate { private $container = []; public function __construct($initial_array = []) { $this->container = \array_change_key_case($initial_array, \CASE_LOWER); } /** * @return int */ #[\ReturnTypeWillChange] public function count() { return \count($this->container); } /** * @return \ArrayIterator */ #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->container); } /** * @return void */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $offset = self::maybeLowercase($offset); if (null === $offset) { $this->container[] = $value; } else { $this->container[$offset] = $value; } } /** * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($offset) { $offset = self::maybeLowercase($offset); return isset($this->container[$offset]); } /** * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { $offset = self::maybeLowercase($offset); unset($this->container[$offset]); } /** * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet($offset) { $offset = self::maybeLowercase($offset); return isset($this->container[$offset]) ? $this->container[$offset] : null; } private static function maybeLowercase($v) { if (\is_string($v)) { return \strtolower($v); } return $v; } } LoggerInterface.php 0000644 00000002144 15107420240 0010311 0 ustar 00 <?php namespace Stripe\Util; /** * Describes a logger instance. * * This is a subset of the interface of the same name in the PSR-3 logger * interface. We guarantee to keep it compatible, but we'd redefined it here so * that we don't have to pull in the extra dependencies for users who don't want * it. * * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md * for the full interface specification. * * The message MUST be a string or object implementing __toString(). * * The message MAY contain placeholders in the form: {foo} where foo * will be replaced by the context data in key "foo". * * The context array can contain arbitrary data, the only assumption that * can be made by implementors is that if an Exception instance is given * to produce a stack trace, it MUST be in a key named "exception". */ interface LoggerInterface { /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message */ public function error($message, array $context = []); } RequestOptions.php 0000644 00000012311 15107420240 0010252 0 ustar 00 <?php namespace Stripe\Util; class RequestOptions { /** * @var array<string> a list of headers that should be persisted across requests */ public static $HEADERS_TO_PERSIST = [ 'Stripe-Account', 'Stripe-Version', ]; /** @var array<string, string> */ public $headers; /** @var null|string */ public $apiKey; /** @var null|string */ public $apiBase; /** * @param null|string $key * @param array<string, string> $headers * @param null|string $base */ public function __construct($key = null, $headers = [], $base = null) { $this->apiKey = $key; $this->headers = $headers; $this->apiBase = $base; } /** * @return array<string, string> */ public function __debugInfo() { return [ 'apiKey' => $this->redactedApiKey(), 'headers' => $this->headers, 'apiBase' => $this->apiBase, ]; } /** * Unpacks an options array and merges it into the existing RequestOptions * object. * * @param null|array|RequestOptions|string $options a key => value array * @param bool $strict when true, forbid string form and arbitrary keys in array form * * @return RequestOptions */ public function merge($options, $strict = false) { $other_options = self::parse($options, $strict); if (null === $other_options->apiKey) { $other_options->apiKey = $this->apiKey; } if (null === $other_options->apiBase) { $other_options->apiBase = $this->apiBase; } $other_options->headers = \array_merge($this->headers, $other_options->headers); return $other_options; } /** * Discards all headers that we don't want to persist across requests. */ public function discardNonPersistentHeaders() { foreach ($this->headers as $k => $v) { if (!\in_array($k, self::$HEADERS_TO_PERSIST, true)) { unset($this->headers[$k]); } } } /** * Unpacks an options array into an RequestOptions object. * * @param null|array|RequestOptions|string $options a key => value array * @param bool $strict when true, forbid string form and arbitrary keys in array form * * @throws \Stripe\Exception\InvalidArgumentException * * @return RequestOptions */ public static function parse($options, $strict = false) { if ($options instanceof self) { return clone $options; } if (null === $options) { return new RequestOptions(null, [], null); } if (\is_string($options)) { if ($strict) { $message = 'Do not pass a string for request options. If you want to set the ' . 'API key, pass an array like ["api_key" => <apiKey>] instead.'; throw new \Stripe\Exception\InvalidArgumentException($message); } return new RequestOptions($options, [], null); } if (\is_array($options)) { $headers = []; $key = null; $base = null; if (\array_key_exists('api_key', $options)) { $key = $options['api_key']; unset($options['api_key']); } if (\array_key_exists('idempotency_key', $options)) { $headers['Idempotency-Key'] = $options['idempotency_key']; unset($options['idempotency_key']); } if (\array_key_exists('stripe_account', $options)) { $headers['Stripe-Account'] = $options['stripe_account']; unset($options['stripe_account']); } if (\array_key_exists('stripe_version', $options)) { $headers['Stripe-Version'] = $options['stripe_version']; unset($options['stripe_version']); } if (\array_key_exists('api_base', $options)) { $base = $options['api_base']; unset($options['api_base']); } if ($strict && !empty($options)) { $message = 'Got unexpected keys in options array: ' . \implode(', ', \array_keys($options)); throw new \Stripe\Exception\InvalidArgumentException($message); } return new RequestOptions($key, $headers, $base); } $message = 'The second argument to Stripe API method calls is an ' . 'optional per-request apiKey, which must be a string, or ' . 'per-request options, which must be an array. (HINT: you can set ' . 'a global apiKey by "Stripe::setApiKey(<apiKey>)")'; throw new \Stripe\Exception\InvalidArgumentException($message); } /** @return string */ private function redactedApiKey() { if (null === $this->apiKey) { return ''; } $pieces = \explode('_', $this->apiKey, 3); $last = \array_pop($pieces); $redactedLast = \strlen($last) > 4 ? (\str_repeat('*', \strlen($last) - 4) . \substr($last, -4)) : $last; $pieces[] = $redactedLast; return \implode('_', $pieces); } } Set.php 0000644 00000001464 15107420240 0006010 0 ustar 00 <?php namespace Stripe\Util; use ArrayIterator; use IteratorAggregate; class Set implements IteratorAggregate { private $_elts; public function __construct($members = []) { $this->_elts = []; foreach ($members as $item) { $this->_elts[$item] = true; } } public function includes($elt) { return isset($this->_elts[$elt]); } public function add($elt) { $this->_elts[$elt] = true; } public function discard($elt) { unset($this->_elts[$elt]); } public function toArray() { return \array_keys($this->_elts); } /** * @return ArrayIterator */ #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->toArray()); } } RandomGenerator.php 0000644 00000001456 15107420240 0010345 0 ustar 00 <?php namespace Stripe\Util; /** * A basic random generator. This is in a separate class so we the generator * can be injected as a dependency and replaced with a mock in tests. */ class RandomGenerator { /** * Returns a random value between 0 and $max. * * @param float $max (optional) * * @return float */ public function randFloat($max = 1.0) { return \mt_rand() / \mt_getrandmax() * $max; } /** * Returns a v4 UUID. * * @return string */ public function uuid() { $arr = \array_values(\unpack('N1a/n4b/N1c', \openssl_random_pseudo_bytes(16))); $arr[2] = ($arr[2] & 0x0FFF) | 0x4000; $arr[3] = ($arr[3] & 0x3FFF) | 0x8000; return \vsprintf('%08x-%04x-%04x-%04x-%04x%08x', $arr); } } ObjectTypes.php 0000644 00000023274 15107420241 0007514 0 ustar 00 <?php namespace Stripe\Util; class ObjectTypes { /** * @var array Mapping from object types to resource classes */ const mapping = [ \Stripe\Collection::OBJECT_NAME => \Stripe\Collection::class, \Stripe\Issuing\CardDetails::OBJECT_NAME => \Stripe\Issuing\CardDetails::class, \Stripe\SearchResult::OBJECT_NAME => \Stripe\SearchResult::class, \Stripe\File::OBJECT_NAME_ALT => \Stripe\File::class, // The beginning of the section generated from our OpenAPI spec \Stripe\Account::OBJECT_NAME => \Stripe\Account::class, \Stripe\AccountLink::OBJECT_NAME => \Stripe\AccountLink::class, \Stripe\AccountSession::OBJECT_NAME => \Stripe\AccountSession::class, \Stripe\ApplePayDomain::OBJECT_NAME => \Stripe\ApplePayDomain::class, \Stripe\ApplicationFee::OBJECT_NAME => \Stripe\ApplicationFee::class, \Stripe\ApplicationFeeRefund::OBJECT_NAME => \Stripe\ApplicationFeeRefund::class, \Stripe\Apps\Secret::OBJECT_NAME => \Stripe\Apps\Secret::class, \Stripe\Balance::OBJECT_NAME => \Stripe\Balance::class, \Stripe\BalanceTransaction::OBJECT_NAME => \Stripe\BalanceTransaction::class, \Stripe\BankAccount::OBJECT_NAME => \Stripe\BankAccount::class, \Stripe\BillingPortal\Configuration::OBJECT_NAME => \Stripe\BillingPortal\Configuration::class, \Stripe\BillingPortal\Session::OBJECT_NAME => \Stripe\BillingPortal\Session::class, \Stripe\Capability::OBJECT_NAME => \Stripe\Capability::class, \Stripe\Card::OBJECT_NAME => \Stripe\Card::class, \Stripe\CashBalance::OBJECT_NAME => \Stripe\CashBalance::class, \Stripe\Charge::OBJECT_NAME => \Stripe\Charge::class, \Stripe\Checkout\Session::OBJECT_NAME => \Stripe\Checkout\Session::class, \Stripe\CountrySpec::OBJECT_NAME => \Stripe\CountrySpec::class, \Stripe\Coupon::OBJECT_NAME => \Stripe\Coupon::class, \Stripe\CreditNote::OBJECT_NAME => \Stripe\CreditNote::class, \Stripe\CreditNoteLineItem::OBJECT_NAME => \Stripe\CreditNoteLineItem::class, \Stripe\Customer::OBJECT_NAME => \Stripe\Customer::class, \Stripe\CustomerBalanceTransaction::OBJECT_NAME => \Stripe\CustomerBalanceTransaction::class, \Stripe\CustomerCashBalanceTransaction::OBJECT_NAME => \Stripe\CustomerCashBalanceTransaction::class, \Stripe\Discount::OBJECT_NAME => \Stripe\Discount::class, \Stripe\Dispute::OBJECT_NAME => \Stripe\Dispute::class, \Stripe\EphemeralKey::OBJECT_NAME => \Stripe\EphemeralKey::class, \Stripe\Event::OBJECT_NAME => \Stripe\Event::class, \Stripe\ExchangeRate::OBJECT_NAME => \Stripe\ExchangeRate::class, \Stripe\File::OBJECT_NAME => \Stripe\File::class, \Stripe\FileLink::OBJECT_NAME => \Stripe\FileLink::class, \Stripe\FinancialConnections\Account::OBJECT_NAME => \Stripe\FinancialConnections\Account::class, \Stripe\FinancialConnections\AccountOwner::OBJECT_NAME => \Stripe\FinancialConnections\AccountOwner::class, \Stripe\FinancialConnections\AccountOwnership::OBJECT_NAME => \Stripe\FinancialConnections\AccountOwnership::class, \Stripe\FinancialConnections\Session::OBJECT_NAME => \Stripe\FinancialConnections\Session::class, \Stripe\FundingInstructions::OBJECT_NAME => \Stripe\FundingInstructions::class, \Stripe\Identity\VerificationReport::OBJECT_NAME => \Stripe\Identity\VerificationReport::class, \Stripe\Identity\VerificationSession::OBJECT_NAME => \Stripe\Identity\VerificationSession::class, \Stripe\Invoice::OBJECT_NAME => \Stripe\Invoice::class, \Stripe\InvoiceItem::OBJECT_NAME => \Stripe\InvoiceItem::class, \Stripe\InvoiceLineItem::OBJECT_NAME => \Stripe\InvoiceLineItem::class, \Stripe\Issuing\Authorization::OBJECT_NAME => \Stripe\Issuing\Authorization::class, \Stripe\Issuing\Card::OBJECT_NAME => \Stripe\Issuing\Card::class, \Stripe\Issuing\Cardholder::OBJECT_NAME => \Stripe\Issuing\Cardholder::class, \Stripe\Issuing\Dispute::OBJECT_NAME => \Stripe\Issuing\Dispute::class, \Stripe\Issuing\Token::OBJECT_NAME => \Stripe\Issuing\Token::class, \Stripe\Issuing\Transaction::OBJECT_NAME => \Stripe\Issuing\Transaction::class, \Stripe\LineItem::OBJECT_NAME => \Stripe\LineItem::class, \Stripe\LoginLink::OBJECT_NAME => \Stripe\LoginLink::class, \Stripe\Mandate::OBJECT_NAME => \Stripe\Mandate::class, \Stripe\PaymentIntent::OBJECT_NAME => \Stripe\PaymentIntent::class, \Stripe\PaymentLink::OBJECT_NAME => \Stripe\PaymentLink::class, \Stripe\PaymentMethod::OBJECT_NAME => \Stripe\PaymentMethod::class, \Stripe\PaymentMethodConfiguration::OBJECT_NAME => \Stripe\PaymentMethodConfiguration::class, \Stripe\PaymentMethodDomain::OBJECT_NAME => \Stripe\PaymentMethodDomain::class, \Stripe\Payout::OBJECT_NAME => \Stripe\Payout::class, \Stripe\Person::OBJECT_NAME => \Stripe\Person::class, \Stripe\Plan::OBJECT_NAME => \Stripe\Plan::class, \Stripe\Price::OBJECT_NAME => \Stripe\Price::class, \Stripe\Product::OBJECT_NAME => \Stripe\Product::class, \Stripe\PromotionCode::OBJECT_NAME => \Stripe\PromotionCode::class, \Stripe\Quote::OBJECT_NAME => \Stripe\Quote::class, \Stripe\Radar\EarlyFraudWarning::OBJECT_NAME => \Stripe\Radar\EarlyFraudWarning::class, \Stripe\Radar\ValueList::OBJECT_NAME => \Stripe\Radar\ValueList::class, \Stripe\Radar\ValueListItem::OBJECT_NAME => \Stripe\Radar\ValueListItem::class, \Stripe\Refund::OBJECT_NAME => \Stripe\Refund::class, \Stripe\Reporting\ReportRun::OBJECT_NAME => \Stripe\Reporting\ReportRun::class, \Stripe\Reporting\ReportType::OBJECT_NAME => \Stripe\Reporting\ReportType::class, \Stripe\Review::OBJECT_NAME => \Stripe\Review::class, \Stripe\SetupAttempt::OBJECT_NAME => \Stripe\SetupAttempt::class, \Stripe\SetupIntent::OBJECT_NAME => \Stripe\SetupIntent::class, \Stripe\ShippingRate::OBJECT_NAME => \Stripe\ShippingRate::class, \Stripe\Sigma\ScheduledQueryRun::OBJECT_NAME => \Stripe\Sigma\ScheduledQueryRun::class, \Stripe\Source::OBJECT_NAME => \Stripe\Source::class, \Stripe\SourceTransaction::OBJECT_NAME => \Stripe\SourceTransaction::class, \Stripe\Subscription::OBJECT_NAME => \Stripe\Subscription::class, \Stripe\SubscriptionItem::OBJECT_NAME => \Stripe\SubscriptionItem::class, \Stripe\SubscriptionSchedule::OBJECT_NAME => \Stripe\SubscriptionSchedule::class, \Stripe\Tax\Calculation::OBJECT_NAME => \Stripe\Tax\Calculation::class, \Stripe\Tax\CalculationLineItem::OBJECT_NAME => \Stripe\Tax\CalculationLineItem::class, \Stripe\Tax\Registration::OBJECT_NAME => \Stripe\Tax\Registration::class, \Stripe\Tax\Settings::OBJECT_NAME => \Stripe\Tax\Settings::class, \Stripe\Tax\Transaction::OBJECT_NAME => \Stripe\Tax\Transaction::class, \Stripe\Tax\TransactionLineItem::OBJECT_NAME => \Stripe\Tax\TransactionLineItem::class, \Stripe\TaxCode::OBJECT_NAME => \Stripe\TaxCode::class, \Stripe\TaxId::OBJECT_NAME => \Stripe\TaxId::class, \Stripe\TaxRate::OBJECT_NAME => \Stripe\TaxRate::class, \Stripe\Terminal\Configuration::OBJECT_NAME => \Stripe\Terminal\Configuration::class, \Stripe\Terminal\ConnectionToken::OBJECT_NAME => \Stripe\Terminal\ConnectionToken::class, \Stripe\Terminal\Location::OBJECT_NAME => \Stripe\Terminal\Location::class, \Stripe\Terminal\Reader::OBJECT_NAME => \Stripe\Terminal\Reader::class, \Stripe\TestHelpers\TestClock::OBJECT_NAME => \Stripe\TestHelpers\TestClock::class, \Stripe\Token::OBJECT_NAME => \Stripe\Token::class, \Stripe\Topup::OBJECT_NAME => \Stripe\Topup::class, \Stripe\Transfer::OBJECT_NAME => \Stripe\Transfer::class, \Stripe\TransferReversal::OBJECT_NAME => \Stripe\TransferReversal::class, \Stripe\Treasury\CreditReversal::OBJECT_NAME => \Stripe\Treasury\CreditReversal::class, \Stripe\Treasury\DebitReversal::OBJECT_NAME => \Stripe\Treasury\DebitReversal::class, \Stripe\Treasury\FinancialAccount::OBJECT_NAME => \Stripe\Treasury\FinancialAccount::class, \Stripe\Treasury\FinancialAccountFeatures::OBJECT_NAME => \Stripe\Treasury\FinancialAccountFeatures::class, \Stripe\Treasury\InboundTransfer::OBJECT_NAME => \Stripe\Treasury\InboundTransfer::class, \Stripe\Treasury\OutboundPayment::OBJECT_NAME => \Stripe\Treasury\OutboundPayment::class, \Stripe\Treasury\OutboundTransfer::OBJECT_NAME => \Stripe\Treasury\OutboundTransfer::class, \Stripe\Treasury\ReceivedCredit::OBJECT_NAME => \Stripe\Treasury\ReceivedCredit::class, \Stripe\Treasury\ReceivedDebit::OBJECT_NAME => \Stripe\Treasury\ReceivedDebit::class, \Stripe\Treasury\Transaction::OBJECT_NAME => \Stripe\Treasury\Transaction::class, \Stripe\Treasury\TransactionEntry::OBJECT_NAME => \Stripe\Treasury\TransactionEntry::class, \Stripe\UsageRecord::OBJECT_NAME => \Stripe\UsageRecord::class, \Stripe\UsageRecordSummary::OBJECT_NAME => \Stripe\UsageRecordSummary::class, \Stripe\WebhookEndpoint::OBJECT_NAME => \Stripe\WebhookEndpoint::class, // The end of the section generated from our OpenAPI spec ]; } Util.php 0000644 00000016470 15107420241 0006176 0 ustar 00 <?php namespace Stripe\Util; use Stripe\StripeObject; abstract class Util { private static $isMbstringAvailable = null; private static $isHashEqualsAvailable = null; /** * Whether the provided array (or other) is a list rather than a dictionary. * A list is defined as an array for which all the keys are consecutive * integers starting at 0. Empty arrays are considered to be lists. * * @param array|mixed $array * * @return bool true if the given object is a list */ public static function isList($array) { if (!\is_array($array)) { return false; } if ([] === $array) { return true; } if (\array_keys($array) !== \range(0, \count($array) - 1)) { return false; } return true; } /** * Converts a response from the Stripe API to the corresponding PHP object. * * @param array $resp the response from the Stripe API * @param array $opts * * @return array|StripeObject */ public static function convertToStripeObject($resp, $opts) { $types = \Stripe\Util\ObjectTypes::mapping; if (self::isList($resp)) { $mapped = []; foreach ($resp as $i) { $mapped[] = self::convertToStripeObject($i, $opts); } return $mapped; } if (\is_array($resp)) { if (isset($resp['object']) && \is_string($resp['object']) && isset($types[$resp['object']])) { $class = $types[$resp['object']]; } else { $class = \Stripe\StripeObject::class; } return $class::constructFrom($resp, $opts); } return $resp; } /** * @param mixed|string $value a string to UTF8-encode * * @return mixed|string the UTF8-encoded string, or the object passed in if * it wasn't a string */ public static function utf8($value) { if (null === self::$isMbstringAvailable) { self::$isMbstringAvailable = \function_exists('mb_detect_encoding') && \function_exists('mb_convert_encoding'); if (!self::$isMbstringAvailable) { \trigger_error('It looks like the mbstring extension is not enabled. ' . 'UTF-8 strings will not properly be encoded. Ask your system ' . 'administrator to enable the mbstring extension, or write to ' . 'support@stripe.com if you have any questions.', \E_USER_WARNING); } } if (\is_string($value) && self::$isMbstringAvailable && 'UTF-8' !== \mb_detect_encoding($value, 'UTF-8', true)) { return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); } return $value; } /** * Compares two strings for equality. The time taken is independent of the * number of characters that match. * * @param string $a one of the strings to compare * @param string $b the other string to compare * * @return bool true if the strings are equal, false otherwise */ public static function secureCompare($a, $b) { if (null === self::$isHashEqualsAvailable) { self::$isHashEqualsAvailable = \function_exists('hash_equals'); } if (self::$isHashEqualsAvailable) { return \hash_equals($a, $b); } if (\strlen($a) !== \strlen($b)) { return false; } $result = 0; for ($i = 0; $i < \strlen($a); ++$i) { $result |= \ord($a[$i]) ^ \ord($b[$i]); } return 0 === $result; } /** * Recursively goes through an array of parameters. If a parameter is an instance of * ApiResource, then it is replaced by the resource's ID. * Also clears out null values. * * @param mixed $h * * @return mixed */ public static function objectsToIds($h) { if ($h instanceof \Stripe\ApiResource) { return $h->id; } if (static::isList($h)) { $results = []; foreach ($h as $v) { $results[] = static::objectsToIds($v); } return $results; } if (\is_array($h)) { $results = []; foreach ($h as $k => $v) { if (null === $v) { continue; } $results[$k] = static::objectsToIds($v); } return $results; } return $h; } /** * @param array $params * * @return string */ public static function encodeParameters($params) { $flattenedParams = self::flattenParams($params); $pieces = []; foreach ($flattenedParams as $param) { list($k, $v) = $param; $pieces[] = self::urlEncode($k) . '=' . self::urlEncode($v); } return \implode('&', $pieces); } /** * @param array $params * @param null|string $parentKey * * @return array */ public static function flattenParams($params, $parentKey = null) { $result = []; foreach ($params as $key => $value) { $calculatedKey = $parentKey ? "{$parentKey}[{$key}]" : $key; if (self::isList($value)) { $result = \array_merge($result, self::flattenParamsList($value, $calculatedKey)); } elseif (\is_array($value)) { $result = \array_merge($result, self::flattenParams($value, $calculatedKey)); } else { \array_push($result, [$calculatedKey, $value]); } } return $result; } /** * @param array $value * @param string $calculatedKey * * @return array */ public static function flattenParamsList($value, $calculatedKey) { $result = []; foreach ($value as $i => $elem) { if (self::isList($elem)) { $result = \array_merge($result, self::flattenParamsList($elem, $calculatedKey)); } elseif (\is_array($elem)) { $result = \array_merge($result, self::flattenParams($elem, "{$calculatedKey}[{$i}]")); } else { \array_push($result, ["{$calculatedKey}[{$i}]", $elem]); } } return $result; } /** * @param string $key a string to URL-encode * * @return string the URL-encoded string */ public static function urlEncode($key) { $s = \urlencode((string) $key); // Don't use strict form encoding by changing the square bracket control // characters back to their literals. This is fine by the server, and // makes these parameter strings easier to read. $s = \str_replace('%5B', '[', $s); return \str_replace('%5D', ']', $s); } public static function normalizeId($id) { if (\is_array($id)) { $params = $id; $id = $params['id']; unset($params['id']); } else { $params = []; } return [$id, $params]; } /** * Returns UNIX timestamp in milliseconds. * * @return int current time in millis */ public static function currentTimeMillis() { return (int) \round(\microtime(true) * 1000); } } ApiVersion.php 0000644 00000000177 15107420241 0007335 0 ustar 00 <?php // File generated from our OpenAPI spec namespace Stripe\Util; class ApiVersion { const CURRENT = '2023-10-16'; } Str.php 0000644 00000004735 15107472632 0006045 0 ustar 00 <?php declare(strict_types=1); namespace Dotenv\Util; use GrahamCampbell\ResultType\Error; use GrahamCampbell\ResultType\Success; use PhpOption\Option; /** * @internal */ final class Str { /** * This class is a singleton. * * @codeCoverageIgnore * * @return void */ private function __construct() { // } /** * Convert a string to UTF-8 from the given encoding. * * @param string $input * @param string|null $encoding * * @return \GrahamCampbell\ResultType\Result<string,string> */ public static function utf8(string $input, string $encoding = null) { if ($encoding !== null && !\in_array($encoding, \mb_list_encodings(), true)) { /** @var \GrahamCampbell\ResultType\Result<string,string> */ return Error::create( \sprintf('Illegal character encoding [%s] specified.', $encoding) ); } $converted = $encoding === null ? @\mb_convert_encoding($input, 'UTF-8') : @\mb_convert_encoding($input, 'UTF-8', $encoding); /** * this is for support UTF-8 with BOM encoding * @see https://en.wikipedia.org/wiki/Byte_order_mark * @see https://github.com/vlucas/phpdotenv/issues/500 */ if (\substr($converted, 0, 3) == "\xEF\xBB\xBF") { $converted = \substr($converted, 3); } /** @var \GrahamCampbell\ResultType\Result<string,string> */ return Success::create($converted); } /** * Search for a given substring of the input. * * @param string $haystack * @param string $needle * * @return \PhpOption\Option<int> */ public static function pos(string $haystack, string $needle) { /** @var \PhpOption\Option<int> */ return Option::fromValue(\mb_strpos($haystack, $needle, 0, 'UTF-8'), false); } /** * Grab the specified substring of the input. * * @param string $input * @param int $start * @param int|null $length * * @return string */ public static function substr(string $input, int $start, int $length = null) { return \mb_substr($input, $start, $length, 'UTF-8'); } /** * Compute the length of the given string. * * @param string $input * * @return int */ public static function len(string $input) { return \mb_strlen($input, 'UTF-8'); } } Regex.php 0000644 00000006000 15107472632 0006332 0 ustar 00 <?php declare(strict_types=1); namespace Dotenv\Util; use GrahamCampbell\ResultType\Error; use GrahamCampbell\ResultType\Success; /** * @internal */ final class Regex { /** * This class is a singleton. * * @codeCoverageIgnore * * @return void */ private function __construct() { // } /** * Perform a preg match, wrapping up the result. * * @param string $pattern * @param string $subject * * @return \GrahamCampbell\ResultType\Result<bool,string> */ public static function matches(string $pattern, string $subject) { return self::pregAndWrap(static function (string $subject) use ($pattern) { return @\preg_match($pattern, $subject) === 1; }, $subject); } /** * Perform a preg match all, wrapping up the result. * * @param string $pattern * @param string $subject * * @return \GrahamCampbell\ResultType\Result<int,string> */ public static function occurrences(string $pattern, string $subject) { return self::pregAndWrap(static function (string $subject) use ($pattern) { return (int) @\preg_match_all($pattern, $subject); }, $subject); } /** * Perform a preg replace callback, wrapping up the result. * * @param string $pattern * @param callable $callback * @param string $subject * @param int|null $limit * * @return \GrahamCampbell\ResultType\Result<string,string> */ public static function replaceCallback(string $pattern, callable $callback, string $subject, int $limit = null) { return self::pregAndWrap(static function (string $subject) use ($pattern, $callback, $limit) { return (string) @\preg_replace_callback($pattern, $callback, $subject, $limit ?? -1); }, $subject); } /** * Perform a preg split, wrapping up the result. * * @param string $pattern * @param string $subject * * @return \GrahamCampbell\ResultType\Result<string[],string> */ public static function split(string $pattern, string $subject) { return self::pregAndWrap(static function (string $subject) use ($pattern) { /** @var string[] */ return (array) @\preg_split($pattern, $subject); }, $subject); } /** * Perform a preg operation, wrapping up the result. * * @template V * * @param callable(string):V $operation * @param string $subject * * @return \GrahamCampbell\ResultType\Result<V,string> */ private static function pregAndWrap(callable $operation, string $subject) { $result = $operation($subject); if (\preg_last_error() !== \PREG_NO_ERROR) { /** @var \GrahamCampbell\ResultType\Result<V,string> */ return Error::create(\preg_last_error_msg()); } /** @var \GrahamCampbell\ResultType\Result<V,string> */ return Success::create($result); } } ClassUtils.php 0000644 00000005310 15110326445 0007343 0 ustar 00 <?php namespace Doctrine\Common\Util; use Doctrine\Persistence\Proxy; use ReflectionClass; use function get_class; use function get_parent_class; use function ltrim; use function rtrim; use function strrpos; use function substr; /** * Class and reflection related functionality for objects that * might or not be proxy objects at the moment. */ class ClassUtils { /** * Gets the real class name of a class name that could be a proxy. * * @param string $className * @psalm-param class-string<Proxy<T>>|class-string<T> $className * * @return string * @psalm-return class-string<T> * * @template T of object */ public static function getRealClass($className) { $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); if ($pos === false) { /** @psalm-var class-string<T> */ return $className; } return substr($className, $pos + Proxy::MARKER_LENGTH + 2); } /** * Gets the real class name of an object (even if its a proxy). * * @param object $object * @psalm-param Proxy<T>|T $object * * @return string * @psalm-return class-string<T> * * @template T of object */ public static function getClass($object) { return self::getRealClass(get_class($object)); } /** * Gets the real parent class name of a class or object. * * @param string $className * @psalm-param class-string $className * * @return string * @psalm-return class-string */ public static function getParentClass($className) { return get_parent_class(self::getRealClass($className)); } /** * Creates a new reflection class. * * @param string $className * @psalm-param class-string $className * * @return ReflectionClass */ public static function newReflectionClass($className) { return new ReflectionClass(self::getRealClass($className)); } /** * Creates a new reflection object. * * @param object $object * * @return ReflectionClass */ public static function newReflectionObject($object) { return self::newReflectionClass(self::getClass($object)); } /** * Given a class name and a proxy namespace returns the proxy name. * * @param string $className * @param string $proxyNamespace * @psalm-param class-string $className * * @return string * @psalm-return class-string */ public static function generateProxyClassName($className, $proxyNamespace) { return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\'); } } Debug.php 0000644 00000011151 15110326445 0006303 0 ustar 00 <?php namespace Doctrine\Common\Util; use ArrayIterator; use ArrayObject; use DateTimeInterface; use Doctrine\Common\Collections\Collection; use Doctrine\Persistence\Proxy; use stdClass; use function array_keys; use function count; use function end; use function explode; use function extension_loaded; use function get_class; use function html_entity_decode; use function ini_get; use function ini_set; use function is_array; use function is_object; use function method_exists; use function ob_end_clean; use function ob_get_contents; use function ob_start; use function spl_object_hash; use function strip_tags; use function var_dump; /** * Static class containing most used debug methods. * * @deprecated The Debug class is deprecated, please use symfony/var-dumper instead. * * @link www.doctrine-project.org */ final class Debug { /** * Private constructor (prevents instantiation). */ private function __construct() { } /** * Prints a dump of the public, protected and private properties of $var. * * @link https://xdebug.org/ * * @param mixed $var The variable to dump. * @param int $maxDepth The maximum nesting level for object properties. * @param bool $stripTags Whether output should strip HTML tags. * @param bool $echo Send the dumped value to the output buffer * * @return string */ public static function dump($var, $maxDepth = 2, $stripTags = true, $echo = true) { $html = ini_get('html_errors'); if ($html !== true) { ini_set('html_errors', 'on'); } if (extension_loaded('xdebug')) { ini_set('xdebug.var_display_max_depth', $maxDepth); } $var = self::export($var, $maxDepth); ob_start(); var_dump($var); $dump = ob_get_contents(); ob_end_clean(); $dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); ini_set('html_errors', $html); if ($echo) { echo $dumpText; } return $dumpText; } /** * @param mixed $var * @param int $maxDepth * * @return mixed */ public static function export($var, $maxDepth) { $return = null; $isObj = is_object($var); if ($var instanceof Collection) { $var = $var->toArray(); } if (! $maxDepth) { return is_object($var) ? get_class($var) : (is_array($var) ? 'Array(' . count($var) . ')' : $var); } if (is_array($var)) { $return = []; foreach ($var as $k => $v) { $return[$k] = self::export($v, $maxDepth - 1); } return $return; } if (! $isObj) { return $var; } $return = new stdClass(); if ($var instanceof DateTimeInterface) { $return->__CLASS__ = get_class($var); $return->date = $var->format('c'); $return->timezone = $var->getTimezone()->getName(); return $return; } $return->__CLASS__ = ClassUtils::getClass($var); if ($var instanceof Proxy) { $return->__IS_PROXY__ = true; $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); } if ($var instanceof ArrayObject || $var instanceof ArrayIterator) { $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); } return self::fillReturnWithClassAttributes($var, $return, $maxDepth); } /** * Fill the $return variable with class attributes * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} * * @param object $var * @param int $maxDepth * * @return mixed */ private static function fillReturnWithClassAttributes($var, stdClass $return, $maxDepth) { $clone = (array) $var; foreach (array_keys($clone) as $key) { $aux = explode("\0", $key); $name = end($aux); if ($aux[0] === '') { $name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); } $return->$name = self::export($clone[$key], $maxDepth - 1); } return $return; } /** * Returns a string representation of an object. * * @param object $obj * * @return string */ public static function toString($obj) { return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); } } XliffUtils.php 0000644 00000014473 15111157672 0007365 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Util; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\InvalidResourceException; /** * Provides some utility methods for XLIFF translation files, such as validating * their contents according to the XSD schema. * * @author Fabien Potencier <fabien@symfony.com> */ class XliffUtils { /** * Gets xliff file version based on the root "version" attribute. * * Defaults to 1.2 for backwards compatibility. * * @throws InvalidArgumentException */ public static function getVersionNumber(\DOMDocument $dom): string { /** @var \DOMNode $xliff */ foreach ($dom->getElementsByTagName('xliff') as $xliff) { $version = $xliff->attributes->getNamedItem('version'); if ($version) { return $version->nodeValue; } $namespace = $xliff->attributes->getNamedItem('xmlns'); if ($namespace) { if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) { throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s".', $namespace)); } return substr($namespace, 34); } } // Falls back to v1.2 return '1.2'; } /** * Validates and parses the given file into a DOMDocument. * * @throws InvalidResourceException */ public static function validateSchema(\DOMDocument $dom): array { $xliffVersion = static::getVersionNumber($dom); $internalErrors = libxml_use_internal_errors(true); if ($shouldEnable = self::shouldEnableEntityLoader()) { $disableEntities = libxml_disable_entity_loader(false); } try { $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion)); if (!$isValid) { return self::getXmlErrors($internalErrors); } } finally { if ($shouldEnable) { libxml_disable_entity_loader($disableEntities); } } $dom->normalizeDocument(); libxml_clear_errors(); libxml_use_internal_errors($internalErrors); return []; } private static function shouldEnableEntityLoader(): bool { static $dom, $schema; if (null === $dom) { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><test/>'); $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); register_shutdown_function(static function () use ($tmpfile) { @unlink($tmpfile); }); $schema = '<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:include schemaLocation="file:///'.str_replace('\\', '/', $tmpfile).'" /> </xsd:schema>'; file_put_contents($tmpfile, '<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="test" type="testType" /> <xsd:complexType name="testType"/> </xsd:schema>'); } return !@$dom->schemaValidateSource($schema); } public static function getErrorsAsString(array $xmlErrors): string { $errorsAsString = ''; foreach ($xmlErrors as $error) { $errorsAsString .= sprintf("[%s %s] %s (in %s - line %d, column %d)\n", \LIBXML_ERR_WARNING === $error['level'] ? 'WARNING' : 'ERROR', $error['code'], $error['message'], $error['file'], $error['line'], $error['column'] ); } return $errorsAsString; } private static function getSchema(string $xliffVersion): string { if ('1.2' === $xliffVersion) { $schemaSource = file_get_contents(__DIR__.'/../Resources/schemas/xliff-core-1.2-transitional.xsd'); $xmlUri = 'http://www.w3.org/2001/xml.xsd'; } elseif ('2.0' === $xliffVersion) { $schemaSource = file_get_contents(__DIR__.'/../Resources/schemas/xliff-core-2.0.xsd'); $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd'; } else { throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion)); } return self::fixXmlLocation($schemaSource, $xmlUri); } /** * Internally changes the URI of a dependent xsd to be loaded locally. */ private static function fixXmlLocation(string $schemaSource, string $xmlUri): string { $newPath = str_replace('\\', '/', __DIR__).'/../Resources/schemas/xml.xsd'; $parts = explode('/', $newPath); $locationstart = 'file:///'; if (0 === stripos($newPath, 'phar://')) { $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); if ($tmpfile) { copy($newPath, $tmpfile); $parts = explode('/', str_replace('\\', '/', $tmpfile)); } else { array_shift($parts); $locationstart = 'phar:///'; } } $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; $newPath = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); return str_replace($xmlUri, $newPath, $schemaSource); } /** * Returns the XML errors of the internal XML parser. */ private static function getXmlErrors(bool $internalErrors): array { $errors = []; foreach (libxml_get_errors() as $error) { $errors[] = [ 'level' => \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', 'code' => $error->code, 'message' => trim($error->message), 'file' => $error->file ?: 'n/a', 'line' => $error->line, 'column' => $error->column, ]; } libxml_clear_errors(); libxml_use_internal_errors($internalErrors); return $errors; } } ArrayConverter.php 0000644 00000007271 15111157672 0010240 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Util; /** * ArrayConverter generates tree like structure from a message catalogue. * e.g. this * 'foo.bar1' => 'test1', * 'foo.bar2' => 'test2' * converts to follows: * foo: * bar1: test1 * bar2: test2. * * @author Gennady Telegin <gtelegin@gmail.com> */ class ArrayConverter { /** * Converts linear messages array to tree-like array. * For example this array('foo.bar' => 'value') will be converted to ['foo' => ['bar' => 'value']]. * * @param array $messages Linear messages array */ public static function expandToTree(array $messages): array { $tree = []; foreach ($messages as $id => $value) { $referenceToElement = &self::getElementByPath($tree, self::getKeyParts($id)); $referenceToElement = $value; unset($referenceToElement); } return $tree; } private static function &getElementByPath(array &$tree, array $parts): mixed { $elem = &$tree; $parentOfElem = null; foreach ($parts as $i => $part) { if (isset($elem[$part]) && \is_string($elem[$part])) { /* Process next case: * 'foo': 'test1', * 'foo.bar': 'test2' * * $tree['foo'] was string before we found array {bar: test2}. * Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2'; */ $elem = &$elem[implode('.', \array_slice($parts, $i))]; break; } $parentOfElem = &$elem; $elem = &$elem[$part]; } if ($elem && \is_array($elem) && $parentOfElem) { /* Process next case: * 'foo.bar': 'test1' * 'foo': 'test2' * * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`. * Cancel treating $tree['foo'] as array and cancel back it expansion, * e.g. make it $tree['foo.bar'] = 'test1' again. */ self::cancelExpand($parentOfElem, $part, $elem); } return $elem; } private static function cancelExpand(array &$tree, string $prefix, array $node): void { $prefix .= '.'; foreach ($node as $id => $value) { if (\is_string($value)) { $tree[$prefix.$id] = $value; } else { self::cancelExpand($tree, $prefix.$id, $value); } } } /** * @return string[] */ private static function getKeyParts(string $key): array { $parts = explode('.', $key); $partsCount = \count($parts); $result = []; $buffer = ''; foreach ($parts as $index => $part) { if (0 === $index && '' === $part) { $buffer = '.'; continue; } if ($index === $partsCount - 1 && '' === $part) { $buffer .= '.'; $result[] = $buffer; continue; } if (isset($parts[$index + 1]) && '' === $parts[$index + 1]) { $buffer .= $part; continue; } if ($buffer) { $result[] = $buffer.$part; $buffer = ''; continue; } $result[] = $part; } return $result; } } Reflection.php 0000644 00000005426 15111215677 0007364 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function array_keys; use function array_merge; use function array_reverse; use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; use ReflectionClass; use ReflectionException; use ReflectionMethod; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Reflection { /** * @psalm-param class-string $className * @psalm-param non-empty-string $methodName * * @psalm-return array{file: non-empty-string, line: non-negative-int} */ public static function sourceLocationFor(string $className, string $methodName): array { try { $reflector = new ReflectionMethod($className, $methodName); $file = $reflector->getFileName(); $line = $reflector->getStartLine(); } catch (ReflectionException) { $file = 'unknown'; $line = 0; } return [ 'file' => $file, 'line' => $line, ]; } /** * @psalm-return list<ReflectionMethod> */ public static function publicMethodsInTestClass(ReflectionClass $class): array { return self::filterAndSortMethods($class, ReflectionMethod::IS_PUBLIC, true); } /** * @psalm-return list<ReflectionMethod> */ public static function methodsInTestClass(ReflectionClass $class): array { return self::filterAndSortMethods($class, null, false); } /** * @psalm-return list<ReflectionMethod> */ private static function filterAndSortMethods(ReflectionClass $class, ?int $filter, bool $sortHighestToLowest): array { $methodsByClass = []; foreach ($class->getMethods($filter) as $method) { $declaringClassName = $method->getDeclaringClass()->getName(); if ($declaringClassName === TestCase::class) { continue; } if ($declaringClassName === Assert::class) { continue; } if (!isset($methodsByClass[$declaringClassName])) { $methodsByClass[$declaringClassName] = []; } $methodsByClass[$declaringClassName][] = $method; } $classNames = array_keys($methodsByClass); if ($sortHighestToLowest) { $classNames = array_reverse($classNames); } $methods = []; foreach ($classNames as $className) { $methods = array_merge($methods, $methodsByClass[$className]); } return $methods; } } ThrowableToStringMapper.php 0000644 00000002275 15111215677 0012057 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function trim; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\PhptAssertionFailedError; use PHPUnit\Framework\SelfDescribing; use Throwable; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class ThrowableToStringMapper { public static function map(Throwable $t): string { if ($t instanceof SelfDescribing) { $buffer = $t->toString(); if ($t instanceof ExpectationFailedException && $t->getComparisonFailure()) { $buffer .= $t->getComparisonFailure()->getDiff(); } if ($t instanceof PhptAssertionFailedError) { $buffer .= $t->diff(); } if (!empty($buffer)) { $buffer = trim($buffer) . "\n"; } return $buffer; } return $t::class . ': ' . $t->getMessage() . "\n"; } } Cloner.php 0000644 00000001354 15111215677 0006510 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use Throwable; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Cloner { /** * @psalm-template OriginalType * * @psalm-param OriginalType $original * * @psalm-return OriginalType */ public static function clone(object $original): object { try { return clone $original; } catch (Throwable) { return $original; } } } Json.php 0000644 00000005344 15111215677 0006202 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use const JSON_PRETTY_PRINT; use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; use function count; use function is_array; use function is_object; use function json_decode; use function json_encode; use function json_last_error; use function ksort; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Json { /** * @throws InvalidJsonException */ public static function prettify(string $json): string { $decodedJson = json_decode($json, false); if (json_last_error()) { throw new InvalidJsonException; } return json_encode($decodedJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } /** * To allow comparison of JSON strings, first process them into a consistent * format so that they can be compared as strings. * * @return array ($error, $canonicalized_json) The $error parameter is used * to indicate an error decoding the json. This is used to avoid ambiguity * with JSON strings consisting entirely of 'null' or 'false'. */ public static function canonicalize(string $json): array { $decodedJson = json_decode($json); if (json_last_error()) { return [true, null]; } self::recursiveSort($decodedJson); $reencodedJson = json_encode($decodedJson); return [false, $reencodedJson]; } /** * JSON object keys are unordered while PHP array keys are ordered. * * Sort all array keys to ensure both the expected and actual values have * their keys in the same order. */ private static function recursiveSort(mixed &$json): void { if (!is_array($json)) { // If the object is not empty, change it to an associative array // so we can sort the keys (and we will still re-encode it // correctly, since PHP encodes associative arrays as JSON objects.) // But EMPTY objects MUST remain empty objects. (Otherwise we will // re-encode it as a JSON array rather than a JSON object.) // See #2919. if (is_object($json) && count((array) $json) > 0) { $json = (array) $json; } else { return; } } ksort($json); foreach ($json as &$value) { self::recursiveSort($value); } } } Exception/PhpProcessException.php 0000644 00000001000 15111215677 0013155 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util\PHP; use PHPUnit\Util\Exception; use RuntimeException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class PhpProcessException extends RuntimeException implements Exception { } Exception/InvalidDirectoryException.php 0000644 00000001316 15111215677 0014354 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function sprintf; use RuntimeException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidDirectoryException extends RuntimeException implements Exception { public function __construct(string $directory) { parent::__construct( sprintf( '"%s" is not a directory', $directory, ), ); } } Exception/XmlException.php 0000644 00000000771 15111215677 0011645 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util\Xml; use PHPUnit\Util\Exception; use RuntimeException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class XmlException extends RuntimeException implements Exception { } Exception/InvalidJsonException.php 0000644 00000000741 15111215677 0013322 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use RuntimeException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidJsonException extends RuntimeException implements Exception { } Exception/Exception.php 0000644 00000000665 15111215677 0011166 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use Throwable; /** * @internal This interface is not covered by the backward compatibility promise for PHPUnit */ interface Exception extends Throwable { } Exception/InvalidVersionOperatorException.php 0000644 00000001351 15111215677 0015550 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function sprintf; use RuntimeException; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class InvalidVersionOperatorException extends RuntimeException implements Exception { public function __construct(string $operator) { parent::__construct( sprintf( '"%s" is not a valid version_compare() operator', $operator, ), ); } } GlobalState.php 0000644 00000021307 15111215677 0007467 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use const PHP_MAJOR_VERSION; use const PHP_MINOR_VERSION; use function array_keys; use function array_reverse; use function array_shift; use function defined; use function get_defined_constants; use function get_included_files; use function in_array; use function ini_get_all; use function is_array; use function is_file; use function is_scalar; use function preg_match; use function serialize; use function sprintf; use function str_ends_with; use function str_starts_with; use function strtr; use function var_export; use Closure; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class GlobalState { /** * @psalm-var list<string> */ private const SUPER_GLOBAL_ARRAYS = [ '_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', ]; /** * @psalm-var array<string, array<string, true>> */ private const DEPRECATED_INI_SETTINGS = [ '7.3' => [ 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, 'iconv.internal_encoding' => true, 'mbstring.func_overload' => true, 'mbstring.http_input' => true, 'mbstring.http_output' => true, 'mbstring.internal_encoding' => true, 'string.strip_tags' => true, ], '7.4' => [ 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, 'iconv.internal_encoding' => true, 'mbstring.func_overload' => true, 'mbstring.http_input' => true, 'mbstring.http_output' => true, 'mbstring.internal_encoding' => true, 'pdo_odbc.db2_instance_name' => true, 'string.strip_tags' => true, ], '8.0' => [ 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, 'iconv.internal_encoding' => true, 'mbstring.http_input' => true, 'mbstring.http_output' => true, 'mbstring.internal_encoding' => true, ], '8.1' => [ 'auto_detect_line_endings' => true, 'filter.default' => true, 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, 'iconv.internal_encoding' => true, 'mbstring.http_input' => true, 'mbstring.http_output' => true, 'mbstring.internal_encoding' => true, 'oci8.old_oci_close_semantics' => true, ], '8.2' => [ 'auto_detect_line_endings' => true, 'filter.default' => true, 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, 'iconv.internal_encoding' => true, 'mbstring.http_input' => true, 'mbstring.http_output' => true, 'mbstring.internal_encoding' => true, 'oci8.old_oci_close_semantics' => true, ], '8.3' => [ 'auto_detect_line_endings' => true, 'filter.default' => true, 'iconv.input_encoding' => true, 'iconv.output_encoding' => true, 'iconv.internal_encoding' => true, 'mbstring.http_input' => true, 'mbstring.http_output' => true, 'mbstring.internal_encoding' => true, 'oci8.old_oci_close_semantics' => true, ], ]; /** * @throws Exception */ public static function getIncludedFilesAsString(): string { return self::processIncludedFilesAsString(get_included_files()); } /** * @psalm-param list<string> $files * * @throws Exception */ public static function processIncludedFilesAsString(array $files): string { $excludeList = new ExcludeList; $prefix = false; $result = ''; if (defined('__PHPUNIT_PHAR__')) { $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; } // Do not process bootstrap script array_shift($files); // If bootstrap script was a Composer bin proxy, skip the second entry as well if (str_ends_with(strtr($files[0], '\\', '/'), '/phpunit/phpunit/phpunit')) { array_shift($files); } foreach (array_reverse($files) as $file) { if (!empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) && in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) { continue; } if ($prefix !== false && str_starts_with($file, $prefix)) { continue; } // Skip virtual file system protocols if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { continue; } if (!$excludeList->isExcluded($file) && is_file($file)) { $result = 'require_once \'' . $file . "';\n" . $result; } } return $result; } public static function getIniSettingsAsString(): string { $result = ''; foreach (ini_get_all(null, false) as $key => $value) { if (self::isIniSettingDeprecated($key)) { continue; } $result .= sprintf( '@ini_set(%s, %s);' . "\n", self::exportVariable($key), self::exportVariable((string) $value), ); } return $result; } public static function getConstantsAsString(): string { $constants = get_defined_constants(true); $result = ''; if (isset($constants['user'])) { foreach ($constants['user'] as $name => $value) { $result .= sprintf( 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, self::exportVariable($value), ); } } return $result; } public static function getGlobalsAsString(): string { $result = ''; foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) { if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) { if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { continue; } $result .= sprintf( '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", $superGlobalArray, $key, self::exportVariable($GLOBALS[$superGlobalArray][$key]), ); } } } $excludeList = self::SUPER_GLOBAL_ARRAYS; $excludeList[] = 'GLOBALS'; foreach (array_keys($GLOBALS) as $key) { if (!$GLOBALS[$key] instanceof Closure && !in_array($key, $excludeList, true)) { $result .= sprintf( '$GLOBALS[\'%s\'] = %s;' . "\n", $key, self::exportVariable($GLOBALS[$key]), ); } } return $result; } private static function exportVariable(mixed $variable): string { if (is_scalar($variable) || $variable === null || (is_array($variable) && self::arrayOnlyContainsScalars($variable))) { return var_export($variable, true); } return 'unserialize(' . var_export(serialize($variable), true) . ')'; } private static function arrayOnlyContainsScalars(array $array): bool { $result = true; foreach ($array as $element) { if (is_array($element)) { $result = self::arrayOnlyContainsScalars($element); } elseif (!is_scalar($element) && $element !== null) { $result = false; } if (!$result) { break; } } return $result; } private static function isIniSettingDeprecated(string $iniSetting): bool { return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]); } } Xml/Loader.php 0000644 00000006130 15111215677 0007231 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util\Xml; use function chdir; use function dirname; use function error_reporting; use function file_get_contents; use function getcwd; use function libxml_get_errors; use function libxml_use_internal_errors; use function sprintf; use DOMDocument; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Loader { /** * @throws XmlException */ public function loadFile(string $filename): DOMDocument { $reporting = error_reporting(0); $contents = file_get_contents($filename); error_reporting($reporting); if ($contents === false) { throw new XmlException( sprintf( 'Could not read XML from file "%s"', $filename, ), ); } return $this->load($contents, $filename); } /** * @throws XmlException */ public function load(string $actual, ?string $filename = null): DOMDocument { if ($actual === '') { if ($filename === null) { throw new XmlException('Could not parse XML from empty string'); } throw new XmlException( sprintf( 'Could not parse XML from empty file "%s"', $filename, ), ); } $document = new DOMDocument; $document->preserveWhiteSpace = false; $internal = libxml_use_internal_errors(true); $message = ''; $reporting = error_reporting(0); // Required for XInclude if ($filename !== null) { // Required for XInclude on Windows if (PHP_OS_FAMILY === 'Windows') { $cwd = getcwd(); @chdir(dirname($filename)); } $document->documentURI = $filename; } $loaded = $document->loadXML($actual); if ($filename !== null) { $document->xinclude(); } foreach (libxml_get_errors() as $error) { $message .= "\n" . $error->message; } libxml_use_internal_errors($internal); error_reporting($reporting); if (isset($cwd)) { @chdir($cwd); } if ($loaded === false || $message !== '') { if ($filename !== null) { throw new XmlException( sprintf( 'Could not load "%s"%s', $filename, $message !== '' ? ":\n" . $message : '', ), ); } if ($message === '') { $message = 'Could not load XML for unknown reason'; } throw new XmlException($message); } return $document; } } Xml/Xml.php 0000644 00000004051 15111215677 0006563 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use const ENT_QUOTES; use function htmlspecialchars; use function mb_convert_encoding; use function ord; use function preg_replace; use function strlen; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Xml { /** * Escapes a string for the use in XML documents. * * Any Unicode character is allowed, excluding the surrogate blocks, FFFE, * and FFFF (not even as character reference). * * @see https://www.w3.org/TR/xml/#charsets */ public static function prepareString(string $string): string { return preg_replace( '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', '', htmlspecialchars( self::convertToUtf8($string), ENT_QUOTES, ), ); } private static function convertToUtf8(string $string): string { if (!self::isUtf8($string)) { $string = mb_convert_encoding($string, 'UTF-8'); } return $string; } private static function isUtf8(string $string): bool { $length = strlen($string); for ($i = 0; $i < $length; $i++) { if (ord($string[$i]) < 0x80) { $n = 0; } elseif ((ord($string[$i]) & 0xE0) === 0xC0) { $n = 1; } elseif ((ord($string[$i]) & 0xF0) === 0xE0) { $n = 2; } elseif ((ord($string[$i]) & 0xF0) === 0xF0) { $n = 3; } else { return false; } for ($j = 0; $j < $n; $j++) { if ((++$i === $length) || ((ord($string[$i]) & 0xC0) !== 0x80)) { return false; } } } return true; } } Filter.php 0000644 00000006606 15111215677 0006520 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function array_unshift; use function defined; use function in_array; use function is_file; use function realpath; use function sprintf; use function str_starts_with; use PHPUnit\Framework\Exception; use PHPUnit\Framework\PhptAssertionFailedError; use Throwable; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Filter { /** * @throws Exception */ public static function getFilteredStacktrace(Throwable $t): string { $filteredStacktrace = ''; if ($t instanceof PhptAssertionFailedError) { $eTrace = $t->syntheticTrace(); $eFile = $t->syntheticFile(); $eLine = $t->syntheticLine(); } elseif ($t instanceof Exception) { $eTrace = $t->getSerializableTrace(); $eFile = $t->getFile(); $eLine = $t->getLine(); } else { if ($t->getPrevious()) { $t = $t->getPrevious(); } $eTrace = $t->getTrace(); $eFile = $t->getFile(); $eLine = $t->getLine(); } if (!self::frameExists($eTrace, $eFile, $eLine)) { array_unshift( $eTrace, ['file' => $eFile, 'line' => $eLine], ); } $prefix = defined('__PHPUNIT_PHAR_ROOT__') ? __PHPUNIT_PHAR_ROOT__ : false; $excludeList = new ExcludeList; foreach ($eTrace as $frame) { if (self::shouldPrintFrame($frame, $prefix, $excludeList)) { $filteredStacktrace .= sprintf( "%s:%s\n", $frame['file'], $frame['line'] ?? '?', ); } } return $filteredStacktrace; } private static function shouldPrintFrame(array $frame, false|string $prefix, ExcludeList $excludeList): bool { if (!isset($frame['file'])) { return false; } $file = $frame['file']; $fileIsNotPrefixed = $prefix === false || !str_starts_with($file, $prefix); // @see https://github.com/sebastianbergmann/phpunit/issues/4033 if (isset($GLOBALS['_SERVER']['SCRIPT_NAME'])) { $script = realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); } else { $script = ''; } return is_file($file) && self::fileIsExcluded($file, $excludeList) && $fileIsNotPrefixed && $file !== $script; } private static function fileIsExcluded(string $file, ExcludeList $excludeList): bool { return (empty($GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST']) || !in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'], true)) && !$excludeList->isExcluded($file); } private static function frameExists(array $trace, string $file, int $line): bool { foreach ($trace as $frame) { if (isset($frame['file'], $frame['line']) && $frame['file'] === $file && $frame['line'] === $line) { return true; } } return false; } } Filesystem.php 0000644 00000001156 15111215677 0007412 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function is_dir; use function mkdir; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Filesystem { public static function createDirectory(string $directory): bool { return !(!is_dir($directory) && !@mkdir($directory, 0o777, true) && !is_dir($directory)); } } ExcludeList.php 0000644 00000012635 15111215677 0007517 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function class_exists; use function defined; use function dirname; use function is_dir; use function realpath; use function str_starts_with; use function sys_get_temp_dir; use Composer\Autoload\ClassLoader; use DeepCopy\DeepCopy; use PharIo\Manifest\Manifest; use PharIo\Version\Version as PharIoVersion; use PhpParser\Parser; use PHPUnit\Framework\TestCase; use ReflectionClass; use SebastianBergmann\CliParser\Parser as CliParser; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeUnit\CodeUnit; use SebastianBergmann\CodeUnitReverseLookup\Wizard; use SebastianBergmann\Comparator\Comparator; use SebastianBergmann\Complexity\Calculator; use SebastianBergmann\Diff\Diff; use SebastianBergmann\Environment\Runtime; use SebastianBergmann\Exporter\Exporter; use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; use SebastianBergmann\GlobalState\Snapshot; use SebastianBergmann\Invoker\Invoker; use SebastianBergmann\LinesOfCode\Counter; use SebastianBergmann\ObjectEnumerator\Enumerator; use SebastianBergmann\RecursionContext\Context; use SebastianBergmann\Template\Template; use SebastianBergmann\Timer\Timer; use SebastianBergmann\Type\TypeName; use SebastianBergmann\Version; use TheSeer\Tokenizer\Tokenizer; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit */ final class ExcludeList { /** * @psalm-var array<string,int> */ private const EXCLUDED_CLASS_NAMES = [ // composer ClassLoader::class => 1, // myclabs/deepcopy DeepCopy::class => 1, // nikic/php-parser Parser::class => 1, // phar-io/manifest Manifest::class => 1, // phar-io/version PharIoVersion::class => 1, // phpunit/phpunit TestCase::class => 2, // phpunit/php-code-coverage CodeCoverage::class => 1, // phpunit/php-file-iterator FileIteratorFacade::class => 1, // phpunit/php-invoker Invoker::class => 1, // phpunit/php-text-template Template::class => 1, // phpunit/php-timer Timer::class => 1, // sebastian/cli-parser CliParser::class => 1, // sebastian/code-unit CodeUnit::class => 1, // sebastian/code-unit-reverse-lookup Wizard::class => 1, // sebastian/comparator Comparator::class => 1, // sebastian/complexity Calculator::class => 1, // sebastian/diff Diff::class => 1, // sebastian/environment Runtime::class => 1, // sebastian/exporter Exporter::class => 1, // sebastian/global-state Snapshot::class => 1, // sebastian/lines-of-code Counter::class => 1, // sebastian/object-enumerator Enumerator::class => 1, // sebastian/recursion-context Context::class => 1, // sebastian/type TypeName::class => 1, // sebastian/version Version::class => 1, // theseer/tokenizer Tokenizer::class => 1, ]; /** * @psalm-var list<string> */ private static array $directories = []; private static bool $initialized = false; private readonly bool $enabled; /** * @psalm-param non-empty-string $directory * * @throws InvalidDirectoryException */ public static function addDirectory(string $directory): void { if (!is_dir($directory)) { throw new InvalidDirectoryException($directory); } self::$directories[] = realpath($directory); } public function __construct(?bool $enabled = null) { if ($enabled === null) { $enabled = !defined('PHPUNIT_TESTSUITE'); } $this->enabled = $enabled; } /** * @psalm-return list<string> */ public function getExcludedDirectories(): array { self::initialize(); return self::$directories; } public function isExcluded(string $file): bool { if (!$this->enabled) { return false; } self::initialize(); foreach (self::$directories as $directory) { if (str_starts_with($file, $directory)) { return true; } } return false; } private static function initialize(): void { if (self::$initialized) { return; } foreach (self::EXCLUDED_CLASS_NAMES as $className => $parent) { if (!class_exists($className)) { continue; } $directory = (new ReflectionClass($className))->getFileName(); for ($i = 0; $i < $parent; $i++) { $directory = dirname($directory); } self::$directories[] = $directory; } // Hide process isolation workaround on Windows. if (PHP_OS_FAMILY === 'Windows') { // tempnam() prefix is limited to first 3 chars. // @see https://php.net/manual/en/function.tempnam.php self::$directories[] = sys_get_temp_dir() . '\\PHP'; } self::$initialized = true; } } Test.php 0000644 00000001651 15111215677 0006205 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function str_starts_with; use PHPUnit\Metadata\Parser\Registry; use ReflectionMethod; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Test { public static function isTestMethod(ReflectionMethod $method): bool { if (!$method->isPublic()) { return false; } if (str_starts_with($method->getName(), 'test')) { return true; } $metadata = Registry::parser()->forMethod( $method->getDeclaringClass()->getName(), $method->getName(), ); return $metadata->isTest()->isNotEmpty(); } } Exporter.php 0000644 00000002221 15111215677 0007070 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function is_array; use function is_scalar; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Exporter { public static function export(mixed $value, bool $exportObjects = false): string { if (self::isScalarOrArrayOfScalars($value) || $exportObjects) { return (new \SebastianBergmann\Exporter\Exporter)->export($value); } return '{enable export of objects to see this value}'; } private static function isScalarOrArrayOfScalars(mixed $value): bool { if (is_scalar($value)) { return true; } if (!is_array($value)) { return false; } foreach ($value as $_value) { if (!self::isScalarOrArrayOfScalars($_value)) { return false; } } return true; } } PHP/DefaultPhpProcess.php 0000644 00000010076 15111215677 0011311 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util\PHP; use function array_merge; use function fclose; use function file_put_contents; use function fwrite; use function is_array; use function is_resource; use function proc_close; use function proc_open; use function rewind; use function stream_get_contents; use function sys_get_temp_dir; use function tempnam; use function unlink; use PHPUnit\Framework\Exception; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ class DefaultPhpProcess extends AbstractPhpProcess { private ?string $tempFile = null; /** * Runs a single job (PHP code) using a separate PHP process. * * @psalm-return array{stdout: string, stderr: string} * * @throws Exception * @throws PhpProcessException */ public function runJob(string $job, array $settings = []): array { if ($this->stdin || $this->useTemporaryFile()) { if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'phpunit_')) || file_put_contents($this->tempFile, $job) === false) { throw new PhpProcessException( 'Unable to write temporary file', ); } $job = $this->stdin; } return $this->runProcess($job, $settings); } /** * Returns an array of file handles to be used in place of pipes. */ protected function getHandles(): array { return []; } /** * Handles creating the child process and returning the STDOUT and STDERR. * * @psalm-return array{stdout: string, stderr: string} * * @throws Exception * @throws PhpProcessException */ protected function runProcess(string $job, array $settings): array { $handles = $this->getHandles(); $env = null; if ($this->env) { $env = $_SERVER ?? []; unset($env['argv'], $env['argc']); $env = array_merge($env, $this->env); foreach ($env as $envKey => $envVar) { if (is_array($envVar)) { unset($env[$envKey]); } } } $pipeSpec = [ 0 => $handles[0] ?? ['pipe', 'r'], 1 => $handles[1] ?? ['pipe', 'w'], 2 => $handles[2] ?? ['pipe', 'w'], ]; $process = proc_open( $this->getCommand($settings, $this->tempFile), $pipeSpec, $pipes, null, $env, ); if (!is_resource($process)) { throw new PhpProcessException( 'Unable to spawn worker process', ); } if ($job) { $this->process($pipes[0], $job); } fclose($pipes[0]); $stderr = $stdout = ''; if (isset($pipes[1])) { $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); } if (isset($pipes[2])) { $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); } if (isset($handles[1])) { rewind($handles[1]); $stdout = stream_get_contents($handles[1]); fclose($handles[1]); } if (isset($handles[2])) { rewind($handles[2]); $stderr = stream_get_contents($handles[2]); fclose($handles[2]); } proc_close($process); $this->cleanup(); return ['stdout' => $stdout, 'stderr' => $stderr]; } /** * @param resource $pipe */ protected function process($pipe, string $job): void { fwrite($pipe, $job); } protected function cleanup(): void { if ($this->tempFile) { unlink($this->tempFile); } } protected function useTemporaryFile(): bool { return false; } } PHP/Template/TestCaseMethod.tpl 0000644 00000006226 15111215677 0012357 0 ustar 00 <?php declare(strict_types=1); use PHPUnit\Event\Facade; use PHPUnit\Runner\CodeCoverage; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry; use PHPUnit\TextUI\XmlConfiguration\Loader; use PHPUnit\TextUI\Configuration\PhpHandler; use PHPUnit\TestRunner\TestResult\PassedTests; // php://stdout does not obey output buffering. Any output would break // unserialization of child process results in the parent process. if (!defined('STDOUT')) { define('STDOUT', fopen('php://temp', 'w+b')); define('STDERR', fopen('php://stderr', 'wb')); } {iniSettings} ini_set('display_errors', 'stderr'); set_include_path('{include_path}'); $composerAutoload = {composerAutoload}; $phar = {phar}; ob_start(); if ($composerAutoload) { require_once $composerAutoload; define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload); } else if ($phar) { require $phar; } function __phpunit_run_isolated_test() { $dispatcher = Facade::instance()->initForIsolation( PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( {offsetSeconds}, {offsetNanoseconds} ), {exportObjects}, ); require_once '{filename}'; if ({collectCodeCoverageInformation}) { CodeCoverage::instance()->init(ConfigurationRegistry::get(), CodeCoverageFilterRegistry::instance(), true); CodeCoverage::instance()->ignoreLines({linesToBeIgnored}); } $test = new {className}('{methodName}'); $test->setData('{dataName}', unserialize('{data}')); $test->setDependencyInput(unserialize('{dependencyInput}')); $test->setInIsolation(true); ob_end_clean(); $test->run(); $output = ''; if (!$test->expectsOutput()) { $output = $test->output(); } ini_set('xdebug.scream', '0'); // Not every STDOUT target stream is rewindable @rewind(STDOUT); if ($stdout = @stream_get_contents(STDOUT)) { $output = $stdout . $output; $streamMetaData = stream_get_meta_data(STDOUT); if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { @ftruncate(STDOUT, 0); @rewind(STDOUT); } } file_put_contents( '{processResultFile}', serialize( [ 'testResult' => $test->result(), 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, 'numAssertions' => $test->numberOfAssertionsPerformed(), 'output' => $output, 'events' => $dispatcher->flush(), 'passedTests' => PassedTests::instance() ] ) ); } function __phpunit_error_handler($errno, $errstr, $errfile, $errline) { return true; } set_error_handler('__phpunit_error_handler'); {constants} {included_files} {globals} restore_error_handler(); ConfigurationRegistry::loadFrom('{serializedConfiguration}'); (new PhpHandler)->handle(ConfigurationRegistry::get()->php()); if ('{bootstrap}' !== '') { require_once '{bootstrap}'; } __phpunit_run_isolated_test(); PHP/Template/PhptTestCase.tpl 0000644 00000002243 15111215677 0012045 0 ustar 00 <?php declare(strict_types=1); use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Driver\Selector; use SebastianBergmann\CodeCoverage\Filter; $composerAutoload = {composerAutoload}; $phar = {phar}; ob_start(); $GLOBALS['__PHPUNIT_ISOLATION_EXCLUDE_LIST'][] = '{job}'; if ($composerAutoload) { require_once $composerAutoload; define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload); } else if ($phar) { require $phar; } $coverage = null; if ('{bootstrap}' !== '') { require_once '{bootstrap}'; } if (class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) { $filter = new Filter; $coverage = new CodeCoverage( (new Selector)->{driverMethod}($filter), $filter ); if ({codeCoverageCacheDirectory}) { $coverage->cacheStaticAnalysis({codeCoverageCacheDirectory}); } $coverage->start(__FILE__); } register_shutdown_function( function() use ($coverage) { $output = null; if ($coverage) { $output = $coverage->stop(); } file_put_contents('{coverageFile}', serialize($output)); } ); ob_end_clean(); require '{job}'; PHP/Template/TestCaseClass.tpl 0000644 00000006220 15111215677 0012176 0 ustar 00 <?php declare(strict_types=1); use PHPUnit\Event\Facade; use PHPUnit\Runner\CodeCoverage; use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry; use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry; use PHPUnit\TextUI\XmlConfiguration\Loader; use PHPUnit\TextUI\Configuration\PhpHandler; use PHPUnit\TestRunner\TestResult\PassedTests; // php://stdout does not obey output buffering. Any output would break // unserialization of child process results in the parent process. if (!defined('STDOUT')) { define('STDOUT', fopen('php://temp', 'w+b')); define('STDERR', fopen('php://stderr', 'wb')); } {iniSettings} ini_set('display_errors', 'stderr'); set_include_path('{include_path}'); $composerAutoload = {composerAutoload}; $phar = {phar}; ob_start(); if ($composerAutoload) { require_once $composerAutoload; define('PHPUNIT_COMPOSER_INSTALL', $composerAutoload); } else if ($phar) { require $phar; } function __phpunit_run_isolated_test() { $dispatcher = Facade::instance()->initForIsolation( PHPUnit\Event\Telemetry\HRTime::fromSecondsAndNanoseconds( {offsetSeconds}, {offsetNanoseconds} ), {exportObjects}, ); require_once '{filename}'; if ({collectCodeCoverageInformation}) { CodeCoverage::instance()->init(ConfigurationRegistry::get(), CodeCoverageFilterRegistry::instance(), true); CodeCoverage::instance()->ignoreLines({linesToBeIgnored}); } $test = new {className}('{name}'); $test->setData('{dataName}', unserialize('{data}')); $test->setDependencyInput(unserialize('{dependencyInput}')); $test->setInIsolation(true); ob_end_clean(); $test->run(); $output = ''; if (!$test->expectsOutput()) { $output = $test->output(); } ini_set('xdebug.scream', '0'); // Not every STDOUT target stream is rewindable @rewind(STDOUT); if ($stdout = @stream_get_contents(STDOUT)) { $output = $stdout . $output; $streamMetaData = stream_get_meta_data(STDOUT); if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { @ftruncate(STDOUT, 0); @rewind(STDOUT); } } file_put_contents( '{processResultFile}', serialize( [ 'testResult' => $test->result(), 'codeCoverage' => {collectCodeCoverageInformation} ? CodeCoverage::instance()->codeCoverage() : null, 'numAssertions' => $test->numberOfAssertionsPerformed(), 'output' => $output, 'events' => $dispatcher->flush(), 'passedTests' => PassedTests::instance() ] ) ); } function __phpunit_error_handler($errno, $errstr, $errfile, $errline) { return true; } set_error_handler('__phpunit_error_handler'); {constants} {included_files} {globals} restore_error_handler(); ConfigurationRegistry::loadFrom('{serializedConfiguration}'); (new PhpHandler)->handle(ConfigurationRegistry::get()->php()); if ('{bootstrap}' !== '') { require_once '{bootstrap}'; } __phpunit_run_isolated_test(); PHP/WindowsPhpProcess.php 0000644 00000002034 15111215677 0011352 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util\PHP; use function tmpfile; use PHPUnit\Framework\Exception; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit * * @see https://bugs.php.net/bug.php?id=51800 */ final class WindowsPhpProcess extends DefaultPhpProcess { /** * @throws Exception * @throws PhpProcessException */ protected function getHandles(): array { if (false === $stdout_handle = tmpfile()) { throw new PhpProcessException( 'A temporary file could not be created; verify that your TEMP environment variable is writable', ); } return [ 1 => $stdout_handle, ]; } protected function useTemporaryFile(): bool { return true; } } PHP/AbstractPhpProcess.php 0000644 00000020436 15111215677 0011471 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util\PHP; use const PHP_SAPI; use function array_keys; use function array_merge; use function assert; use function escapeshellarg; use function file_exists; use function file_get_contents; use function ini_get_all; use function restore_error_handler; use function set_error_handler; use function trim; use function unlink; use function unserialize; use ErrorException; use PHPUnit\Event\Code\TestMethodBuilder; use PHPUnit\Event\Code\ThrowableBuilder; use PHPUnit\Event\Facade; use PHPUnit\Event\NoPreviousThrowableException; use PHPUnit\Event\TestData\MoreThanOneDataSetFromDataProviderException; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Exception; use PHPUnit\Framework\Test; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\CodeCoverage; use PHPUnit\TestRunner\TestResult\PassedTests; use SebastianBergmann\Environment\Runtime; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ abstract class AbstractPhpProcess { protected bool $stderrRedirection = false; protected string $stdin = ''; protected string $arguments = ''; /** * @psalm-var array<string, string> */ protected array $env = []; public static function factory(): self { if (PHP_OS_FAMILY === 'Windows') { return new WindowsPhpProcess; } return new DefaultPhpProcess; } /** * Defines if should use STDERR redirection or not. * * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. */ public function setUseStderrRedirection(bool $stderrRedirection): void { $this->stderrRedirection = $stderrRedirection; } /** * Returns TRUE if uses STDERR redirection or FALSE if not. */ public function useStderrRedirection(): bool { return $this->stderrRedirection; } /** * Sets the input string to be sent via STDIN. */ public function setStdin(string $stdin): void { $this->stdin = $stdin; } /** * Returns the input string to be sent via STDIN. */ public function getStdin(): string { return $this->stdin; } /** * Sets the string of arguments to pass to the php job. */ public function setArgs(string $arguments): void { $this->arguments = $arguments; } /** * Returns the string of arguments to pass to the php job. */ public function getArgs(): string { return $this->arguments; } /** * Sets the array of environment variables to start the child process with. * * @psalm-param array<string, string> $env */ public function setEnv(array $env): void { $this->env = $env; } /** * Returns the array of environment variables to start the child process with. */ public function getEnv(): array { return $this->env; } /** * Runs a single test in a separate PHP process. * * @throws \PHPUnit\Runner\Exception * @throws Exception * @throws MoreThanOneDataSetFromDataProviderException * @throws NoPreviousThrowableException */ public function runTestJob(string $job, Test $test, string $processResultFile): void { $_result = $this->runJob($job); $processResult = ''; if (file_exists($processResultFile)) { $processResult = file_get_contents($processResultFile); @unlink($processResultFile); } $this->processChildResult( $test, $processResult, $_result['stderr'], ); } /** * Returns the command based into the configurations. */ public function getCommand(array $settings, string $file = null): string { $runtime = new Runtime; $command = $runtime->getBinary(); if ($runtime->hasPCOV()) { $settings = array_merge( $settings, $runtime->getCurrentSettings( array_keys(ini_get_all('pcov')), ), ); } elseif ($runtime->hasXdebug()) { $settings = array_merge( $settings, $runtime->getCurrentSettings( array_keys(ini_get_all('xdebug')), ), ); } $command .= $this->settingsToParameters($settings); if (PHP_SAPI === 'phpdbg') { $command .= ' -qrr'; if (!$file) { $command .= 's='; } } if ($file) { $command .= ' ' . escapeshellarg($file); } if ($this->arguments) { if (!$file) { $command .= ' --'; } $command .= ' ' . $this->arguments; } if ($this->stderrRedirection) { $command .= ' 2>&1'; } return $command; } /** * Runs a single job (PHP code) using a separate PHP process. */ abstract public function runJob(string $job, array $settings = []): array; protected function settingsToParameters(array $settings): string { $buffer = ''; foreach ($settings as $setting) { $buffer .= ' -d ' . escapeshellarg($setting); } return $buffer; } /** * @throws \PHPUnit\Runner\Exception * @throws Exception * @throws MoreThanOneDataSetFromDataProviderException * @throws NoPreviousThrowableException */ private function processChildResult(Test $test, string $stdout, string $stderr): void { if (!empty($stderr)) { $exception = new Exception(trim($stderr)); assert($test instanceof TestCase); Facade::emitter()->testErrored( TestMethodBuilder::fromTestCase($test), ThrowableBuilder::from($exception), ); return; } set_error_handler( /** * @throws ErrorException */ static function (int $errno, string $errstr, string $errfile, int $errline): never { throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); }, ); try { $childResult = unserialize($stdout); restore_error_handler(); if ($childResult === false) { $exception = new AssertionFailedError('Test was run in child process and ended unexpectedly'); assert($test instanceof TestCase); Facade::emitter()->testErrored( TestMethodBuilder::fromTestCase($test), ThrowableBuilder::from($exception), ); Facade::emitter()->testFinished( TestMethodBuilder::fromTestCase($test), 0, ); } } catch (ErrorException $e) { restore_error_handler(); $childResult = false; $exception = new Exception(trim($stdout), 0, $e); assert($test instanceof TestCase); Facade::emitter()->testErrored( TestMethodBuilder::fromTestCase($test), ThrowableBuilder::from($exception), ); } if ($childResult !== false) { if (!empty($childResult['output'])) { $output = $childResult['output']; } Facade::instance()->forward($childResult['events']); PassedTests::instance()->import($childResult['passedTests']); assert($test instanceof TestCase); $test->setResult($childResult['testResult']); $test->addToAssertionCount($childResult['numAssertions']); if (CodeCoverage::instance()->isActive() && $childResult['codeCoverage'] instanceof \SebastianBergmann\CodeCoverage\CodeCoverage) { CodeCoverage::instance()->codeCoverage()->merge( $childResult['codeCoverage'], ); } } if (!empty($output)) { print $output; } } } Color.php 0000644 00000011207 15111215677 0006342 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use const DIRECTORY_SEPARATOR; use const PHP_EOL; use function array_map; use function count; use function explode; use function implode; use function max; use function min; use function preg_replace; use function preg_replace_callback; use function preg_split; use function sprintf; use function str_pad; use function strtr; use function trim; /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ final class Color { /** * @psalm-var array<string,string> */ private const WHITESPACE_MAP = [ ' ' => '·', "\t" => '⇥', ]; /** * @psalm-var array<string,string> */ private const WHITESPACE_EOL_MAP = [ ' ' => '·', "\t" => '⇥', "\n" => '↵', "\r" => '⟵', ]; /** * @psalm-var array<string,string> */ private static array $ansiCodes = [ 'reset' => '0', 'bold' => '1', 'dim' => '2', 'dim-reset' => '22', 'underlined' => '4', 'fg-default' => '39', 'fg-black' => '30', 'fg-red' => '31', 'fg-green' => '32', 'fg-yellow' => '33', 'fg-blue' => '34', 'fg-magenta' => '35', 'fg-cyan' => '36', 'fg-white' => '37', 'bg-default' => '49', 'bg-black' => '40', 'bg-red' => '41', 'bg-green' => '42', 'bg-yellow' => '43', 'bg-blue' => '44', 'bg-magenta' => '45', 'bg-cyan' => '46', 'bg-white' => '47', ]; public static function colorize(string $color, string $buffer): string { if (trim($buffer) === '') { return $buffer; } $codes = array_map('\trim', explode(',', $color)); $styles = []; foreach ($codes as $code) { if (isset(self::$ansiCodes[$code])) { $styles[] = self::$ansiCodes[$code] ?? ''; } } if (empty($styles)) { return $buffer; } return self::optimizeColor(sprintf("\x1b[%sm", implode(';', $styles)) . $buffer . "\x1b[0m"); } public static function colorizeTextBox(string $color, string $buffer): string { $lines = preg_split('/\r\n|\r|\n/', $buffer); $padding = max(array_map('\strlen', $lines)); $styledLines = []; foreach ($lines as $line) { $styledLines[] = self::colorize($color, str_pad($line, $padding)); } return implode(PHP_EOL, $styledLines); } public static function colorizePath(string $path, ?string $previousPath = null, bool $colorizeFilename = false): string { if ($previousPath === null) { $previousPath = ''; } $path = explode(DIRECTORY_SEPARATOR, $path); $previousPath = explode(DIRECTORY_SEPARATOR, $previousPath); for ($i = 0; $i < min(count($path), count($previousPath)); $i++) { if ($path[$i] === $previousPath[$i]) { $path[$i] = self::dim($path[$i]); } } if ($colorizeFilename) { $last = count($path) - 1; $path[$last] = preg_replace_callback( '/([\-_.]+|phpt$)/', static fn ($matches) => self::dim($matches[0]), $path[$last], ); } return self::optimizeColor(implode(self::dim(DIRECTORY_SEPARATOR), $path)); } public static function dim(string $buffer): string { if (trim($buffer) === '') { return $buffer; } return "\e[2m{$buffer}\e[22m"; } public static function visualizeWhitespace(string $buffer, bool $visualizeEOL = false): string { $replaceMap = $visualizeEOL ? self::WHITESPACE_EOL_MAP : self::WHITESPACE_MAP; return preg_replace_callback( '/\s+/', static fn ($matches) => self::dim(strtr($matches[0], $replaceMap)), $buffer, ); } private static function optimizeColor(string $buffer): string { return preg_replace( [ "/\e\\[22m\e\\[2m/", "/\e\\[([^m]*)m\e\\[([1-9][0-9;]*)m/", "/(\e\\[[^m]*m)+(\e\\[0m)/", ], [ '', "\e[$1;$2m", '$2', ], $buffer, ); } } VersionComparisonOperator.php 0000644 00000003045 15111215677 0012461 0 ustar 00 <?php declare(strict_types=1); /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann <sebastian@phpunit.de> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace PHPUnit\Util; use function in_array; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit * * @psalm-immutable */ final class VersionComparisonOperator { /** * @psalm-var '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' */ private readonly string $operator; /** * @psalm-param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator * * @throws InvalidVersionOperatorException */ public function __construct(string $operator) { $this->ensureOperatorIsValid($operator); $this->operator = $operator; } /** * @psalm-return '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' */ public function asString(): string { return $this->operator; } /** * @psalm-param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator * * @throws InvalidVersionOperatorException */ private function ensureOperatorIsValid(string $operator): void { if (!in_array($operator, ['<', 'lt', '<=', 'le', '>', 'gt', '>=', 'ge', '==', '=', 'eq', '!=', '<>', 'ne'], true)) { throw new InvalidVersionOperatorException($operator); } } }
Simpan