Browse Source

Rework JsonFile API slightly, avoid tokenizing in php if the syntax is valid

Jordi Boggiano 13 years ago
parent
commit
bd54e43d37

+ 1 - 1
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",

+ 3 - 1
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('<error>'.$e->getMessage().'</error>');
             return 1;

+ 2 - 1
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']);

+ 50 - 47
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
-     * 
-     * @param string $json
-     * @return boolean
-     * @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
-     * 
+     * Validates the syntax of a JSON string
+     *
      * @param string $json
-     * @return array
+     * @return Boolean true on success
      * @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;

+ 2 - 7
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()