diff --git a/src/Json/JsonFile.php b/src/Json/JsonFile.php index c543845..1f02555 100644 --- a/src/Json/JsonFile.php +++ b/src/Json/JsonFile.php @@ -1,5 +1,7 @@ */ -class JsonFile extends BaseJsonFile +final class JsonFile extends \Composer\Json\JsonFile { /** - * @var string[] - */ - private $arrayKeys; - - /** - * @var int - */ - private $indent; - - /** - * @var string[] + * @psalm-var string[]|null */ - private static $encodeArrayKeys = array(); - + private array $arrayKeys = []; + private int|null $indent = null; /** - * @var int + * @psalm-var string[] */ - private static $encodeIndent = JsonFormatter::DEFAULT_INDENT; + private static array $encodeArrayKeys = []; + private static int $encodeIndent = JsonFormatter::DEFAULT_INDENT; /** * Get the list of keys to be retained with an array representation if they are empty. * - * @return string[] + * @psalm-return string[] */ - public function getArrayKeys() + public function getArrayKeys(): array { - if (null === $this->arrayKeys) { + if ([] === $this->arrayKeys) { $this->parseOriginalContent(); } @@ -56,10 +47,8 @@ public function getArrayKeys() /** * Get the indent for this json file. - * - * @return int */ - public function getIndent() + public function getIndent(): int { if (null === $this->indent) { $this->parseOriginalContent(); @@ -68,10 +57,7 @@ public function getIndent() return $this->indent; } - /** - * {@inheritdoc} - */ - public function read() + public function read(): array { $data = parent::read(); $this->getArrayKeys(); @@ -80,22 +66,19 @@ public function read() return $data; } - /** - * {@inheritdoc} - */ - public function write(array $hash, int $options = 448) + public function write(array $hash, int $options = 448): void { self::$encodeArrayKeys = $this->getArrayKeys(); self::$encodeIndent = $this->getIndent(); parent::write($hash, $options); - self::$encodeArrayKeys = array(); - self::$encodeIndent = JsonFormatter::DEFAULT_INDENT; + self::$encodeArrayKeys = []; + self::$encodeIndent = 4; } /** * {@inheritdoc} */ - public static function encode($data, int $options = 448, string $indent = self::INDENT_DEFAULT): string + public static function encode(mixed $data, int $options = 448, string $indent = self::INDENT_DEFAULT): string { $result = parent::encode($data, $options, $indent); @@ -105,7 +88,7 @@ public static function encode($data, int $options = 448, string $indent = self:: /** * Parse the original content. */ - private function parseOriginalContent() + private function parseOriginalContent(): void { $content = $this->exists() ? file_get_contents($this->getPath()) : ''; $this->arrayKeys = JsonFormatter::getArrayKeys($content); diff --git a/src/Json/JsonFormatter.php b/src/Json/JsonFormatter.php index 3b031bf..4927424 100644 --- a/src/Json/JsonFormatter.php +++ b/src/Json/JsonFormatter.php @@ -1,5 +1,7 @@ */ -class JsonFormatter +final class JsonFormatter { - const DEFAULT_INDENT = 4; - const ARRAY_KEYS_REGEX = '/["\']([\w\d_\-.]+)["\']:\s\[]/'; - const INDENT_REGEX = '/^[{\[][\r\n]([ ]+)["\']/'; + public const DEFAULT_INDENT = 4; + public const ARRAY_KEYS_REGEX = '/["\']([\w\d_\-.]+)["\']:\s\[]/'; + public const INDENT_REGEX = '/^[{\[][\r\n]([ ]+)["\']/'; /** * Get the list of keys to be retained with an array representation if they are empty. * - * @param string $content The content + * @param string $content The content. * - * @return string[] + * @psalm-return string[] The list of keys to be retained with an array representation if they are empty. */ - public static function getArrayKeys($content) + public static function getArrayKeys(string $content): array { preg_match_all(self::ARRAY_KEYS_REGEX, trim($content), $matches); - return !empty($matches) ? $matches[1] : array(); + return !empty($matches) ? $matches[1] : []; } /** * Get the indent of file. * * @param string $content The content - * - * @return int */ - public static function getIndent($content) + public static function getIndent(string $content): int { $indent = self::DEFAULT_INDENT; - preg_match(self::INDENT_REGEX, trim($content), $matches); + \preg_match(self::INDENT_REGEX, \trim($content), $matches); if (!empty($matches)) { $indent = \strlen($matches[1]); @@ -60,42 +58,79 @@ public static function getIndent($content) /** * Format the data in JSON. * - * @param string $json The original JSON - * @param string[] $arrayKeys The list of keys to be retained with an array representation if they are empty - * @param int $indent The space count for indent - * @param bool $formatJson Check if the json must be formatted + * @param string $json The original JSON. + * @param array $arrayKeys The list of keys to be retained with an array representation if they are empty. + * @param int $indent The space count for indent. + * @param bool $formatJson Check if the json must be formatted. * * @return string */ - public static function format($json, array $arrayKeys = array(), $indent = self::DEFAULT_INDENT, $formatJson = true) - { + public static function format( + string $json, + array $arrayKeys = [], + $indent = self::DEFAULT_INDENT, + $formatJson = true + ): string { if ($formatJson) { - $json = ComposerJsonFormatter::format($json, true, true); + $json = self::formatInternal($json, true, true); } - + if (4 !== $indent) { - $json = str_replace(' ', sprintf('%'.$indent.'s', ''), $json); + $json = \str_replace(' ', \str_repeat(' ', $indent), $json); } - + return self::replaceArrayByMap($json, $arrayKeys); } + /** + * Format the data in JSON. + * + * @param bool $unescapeUnicode Un escape unicode. + * @param bool $unescapeSlashes Un escape slashes. + */ + private static function formatInternal(string $json, bool $unescapeUnicode, bool $unescapeSlashes): string + { + $array = \json_decode($json, true); + + if ($unescapeUnicode) { + \array_walk_recursive($array, function (&$item) { + if (\is_string($item)) { + $item = \preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) { + return \mb_convert_encoding(\pack('H*', $match[1]), 'UTF-8', 'UCS-2BE'); + }, $item); + } + }); + } + + if ($unescapeSlashes) { + \array_walk_recursive($array, function (&$item) { + if (\is_string($item)) { + $item = \str_replace('\\/', '/', $item); + } + }); + } + + return \json_encode($array, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + } + /** * Replace the empty array by empty map. * - * @param string $json The original JSON - * @param string[] $arrayKeys The list of keys to be retained with an array representation if they are empty + * @param string $json The original JSON. + * @param array $arrayKeys The list of keys to be retained with an array representation if they are empty. + * + * @psalm-param string[] $arrayKeys The list of keys to be retained with an array representation if they are empty. * * @return string */ - private static function replaceArrayByMap($json, array $arrayKeys) + private static function replaceArrayByMap(string $json, array $arrayKeys): string { - preg_match_all(self::ARRAY_KEYS_REGEX, $json, $matches, PREG_SET_ORDER); + \preg_match_all(self::ARRAY_KEYS_REGEX, $json, $matches, PREG_SET_ORDER); foreach ($matches as $match) { if (!\in_array($match[1], $arrayKeys, true)) { - $replace = str_replace('[]', '{}', $match[0]); - $json = str_replace($match[0], $replace, $json); + $replace = \str_replace('[]', '{}', $match[0]); + $json = \str_replace($match[0], $replace, $json); } } diff --git a/resources/doc/config.md b/src/Resources/doc/config.md similarity index 100% rename from resources/doc/config.md rename to src/Resources/doc/config.md diff --git a/resources/doc/events.md b/src/Resources/doc/events.md similarity index 100% rename from resources/doc/events.md rename to src/Resources/doc/events.md diff --git a/resources/doc/faqs.md b/src/Resources/doc/faqs.md similarity index 100% rename from resources/doc/faqs.md rename to src/Resources/doc/faqs.md diff --git a/resources/doc/index.md b/src/Resources/doc/index.md similarity index 100% rename from resources/doc/index.md rename to src/Resources/doc/index.md diff --git a/resources/doc/usage.md b/src/Resources/doc/usage.md similarity index 100% rename from resources/doc/usage.md rename to src/Resources/doc/usage.md diff --git a/tests/Json/JsonFormatterTest.php b/tests/Json/JsonFormatterTest.php index c93b95d..b499d11 100644 --- a/tests/Json/JsonFormatterTest.php +++ b/tests/Json/JsonFormatterTest.php @@ -51,8 +51,8 @@ public function testGetIndent() } public function testFormat() - { - $expected = <<<'JSON' +{ + $expected = <<<'JSON' { "name": "test", "contributors": [], @@ -62,16 +62,16 @@ public function testFormat() "devDependencies": {} } JSON; - $data = array( - 'name' => 'test', - 'contributors' => array(), - 'dependencies' => array( - '@foo/bar' => '^1.0.0', - ), - 'devDependencies' => array(), - ); - $content = json_encode($data); + $data = array( + 'name' => 'test', + 'contributors' => array(), + 'dependencies' => array( + '@foo/bar' => '^1.0.0', + ), + 'devDependencies' => array(), + ); + $content = json_encode($data); - static::assertSame($expected, JsonFormatter::format($content, array('contributors'), 2)); - } + static::assertSame($expected, JsonFormatter::format($content, array('contributors'), 2)); +} }