From bd54e43d37959b1cc506ef962bf810d9fbc383f1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 5 Mar 2012 22:01:47 +0100 Subject: [PATCH] Rework JsonFile API slightly, avoid tokenizing in php if the syntax is valid --- composer.json | 2 +- src/Composer/Command/ValidateCommand.php | 4 +- src/Composer/Factory.php | 3 +- src/Composer/Json/JsonFile.php | 97 ++++++++++++----------- tests/Composer/Test/Json/JsonFileTest.php | 9 +-- 5 files changed, 58 insertions(+), 57 deletions(-) diff --git a/composer.json b/composer.json index 236584f85..69d20af20 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ } ], "require": { - "php": ">=5.3.0", + "php": ">=5.3.0", "justinrainbow/json-schema": ">=1.1.0", "seld/jsonlint": "*", "symfony/console": "dev-master", diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 41f516ad5..61725bc4a 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -53,7 +53,9 @@ EOT } try { - JsonFile::parseJson(file_get_contents($file), true); + $json = new JsonFile($file); + $json->read(); + $json->validateSchema(); } catch (\Exception $e) { $output->writeln(''.$e->getMessage().''); return 1; diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 1d39dea73..4f990f0ca 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -55,7 +55,8 @@ class Factory 'process-timeout' => 300, ); - $packageConfig = $file->read(true); + $packageConfig = $file->read(); + $file->validateSchema(); if (isset($packageConfig['config']) && is_array($packageConfig['config'])) { $packageConfig['config'] = array_merge($composerConfig, $packageConfig['config']); diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index 110401688..ab1844672 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -66,30 +66,29 @@ class JsonFile /** * Reads json file. * - * @param string $json path or json string - * * @return array */ - public function read($validate = false) + public function read() { $ctx = StreamContextFactory::getContext(array( 'http' => array( 'header' => 'User-Agent: Composer/'.Composer::VERSION."\r\n" - ))); + ) + )); $json = file_get_contents($this->path, false, $ctx); if (!$json) { throw new \RuntimeException('Could not read '.$this->path.', you are probably offline'); } - return static::parseJson($json, $validate); + return static::parseJson($json); } /** * Writes json file. * - * @param array $hash writes hash into json file - * @param int $options json_encode options + * @param array $hash writes hash into json file + * @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) */ public function write(array $hash, $options = 448) { @@ -109,14 +108,46 @@ class JsonFile file_put_contents($this->path, static::encode($hash, $options). ($options & JSON_PRETTY_PRINT ? "\n" : '')); } + /** + * Validates the schema of the current json file according to composer-schema.json rules + * + * @param string $json + * @return Boolean true on success + * @throws \UnexpectedValueException + */ + public function validateSchema() + { + $content = file_get_contents($this->path); + $data = json_decode($content); + + if (null === $data && 'null' !== $content) { + self::validateSyntax($content); + } + + $schema = json_decode(file_get_contents(__DIR__ . '/../../../res/composer-schema.json')); + + $validator = new Validator(); + $validator->check($data, $schema); + + if (!$validator->isValid()) { + $msg = "\n"; + foreach ((array) $validator->getErrors() as $error) { + $msg .= ($error['property'] ? $error['property'].' : ' : '').$error['message']."\n"; + } + throw new \UnexpectedValueException('Your composer.json is invalid. The following errors were found:' . $msg); + } + + return true; + } + /** * Encodes an array into (optionally pretty-printed) JSON * - * Original code for this function can be found at: + * This code is based on the function found at: * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ * * @param mixed $data Data to encode into a formatted JSON string - * @param int $options json_encode options + * @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) * @return string Encoded json */ static public function encode($data, $options = 448) @@ -218,61 +249,33 @@ class JsonFile * Parses json string and returns hash. * * @param string $json json string - * @param boolean $validateSchema wether to validate the json schema * * @return mixed */ - public static function parseJson($json, $validateSchema=false) - { - $data = static::validateSyntax($json); - - if ($validateSchema) { - static::validateSchema($json); + public static function parseJson($json) + { + $data = json_decode($json, true); + if (null === $data && 'null' !== $json) { + self::validateSyntax($json); } return $data; } /** - * validates a composer.json against the schema - * + * Validates the syntax of a JSON string + * * @param string $json - * @return boolean + * @return Boolean true on success * @throws \UnexpectedValueException */ - public static function validateSchema($json) - { - $data = json_decode($json); - $schema = json_decode(file_get_contents(__DIR__ . '/../../../res/composer-schema.json')); - - $validator = new Validator(); - - $validator->check($data, $schema); - - if (!$validator->isValid()) { - $msg = "\n"; - foreach ((array) $validator->getErrors() as $error) { - $msg .= ($error['property'] ? $error['property'].' : ' : '').$error['message']."\n"; - } - - throw new \UnexpectedValueException('Your composer.json did not validate against the schema. The following mistakes were found:'.$msg); - } - } - - /** - * validates the json syntax - * - * @param string $json - * @return array - * @throws \UnexpectedValueException - */ - public static function validateSyntax($json) + protected static function validateSyntax($json) { $parser = new JsonParser(); $result = $parser->lint($json); if (null === $result) { - return json_decode($json, true); + return true; } throw $result; diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 7bb7aecc5..3090a450f 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -87,13 +87,8 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase public function testSchemaValidation() { - $json = file_get_contents(__DIR__.'/../../../../composer.json'); - - try { - $this->assertNull(JsonFile::validateSchema($json)); - } catch (\UnexpectedValueException $e) { - $this->fail('invalid schema'); - } + $json = new JsonFile(__DIR__.'/../../../../composer.json'); + $this->assertTrue($json->validateSchema()); } public function testParseErrorDetectMissingCommaMultiline()