diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php old mode 100644 new mode 100755 index 16675deb4..dc2538132 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -86,7 +86,7 @@ class JsonFile throw new \RuntimeException('Could not read '.$this->path."\n\n".$e->getMessage()); } - return static::parseJson($json); + return static::parseJson($json, $this->path); } /** @@ -126,7 +126,7 @@ class JsonFile $data = json_decode($content); if (null === $data && 'null' !== $content) { - self::validateSyntax($content); + self::validateSyntax($content, $this->path); } $schemaFile = __DIR__ . '/../../../res/composer-schema.json'; @@ -148,7 +148,7 @@ class JsonFile foreach ((array) $validator->getErrors() as $error) { $errors[] = ($error['property'] ? $error['property'].' : ' : '').$error['message']; } - throw new JsonValidationException($errors); + throw new JsonValidationException('JSON file doesnt match expected schema "'.$this->path.'"', $errors); } return true; @@ -265,14 +265,15 @@ class JsonFile * Parses json string and returns hash. * * @param string $json json string + * @param string $file the json file * * @return mixed */ - public static function parseJson($json) + public static function parseJson($json, $file = null) { $data = json_decode($json, true); if (null === $data && JSON_ERROR_NONE !== json_last_error()) { - self::validateSyntax($json); + self::validateSyntax($json, $file); } return $data; @@ -282,21 +283,23 @@ class JsonFile * Validates the syntax of a JSON string * * @param string $json + * @param string $file * @return bool true on success * @throws \UnexpectedValueException + * @throws JsonValidationException */ - protected static function validateSyntax($json) + protected static function validateSyntax($json, $file = null) { $parser = new JsonParser(); $result = $parser->lint($json); if (null === $result) { if (defined('JSON_ERROR_UTF8') && JSON_ERROR_UTF8 === json_last_error()) { - throw new \UnexpectedValueException('JSON file is not UTF-8 encoded'); + throw new \UnexpectedValueException('JSON file is not UTF-8 encoded "'.$file.'"'); } return true; } - throw $result; + throw new JsonValidationException('JSON file is not valid "'.$file.'"'."\n".$result->getMessage(), $result->getDetails()); } } diff --git a/src/Composer/Json/JsonValidationException.php b/src/Composer/Json/JsonValidationException.php index 30bef88b6..0b2b2ba70 100644 --- a/src/Composer/Json/JsonValidationException.php +++ b/src/Composer/Json/JsonValidationException.php @@ -21,10 +21,10 @@ class JsonValidationException extends Exception { protected $errors; - public function __construct(array $errors) + public function __construct($message, $errors = array()) { - parent::__construct(implode("\n", $errors)); $this->errors = $errors; + parent::__construct($message); } public function getErrors() diff --git a/src/Composer/Package/Loader/JsonLoader.php b/src/Composer/Package/Loader/JsonLoader.php index 27ff7c15a..fcecc0014 100644 --- a/src/Composer/Package/Loader/JsonLoader.php +++ b/src/Composer/Package/Loader/JsonLoader.php @@ -24,7 +24,7 @@ class JsonLoader extends ArrayLoader if ($json instanceof JsonFile) { $config = $json->read(); } elseif (file_exists($json)) { - $config = JsonFile::parseJson(file_get_contents($json)); + $config = JsonFile::parseJson(file_get_contents($json), $json); } elseif (is_string($json)) { $config = JsonFile::parseJson($json); } diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index 0ed579fa4..b0daaba5d 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -44,7 +44,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository)); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository; + $repoData = JsonFile::parseJson($this->getContents($resource), $resource); $this->rootIdentifier = !empty($repoData['main_branch']) ? $repoData['main_branch'] : 'master'; } @@ -86,15 +87,17 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = $this->getContents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $resource = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'; + $composer = $this->getContents($resource); if (!$composer) { return; } - $composer = JsonFile::parseJson($composer); + $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { - $changeset = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier)); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier; + $changeset = JsonFile::parseJson($this->getContents($resource), $resource); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -109,7 +112,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getTags() { if (null === $this->tags) { - $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'; + $tagsData = JsonFile::parseJson($this->getContents($resource), $resource); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -125,7 +129,8 @@ class GitBitbucketDriver extends VcsDriver implements VcsDriverInterface public function getBranches() { if (null === $this->branches) { - $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches')); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'; + $branchData = JsonFile::parseJson($this->getContents($resource), $resource); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index f31d27bb6..dca7366e2 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -123,13 +123,14 @@ class GitDriver extends VcsDriver public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $this->process->execute(sprintf('git show %s:composer.json', escapeshellarg($identifier)), $composer, $this->repoDir); + $resource = sprintf('%s:composer.json', escapeshellarg($identifier)); + $this->process->execute(sprintf('git show %s', $resource), $composer, $this->repoDir); if (!trim($composer)) { return; } - $composer = JsonFile::parseJson($composer); + $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { $this->process->execute(sprintf('git log -1 --format=%%at %s', escapeshellarg($identifier)), $output, $this->repoDir); diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php old mode 100644 new mode 100755 index 87d210af4..57a2f4f8b --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -127,7 +127,8 @@ class GitHubDriver extends VcsDriver if (!isset($this->infoCache[$identifier])) { try { - $composer = $this->getContents('https://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'); + $resource = 'https://raw.github.com/'.$this->owner.'/'.$this->repository.'/'.$identifier.'/composer.json'; + $composer = $this->getContents($resource); } catch (TransportException $e) { if (404 !== $e->getCode()) { throw $e; @@ -137,10 +138,11 @@ class GitHubDriver extends VcsDriver } if ($composer) { - $composer = JsonFile::parseJson($composer); + $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { - $commit = JsonFile::parseJson($this->getContents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier)); + $resource = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/commits/'.$identifier; + $commit = JsonFile::parseJson($this->getContents($resource), $resource); $composer['time'] = $commit['commit']['committer']['date']; } if (!isset($composer['support']['source'])) { @@ -171,7 +173,8 @@ class GitHubDriver extends VcsDriver return $this->gitDriver->getTags(); } if (null === $this->tags) { - $tagsData = JsonFile::parseJson($this->getContents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags')); + $resource = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/tags'; + $tagsData = JsonFile::parseJson($this->getContents($resource), $resource); $this->tags = array(); foreach ($tagsData as $tag) { $this->tags[$tag['name']] = $tag['commit']['sha']; @@ -190,7 +193,8 @@ class GitHubDriver extends VcsDriver return $this->gitDriver->getBranches(); } if (null === $this->branches) { - $branchData = JsonFile::parseJson($this->getContents('https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads')); + $resource = 'https://api.github.com/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads'; + $branchData = JsonFile::parseJson($this->getContents($resource), $resource); $this->branches = array(); foreach ($branchData as $branch) { $name = substr($branch['ref'], 11); @@ -245,7 +249,7 @@ class GitHubDriver extends VcsDriver throw new \RuntimeException("Either you have entered invalid credentials or this GitHub repository does not exists (404)"); } try { - $repoData = JsonFile::parseJson($this->getContents($repoDataUrl)); + $repoData = JsonFile::parseJson($this->getContents($repoDataUrl), $repoDataUrl); if (isset($repoData['default_branch'])) { $this->rootIdentifier = $repoData['default_branch']; } elseif (isset($repoData['master_branch'])) { diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index d0d1097b2..f31f61c44 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -44,7 +44,8 @@ class HgBitbucketDriver extends VcsDriver public function getRootIdentifier() { if (null === $this->rootIdentifier) { - $repoData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'; + $repoData = JsonFile::parseJson($this->getContents($resource), $resource); $this->rootIdentifier = $repoData['tip']['raw_node']; } @@ -86,15 +87,17 @@ class HgBitbucketDriver extends VcsDriver public function getComposerInformation($identifier) { if (!isset($this->infoCache[$identifier])) { - $composer = $this->getContents($this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'); + $resource = $this->getScheme() . '://bitbucket.org/'.$this->owner.'/'.$this->repository.'/raw/'.$identifier.'/composer.json'; + $composer = $this->getContents($resource); if (!$composer) { return; } - $composer = JsonFile::parseJson($composer); + $composer = JsonFile::parseJson($composer, $resource); if (!isset($composer['time'])) { - $changeset = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier)); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/changesets/'.$identifier; + $changeset = JsonFile::parseJson($this->getContents($resource), $resource); $composer['time'] = $changeset['timestamp']; } $this->infoCache[$identifier] = $composer; @@ -109,7 +112,8 @@ class HgBitbucketDriver extends VcsDriver public function getTags() { if (null === $this->tags) { - $tagsData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags')); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/tags'; + $tagsData = JsonFile::parseJson($this->getContents($resource), $resource); $this->tags = array(); foreach ($tagsData as $tag => $data) { $this->tags[$tag] = $data['raw_node']; @@ -125,7 +129,8 @@ class HgBitbucketDriver extends VcsDriver public function getBranches() { if (null === $this->branches) { - $branchData = JsonFile::parseJson($this->getContents($this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches')); + $resource = $this->getScheme() . '://api.bitbucket.org/1.0/repositories/'.$this->owner.'/'.$this->repository.'/branches'; + $branchData = JsonFile::parseJson($this->getContents($resource), $resource); $this->branches = array(); foreach ($branchData as $branch => $data) { $this->branches[$branch] = $data['raw_node']; diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php old mode 100644 new mode 100755 index 1008bbe90..c7cea695a --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -100,7 +100,7 @@ class HgDriver extends VcsDriver return; } - $composer = JsonFile::parseJson($composer); + $composer = JsonFile::parseJson($composer, $identifier); if (!isset($composer['time'])) { $this->process->execute(sprintf('cd %s && hg log --template "{date|rfc822date}" -r %s', escapeshellarg($this->tmpDir), escapeshellarg($identifier)), $output); diff --git a/src/Composer/Repository/Vcs/SvnDriver.php b/src/Composer/Repository/Vcs/SvnDriver.php old mode 100644 new mode 100755 index 1d087aad5..1589ffb04 --- a/src/Composer/Repository/Vcs/SvnDriver.php +++ b/src/Composer/Repository/Vcs/SvnDriver.php @@ -107,7 +107,8 @@ class SvnDriver extends VcsDriver } try { - $output = $this->execute('svn cat', $this->baseUrl . $path . 'composer.json' . $rev); + $resource = $path.'composer.json'; + $output = $this->execute('svn cat', $this->baseUrl . $resource . $rev); if (!trim($output)) { return; } @@ -115,7 +116,7 @@ class SvnDriver extends VcsDriver throw new TransportException($e->getMessage()); } - $composer = JsonFile::parseJson($output); + $composer = JsonFile::parseJson($output, $this->baseUrl . $resource . $rev); if (!isset($composer['time'])) { $output = $this->execute('svn info', $this->baseUrl . $path . $rev); diff --git a/tests/Composer/Test/Json/JsonFileTest.php b/tests/Composer/Test/Json/JsonFileTest.php index 159017aa6..55bf603ea 100644 --- a/tests/Composer/Test/Json/JsonFileTest.php +++ b/tests/Composer/Test/Json/JsonFileTest.php @@ -13,6 +13,7 @@ namespace Composer\Test\Json; use Seld\JsonLint\ParsingException; +use Composer\Json\JsonValidationException; use Composer\Json\JsonFile; class JsonFileTest extends \PHPUnit_Framework_TestCase @@ -197,8 +198,9 @@ class JsonFileTest extends \PHPUnit_Framework_TestCase try { JsonFile::parseJson($json); $this->fail(); - } catch (ParsingException $e) { + } catch (JsonValidationException $e) { $this->assertContains($text, $e->getMessage()); + $this->assertNotEmpty($e->getErrors()); } }