Skip to content

Commit

Permalink
improve exception and error handling during transformation (#24)
Browse files Browse the repository at this point in the history
* improve exception and error handling during transformation
  • Loading branch information
frederikbosch authored May 10, 2019
1 parent 4f5e1f6 commit 99da1fc
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 52 deletions.
7 changes: 7 additions & 0 deletions .phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
parameters:
ignoreErrors:
# Ignore documentElement errors:
- '# on DOMElement|null#'

includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ before_script:
- if [ "$dependencies" = "highest" ]; then composer update -n; fi;
script:
- ./vendor/bin/phpunit -c phpunit.xml --coverage-clover=coverage.clover
- ./vendor/bin/phpstan analyse -l max src
- ./vendor/bin/phpstan analyse -l max test
- ./vendor/bin/phpstan analyse -l max -c .phpstan.neon src
- ./vendor/bin/phpstan analyse -l max -c .phpstan.neon test
- if [ "$TRAVIS_PHP_VERSION" != "nightly" ] && [ -z "$dependencies" ]; then ./vendor/bin/php-cs-fixer fix --dry-run --verbose --config .php_cs.dist ./src ./test ; fi
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"psr/simple-cache": "^1.0"
},
"require-dev" : {
"phpunit/phpunit": "^7.0",
"phpstan/phpstan": "^0.11.5",
"phpunit/phpunit": "^7.5.10",
"phpstan/phpstan": "^0.11.6",
"phpstan/phpstan-phpunit": "^0.11",
"friendsofphp/php-cs-fixer": "^2.9"
},
Expand Down
10 changes: 9 additions & 1 deletion src/Callback/Arguments.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Genkgo\Xsl\Schema\DataTypeParser;
use Genkgo\Xsl\Schema\XmlSchema;

final class Arguments
final class Arguments implements \Countable
{
/**
* @var array
Expand Down Expand Up @@ -140,6 +140,14 @@ public function assertSchemaType(int $number, string $name): void
}
}

/**
* @return int
*/
public function count(): int
{
return \count($this->arguments);
}

private function convertFromSchemaTypeToPhpType(\DOMNode $node)
{
if ($node->namespaceURI === XmlSchema::URI) {
Expand Down
17 changes: 13 additions & 4 deletions src/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use Genkgo\Xsl\Exception\ReadOnlyStreamException;
use Genkgo\Xsl\Exception\StreamException;
use Genkgo\Xsl\Exception\TransformationException;

final class Stream
{
Expand Down Expand Up @@ -44,10 +45,18 @@ public function stream_open($path)
if (isset($streamContext['gxsl']['transpiler'])) {
$transpiler = $streamContext['gxsl']['transpiler'];

if ($this->isRoot($path)) {
$this->content = $transpiler->transpileRoot();
} else {
$this->content = $transpiler->transpileFile($path);
try {
if ($this->isRoot($path)) {
$this->content = $transpiler->transpileRoot();
} else {
$this->content = $transpiler->transpileFile($path);
}
} catch (\Throwable $e) {
throw new TransformationException(
'Cannot transpile ' . $path . '. ' . $e->getMessage(),
0,
$e
);
}
} else {
if ($this->isRoot($path)) {
Expand Down
4 changes: 1 addition & 3 deletions src/Transpiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@ public function transpileRoot()
*/
public function transpile(DOMDocument $document)
{
// https://github.com/phpstan/phpstan/pull/2089
/** @var \DOMElement|null $root */
$root = $document->documentElement;
if ($root instanceof \DOMElement === false) {
if ($root === null) {
return;
}

Expand Down
88 changes: 64 additions & 24 deletions src/XsltProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@

namespace Genkgo\Xsl;

use DOMDocument;
use Genkgo\Xsl\Callback\FunctionCollection;
use Genkgo\Xsl\Callback\PhpCallback;
use Genkgo\Xsl\Exception\TransformationException;
use Genkgo\Xsl\Util\TransformerCollection;
use Psr\SimpleCache\CacheInterface;
use SimpleXMLElement;
use XSLTProcessor as PhpXsltProcessor;

final class XsltProcessor extends PhpXsltProcessor
Expand Down Expand Up @@ -58,74 +56,116 @@ public function importStyleSheet($stylesheet)
}

/**
* @param DOMDocument|SimpleXMLElement $doc
* @param \DOMDocument|\SimpleXMLElement $doc
* @return string
*/
public function transformToXML($doc)
{
$styleSheet = $this->styleSheetToDomDocument();

$transpiler = $this->createTranspiler($styleSheet);
$useInternalErrors = \libxml_use_internal_errors(true);
$useInternalErrors = \libxml_use_internal_errors(false);

$previousHandler = \set_error_handler(
function ($number, $message) {
throw new TransformationException(
'Transformation failed: ' . $message,
$number
);
}
);

try {
parent::importStylesheet($this->getTranspiledStyleSheet($transpiler, $styleSheet));
$this->throwOnLibxmlError();

return $transpiler->transform(function () use ($doc) {
return parent::transformToXml($doc);
$result = parent::transformToXml($doc);
$this->throwOnLibxmlError();
return $result;
});
} catch (\Throwable $e) {
throw new TransformationException('Transformation failed: ' . $e->getMessage(), 0, $e);
} finally {
\libxml_clear_errors();
\libxml_use_internal_errors($useInternalErrors);
\set_error_handler($previousHandler);
}
}

/**
* @param \DOMNode $doc
* @return DOMDocument
* @return \DOMDocument
*/
public function transformToDoc($doc)
{
$styleSheet = $this->styleSheetToDomDocument();

$transpiler = $this->createTranspiler($styleSheet);
$useInternalErrors = \libxml_use_internal_errors(true);
$useInternalErrors = \libxml_use_internal_errors(false);

$previousHandler = \set_error_handler(
function ($number, $message) {
throw new TransformationException(
'Transformation failed: ' . $message,
$number
);
}
);

try {
parent::importStylesheet($this->getTranspiledStyleSheet($transpiler, $styleSheet));
$this->throwOnLibxmlError();

return $transpiler->transform(function () use ($doc) {
return parent::transformToDoc($doc);
$result = parent::transformToDoc($doc);
$this->throwOnLibxmlError();
return $result;
});
} catch (\Throwable $e) {
throw new TransformationException('Transformation failed: ' . $e->getMessage(), 0, $e);
} finally {
\libxml_clear_errors();
\libxml_use_internal_errors($useInternalErrors);
\set_error_handler($previousHandler);
}
}

/**
* @param DOMDocument|SimpleXMLElement $doc
* @param \DOMDocument|\SimpleXMLElement $doc
* @param string $uri
* @return int
*/
public function transformToUri($doc, $uri)
{
$styleSheet = $this->styleSheetToDomDocument();
$transpiler = $this->createTranspiler($styleSheet);
$useInternalErrors = \libxml_use_internal_errors(true);
$useInternalErrors = \libxml_use_internal_errors(false);

$previousHandler = \set_error_handler(
function ($number, $message) {
throw new TransformationException(
'Transformation failed: ' . $message,
$number
);
}
);

try {
parent::importStylesheet($this->getTranspiledStyleSheet($transpiler, $styleSheet));
$this->throwOnLibxmlError();

return $transpiler->transform(function () use ($doc, $uri) {
return parent::transformToUri($doc, $uri);
$result = parent::transformToUri($doc, $uri);
$this->throwOnLibxmlError();
return $result;
});
} catch (\Throwable $e) {
throw new TransformationException('Transformation failed: ' . $e->getMessage(), 0, $e);
} finally {
\libxml_clear_errors();
\libxml_use_internal_errors($useInternalErrors);
\set_error_handler($previousHandler);
}
}

Expand All @@ -143,10 +183,10 @@ public function registerPHPFunctions($restrict = null)

/**
* @param Transpiler $transpiler
* @param DOMDocument $styleSheet
* @return DOMDocument
* @param \DOMDocument $styleSheet
* @return \DOMDocument
*/
private function getTranspiledStyleSheet(Transpiler $transpiler, DOMDocument $styleSheet)
private function getTranspiledStyleSheet(Transpiler $transpiler, \DOMDocument $styleSheet)
{
$this->boot();

Expand All @@ -155,7 +195,7 @@ private function getTranspiledStyleSheet(Transpiler $transpiler, DOMDocument $st

return $this->createTranspiledDocument($styleSheet);
}

private function boot()
{
if (self::$booted === false) {
Expand All @@ -165,10 +205,10 @@ private function boot()
}

/**
* @param DOMDocument $styleSheet
* @param \DOMDocument $styleSheet
* @return Transpiler
*/
private function createTranspiler(DOMDocument $styleSheet)
private function createTranspiler(\DOMDocument $styleSheet)
{
$phpFunctions = $this->phpFunctions;

Expand Down Expand Up @@ -211,10 +251,10 @@ private function getNamespaces($xpathCompiler)
}

/**
* @param DOMDocument $styleSheet
* @return DOMDocument
* @param \DOMDocument $styleSheet
* @return \DOMDocument
*/
private function createTranspiledDocument(DOMDocument $styleSheet)
private function createTranspiledDocument(\DOMDocument $styleSheet)
{
$documentURI = $styleSheet->documentURI;

Expand All @@ -224,17 +264,17 @@ private function createTranspiledDocument(DOMDocument $styleSheet)
}
// @codeCoverageIgnoreEnd

$transpiledStyleSheet = new DOMDocument('1.0', 'UTF-8');
$transpiledStyleSheet = new \DOMDocument('1.0', 'UTF-8');
$transpiledStyleSheet->load(Stream::PROTOCOL . Stream::HOST . $documentURI . Stream::ROOT);
return $transpiledStyleSheet;
}

/**
* @return DOMDocument
* @return \DOMDocument
*/
private function styleSheetToDomDocument()
{
if ($this->styleSheet instanceof SimpleXMLElement) {
if ($this->styleSheet instanceof \SimpleXMLElement) {
$document = \dom_import_simplexml($this->styleSheet);
if ($document === false) {
throw new \UnexpectedValueException('Cannot transform SimpleXMLElement to DOMDocument');
Expand All @@ -243,7 +283,7 @@ private function styleSheetToDomDocument()
return $document->ownerDocument;
}

if ($this->styleSheet instanceof DOMDocument) {
if ($this->styleSheet instanceof \DOMDocument) {
return $this->styleSheet;
}

Expand Down
6 changes: 3 additions & 3 deletions test/Integration/DisableEntitiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

use DOMDocument;
use Genkgo\Xsl\Cache\NullCache;
use Genkgo\Xsl\Exception\TransformationException;
use Genkgo\Xsl\ProcessorFactory;
use Genkgo\Xsl\XsltProcessor;

final class DisableEntitiesTest extends AbstractIntegrationTestCase
{
public function testDisableEntitiesWhenDocumentAlreadyLoaded()
{
$this->expectException(\DOMException::class);
$this->expectException(TransformationException::class);

\file_put_contents(\sys_get_temp_dir() . '/xsl-passwd', 'test');
$factory = new ProcessorFactory(new NullCache());
Expand All @@ -30,7 +30,7 @@ public function testDisableEntitiesWhenDocumentAlreadyLoaded()

public function testDisableEntitiesInInclude()
{
$this->expectException(\DOMException::class);
$this->expectException(TransformationException::class);

\file_put_contents(\sys_get_temp_dir() . '/xsl-passwd', 'test');
$factory = new ProcessorFactory(new NullCache());
Expand Down
6 changes: 3 additions & 3 deletions test/Integration/Schema/XsDateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Genkgo\Xsl\Integration\Schema;

use Genkgo\Xsl\Exception\CastException;
use Genkgo\Xsl\Exception\TransformationException;

class XsDateTest extends AbstractSchemaTest
{
Expand All @@ -16,15 +16,15 @@ public function testConstructor()

public function testWrongConstructor()
{
$this->expectException(CastException::class);
$this->expectException(TransformationException::class);
$this->expectExceptionMessage('Cannot create date from 20');

$this->transformFile('Stubs/Schema/date-wrong-constructor.xsl');
}

public function testTooManyElements()
{
$this->expectException(CastException::class);
$this->expectException(TransformationException::class);
$this->expectExceptionMessage('Cannot convert list of elements to string');

$this->transformFile('Stubs/Schema/date-too-many-elements.xsl');
Expand Down
4 changes: 2 additions & 2 deletions test/Integration/Schema/XsDateTimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Genkgo\Xsl\Integration\Schema;

use Genkgo\Xsl\Exception\CastException;
use Genkgo\Xsl\Exception\TransformationException;

class XsDateTimeTest extends AbstractSchemaTest
{
Expand All @@ -16,7 +16,7 @@ public function testConstructor()

public function testWrongConstructor()
{
$this->expectException(CastException::class);
$this->expectException(TransformationException::class);
$this->transformFile('Stubs/Schema/dateTime-wrong-constructor.xsl');
}
}
Loading

0 comments on commit 99da1fc

Please sign in to comment.