Файловый менеджер - Редактировать - /var/www/html/Tokens.zip
Ðазад
PK ! x V? ? KV.phpnu �Iw�� <?php // phpcs:disable MediaWiki.Commenting.FunctionComment.DefaultNullTypeParam -- T218324, T218816 declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; /** * Represents a Key-value pair. */ class KV implements \JsonSerializable { /** * Commonly a string, but where the key might be templated, * this can be an array of tokens even. * * @var string|Token|array<Token|string> */ public $k; /** @var string|Token|array<Token|string>|KV[] */ public $v; /** @var KVSourceRange|null wikitext source offsets */ public $srcOffsets; /** @var string|null wikitext source */ public $ksrc; /** @var string|null wikitext source */ public $vsrc; /** * @param string|Token|array<Token|string> $k * Commonly a string, but where the key might be templated, * this can be an array of tokens even. * @param string|Token|array<Token|string>|KV[] $v * The value: string, token, of an array of tokens * @param ?KVSourceRange $srcOffsets wikitext source offsets * @param ?string $ksrc * @param ?string $vsrc */ public function __construct( $k, $v, ?KVSourceRange $srcOffsets = null, ?string $ksrc = null, ?string $vsrc = null ) { $this->k = $k; $this->v = $v; $this->srcOffsets = $srcOffsets; if ( isset( $ksrc ) ) { $this->ksrc = $ksrc; } if ( isset( $vsrc ) ) { $this->vsrc = $vsrc; } } /** * BUG: When there are multiple matching attributes, Sanitizer lets the last one win * whereas this method is letting the first one win. This can introduce subtle bugs! * * Lookup a string key in a KV array and return the first matching KV object * * @param KV[]|null $kvs * @param string $key * @return ?KV */ public static function lookupKV( ?array $kvs, string $key ): ?KV { if ( $kvs === null ) { return null; } foreach ( $kvs as $kv ) { // PORT-FIXME: JS trim() will remove non-ASCII spaces (such as NBSP) too, // while PHP's won't. Does that matter? if ( is_string( $kv->k ) && trim( $kv->k ) === $key ) { return $kv; } } return null; } /** * Lookup a string key (first occurrence) in a KV array * and return the value of the KV object * * @param KV[]|null $kvs * @param string $key * @return string|Token|array<Token|string>|null */ public static function lookup( ?array $kvs, string $key ) { $kv = self::lookupKV( $kvs, $key ); // PORT_FIXME: Potential bug lurking here ... if $kv->v is an array // this will return a copy, which if modified will not reflect // in the original KV object. return $kv->v ?? null; } /** * Return the key portion of the KV's source offsets, or else null * if no source offsets are known. * @return SourceRange|null */ public function keyOffset(): ?SourceRange { return $this->srcOffsets->key ?? null; } /** * Return the value portion of the KV's source offsets, or else null * if no source offsets are known. * @return SourceRange|null */ public function valueOffset(): ?SourceRange { return $this->srcOffsets->value ?? null; } /** * @inheritDoc */ public function jsonSerialize(): array { $ret = [ "k" => $this->k, "v" => $this->v ]; if ( $this->srcOffsets ) { $ret["srcOffsets"] = $this->srcOffsets; } if ( isset( $this->ksrc ) ) { $ret["ksrc"] = $this->ksrc; } if ( isset( $this->vsrc ) ) { $ret["vsrc"] = $this->vsrc; } return $ret; } } PK ! %�d5� � InvalidTokenException.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use RuntimeException; /** * Thrown when a token is invalid */ class InvalidTokenException extends RuntimeException { } PK ! d�� � CommentTk.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Parsoid\NodeData\DataMw; use Wikimedia\Parsoid\NodeData\DataParsoid; /** * Represents a comment */ class CommentTk extends Token { /** @var string Comment text */ public $value; public function __construct( string $value, ?DataParsoid $dataParsoid = null, ?DataMw $dataMw = null ) { // $dataParsoid won't survive in the DOM, but still useful for token serialization // FIXME: verify if this is still required given that html->wt doesn't // use tokens anymore. That was circa 2012 serializer code. parent::__construct( $dataParsoid, $dataMw ); $this->value = $value; } /** * @inheritDoc */ public function jsonSerialize(): array { return [ 'type' => $this->getType(), 'value' => $this->value, 'dataParsoid' => $this->dataParsoid, 'dataMw' => $this->dataMw, ]; } } PK ! 9�A A EOFTk.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; /** * Represents EOF */ class EOFTk extends Token { public function __construct() { parent::__construct( null, null ); } /** * @inheritDoc */ public function jsonSerialize(): array { return [ 'type' => $this->getType() ]; } } PK ! ?�� KVSourceRange.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Assert\Assert; use Wikimedia\JsonCodec\JsonCodecable; use Wikimedia\JsonCodec\JsonCodecableTrait; /** * Represents a source offset range for a key-value pair. */ class KVSourceRange implements JsonCodecable { use JsonCodecableTrait; /** * Source range for the key. * @var SourceRange */ public $key; /** * Source range for the value. * @var SourceRange */ public $value; /** * Create a new key-value source offset range. * @param int $keyStart The start index of the key * (unicode code points, inclusive) * @param int $keyEnd The end index of the key * (unicode code points, exclusive) * @param int $valueStart The start index of the value * (unicode code points, inclusive) * @param int $valueEnd The end index of the value * (unicode code points, exclusive) */ public function __construct( int $keyStart, int $keyEnd, int $valueStart, int $valueEnd ) { $this->key = new SourceRange( $keyStart, $keyEnd ); $this->value = new SourceRange( $valueStart, $valueEnd ); } /** * Return a new key-value source offset range shifted by $amount. * @param int $amount The amount to shift by * @return KVSourceRange */ public function offset( int $amount ): KVSourceRange { return new KVSourceRange( $this->key->start + $amount, $this->key->end + $amount, $this->value->start + $amount, $this->value->end + $amount ); } /** * Create a new key-value source offset range from an array of * integers (such as created during JSON serialization). * @param int[] $so * @return KVSourceRange */ public static function newFromJsonArray( array $so ): KVSourceRange { Assert::invariant( count( $so ) === 4, 'Not enough elements in KVSourceRange array' ); return new KVSourceRange( $so[0], $so[1], $so[2], $so[3] ); } /** * @inheritDoc */ public function toJsonArray(): array { return [ $this->key->start, $this->key->end, $this->value->start, $this->value->end, ]; } } PK ! &)'&� � TagTk.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Parsoid\NodeData\DataMw; use Wikimedia\Parsoid\NodeData\DataParsoid; /** * HTML tag token */ class TagTk extends Token { /** @var string Name of the end tag */ private $name; /** * @param string $name * @param KV[] $attribs * @param ?DataParsoid $dataParsoid data-parsoid object * @param ?DataMw $dataMw data-mw object */ public function __construct( string $name, array $attribs = [], ?DataParsoid $dataParsoid = null, ?DataMw $dataMw = null ) { parent::__construct( $dataParsoid, $dataMw ); $this->name = $name; $this->attribs = $attribs; } public function getName(): string { return $this->name; } /** * @inheritDoc */ public function jsonSerialize(): array { return [ 'type' => $this->getType(), 'name' => $this->name, 'attribs' => $this->attribs, 'dataParsoid' => $this->dataParsoid, 'dataMw' => $this->dataMw, ]; } } PK ! Ml�آ � NlTk.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Parsoid\NodeData\DataMw; use Wikimedia\Parsoid\NodeData\DataParsoid; /** * Newline token. */ class NlTk extends Token { /** * @param ?SourceRange $tsr * TSR ("tag source range") represents the (start, end) wikitext * byte offsets for a token (in this case, the newline) in the * UTF8-encoded source string * @param ?DataParsoid $dataParsoid * @param ?DataMw $dataMw */ public function __construct( ?SourceRange $tsr, ?DataParsoid $dataParsoid = null, ?DataMw $dataMw = null ) { parent::__construct( $dataParsoid, $dataMw ); if ( $dataParsoid == null && $tsr !== null ) { $this->dataParsoid->tsr = $tsr; } } /** * @inheritDoc */ public function jsonSerialize(): array { return [ 'type' => $this->getType(), 'dataParsoid' => $this->dataParsoid, 'dataMw' => $this->dataMw, ]; } } PK ! w�<�� � SelfclosingTagTk.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Parsoid\NodeData\DataMw; use Wikimedia\Parsoid\NodeData\DataParsoid; /** * Token for a self-closing tag (HTML or otherwise) */ class SelfclosingTagTk extends Token { /** @var string Name of the end tag */ private $name; /** * @param string $name * @param KV[] $attribs * @param ?DataParsoid $dataParsoid * @param ?DataMw $dataMw */ public function __construct( string $name, array $attribs = [], ?DataParsoid $dataParsoid = null, ?DataMw $dataMw = null ) { parent::__construct( $dataParsoid, $dataMw ); $this->name = $name; $this->attribs = $attribs; } public function getName(): string { return $this->name; } /** * @inheritDoc */ public function jsonSerialize(): array { return [ 'type' => $this->getType(), 'name' => $this->name, 'attribs' => $this->attribs, 'dataParsoid' => $this->dataParsoid, 'dataMw' => $this->dataMw, ]; } } PK ! ,\]L@) @) Token.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use stdClass; use Wikimedia\Assert\Assert; use Wikimedia\JsonCodec\JsonCodec; use Wikimedia\Parsoid\NodeData\DataMw; use Wikimedia\Parsoid\NodeData\DataParsoid; use Wikimedia\Parsoid\Utils\DOMDataUtils; use Wikimedia\Parsoid\Wt2Html\Frame; /** * Catch-all class for all token types. */ abstract class Token implements \JsonSerializable { public DataParsoid $dataParsoid; public ?DataMw $dataMw = null; /** @var KV[] */ public $attribs; protected function __construct( ?DataParsoid $dataParsoid, ?DataMw $dataMw ) { $this->dataParsoid = $dataParsoid ?? new DataParsoid; $this->dataMw = $dataMw; } /** * @inheritDoc */ #[\ReturnTypeWillChange] abstract public function jsonSerialize(); /** * Get a name for the token. * Derived classes can override this. * @return string */ public function getName(): string { return $this->getType(); } /** * Returns a string key for this token * @return string */ public function getType(): string { $classParts = explode( '\\', get_class( $this ) ); return end( $classParts ); } /** * Generic set attribute method. * * @param string $name * Always a string when used this way. * The more complex form (where the key is a non-string) are found when * KV objects are constructed in the tokenizer. * @param string|Token|array<Token|string> $value * @param ?KVSourceRange $srcOffsets */ public function addAttribute( string $name, $value, ?KVSourceRange $srcOffsets = null ): void { $this->attribs[] = new KV( $name, $value, $srcOffsets ); } /** * Generic set attribute method with support for change detection. * Set a value and preserve the original wikitext that produced it. * * @param string $name * @param string|Token|array<Token|string> $value * @param mixed $origValue */ public function addNormalizedAttribute( string $name, $value, $origValue ): void { $this->addAttribute( $name, $value ); $this->setShadowInfo( $name, $value, $origValue ); } /** * Generic attribute accessor. * * @param string $name * @return string|Token|array<Token|string>|KV[]|null */ public function getAttributeV( string $name ) { return KV::lookup( $this->attribs, $name ); } /** * Generic attribute accessor. * * @param string $name * @return KV|null */ public function getAttributeKV( string $name ) { return KV::lookupKV( $this->attribs, $name ); } /** * Generic attribute accessor. * * @param string $name * @return bool */ public function hasAttribute( string $name ): bool { return $this->getAttributeKV( $name ) !== null; } /** * Set an unshadowed attribute. * * @param string $name * @param string|Token|array<Token|string> $value */ public function setAttribute( string $name, $value ): void { // First look for the attribute and change the last match if found. for ( $i = count( $this->attribs ) - 1; $i >= 0; $i-- ) { $kv = $this->attribs[$i]; $k = $kv->k; if ( is_string( $k ) && mb_strtolower( $k ) === $name ) { $kv->v = $value; $this->attribs[$i] = $kv; return; } } // Nothing found, just add the attribute $this->addAttribute( $name, $value ); } /** * Store the original value of an attribute in a token's dataParsoid. * * @param string $name * @param mixed $value * @param mixed $origValue */ public function setShadowInfo( string $name, $value, $origValue ): void { // Don't shadow if value is the same or the orig is null if ( $value !== $origValue && $origValue !== null ) { $this->dataParsoid->a ??= []; $this->dataParsoid->a[$name] = $value; $this->dataParsoid->sa ??= []; $this->dataParsoid->sa[$name] = $origValue; } } /** * Attribute info accessor for the wikitext serializer. Performs change * detection and uses unnormalized attribute values if set. Expects the * context to be set to a token. * * @param string $name * @return array Information about the shadow info attached to this attribute: * - value: (string|Token|array<Token|string>) * When modified is false and fromsrc is true, this is always a string. * - modified: (bool) * - fromsrc: (bool) */ public function getAttributeShadowInfo( string $name ): array { $curVal = $this->getAttributeV( $name ); // Not the case, continue regular round-trip information. if ( !property_exists( $this->dataParsoid, 'a' ) || !array_key_exists( $name, $this->dataParsoid->a ) ) { return [ "value" => $curVal, // Mark as modified if a new element "modified" => $this->dataParsoid->isModified(), "fromsrc" => false ]; } elseif ( $this->dataParsoid->a[$name] !== $curVal ) { return [ "value" => $curVal, "modified" => true, "fromsrc" => false ]; } elseif ( !property_exists( $this->dataParsoid, 'sa' ) || !array_key_exists( $name, $this->dataParsoid->sa ) ) { return [ "value" => $curVal, "modified" => false, "fromsrc" => false ]; } else { return [ "value" => $this->dataParsoid->sa[$name], "modified" => false, "fromsrc" => true ]; } } /** * Completely remove all attributes with this name. * * @param string $name */ public function removeAttribute( string $name ): void { foreach ( $this->attribs as $i => $kv ) { if ( mb_strtolower( $kv->k ) === $name ) { unset( $this->attribs[$i] ); } } $this->attribs = array_values( $this->attribs ); } /** * Add a space-separated property value. * These are Parsoid-added attributes, not something present in source. * So, only a regular ASCII space characters will be used here. * * @param string $name The attribute name * @param string $value The value to add to the attribute */ public function addSpaceSeparatedAttribute( string $name, string $value ): void { $curVal = $this->getAttributeKV( $name ); if ( $curVal !== null ) { if ( in_array( $value, explode( ' ', $curVal->v ), true ) ) { // value is already included, nothing to do. return; } // Value was not yet included in the existing attribute, just add // it separated with a space $this->setAttribute( $curVal->k, $curVal->v . ' ' . $value ); } else { // the attribute did not exist at all, just add it $this->addAttribute( $name, $value ); } } /** * Get the wikitext source of a token. * * @param Frame $frame * @return string */ public function getWTSource( Frame $frame ): string { $tsr = $this->dataParsoid->tsr ?? null; if ( !( $tsr instanceof SourceRange ) ) { throw new InvalidTokenException( 'Expected token to have tsr info.' ); } $srcText = $frame->getSrcText(); Assert::invariant( $tsr->end >= $tsr->start, 'Bad TSR' ); return $tsr->substr( $srcText ); } /** * Create key value set from an array * * @param array $a * @return array */ private static function kvsFromArray( array $a ): array { $kvs = []; foreach ( $a as $e ) { if ( is_array( $e["k"] ?? null ) ) { self::rebuildNestedTokens( $e["k"] ); } $v = $e['v'] ?? null; if ( is_array( $v ) ) { // $v is either an array of Tokens or an array of KVs if ( count( $v ) > 0 ) { if ( is_array( $v[0] ) && array_key_exists( 'k', $v[0] ) ) { $v = self::kvsFromArray( $v ); } else { self::rebuildNestedTokens( $v ); } } } $so = $e["srcOffsets"] ?? null; if ( $so ) { $so = KVSourceRange::newFromJsonArray( $so ); } $kvs[] = new KV( $e["k"] ?? null, $v, $so, $e["ksrc"] ?? null, $e["vsrc"] ?? null ); } return $kvs; } /** * @param iterable|stdClass|DataParsoid &$a */ private static function rebuildNestedTokens( &$a ): void { // objects do not count as iterables in PHP but can be iterated nevertheless // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach foreach ( $a as &$v ) { $v = self::getToken( $v ); } unset( $v ); // Future-proof protection } /** * Get a token from some PHP structure. Used by the PHPUnit tests. * * @param KV|Token|array|string|int|float|bool|null $input * @return Token|string|int|float|bool|null|array<Token|string|int|float|bool|null> */ public static function getToken( $input ) { if ( !$input ) { return $input; } if ( is_array( $input ) && isset( $input['type'] ) ) { $codec = new JsonCodec(); if ( isset( $input['dataParsoid'] ) ) { $da = $codec->newFromJsonArray( $input['dataParsoid'], DOMDataUtils::getCodecHints()['data-parsoid'] ); } else { $da = null; } if ( isset( $input['dataMw'] ) ) { $dmw = $codec->newFromJsonArray( $input['dataMw'], DOMDataUtils::getCodecHints()['data-mw'] ); } else { $dmw = null; } // In theory this should be refactored to use JsonCodecable // and remove the ad-hoc deserialization code here. switch ( $input['type'] ) { case "SelfclosingTagTk": $token = new SelfclosingTagTk( $input['name'], self::kvsFromArray( $input['attribs'] ), $da, $dmw ); break; case "TagTk": $token = new TagTk( $input['name'], self::kvsFromArray( $input['attribs'] ), $da, $dmw ); break; case "EndTagTk": $token = new EndTagTk( $input['name'], self::kvsFromArray( $input['attribs'] ), $da, $dmw ); break; case "NlTk": $token = new NlTk( $da->tsr ?? null, $da, $dmw ); break; case "EOFTk": $token = new EOFTk(); break; case "CommentTk": $token = new CommentTk( $input["value"], $da, $dmw ); break; default: // Looks like data-parsoid can have a 'type' property in some cases // We can change that usage and then throw an exception here $token = &$input; } } elseif ( is_array( $input ) ) { $token = &$input; } else { $token = $input; } if ( is_array( $token ) ) { self::rebuildNestedTokens( $token ); } elseif ( $token instanceof Token ) { if ( !empty( $token->attribs ) ) { self::rebuildNestedTokens( $token->attribs ); } self::rebuildNestedTokens( $token->dataParsoid ); } return $token; } public function fetchExpandedAttrValue( string $key ): ?string { if ( preg_match( '/mw:ExpandedAttrs/', $this->getAttributeV( 'typeof' ) ?? '' ) ) { $dmw = $this->dataMw; if ( !isset( $dmw->attribs ) ) { return null; } foreach ( $dmw->attribs as $attr ) { if ( ( $attr->key['txt'] ?? null ) === $key ) { return $attr->value['html'] ?? null; } } } return null; } } PK ! q�u}� � EndTagTk.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Parsoid\NodeData\DataMw; use Wikimedia\Parsoid\NodeData\DataParsoid; /** * Represents an HTML end tag token */ class EndTagTk extends Token { /** @var string Name of the end tag */ private $name; /** * @param string $name * @param KV[] $attribs * @param ?DataParsoid $dataParsoid * @param ?DataMw $dataMw */ public function __construct( string $name, array $attribs = [], ?DataParsoid $dataParsoid = null, ?DataMw $dataMw = null ) { parent::__construct( $dataParsoid, $dataMw ); $this->name = $name; $this->attribs = $attribs; } public function getName(): string { return $this->name; } /** * @inheritDoc */ public function jsonSerialize(): array { return [ 'type' => $this->getType(), 'name' => $this->name, 'attribs' => $this->attribs, 'dataParsoid' => $this->dataParsoid, 'dataMw' => $this->dataMw, ]; } } PK ! ���c SourceRange.phpnu �Iw�� <?php declare( strict_types = 1 ); namespace Wikimedia\Parsoid\Tokens; use Wikimedia\Assert\Assert; use Wikimedia\JsonCodec\JsonCodecable; use Wikimedia\JsonCodec\JsonCodecableTrait; use Wikimedia\Parsoid\Utils\PHPUtils; /** * Represents a source offset range. */ class SourceRange implements JsonCodecable { use JsonCodecableTrait; /** * Offset of the first character (range start is inclusive). * @var ?int */ public $start; /** * Offset just past the last character (range end is exclusive). * @var ?int */ public $end; /** * Create a new source offset range. * @param ?int $start The starting index (UTF-8 byte count, inclusive) * @param ?int $end The ending index (UTF-8 byte count, exclusive) */ public function __construct( ?int $start, ?int $end ) { $this->start = $start; $this->end = $end; } /** * Return a KVSourceRange where this SourceRange is the key, * and the value has zero length. * @return KVSourceRange */ public function expandTsrK(): KVSourceRange { return new KVSourceRange( $this->start, $this->end, $this->end, $this->end ); } /** * Return a KVSourceRange where this SourceRange is the value, * and the key has zero length. * @return KVSourceRange */ public function expandTsrV(): KVSourceRange { return new KVSourceRange( $this->start, $this->start, $this->start, $this->end ); } /** * Return a KVSourceRange by using this SourceRange for the key * and the given SourceRange parameter for the value. * @param SourceRange $value * @return KVSourceRange */ public function join( SourceRange $value ): KVSourceRange { return new KVSourceRange( $this->start, $this->end, $value->start, $value->end ); } /** * Return the substring of the given string corresponding to this * range. * @param string $str The source text string * @return string */ public function substr( string $str ): string { $start = $this->start; $length = $this->length(); Assert::invariant( ( $start ?? -1 ) >= 0, "Bad SourceRange start" ); // @phan-suppress-next-line PhanCoalescingNeverNull Assert::invariant( ( $length ?? -1 ) >= 0, "Bad SourceRange length" ); return PHPUtils::safeSubstr( $str, $start, $length ); } /** * Return a new source range shifted by $amount. * @param int $amount The amount to shift by * @return SourceRange */ public function offset( int $amount ): SourceRange { return new SourceRange( $this->start + $amount, $this->end + $amount ); } /** * Return a range from the end of this range to the start of the given * range. * @param SourceRange $sr * @return SourceRange */ public function to( SourceRange $sr ): SourceRange { return new SourceRange( $this->end, $sr->start ); } /** * Return the length of this source range. * @return int */ public function length(): int { return $this->end - $this->start; } /** * Create a new source offset range from an array of * integers (such as created during JSON serialization). * @param int[] $sr * @return SourceRange * @deprecated */ public static function fromArray( array $sr ): SourceRange { // Dynamic dispatch (DomSourceRange subclasses this) return static::newFromJsonArray( $sr ); } /** @inheritDoc */ public function toJsonArray(): array { return [ $this->start, $this->end ]; } /** @inheritDoc */ public static function newFromJsonArray( array $json ): SourceRange { Assert::invariant( count( $json ) === 2, 'Wrong # of elements in SourceRange array' ); return new SourceRange( $json[0], $json[1] ); } } PK ! x V? ? KV.phpnu �Iw�� PK ! %�d5� � u InvalidTokenException.phpnu �Iw�� PK ! d�� � � CommentTk.phpnu �Iw�� PK ! 9�A A A EOFTk.phpnu �Iw�� PK ! ?�� � KVSourceRange.phpnu �Iw�� PK ! &)'&� � TagTk.phpnu �Iw�� PK ! Ml�آ � NlTk.phpnu �Iw�� PK ! w�<�� � �# SelfclosingTagTk.phpnu �Iw�� PK ! ,\]L@) @) ( Token.phpnu �Iw�� PK ! q�u}� � �Q EndTagTk.phpnu �Iw�� PK ! ���c �U SourceRange.phpnu �Iw�� PK O �c
| ver. 1.1 | |
.
| PHP 8.4.18 | Ð“ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ñтраницы: 0 |
proxy
|
phpinfo
|
ÐаÑтройка