diff --git a/bin/php-openapi b/bin/php-openapi index 6f958da..b5f36ad 100755 --- a/bin/php-openapi +++ b/bin/php-openapi @@ -231,6 +231,9 @@ function read_input($inputFile, $inputFormat) } catch (Symfony\Component\Yaml\Exception\ParseException $e) { error($e->getMessage()); exit(1); + } catch (cebe\openapi\exceptions\IOException $e) { + error($e->getMessage()); + exit(1); } return $openApi; } diff --git a/src/Reader.php b/src/Reader.php index a2900d1..08c2996 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -7,6 +7,7 @@ namespace cebe\openapi; +use cebe\openapi\exceptions\IOException; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\spec\OpenApi; @@ -63,10 +64,17 @@ public static function readFromYaml(string $yaml, string $baseType = OpenApi::cl * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. * @throws UnresolvableReferenceException in case references could not be resolved. + * @throws IOException when the file is not readable. */ public static function readFromJsonFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface { - $spec = static::readFromJson(file_get_contents($fileName), $baseType); + $fileContent = file_get_contents($fileName); + if ($fileContent === false) { + $e = new IOException("Failed to read file: '$fileName'"); + $e->fileName = $fileName; + throw $e; + } + $spec = static::readFromJson($fileContent, $baseType); $spec->setReferenceContext(new ReferenceContext($spec, $fileName)); if ($resolveReferences) { $spec->resolveReferences(); @@ -89,10 +97,17 @@ public static function readFromJsonFile(string $fileName, string $baseType = Ope * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. * @throws UnresolvableReferenceException in case references could not be resolved. + * @throws IOException when the file is not readable. */ public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface { - $spec = static::readFromYaml(file_get_contents($fileName), $baseType); + $fileContent = file_get_contents($fileName); + if ($fileContent === false) { + $e = new IOException("Failed to read file: '$fileName'"); + $e->fileName = $fileName; + throw $e; + } + $spec = static::readFromYaml($fileContent, $baseType); $spec->setReferenceContext(new ReferenceContext($spec, $fileName)); if ($resolveReferences) { $spec->resolveReferences(); diff --git a/src/Writer.php b/src/Writer.php index 7388f4b..aecb213 100644 --- a/src/Writer.php +++ b/src/Writer.php @@ -7,6 +7,7 @@ namespace cebe\openapi; +use cebe\openapi\exceptions\IOException; use cebe\openapi\spec\OpenApi; use Symfony\Component\Yaml\Yaml; @@ -40,19 +41,25 @@ public static function writeToYaml(SpecObjectInterface $object): string * Write OpenAPI spec object to JSON file. * @param SpecObjectInterface|OpenApi the OpenApi object instance. * @param string $fileName file name to write to. + * @throws IOException when writing the file fails. */ public static function writeToJsonFile(SpecObjectInterface $object, string $fileName): void { - file_put_contents($fileName, static::writeToJson($object)); + if (file_put_contents($fileName, static::writeToJson($object)) === false) { + throw new IOException("Failed to write file: '$fileName'"); + } } /** * Write OpenAPI spec object to YAML file. * @param SpecObjectInterface|OpenApi the OpenApi object instance. * @param string $fileName file name to write to. + * @throws IOException when writing the file fails. */ public static function writeToYamlFile(SpecObjectInterface $object, string $fileName): void { - file_put_contents($fileName, static::writeToYaml($object)); + if (file_put_contents($fileName, static::writeToYaml($object)) === false) { + throw new IOException("Failed to write file: '$fileName'"); + } } } diff --git a/src/exceptions/IOException.php b/src/exceptions/IOException.php new file mode 100644 index 0000000..1a68602 --- /dev/null +++ b/src/exceptions/IOException.php @@ -0,0 +1,20 @@ + and contributors + * @license https://github.com/cebe/php-openapi/blob/master/LICENSE + */ + +namespace cebe\openapi\exceptions; + +/** + * This exception is thrown when reading or writing of a file fails. + * @since 1.2.1 + */ +class IOException extends \Exception +{ + /** + * @var string|null if available, the name of the affected file. + */ + public $fileName; +} diff --git a/src/spec/Reference.php b/src/spec/Reference.php index c28c303..bbc81a4 100644 --- a/src/spec/Reference.php +++ b/src/spec/Reference.php @@ -8,6 +8,7 @@ namespace cebe\openapi\spec; use cebe\openapi\DocumentContextInterface; +use cebe\openapi\exceptions\IOException; use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\openapi\json\InvalidJsonPointerSyntaxException; @@ -156,6 +157,12 @@ public function resolve(ReferenceContext $context = null) } } $jsonReference = $this->_jsonReference; + if ($jsonReference === null) { + if ($context->throwException) { + throw new UnresolvableReferenceException(implode("\n", $this->getErrors())); + } + return $this; + } try { if ($jsonReference->getDocumentUri() === '') { // resolve in current document @@ -204,6 +211,14 @@ public function resolve(ReferenceContext $context = null) $this->_errors[] = $message; $this->_jsonReference = null; return $this; + } catch (UnresolvableReferenceException $e) { + $e->context = $this->getDocumentPosition(); + if ($context->throwException) { + throw $e; + } + $this->_errors[] = $e->getMessage(); + $this->_jsonReference = null; + return $this; } } @@ -214,6 +229,11 @@ private function fetchReferencedFile($uri) { try { $content = file_get_contents($uri); + if ($content === false) { + $e = new IOException("Failed to read file: '$uri'"); + $e->fileName = $uri; + throw $e; + } // TODO lazy content detection, should probably be improved if (strpos(ltrim($content), '{') === 0) { return json_decode($content, true);