Viewing file: JsonFile.php (9.56 KB) -rwxrwxr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */
namespace SIU\JsonUtil;
use JsonSchema\Validator; use Seld\JsonLint\JsonParser; use Seld\JsonLint\ParsingException;
/** * Reads/writes json files. * * @author Konstantin Kudryashiv <ever.zet@gmail.com> * @author Jordi Boggiano <j.boggiano@seld.be> */ class JsonFile { const LAX_SCHEMA = 1; const STRICT_SCHEMA = 2;
const JSON_UNESCAPED_SLASHES = 64; const JSON_PRETTY_PRINT = 128; const JSON_UNESCAPED_UNICODE = 256;
private $path;
/** * Initializes json file reader/parser. * * @param string $path path to a lockfile * @throws \InvalidArgumentException */ public function __construct($path) { $this->path = $path; }
/** * @return string */ public function getPath() { return $this->path; }
/** * Checks whether json file exists. * * @return bool */ public function exists() { return is_file($this->path); }
/** * Reads json file. * * @throws \RuntimeException * @return mixed */ public function read() { try { $json = file_get_contents($this->path); } catch (\Exception $e) { throw new \RuntimeException('Could not read '.$this->path."\n\n".$e->getMessage()); }
return static::parseJson($json, $this->path); }
/** * Writes json file. * * @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) * @throws \Exception */ public function write(array $hash, $options = 448) { $dir = dirname($this->path); if (!is_dir($dir)) { if (file_exists($dir)) { throw new \UnexpectedValueException( $dir.' exists and is not a directory.' ); } if (!@mkdir($dir, 0777, true)) { throw new \UnexpectedValueException( $dir.' does not exist and could not be created.' ); } }
$retries = 3; while ($retries--) { try { file_put_contents($this->path, static::encode($hash, $options). ($options & self::JSON_PRETTY_PRINT ? "\n" : '')); break; } catch (\Exception $e) { if ($retries) { usleep(500000); continue; }
throw $e; } } }
/** * Validates the schema of the current json file according to composer-schema.json rules * * @param string $schemaFile Path to the schema file * @param int $schema a JsonFile::*_SCHEMA constant * @return bool true on success * @throws JsonValidationException */ public function validateSchema($schemaFile, $schema = self::STRICT_SCHEMA) { $jsonString = file_get_contents($this->path); return self::validateStringSchema($jsonString, $schemaFile, $schema, $this->path); }
/** * Validates the schema of the current hash according to composer-schema.json rules * * @param array $hash writes hash into json file * @param string $schemaFile Path to the schema file * @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) * @param int $schema a JsonFile::*_SCHEMA constant * @return bool true on success * @throws JsonValidationException */ public function validateHashSchema(array $hash, $schemaFile, $options = 448, $schema = self::STRICT_SCHEMA) { $jsonString = self::encode($hash, $options); return self::validateStringSchema($jsonString, $schemaFile, $schema, $this->path); }
/** * Encodes an array into (optionally pretty-printed) JSON * * @param mixed $data Data to encode into a formatted JSON string * @param int $options json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) * @return string Encoded json */ public static function encode($data, $options = 448) { if (PHP_VERSION_ID >= 50400) { $json = json_encode($data, $options); if (false === $json) { self::throwEncodeError(json_last_error()); }
// compact brackets to follow recent php versions if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(phpversion('json'), '1.3.6', '<'))) { $json = preg_replace('/\[\s+\]/', '[]', $json); $json = preg_replace('/\{\s+\}/', '{}', $json); }
return $json; }
$json = json_encode($data); if (false === $json) { self::throwEncodeError(json_last_error()); }
$prettyPrint = (bool) ($options & self::JSON_PRETTY_PRINT); $unescapeUnicode = (bool) ($options & self::JSON_UNESCAPED_UNICODE); $unescapeSlashes = (bool) ($options & self::JSON_UNESCAPED_SLASHES);
if (!$prettyPrint && !$unescapeUnicode && !$unescapeSlashes) { return $json; }
$result = JsonFormatter::format($json, $unescapeUnicode, $unescapeSlashes);
return $result; }
/** * Throws an exception according to a given code with a customized message * * @param int $code return code of json_last_error function * @throws \RuntimeException */ private static function throwEncodeError($code) { switch ($code) { case JSON_ERROR_DEPTH: $msg = 'Maximum stack depth exceeded'; break; case JSON_ERROR_STATE_MISMATCH: $msg = 'Underflow or the modes mismatch'; break; case JSON_ERROR_CTRL_CHAR: $msg = 'Unexpected control character found'; break; case JSON_ERROR_UTF8: $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; break; default: $msg = 'Unknown error'; }
throw new \RuntimeException('JSON encoding failed: '.$msg); }
/** * Parses json string and returns hash. * * @param string $json json string * @param string $file the json file * * @return mixed */ public static function parseJson($json, $file = null) { if (null === $json) { return; }
$data = json_decode($json, true);
if (null === $data && JSON_ERROR_NONE !== json_last_error()) { self::validateSyntax($json, $file); }
return $data; }
/** * Validates the syntax of a JSON string * * @param string $json * @param string $file * @return bool true on success * @throws \UnexpectedValueException * @throws JsonValidationException * @throws ParsingException */ 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('"'.$file.'" is not UTF-8, could not parse as JSON'); }
return true; }
throw new ParsingException('"'.$file.'" does not contain valid JSON'."\n".$result->getMessage(), $result->getDetails()); }
/** * @param string $jsonString * @param string $schemaFile Path to the schema file * @param int $schema * @param string $file * @return bool * @throws JsonValidationException * @throws ParsingException */ public static function validateStringSchema($jsonString, $schemaFile, $schema = self::STRICT_SCHEMA, $file = null) { $data = json_decode($jsonString); if (null === $data && 'null' !== $jsonString) { self::validateSyntax($jsonString, $file); }
$schemaData = json_decode(file_get_contents($schemaFile));
if ($schema === self::LAX_SCHEMA) { $schemaData->additionalProperties = true; $schemaData->required = array(); }
$validator = new Validator(); $validator->check($data, $schemaData);
// TODO add more validation like check version constraints and such, perhaps build that into the arrayloader?
if (!$validator->isValid()) { $errors = array(); foreach ((array) $validator->getErrors() as $error) { $errors[] = ($error['property'] ? $error['property'].' : ' : '').$error['message']; }
if ($file !== null) { throw new JsonValidationException('"'.$file.'" does not match the expected JSON schema', $errors); } else { throw new JsonValidationException('The string does not match the expected JSON schema', $errors); }
}
return true; } }
|