diff --git a/.gitignore b/.gitignore
index 87904ecb..f2b1db6b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,6 @@
/docs/html/
/laminas-mkdoc-theme.tgz
/laminas-mkdoc-theme/
+/.phpunit.result.cache
/phpunit.xml
/vendor/
diff --git a/.travis.yml b/.travis.yml
index d63ede4b..677679fc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,36 +12,24 @@ env:
matrix:
fast_finish: true
include:
- - php: 5.6
- env:
- - DEPS=lowest
- - php: 5.6
- env:
- - DEPS=latest
- - php: 7
- env:
- - DEPS=lowest
- - php: 7
- env:
- - DEPS=latest
- - php: 7.1
+ - php: 7.3
env:
- DEPS=lowest
- - php: 7.1
+ - php: 7.3
env:
- DEPS=latest
- CS_CHECK=true
- TEST_COVERAGE=true
- - php: 7.2
+ - php: 7.4
env:
- DEPS=lowest
- - php: 7.2
+ - php: 7.4
env:
- DEPS=latest
- - php: 7.3
+ - php: 8.0
env:
- DEPS=lowest
- - php: 7.3
+ - php: 8.0
env:
- DEPS=latest
diff --git a/composer.json b/composer.json
index 0b1ccaa1..f2437f06 100644
--- a/composer.json
+++ b/composer.json
@@ -25,12 +25,14 @@
}
},
"require": {
- "php": "^5.6 || ^7.0",
- "laminas/laminas-zendframework-bridge": "^1.0"
+ "php": "^7.3 || ~8.0.0",
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "laminas/laminas-zendframework-bridge": "^1.1"
},
"require-dev": {
"laminas/laminas-coding-standard": "~1.0.0",
- "phpunit/phpunit": "^5.7.23 || ^6.4.3"
+ "phpunit/phpunit": "^9.4.3"
},
"autoload": {
"psr-4": {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 9fbc2cf8..93d33cc0 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,17 +1,16 @@
-
-
- ./test
-
-
-
-
-
- ./src
-
-
+ xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
+ bootstrap="vendor/autoload.php"
+ colors="true">
+
+
+ ./src
+
+
+
+
+ ./test
+
+
diff --git a/src/Document.php b/src/Document.php
index e403095c..41d6b24b 100644
--- a/src/Document.php
+++ b/src/Document.php
@@ -231,7 +231,7 @@ protected function setErrors($errors)
protected function getDomDocumentFromString($stringDocument)
{
libxml_use_internal_errors(true);
- libxml_disable_entity_loader(true);
+ $disableEntityLoaderFlag = self::disableEntityLoader();
$encoding = $this->getEncoding();
$domDoc = null === $encoding ? new DOMDocument('1.0') : new DOMDocument('1.0', $encoding);
@@ -261,7 +261,7 @@ protected function getDomDocumentFromString($stringDocument)
libxml_clear_errors();
}
- libxml_disable_entity_loader(false);
+ self::disableEntityLoader($disableEntityLoaderFlag);
libxml_use_internal_errors(false);
if (! $success) {
@@ -311,4 +311,22 @@ public function registerXpathPhpFunctions($xpathPhpFunctions = true)
{
$this->xpathPhpFunctions = $xpathPhpFunctions;
}
+
+ /**
+ * Disable the ability to load external XML entities based on libxml version
+ *
+ * If we are using libxml < 2.9, unsafe XML entity loading must be
+ * disabled with a flag.
+ *
+ * If we are using libxml >= 2.9, XML entity loading is disabled by default.
+ *
+ * @return bool
+ */
+ private static function disableEntityLoader($flag = true)
+ {
+ if (LIBXML_VERSION < 20900) {
+ return libxml_disable_entity_loader($flag);
+ }
+ return $flag;
+ }
}
diff --git a/src/Query.php b/src/Query.php
index b6bb7915..ecbff610 100644
--- a/src/Query.php
+++ b/src/Query.php
@@ -232,7 +232,7 @@ public function queryXpath($xpathQuery, $query = null, DOMNode $contextNode = nu
$encoding = $this->getEncoding();
libxml_use_internal_errors(true);
- libxml_disable_entity_loader(true);
+ $disableEntityLoaderFlag = self::disableEntityLoader();
if (null === $encoding) {
$domDoc = new DOMDocument('1.0');
} else {
@@ -261,7 +261,7 @@ public function queryXpath($xpathQuery, $query = null, DOMNode $contextNode = nu
$this->documentErrors = $errors;
libxml_clear_errors();
}
- libxml_disable_entity_loader(false);
+ self::disableEntityLoader($disableEntityLoaderFlag);
libxml_use_internal_errors(false);
if (! $success) {
@@ -320,4 +320,22 @@ protected function getNodeList($document, $xpathQuery, DOMNode $contextNode = nu
$nodeList = $xpath->queryWithErrorException($xpathQuery, $contextNode);
return $nodeList;
}
+
+ /**
+ * Disable the ability to load external XML entities based on libxml version
+ *
+ * If we are using libxml < 2.9, unsafe XML entity loading must be
+ * disabled with a flag.
+ *
+ * If we are using libxml >= 2.9, XML entity loading is disabled by default.
+ *
+ * @return bool
+ */
+ private static function disableEntityLoader($flag = true)
+ {
+ if (LIBXML_VERSION < 20900) {
+ return libxml_disable_entity_loader($flag);
+ }
+ return $flag;
+ }
}
diff --git a/test/DOMXPathTest.php b/test/DOMXPathTest.php
index 11674e4a..ead689d4 100644
--- a/test/DOMXPathTest.php
+++ b/test/DOMXPathTest.php
@@ -19,7 +19,7 @@ class DOMXPathTest extends TestCase
/** @var DOMDocument */
private $document;
- protected function setUp()
+ protected function setUp(): void
{
$this->document = new DOMDocument('');
}
diff --git a/test/Document/NodeListTest.php b/test/Document/NodeListTest.php
index 143d3173..56386dba 100644
--- a/test/Document/NodeListTest.php
+++ b/test/Document/NodeListTest.php
@@ -26,7 +26,7 @@ class NodeListTest extends TestCase
/** @var NodeList|DOMNode[] */
private $nodeList;
- protected function setUp()
+ protected function setUp(): void
{
$document = new DOMDocument();
$document->loadHTML('
');
diff --git a/test/Document/QueryTest.php b/test/Document/QueryTest.php
index d134ad7b..1ec576e5 100644
--- a/test/Document/QueryTest.php
+++ b/test/Document/QueryTest.php
@@ -19,7 +19,7 @@ class QueryTest extends TestCase
public function testTransformShouldReturnStringByDefault()
{
$test = Query::cssToXpath('');
- $this->assertInternalType('string', $test);
+ $this->assertIsString($test);
}
/**
@@ -28,8 +28,8 @@ public function testTransformShouldReturnStringByDefault()
public function testTransformShouldReturnMultiplePathsWhenExpressionContainsCommas()
{
$test = Query::cssToXpath('#foo, #bar');
- $this->assertInternalType('string', $test);
- $this->assertContains('|', $test);
+ $this->assertIsString($test);
+ $this->assertStringContainsString('|', $test);
$this->assertCount(2, explode('|', $test));
}
@@ -48,13 +48,13 @@ public function testTransformShouldRecognizeDotSymbolAsClass()
public function testTransformShouldAssumeSpacesToIndicateRelativeXpathQueries()
{
$test = Query::cssToXpath('div#foo .bar');
- $this->assertContains('|', $test);
+ $this->assertStringContainsString('|', $test);
$expected = [
"//div[@id='foo']//*[contains(concat(' ', normalize-space(@class), ' '), ' bar ')]",
"//div[@id='foo'][contains(concat(' ', normalize-space(@class), ' '), ' bar ')]",
];
foreach ($expected as $path) {
- $this->assertContains($path, $test);
+ $this->assertStringContainsString($path, $test);
}
}
@@ -70,8 +70,8 @@ public function testTransformShouldWriteChildSelectorsAsAbsoluteXpathRelations()
public function testMultipleComplexCssSpecificationShouldTransformToExpectedXpath()
{
$test = Query::cssToXpath('div#foo span.bar, #bar li.baz a');
- $this->assertInternalType('string', $test);
- $this->assertContains('|', $test);
+ $this->assertIsString($test);
+ $this->assertStringContainsString('|', $test);
$actual = explode('|', $test);
$expected = [
"//div[@id='foo']//span[contains(concat(' ', normalize-space(@class), ' '), ' bar ')]",
@@ -86,7 +86,7 @@ public function testMultipleComplexCssSpecificationShouldTransformToExpectedXpat
public function testClassNotationWithoutSpecifiedTagShouldResultInMultipleQueries()
{
$test = Query::cssToXpath('div.foo .bar a .baz span');
- $this->assertContains('|', $test);
+ $this->assertStringContainsString('|', $test);
// @codingStandardsIgnoreStart
$segments = [
"//div[contains(concat(' ', normalize-space(@class), ' '), ' foo ')]//*[contains(concat(' ', normalize-space(@class), ' '), ' bar ')]//a//*[contains(concat(' ', normalize-space(@class), ' '), ' baz ')]//span",
@@ -96,7 +96,7 @@ public function testClassNotationWithoutSpecifiedTagShouldResultInMultipleQuerie
];
// @codingStandardsIgnoreEnd
foreach ($segments as $xpath) {
- $this->assertContains($xpath, $test);
+ $this->assertStringContainsString($xpath, $test);
}
}
diff --git a/test/DocumentTest.php b/test/DocumentTest.php
index 22273847..d9f6ef8b 100644
--- a/test/DocumentTest.php
+++ b/test/DocumentTest.php
@@ -29,10 +29,8 @@ class DocumentTest extends TestCase
/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
- *
- * @return void
*/
- public function setUp()
+ public function setUp(): void
{
$this->document = new Document();
}
@@ -233,17 +231,15 @@ public function testXpathPhpFunctionsShouldBeEnabledWithoutParameter()
public function testXpathPhpFunctionsShouldBeNotCalledWhenSpecifiedFunction()
{
$this->loadHtml();
- try {
- $this->document->registerXpathPhpFunctions('stripos');
- $result = Document\Query::execute(
- '//meta[php:functionString("strtolower", @http-equiv) = "content-type"]',
- $this->document
- );
- } catch (\Exception $e) {
- // $e->getMessage() - Not allowed to call handler 'strtolower()
- return;
- }
- $this->fail('Not allowed to call handler strtolower()');
+ $this->document->registerXpathPhpFunctions('stripos');
+
+ $this->expectException(PHP_VERSION_ID >= 80000 ? \Error::class : \ErrorException::class);
+ $this->expectExceptionMessageMatches('/Not allowed to call handler .strtolower()/');
+
+ Document\Query::execute(
+ '//meta[php:functionString("strtolower", @http-equiv) = "content-type"]',
+ $this->document
+ );
}
/**
@@ -255,7 +251,7 @@ public function testLoadingDocumentWithErrorsShouldNotRaisePhpErrors()
$this->document = new Document($file);
$result = Document\Query::execute('p', $this->document, Document\Query::TYPE_CSS);
$errors = $this->document->getErrors();
- $this->assertInternalType('array', $errors);
+ $this->assertIsArray($errors);
$this->assertNotEmpty($errors);
}