Repository: theseer/fDOMDocument
Branch: master
Commit: e598008a257b
Files: 35
Total size: 140.9 KB
Directory structure:
gitextract_1yhyg96l/
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.xml
├── composer.json
├── fDOMDocument.spec
├── phive.xml
├── phpcs.xml
├── phpunit.xml.dist
├── src/
│ ├── XPathQuery.php
│ ├── XPathQueryException.php
│ ├── autoload.php
│ ├── css/
│ │ ├── DollarEqualRule.php
│ │ ├── NotRule.php
│ │ ├── NthChildRule.php
│ │ ├── RegexRule.php
│ │ ├── RuleInterface.php
│ │ └── Translator.php
│ ├── fDOMDocument.php
│ ├── fDOMDocumentFragment.php
│ ├── fDOMElement.php
│ ├── fDOMException.php
│ ├── fDOMNode.php
│ └── fDOMXPath.php
└── tests/
├── Translator.test.php
├── XPathQuery.test.php
├── _data/
│ ├── broken.xml
│ ├── selector.xml
│ ├── undefentity.xml
│ └── valid.xml
├── fDOMDocument.test.php
├── fDOMDocumentFragment.test.php
├── fDOMElement.test.php
└── fDOMXPath.test.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.buildpath
.project
.settings
/fDOMDocument-1.1.0.tgz
.idea
build
composer.lock
composer.phar
vendor/
================================================
FILE: .travis.yml
================================================
language: php
sudo: false
php:
- "5.3"
- "5.4"
- "5.5"
- "5.6"
- "7.0"
- "7.1"
- nightly
# - hhvm
matrix:
allow_failures:
- php: nightly
# - php: hhvm
fast_finish: true
cache:
directories:
- $HOME/.composer/cache
notifications:
email: false
before_script:
- composer self-update
- composer install
- composer require --dev "phpunit/phpunit ^4.8.35|^5.6|^6.0"
script:
- vendor/bin/phpunit --verbose
================================================
FILE: LICENSE
================================================
fDOMDocument
Copyright (c) 2010-2012 Arne Blankerts <arne@blankerts.de>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Arne Blankerts nor the names of contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
fDOMDocument
============
> :warning: **This project is Archived**
> There will be no new features or general bugfixes. In case you found a security problem, please contact me and I'll consider creating an additional release addressing it. Otherwise no updates are planned. The current (potentially final) release `1.6.7` should work under PHP 5.3.3 - PHP 8.1.
The classes contained within this repository extend the standard DOM to use exceptions at
all occasions of errors instead of PHP warnings or notices. They also add various custom methods
and shortcuts for convenience and to simplify the usage of DOM.
Requirements
------------
PHP: 5.3.3 (5.3.0-5.3.2 had serious issues with spl stacked autoloaders)
Extensions: dom, libxml
Aus of 1.6.7, tests will require PHPUnit 8.5 and PHP 7.3+
Installation
------------
Apart from cloning this repository, fDOMDocument can be installed using by any of the following methods.
##### Composer
As fDOMDocument is a library and does not provide any cli tools, you can only add it to your own project:
{
"require": {
"theseer/fdomdocument": "^1.6"
}
}
##### YUM/DNF (Fedora / Redhat / CentOS)
The following command will install fDOMDocument via its RPM package:
sudo yum install php-theseer-fDOMDocument
Usage
-----
fDOMDocument is designed as a drop in replacement for DOMDocument. You can either use the composer generated
autoloader or the provided one.
Usage Samples
-------------
<?php
require '/path/to/autoload.php';
$dom = new TheSeer\fDOM\fDOMDocument();
try {
$dom->loadXML('<?xml version="1.0" ?><root><child name="foo" /></root>');
} catch (fDOMException $e) {
die($e);
}
$child = $dom->queryOne('//child');
print_r($child->getAttribute('name'));
print_r($child->getAttribute('missing','DefaultValue'));
?>
Changelog
---------
##### Release 1.6.7
* Add `#[\ReturnTypeWillChange]` attribute to shut up PHP 8.1 notices on return types
* Fix deprecation notice for passing `NULL` where `int` is expected
##### Release 1.6.6
* Merge PRs 33+34: Add support for parameter "asTextNode" to fDOMElement::appendElement().
fDOMElement::appendElementNS() and fDOMElement::appendElementPrefix
##### Releaes 1.6.5
* Revert git exports limitations as they cause unwanted side effects
##### Releaes 1.6.4
* Merge PR 31 to optimize travis builds and git exports (Thanks to @willemstuursma)
##### Releaes 1.6.3
* Merge PR 29 to fix issues with PHP 7.2
##### Release 1.6.2
* Handle empty string warings from PHP
##### Release 1.6.1
* Added Workaround for [HHVM Issue #5412](https://github.com/facebook/hhvm/issues/5412)
##### Release 1.6.0
* Added ```createElement*``` to ```fDOMEmenet``` and ```fDOMDocumentFragment``` as shortcuts
* Added ```appendElement*``` to ```fDOMDocumentFragment``` as shortcuts
* Enhanced the exception messages of save errors with filenames to contain the filename
* Fixed fDomDocumentFragment::__toString to actually work
* Updated / Added some tests
##### Release 1.5.0
* Added ```select``` to ```fDOMDocument```,```fDOMElement``` and ```fDOMNode``` to support
CSS Selectors in favor of XPath only to find nodes
* Added ```query``` and ```queryOne``` forwardes to ```fDOMNode```
##### Release 1.4.3
* Added ```saveXML``` and ```saveHTML``` to ```fDOMNode``` and ```fDOMElement``` as a
shortcut to calling those methods on the ownerDocument
##### Release 1.4.2
* Added ```__toString``` support to ```fDOMNode```, ```fDOMElement```, ```fDOMDocument``` and ```fDOMDocumentFragment```
##### Release 1.4.1
* Removed unused Interface ```fDOMNodeInterface``` from code base
##### Release 1.4.0
* Added XPathQuery helper object, allowing for a prepared statement alike API around XPath
##### Release 1.3.2
* Added ```__clone``` method to reset domxpath object when domdocument gets cloned (Thanks to Markus Ineichen for pointing it out)
##### Release 1.3.1
* PHP 5.3 compatibility: changed interal behavior for incompatible changes from PHP 5.3 to 5.4 (Thanks to Jens Graefe for pointing it out)
##### Release 1.3.0
* Added appendTextNode method (Thanks to Markus Ineichen)
* Added appendElement / appendElementNS to DOMDocument to support documentElement "creation" (Thanks to Markus Ineichen)
* Overwrite createElement / createElementNS to throw exception on error
* Removed fDOMFilter code: Unmaintained and broken in its current form
* Added (static) Flag for fDOMException to globally enable full exception message
* Added Unit tests
##### Release 1.2.4
* PHP 5.4 compatibilty: added support for optional options bitmask on additional methods
##### Release 1.2.3
* Cleanup code style to adhere coding standard
* Added entity support for Attributes
* Added phpcs file to make coding standard public
##### Release 1.2.2
* Fix Exception to not overwrite final methods of \Exception
##### Release 1.2.1
* Changed fDOMDocument to be no longer final, use lsb to lookup actual class in constructor.
This should fix test/mock issues.
##### Release 1.2.0
* Changed fException to be more compatible with standard exceptions by adding a switch to get full info by getMessage()
* Merged setAttributes() and setAttributesNS() methods from Andreas
* Fixed internal registerNamespace variable mixup
##### Release 1.1.0
* Renamed files to mimic classname cases
* Fixed inSameDocument to support DOMDocument as well as DOMNodes
* Added fDOMXPath class providing queryOne(), qoute() and prepare()
* Adjusted forwarders in fDOMDocument to make use of new object
* Fixed various return values to statically return true for compatibility with original API
* Applied Workaround to fix potential problems with lost references to instances of fDOMDocument
* Support registerPHPFunctions
* Bump Copyright
* Added missing docblocks
##### Release 1.0.2
* Indenting and typo fixes, minor bugfixes
##### Release 1.0.1
* Bugfix: typehints corrected
##### Release 1.0.0
* Initial release
================================================
FILE: build.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by PHP Project Wizard (PPW) 1.0.4 on Fri Mar 11 16:37:31 CET 2011 -->
<project name="fDOMDocument" default="build" basedir=".">
<property name="source" value="src"/>
<target name="clean" description="Clean up and create artifact directories">
<delete dir="${basedir}/build/api"/>
<delete dir="${basedir}/build/code-browser"/>
<delete dir="${basedir}/build/coverage"/>
<delete dir="${basedir}/build/logs"/>
<delete dir="${basedir}/build/pdepend"/>
<mkdir dir="${basedir}/build/api"/>
<mkdir dir="${basedir}/build/code-browser"/>
<mkdir dir="${basedir}/build/coverage"/>
<mkdir dir="${basedir}/build/logs"/>
<mkdir dir="${basedir}/build/pdepend"/>
</target>
<target name="phpab">
<exec executable="phpab">
<arg line='-o src/autoload.php src' />
</exec>
</target>
<target name="phpunit" description="Run unit tests using PHPUnit and generates junit.xml and clover.xml">
<exec executable="phpunit" failonerror="true"/>
</target>
<target name="parallelTasks" description="Run the pdepend, phpmd, phpcpd, phpcs, phpdoc and phploc tasks in parallel using a maximum of 2 threads.">
<parallel threadCount="2">
<sequential>
<antcall target="pdepend"/>
<antcall target="phpmd"/>
</sequential>
<antcall target="phpcpd"/>
<antcall target="phpcs"/>
<antcall target="phploc"/>
</parallel>
</target>
<target name="pdepend" description="Generate jdepend.xml and software metrics charts using PHP_Depend">
<exec executable="pdepend">
<arg line="--jdepend-xml=${basedir}/build/logs/jdepend.xml
--jdepend-chart=${basedir}/build/pdepend/dependencies.svg
--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg
${source}" />
</exec>
</target>
<target name="phpmd" description="Generate pmd.xml using PHPMD">
<exec executable="phpmd">
<arg line="${source}
xml
codesize,design,naming,unusedcode
--reportfile ${basedir}/build/logs/pmd.xml" />
</exec>
</target>
<target name="phpcpd" description="Generate pmd-cpd.xml using PHPCPD">
<exec executable="phpcpd">
<arg line="--log-pmd ${basedir}/build/logs/pmd-cpd.xml ${source}" />
</exec>
</target>
<target name="phploc" description="Generate phploc.csv">
<exec executable="phploc">
<arg line="--log-csv ${basedir}/build/logs/phploc.csv ${source}" />
</exec>
</target>
<target name="phpcs" description="Generate checkstyle.xml using PHP_CodeSniffer">
<exec executable="phpcs" output="/dev/null">
<arg line="--report=checkstyle
--report-file=${basedir}/build/logs/checkstyle.xml
--standard=phpcs.xml
${source}" />
</exec>
</target>
<target name="phpcb" description="Aggregate tool output with PHP_CodeBrowser">
<exec executable="phpcb">
<arg line="--log ${basedir}/build/logs
--source ${source}
--output ${basedir}/build/code-browser" />
</exec>
</target>
<target name="build" depends="clean,parallelTasks,phpunit,phpcb"/>
</project>
================================================
FILE: composer.json
================================================
{
"name": "theseer/fdomdocument",
"description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.",
"homepage": "https://github.com/theseer/fDOMDocument",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Arne Blankerts",
"email": "arne@blankerts.de",
"role": "lead"
}
],
"support": {
"issues": "https://github.com/theseer/fDOMDocument/issues"
},
"require": {
"php": ">=5.3.3",
"ext-dom": "*",
"lib-libxml": "*"
},
"require-dev": {
"php": ">=7.3"
},
"autoload": {
"classmap": [
"src/"
]
}
}
================================================
FILE: fDOMDocument.spec
================================================
%define _pearDir /usr/share/pear/
%define _sourcedir src/
Summary: fDOMDocument - An Extension to PHP's standard DOM to add various convenience methods and exceptions by default
Name: fDOMDocument
Version: 1.1.0
Release: 1
Group: System Environment/Libraries
License: Arne Blankerts
Vendor: Arne Blankerts
URL: https://github.com/theseer/
#Source:
Provides: fDOMDocument-%{version}%{release}
BuildRoot: %{_tmppath}/%{name}-%{version}%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
Requires: php-common, php-dom
%description
fDOMDocument - An Extension to PHP's standard DOM to add various convenience methods and exceptions by default
%install
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
install -m 755 -d $RPM_BUILD_ROOT%{_pearDir}TheSeer
install -m 755 -d $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument
install -m 755 -d $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMFilter
cp %{_sourcedir}fDOMDocument.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMDocument.php
cp %{_sourcedir}fDOMDocumentFragment.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMDocumentFragment.php
cp %{_sourcedir}fDOMElement.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMElement.php
cp %{_sourcedir}fDOMException.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMException.php
cp %{_sourcedir}fDOMFilter.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMFilter.php
cp %{_sourcedir}fDOMNode.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMNode.php
cp %{_sourcedir}fDOMXPath.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMXPath.php
cp %{_sourcedir}fDOMFilter/xhtml.php $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/fDOMFilter/xhtml.php
phpab -o $RPM_BUILD_ROOT%{_pearDir}TheSeer/fDOMDocument/autoload.php TheSeer/fDOMDocument
%post
%files
%defattr(-,root,root)
%dir %{_pearDir}TheSeer/fDOMDocument
%dir %{_pearDir}TheSeer/fDOMDocument/fDOMFilter
%{_pearDir}TheSeer/fDOMDocument/*
%{_pearDir}TheSeer/fDOMDocument/fDOMFilter/*
%{_pearDir}TheSeer/fDOMDocument/autoload.php
%{_pearDir}TheSeer/fDOMDocument/fDOMDocument.php
%{_pearDir}TheSeer/fDOMDocument/fDOMDocumentFragment.php
%{_pearDir}TheSeer/fDOMDocument/fDOMElement.php
%{_pearDir}TheSeer/fDOMDocument/fDOMException.php
%{_pearDir}TheSeer/fDOMDocument/fDOMFilter.php
%{_pearDir}TheSeer/fDOMDocument/fDOMNode.php
%{_pearDir}TheSeer/fDOMDocument/fDOMXPath.php
%{_pearDir}TheSeer/fDOMDocument/fDOMFilter/xhtml.php
%changelog
* Tue Jul 04 2011 Maik 'M4ikT' Thieme <maik.thieme@gmail.com> 1.1.0
- Initial package release
================================================
FILE: phive.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^8.5" installed="8.5.23" location="./tools/phpunit" copy="false"/>
</phive>
================================================
FILE: phpcs.xml
================================================
<?xml version="1.0"?>
<ruleset name="Arne">
<description>Arne Blankerts' coding standard</description>
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
<rule ref="Generic.Commenting.Todo"/>
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n"/>
</properties>
</rule>
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
<rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie" />
<rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
<rule ref="PEAR.Functions.ValidDefaultValue"/>
<rule ref="Generic.NamingConventions.ConstructorName"/>
<!-- <rule ref="PEAR.NamingConventions.ValidClassName"/>-->
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<rule ref="Generic.PHP.NoSilencedErrors"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<!-- Namespace handling broken, so disabled -->
<!--<rule ref="Generic.WhiteSpace.ScopeIndent"/>-->
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
</ruleset>
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.5/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
verbose="true"
bootstrap="src/autoload.php"
convertErrorsToExceptions="true"
convertDeprecationsToExceptions="true"
>
<testsuites>
<testsuite name="fDOMDocument">
<directory suffix=".test.php">tests</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-html" target="build/coverage" lowUpperBound="35" highLowerBound="70"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="junit" target="build/logs/junit.xml" />
</logging>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
<exclude>
<directory suffix=".php">src/bootstrap</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
================================================
FILE: src/XPathQuery.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* Class XPathQuery
*
* @package TheSeer\fDOM
*/
class XPathQuery {
/**
* @var string
*/
private $query;
/**
* Key-value Map for bound values
*
* @var array
*/
private $values = array();
/**
* @param string $query
*/
public function __construct($query) {
$this->setQuery($query);
}
/**
* Set Query.
*
* @param string $query
*/
private function setQuery($query) {
$this->query = $query;
$res = preg_match_all('/(:(\w*))/', $query, $matches);
if ($res > 0) {
$this->values = array_fill_keys($matches[2], '');
}
}
/**
* Returns keys.
*
* @return array
*/
public function getKeys() {
return array_keys($this->values);
}
/**
* Bind value to key.
*
* @param string $key
* @param string $value
*
* @throws XPathQueryException
*/
public function bind($key, $value) {
if (!array_key_exists($key, $this->values)) {
throw new XPathQueryException("'$key' not found in query'", XPathQueryException::KeyNotFound );
}
$this->values[$key] = $value;
}
/**
* Generate query.
*
* @param \DOMNode $ctx
* @param array $values
*
* @return string
*/
public function generate(\DOMNode $ctx, array $values = NULL) {
return $this->buildQuery($this->getXPathObjectFor($ctx), $values);
}
/**
* Evaluate Query.
*
* @param \DOMNode $ctx
* @param array $values
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return mixed
*/
public function evaluate(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->evaluate($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
/**
* Execute Query.
*
* @param \DOMNode $ctx
* @param array $values
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return mixed
*/
public function query(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->evaluate($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
/**
* Execute Query and return first result.
*
* @param \DOMNode $ctx
* @param array $values
* @param bool $registerNodeNS
*
* @return \DOMNode
*/
public function queryOne(\DOMNode $ctx, array $values = NULL, $registerNodeNS = TRUE) {
$xp = $this->getXPathObjectFor($ctx);
return $xp->queryOne($this->buildQuery($xp, $values), $ctx, $registerNodeNS);
}
/**
* Return xPath for node
*
* @param \DOMNode $ctx
*
* @throws fDOMException
*
* @return fDOMXPath
*/
private function getXPathObjectFor(\DOMNode $ctx) {
$dom = $ctx instanceof \DOMDocument ? $ctx : $ctx->ownerDocument;
if ($dom instanceOf fDOMDocument) {
return $dom->getDOMXPath();
}
return new fDOMXPath($dom);
}
/**
* Build query using values.
*
* @param fDOMXPath $xp
* @param array $values
*
* @throws XPathQueryException
*
* @return string
*/
private function buildQuery(fDOMXPath $xp, array $values = NULL) {
$backup = $this->values;
if (is_array($values) && count($values) > 0) {
foreach($values as $k => $v) {
$this->bind($k, $v);
}
}
$query = $xp->prepare($this->query, $this->values);
$this->values = $backup;
return $query;
}
}
}
================================================
FILE: src/XPathQueryException.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
class XPathQueryException extends \Exception {
const KeyNotFound = 1;
}
}
================================================
FILE: src/autoload.php
================================================
<?php
// @codingStandardsIgnoreFile
// @codeCoverageIgnoreStart
// this is an autogenerated file - do not edit
spl_autoload_register(
function($class) {
static $classes = null;
if ($classes === null) {
$classes = array(
'theseer\\fdom\\css\\dollarequalrule' => '/css/DollarEqualRule.php',
'theseer\\fdom\\css\\notrule' => '/css/NotRule.php',
'theseer\\fdom\\css\\nthchildrule' => '/css/NthChildRule.php',
'theseer\\fdom\\css\\regexrule' => '/css/RegexRule.php',
'theseer\\fdom\\css\\ruleinterface' => '/css/RuleInterface.php',
'theseer\\fdom\\css\\translator' => '/css/Translator.php',
'theseer\\fdom\\fdomdocument' => '/fDOMDocument.php',
'theseer\\fdom\\fdomdocumentfragment' => '/fDOMDocumentFragment.php',
'theseer\\fdom\\fdomelement' => '/fDOMElement.php',
'theseer\\fdom\\fdomexception' => '/fDOMException.php',
'theseer\\fdom\\fdomnode' => '/fDOMNode.php',
'theseer\\fdom\\fdomxpath' => '/fDOMXPath.php',
'theseer\\fdom\\xpathquery' => '/XPathQuery.php',
'theseer\\fdom\\xpathqueryexception' => '/XPathQueryException.php'
);
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require __DIR__ . $classes[$cn];
}
}
);
// @codeCoverageIgnoreEnd
================================================
FILE: src/css/DollarEqualRule.php
================================================
<?php
namespace TheSeer\fDOM\CSS {
class DollarEqualRule implements RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/\[([a-zA-Z0-9\_\-]+)\$=([^\]]+)\]/',
array($this, 'callback'),
$selector
);
}
/**
* Build query from matches.
*
* @param array $matches
*
* @return string
*/
private function callback(array $matches) {
return '[substring(@' . $matches[1] . ',string-length(@' . $matches[1] . ')-' . (strlen($matches[2]) - 3) . ')=' . $matches[1] . ']';
}
}
}
================================================
FILE: src/css/NotRule.php
================================================
<?php
namespace TheSeer\fDOM\CSS {
class NotRule implements RuleInterface {
/**
* @var Translator
*/
private $translator;
/**
* @param Translator $translator
*/
public function __construct(Translator $translator) {
$this->translator = $translator;
}
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/([a-zA-Z0-9\_\-\*]+):not\(([^\)]*)\)/',
array($this, 'callback'),
$selector
);
}
/**
* @param array $matches
*
* @return string
*/
private function callback(array $matches) {
$subresult = preg_replace(
'/^[^\[]+\[([^\]]*)\].*$/',
'$1',
$this->translator->translate($matches[2])
);
return $matches[1] . '[not(' . $subresult . ')]';
}
}
}
================================================
FILE: src/css/NthChildRule.php
================================================
<?php
namespace TheSeer\fDOM\CSS {
class NthChildRule implements RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace_callback(
'/([a-zA-Z0-9\_\-\*]+):nth-child\(([^\)]*)\)/',
array($this, 'callback'),
$selector
);
}
/**
* @param array $matches
*
* @return string
*/
private function callback(array $matches) {
switch($matches[2]){
case 'n': {
return $matches[1];
}
case 'even': {
return '*[position() mod 2=0 and position()>=0]/self::' . $matches[1];
}
case 'odd': {
return $matches[1] . '[(count(preceding-sibling::*) + 1) mod 2=1]';
}
default: {
$b = !isset($matches[2]) || empty($matches[2]) ? '0' : $matches[2];
$b = preg_replace('/^([0-9]*)n.*?([0-9]*)$/', '$1+$2', $b);
$b = explode('+', $b);
if (!isset($b[1])) {
$b[1] = '0';
}
return '*[(position()-' . $b[1] . ') mod ' . $b[0] . '=0 and position()>=' . $b[1] . ']/self::' . $matches[1];
}
}
}
}
}
================================================
FILE: src/css/RegexRule.php
================================================
<?php
namespace TheSeer\fDOM\CSS {
class RegexRule implements RuleInterface {
/**
* @var string
*/
private $regex;
/**
* @var string
*/
private $replacement;
/**
* @param string $regex
* @param string $replacement
*/
public function __construct($regex, $replacement) {
$this->regex = $regex;
$this->replacement = $replacement;
}
/**
* @param $selector
*
* @return string
*/
public function apply($selector) {
return preg_replace($this->regex, $this->replacement, $selector);
}
}
}
================================================
FILE: src/css/RuleInterface.php
================================================
<?php
namespace TheSeer\fDOM\CSS {
interface RuleInterface {
/**
* @param $selector
*
* @return string
*/
public function apply($selector);
}
}
================================================
FILE: src/css/Translator.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\CSS {
/**
* Class Translator
*
* The regular expressions used in this class are heavily inspired by and mostly adopted from
* the css2xpath.js code by Andrea Giammarchi (http://code.google.com/p/css2xpath/).
* The JavaScript version (css2xpath.js) is licensed under the MIT License
*
*/
class Translator {
/**
* @var array
*/
private $rules;
/**
* @param string $selector A CSS Selector string
*
* @return string
*/
public function translate($selector) {
foreach($this->getRules() as $rule) {
/** @var RuleInterface $rule */
$selector = $rule->apply($selector);
}
return '//' . $selector;
}
/**
* @return array
*/
private function getRules() {
if ($this->rules != NULL) {
return $this->rules;
}
$this->rules = array(
// prefix|name
new RegexRule('/([a-zA-Z0-9\_\-\*]+)\|([a-zA-Z0-9\_\-\*]+)/', '$1:$2'),
// add @ for attribs
new RegexRule("/\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]/", '[@$1$2]'),
// multiple queries
new RegexRule("/\s*,\s*/", '|'),
// , + ~ >
new RegexRule("/\s*(\+|~|>)\s*/", '$1'),
//* ~ + >
new RegexRule("/([a-zA-Z0-9\_\-\*])~([a-zA-Z0-9\_\-\*])/", '$1/following-sibling::$2'),
new RegexRule("/([a-zA-Z0-9\_\-\*])\+([a-zA-Z0-9\_\-\*])/", '$1/following-sibling::*[1]/self::$2'),
new RegexRule("/([a-zA-Z0-9\_\-\*])>([a-zA-Z0-9\_\-\*])/", '$1/$2'),
// all unescaped stuff escaped
new RegexRule("/\[([^=]+)=([^'|'][^\]]*)\]/", '[$1="$2"]'),
// all descendant or self to //
new RegexRule("/(^|[^a-zA-Z0-9\_\-\*])(#|\.)([a-zA-Z0-9\_\-]+)/", '$1*$2$3'),
new RegexRule("/([\>\+\|\~\,\s])([a-zA-Z\*]+)/", '$1//$2'),
new RegexRule("/\s+\/\//", '//'),
// :first-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):first-child/", '*[1]/self::$1'),
// :last-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):last-child/", '$1[not(following-sibling::*)]'),
// :only-child
new RegexRule("/([a-zA-Z0-9\_\-\*]+):only-child/", '*[last()=1]/self::$1'),
// :empty
new RegexRule("/([a-zA-Z0-9\_\-\*]+):empty/", '$1[not(*) and not(normalize-space())]'),
// :not
new NotRule($this),
// :nth-child
new NthChildRule(),
// :contains(selectors)
new RegexRule('/:contains\(([^\)]*)\)/', '[contains(string(.),"$1")]'),
// |= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\|=([^\]]+)\]/", '[@$1=$2 or starts-with(@$1,concat($2,"-"))]'),
// *= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\*=([^\]]+)\]/", '[contains(@$1,$2)]'),
// ~= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)~=([^\]]+)\]/", '[contains(concat(" ",normalize-space(@$1)," "),concat(" ",$2," "))]'),
// ^= attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\^=([^\]]+)\]/", '[starts-with(@$1,$2)]'),
// $= attrib
new DollarEqualRule(),
// != attrib
new RegexRule("/\[([a-zA-Z0-9\_\-]+)\!=([^\]]+)\]/", '[not(@$1) or @$1!=$2]'),
// ids and classes
new RegexRule("/#([a-zA-Z0-9\_\-]+)/", '[@id="$1"]'),
new RegexRule("/\.([a-zA-Z0-9\_\-]+)/", '[contains(concat(" ",normalize-space(@class)," ")," $1 ")]'),
// normalize multiple filters
new RegexRule("/\]\[([^\]]+)/", ' and ($1)')
);
return $this->rules;
}
}
}
================================================
FILE: src/fDOMDocument.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
use TheSeer\fDOM\CSS\Translator;
/**
* fDOMDocument extension to PHP's DOMDocument.
* This class adds various convenience methods to simplify APIs
* It is set to final since further extending it would even more
* break the Object structure after use of registerNodeClass.
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMDocument extends \DOMDocument {
/**
* XPath Object instance
*
* @var fDOMXPath
*/
private $xp = NULL;
/**
* List of registered prefixes and their namespace uri
* @var array
*/
private $prefixes = array();
/**
* Extended DOMDocument constructor
*
* @param string $version XML Version, should be 1.0
* @param string $encoding Encoding, defaults to utf-8
* @param array $streamOptions optional stream options array
*
* @return fDOMDocument
*/
public function __construct($version = '1.0', $encoding = 'utf-8', $streamOptions = NULL) {
if (!is_null($streamOptions)) {
$this->setStreamContext($streamOptions);
}
libxml_use_internal_errors(TRUE);
$rc = parent::__construct($version, $encoding);
$this->registerNodeClasses();
return $rc;
}
/**
* Reset XPath object so the clone gets a new instance when needed
*/
public function __clone() {
$this->registerNodeClasses();
$this->xp = new fDOMXPath($this);
foreach($this->prefixes as $prefix => $uri) {
$this->xp->registerNamespace($prefix, $uri);
}
}
/**
* @return string
*/
public function __toString() {
return $this->C14N();
}
/**
* Set Stream context options
*
* @param array $options Stream context options
*
* @return boolean true on success, false on failure
*/
public function setStreamContext(array $options) {
if (!count($options)) {
return FALSE;
}
$context = stream_context_create($options);
libxml_set_streams_context($context);
return TRUE;
}
/**
* Wrapper to DOMDocument load with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $fname File to load
* @param int|null $options LibXML Flags to pass
*
* @throws fDOMException
*
* @return bool|mixed
*/
public function load($fname, $options = LIBXML_NONET) {
if ($fname === '') {
throw new fDOMException('empty filename is not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
$tmp = parent :: load($fname, $options);
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException("loading file '$fname' failed.", fDOMException::LoadError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument loadXML with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $source XML source code
* @param integer $options LibXML option flags
*
* @throws fDOMException
*
* @return boolean
*/
public function loadXML($source, $options = LIBXML_NONET) {
if ($source === '') {
throw new fDOMException('empty string not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
$tmp = parent :: loadXML($source, $options);
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException('parsing string failed:' . (libxml_get_last_error())->message, fDOMException::ParseError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument loadHTMLFile with exception handling.
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $fname html file to load
* @param integer $options Options bitmask (@see DOMDocument::loadHTMLFile)
*
* @throws fDOMException
*
* @return boolean
*/
public function loadHTMLFile($fname, $options = NULL) {
if ($fname === '') {
throw new fDOMException('empty filename is not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if ($options !== NULL) {
throw new fDOMException('Passing options requires PHP 5.4.0+', fDOMException::LoadError);
}
$tmp = parent :: loadHTMLFile($fname);
} else {
$tmp = parent :: loadHTMLFile($fname, $options);
}
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException("loading html file '$fname' failed", fDOMException::LoadError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument loadHTML with exception handling
* Returns true on success to satisfy the compatibilty of the original DOM Api
*
* @param string $source html source code
* @param integer $options Options bitmask (@see DOMDocument::loadHTML)
*
* @throws fDOMException
*
* @return boolean
*/
public function loadHTML($source, $options = NULL) {
if ($source === '') {
throw new fDOMException('empty string not allowed', fDOMException::ParseError);
}
$this->xp = NULL;
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
if ($options !== NULL) {
throw new fDOMException('Passing options requires PHP 5.4.0+', fDOMException::LoadError);
}
$tmp = parent :: loadHTML($source);
} else {
$tmp = parent :: loadHTML($source, $options);
}
if (!$tmp || libxml_get_last_error()) {
throw new fDOMException('parsing html string failed', fDOMException::ParseError);
}
$this->registerNodeClasses();
return TRUE;
}
/**
* Wrapper to DOMDocument::save with exception handling
*
* @param string $filename filename to save to
* @param integer $options Options bitmask (@see DOMDocument::save)
*
* @throws fDOMException
*
* @return integer bytes saved
*/
#[\ReturnTypeWillChange]
public function save($filename, $options = NULL) {
$tmp = parent::save($filename, $options);
if (!$tmp) {
throw new fDOMException("Saving XML to file '$filename' failed", fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveHTML with exception handling
*
* @param \DOMNode|null $node Context DOMNode (optional)
*
* @throws fDOMException
*
* @return string html content
*/
#[\ReturnTypeWillChange]
public function saveHTML(\DOMNode $node = NULL) {
if (version_compare(PHP_VERSION, '5.3.6', '<') && $node !== NULL) {
throw new fDOMException('Passing a context node requires PHP 5.3.6+', fDOMException::SaveError);
}
$tmp = parent::saveHTML($node);
if (!$tmp) {
throw new fDOMException('Serializing to HTML failed', fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveHTMLfile with exception handling
*
* @param string $filename filename to save to
* @param integer $options Options bitmask (@see DOMDocument::saveHTMLFile)
*
* @throws fDOMException
*
* @return integer bytes saved
*/
#[\ReturnTypeWillChange]
public function saveHTMLFile($filename, $options = NULL) {
$tmp = parent::saveHTMLFile($filename, $options);
if (!$tmp) {
throw new fDOMException("Saving HTML to file '$filename' failed", fDOMException::SaveError);
}
return $tmp;
}
/**
* Wrapper to DOMDocument::saveXML with exception handling
*
* @param \DOMNode $node node to start serializing at
* @param integer $options options flags as bitmask
*
* @throws fDOMException
*
* @return string serialized XML
*/
#[\ReturnTypeWillChange]
public function saveXML(\DOMNode $node = NULL, $options = NULL) {
try {
if ($options !== null) {
$tmp = parent::saveXML($node, $options);
} else {
$tmp = parent::saveXML($node);
}
if (!$tmp) {
throw new fDOMException('Serializing to XML failed', fDOMException::SaveError);
}
return $tmp;
} catch (\Exception $e) {
if (!$e instanceof fDOMException) {
throw new fDOMException($e->getMessage(), fDOMException::SaveError, $e);
}
throw $e;
}
}
/**
* get Instance of DOMXPath Object for current DOM
*
* @throws fDOMException
*
* @return fDOMXPath
*/
public function getDOMXPath() {
if (is_null($this->xp)) {
$this->xp = new fDOMXPath($this);
}
if (!$this->xp) {
throw new fDOMException('creating DOMXPath object failed.', fDOMException::NoDOMXPath);
}
return $this->xp;
}
/**
* Convert a given DOMNodeList into a DOMFragment
*
* @param \DOMNodeList $list The Nodelist to process
* @param boolean $move Signale if nodes are to be moved into fragment or not
*
* @return fDOMDocumentFragment
*/
public function nodeList2Fragment(\DOMNodeList $list, $move=FALSE) {
$frag = $this->createDocumentFragment();
/** @var fDOMNode $node */
foreach($list as $node) {
$frag->appendChild($move ? $node : $node->cloneNode(TRUE));
}
return $this->ensureIntance($frag);
}
/**
* Perform an xpath query
*
* @param String $q query string containing xpath
* @param \DOMNode|null $ctx (optional) Context DOMNode
* @param boolean $registerNodeNS Register flag pass through
*
* @return \DOMNodeList
*/
public function query($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->evaluate($q, $ctx, $registerNodeNS);
}
/**
* Perform an xpath query and return only the 1st match
*
* @param String $q query string containing xpath
* @param \DOMNode $ctx (optional) Context DOMNode
* @param boolean $registerNodeNS Register flag pass thru
*
* @return fDOMNode
*/
public function queryOne($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->queryOne($q, $ctx, $registerNodeNS);
}
/**
* Forwarder to fDOMXPath's prepare method allowing for easy and secure
* placeholder replacement comparable to sql's prepared statements
* .
* @param string $xpath String containing xpath with :placeholder markup
* @param array $valueMap array containing keys (:placeholder) and value pairs to be quoted
*
* @return string
*/
public function prepareQuery($xpath, array $valueMap) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
return $this->xp->prepare($xpath, $valueMap);
}
/**
* Use a CSS Level 3 Selector string to query select nodes
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
$translator = new Translator();
$xpath = $translator->translate($selector);
if ($ctx !== NULL) {
$xpath = '.' . $xpath;
}
return $this->query($xpath, $ctx, $registerNodeNS);
}
/**
* Forward to DOMXPath->registerNamespace()
*
* @param string $prefix The prefix to use
* @param string $uri The uri to assign to this prefix
*
* @throws fDOMException
*
* @return void
*/
public function registerNamespace($prefix, $uri) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
if (!$this->xp->registerNamespace($prefix, $uri)) {
throw new fDOMException("Registering namespace '$uri' with prefix '$prefix' failed.", fDOMException::RegistrationFailed);
}
$this->prefixes[$prefix] = $uri;
}
/**
* Forward to DOMXPath->registerPHPFunctions()
*
* @param mixed $restrict array of function names or string with functionname to restrict callabilty to
*
* @throws fDOMException
*
* @return void
*/
public function registerPHPFunctions($restrict = NULL) {
if (is_null($this->xp)) {
$this->getDOMXPath();
}
$this->xp->registerPHPFunctions($restrict);
if (libxml_get_last_error()) {
throw new fDOMException("Registering php functions failed.", fDOMException::RegistrationFailed);
}
}
/**
* Create a new element in namespace defined by given prefix
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement Reference to created fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
if (!isset($this->prefixes[$prefix])) {
throw new fDOMException("'$prefix' not bound", fDOMException::UnboundPrefix);
}
return $this->createElementNS($this->prefixes[$prefix], $prefix.':'.$name, $content, $asTextNode);
}
/**
* Create a new fDOMElement and return it, optionally set content
*
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement Reference to created fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
try {
$node = parent::createElement($name);
if (!$node) {
throw new fDOMException("Creating element with name '$name' failed", fDOMException::NameInvalid);
}
if ($content !== NULL) {
if ($asTextnode) {
$node->appendChild($this->createTextnode($content));
} else {
$node->nodeValue = $content;
}
if (libxml_get_errors()) {
throw new fDOMException("Setting content value failed", fDOMException::SetFailedError);
}
}
return $this->ensureIntance($node);
} catch (\DOMException $e) {
throw new fDOMException("Creating elemnt with name '$name' failed", 0, $e);
}
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param string $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
$node = parent::createElementNS($namespace, $name);
if (!$node) {
throw new fDOMException("Creating element with name '$name' failed", fDOMException::NameInvalid);
}
if ($content !== NULL) {
if ($asTextNode) {
$node->appendChild($this->createTextnode($content));
} else {
$node->nodeValue = $content;
}
if (libxml_get_errors()) {
throw new fDOMException("Setting content value failed", fDOMException::SetFailedError);
}
}
return $this->ensureIntance($node);
}
/**
* @return fDOMDocumentFragment
*
*/
#[\ReturnTypeWillChange]
public function createDocumentFragment() {
return $this->ensureIntance(parent::createDocumentFragment());
}
/**
* Check if the given node is in the same document
*
* @param \DOMNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
if ($node instanceof \DOMDocument) {
return $this->isSameNode($node);
}
return $this->isSameNode($node->ownerDocument);
}
/**
* Create a new element and append it as documentElement
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = NULL, $asTextNode = FALSE) {
return $this->appendChild(
$this->createElement($name, $content, $asTextNode)
);
}
/**
* Create a new element in given namespace and append it as documentElement
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextNode
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = NULL, $asTextNode = FALSE) {
return $this->appendChild(
$this->createElementNS($ns, $name, $content, $asTextNode)
);
}
/**
* This is a workaround for hhvm's broken registerNodeClass handling
* (https://github.com/facebook/hhvm/issues/1848)
*
* @param \DOMNode $node
*
* @return \DOMNode
*/
private function ensureIntance(\DOMNode $node) {
if ($node instanceof fDOMNode || $node instanceof fDOMElement || $node instanceof fDOMDocumentFragment) {
return $node;
}
return $this->importNode($node, TRUE);
}
/**
* Register replacements
*
* Called from constructor and, as a workaround for (https://github.com/facebook/hhvm/issues/5412),
* after load(), loadXML(), loadHTML() and loadHTMLFile()
*/
private function registerNodeClasses() {
$this->registerNodeClass('DOMDocument', get_called_class());
$this->registerNodeClass('DOMNode', 'TheSeer\fDOM\fDOMNode');
$this->registerNodeClass('DOMElement', 'TheSeer\fDOM\fDOMElement');
$this->registerNodeClass('DOMDocumentFragment', 'TheSeer\fDOM\fDOMDocumentFragment');
}
} // fDOMDocument
}
================================================
FILE: src/fDOMDocumentFragment.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDOMDocumentFragment
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMDocumentFragment extends \DOMDocumentFragment {
/**
* @return string
*/
public function __toString() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to standard method with exception support
*
* @param string $str Data string to parse and append
*
* @throws fDOMException
*
* @return bool true on success
*/
#[\ReturnTypeWillChange]
public function appendXML($str) {
if (!parent::appendXML($str)) {
throw new fDOMException('Appending xml string failed', fDOMException::ParseError);
}
return true;
}
/**
* Create a new element and append it
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = null) {
$node = $this->ownerDocument->createElement($name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = null) {
$node = $this->ownerDocument->createElementNS($ns, $name, $content);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementPrefix($prefix, $name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new text node and append it
*
* @param string $content Text content to be added
*
* @return \DOMText
*/
public function appendTextNode($content) {
$text = $this->ownerDocument->createTextNode($content);
$this->appendChild($text);
return $text;
}
/**
* Check if the given node is in the same document
*
* @param \DOMNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx ? $ctx : $this, $registerNodeNS);
}
} // fDOMDocumentFragment
}
================================================
FILE: src/fDOMElement.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDomElement
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMElement extends \DOMElement {
/**
* @return string
*/
public function __toString() {
return $this->C14N();
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx, $registerNodeNS);
}
/**
* Parse and append XML String to node
*
* @param String $str string to process
*
* @return fDomDocumentFragment Reference to the created Fragment
*/
public function appendXML($str) {
$frag = $this->ownerDocument->createDocumentFragment();
$frag->appendXML($str);
$this->appendChild($frag);
return $frag;
}
/**
* Create a new element and append it
*
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElement($name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElement($name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $ns Namespace of node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementNS($ns, $name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElementNS($ns, $name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new element in given namespace and append it
*
* @param string $prefix Namespace prefix for node to create
* @param string $name Name of not element to create
* @param string $content Optional content to be set
* @param bool $asTextnode Create content as textNode rather then setting nodeValue
*
* @return fDOMElement Reference to created fDOMElement
*/
public function appendElementPrefix($prefix, $name, $content = null, $asTextnode = FALSE) {
$node = $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextnode);
$this->appendChild($node);
return $node;
}
/**
* Create a new text node and append it
*
* @param string $content Text content to be added
*
* @return \DOMText
*/
public function appendTextNode($content) {
$text = $this->ownerDocument->createTextNode($content);
$this->appendChild($text);
return $text;
}
/**
* Create a new fDOMElement
*
* @see fDOMDocument::createElement
*
* @param string $name
* @param string $content
* @param bool $asTextnode
*
* @return fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
return $this->ownerDocument->createElement($name, $content, $asTextnode);
}
/**
* Create a new fDOMElement in namespace defined by prefix
*
* @see fDOMDocument::createElementPrefix
*
* @param string $prefix
* @param string $name
* @param string $content
* @param bool $asTextNode
*
* @return fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextNode);
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementNS($namespace, $name, $content, $asTextNode);
}
/**
* Wrapper to DomElement->getAttribute with default value option
*
* Note: A set but emptry attribute does NOT trigger use of the default
*
* @param string $attr Attribute to access
* @param string $default Default value to use if the attribute is not set
*
* @return string
*/
#[\ReturnTypeWillChange]
public function getAttribute($attr, $default='') {
return $this->hasAttribute($attr) ? parent::getAttribute($attr) : $default;
}
/**
* Wrapper to DomElement->getAttributeNS with default value option
*
* Note: A set but empty attribute does NOT trigger use of the default
*
* @param string $ns Namespace of attribute
* @param string $attr Attribute to access
* @param string $default Default value to use if the attribute is not set
*
* @return string
*/
#[\ReturnTypeWillChange]
public function getAttributeNS($ns, $attr, $default='') {
return $this->hasAttributeNS($ns, $attr) ? parent::getAttributeNS($ns, $attr) : $default;
}
/**
* Wrapper to DOMElement::setAttribute with additional entities support
*
* @param string $attr Attribute name to set
* @param string $value Value to set attribute to
* @param bool $keepEntities Flag to signale if entities should be kept
*
* @throws fDOMException
*
* @return \DOMAttr
*
* @see DOMElement::setAttribute()
*/
#[\ReturnTypeWillChange]
public function setAttribute($attr, $value, $keepEntities=false) {
if ($keepEntities === true) {
$attrNode = $this->ownerDocument->createAttribute($attr);
if (!$attrNode) {
throw new fDOMException("Setting attribute '$attr' failed.", fDOMException::SetFailedError);
}
$attrNode->value = $value;
$this->appendChild($attrNode);
return $attrNode;
}
return parent::setAttribute($attr, $value);
}
/**
* Wrapper to namespace aware DOMElement::setAttributeNS with additional entities support
*
* @param string $ns namespace attribute should be in
* @param string $attr Attribute name to set
* @param string $value Value to set attribute to
* @param bool $keepEntities Flag to signale if entities should be kept
*
* @throws fDOMException
*
* @return \DOMAttr|null
* @see DOMElement::setAttribute()
*/
#[\ReturnTypeWillChange]
public function setAttributeNS($ns, $attr, $value, $keepEntities=false) {
if ($keepEntities === true) {
$attrNode = $this->ownerDocument->createAttributeNS($ns, $attr);
if (!$attrNode) {
throw new fDOMException("Setting attribute '$attr' failed.", fDOMException::SetFailedError);
}
$attrNode->value = $value;
$this->appendChild($attrNode);
return $attrNode;
}
return parent::setAttributeNS($ns, $attr, $value);
}
/**
* Helper to add multiple attributes to an element
*
* @param array $attr Attributes to add as key-value pair
* @param bool $keepEntities Flag wether to keep entities
*
* @return array List with references to created DOMAttr
*/
public function setAttributes(array $attr, $keepEntities=false) {
$attList = array();
foreach($attr as $name => $value) {
$attList[] = $this->setAttribute($name, $value, $keepEntities);
}
return $attList;
}
/**
* Helper to add multiple attributes with the given namespace and prefix
*
* @param string $ns Namespace of attribute
* @param string $prefix Namespace prefix for attribute to create
* @param array $attr Attributes to add
* @param bool $keepEntities Flag wether to keep entities
*
* @return void
*/
public function setAttributesNS($ns, $prefix, array $attr, $keepEntities=false) {
foreach($attr as $name => $value) {
$this->setAttributeNS($ns, $prefix.':'.$name, $value, $keepEntities);
}
}
/**
* Helper method to get children by name
*
* @param string $tagName tagname to search for
*
* @return \DOMNodeList
*/
public function getChildrenByTagName($tagName) {
return $this->query("*[local-name()='$tagName']");
}
/**
* Helper method to get children by name and namespace
*
* @param string $ns namespace nodes have to be in
* @param string $tagName tagname to search for
*
* @return \DOMNodeList
*/
public function getChildrenByTagNameNS($ns, $tagName) {
return $this->query("*[local-name()='$tagName' and namespace-uri()='$ns']");
}
/**
* Check if the given node is in the same document
*
* @param \DomNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DomNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Wrapper to DomDocument::saveXML() with current node as context
*
* @return string
*/
public function saveXML() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to DomDocument::saveHTML() with current node as context
*
* @return string
*/
public function saveHTML() {
return $this->ownerDocument->saveHTML($this);
}
} // fDOMElement
}
================================================
FILE: src/fDOMException.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* @package fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license BSD License
*/
namespace TheSeer\fDOM {
/**
* fDOMException
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMException extends \Exception {
const LoadError = 1;
const ParseError = 2;
const SaveError = 3;
const QueryError = 4;
const RegistrationFailed = 5;
const NoDOMXPath = 6;
const UnboundPrefix = 7;
const SetFailedError = 8;
const NameInvalid = 9;
/**
* List of libxml error objects
*
* @var array
*/
private $errorList;
/**
* Full Error message
*
* @var string
*/
private $fullMessage = null;
/**
* Short Error Message
*
* @var string
*/
private $shortMessage = null;
private static $fullMesageMode = true;
/**
* Constructor
*
* @param string $message Exception message
* @param integer $code Exception code
* @param \Exception $chain optional chained exception
*
*/
public function __construct($message, $code = 0, \Exception $chain = NULL) {
$this->shortMessage = $message;
$this->errorList = libxml_get_errors();
libxml_clear_errors();
parent :: __construct($message, $code, $chain);
$this->fullMessage = $message."\n\n";
foreach ($this->errorList as $error) {
// hack, skip "attempt to load external pseudo error"
if ($error->code=='1543') {
continue;
}
if (empty($error->file)) {
$this->fullMessage .= '[XML-STRING] ';
} else {
$this->fullMessage .= '['.$error->file.'] ';
}
$this->fullMessage .= '[Line: '.$error->line.' - Column: '.$error->column.'] ';
switch ($error->level) {
case LIBXML_ERR_WARNING:
$this->fullMessage .= "Warning $error->code: ";
break;
case LIBXML_ERR_ERROR:
$this->fullMessage .= "Error $error->code: ";
break;
case LIBXML_ERR_FATAL:
$this->fullMessage .= "Fatal Error $error->code: ";
break;
}
$this->fullMessage .= str_replace("\n", '', $error->message)."\n";
if (self::$fullMesageMode) {
$this->message = $this->fullMessage;
}
}
}
/**
* Accessor to fullMessage
*
* @return string
*/
public function getFullMessage() {
return $this->fullMessage;
}
/**
* Access to shortMessage
*
* @return string
*/
public function getShortMessage() {
return $this->shortMessage;
}
/**
* Accessor to errorList objets
*
* @return array
*/
public function getErrorList() {
return $this->errorList;
}
/**
* Toggle wehter getMessage() should return full or only exception message
*
* @param boolean $full Flag to enable or disable full message output
*
* @return void
*/
public function toggleFullMessage($full = true) {
$this->message = $full ? $this->fullMessage : $this->shortMessage;
}
/**
* Magic method for string context
*
* @return string
*/
public function __toString() {
return $this->fullMessage;
}
} // fDOMException
}
================================================
FILE: src/fDOMNode.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDomNode
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
* @property fDOMDocument $ownerDocument
*
*/
class fDOMNode extends \DOMNode {
/**
* @return string
*/
public function __toString() {
return $this->C14N();
}
/**
* Create a new fDOMElement
*
* @see fDOMDocument::createElement
*
* @param string $name
* @param string $content
* @param bool $asTextnode
*
* @return fDOMElement
*/
public function createElement($name, $content = NULL, $asTextnode = FALSE) {
return $this->ownerDocument->createElement($name, $content, $asTextnode);
}
/**
* Create a new fDOMElement in namespace defined by prefix
*
* @see fDOMDocument::createElementPrefix
*
* @param string $prefix
* @param string $name
* @param string $content
* @param bool $asTextNode
*
* @return fDOMElement
*/
public function createElementPrefix($prefix, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementPrefix($prefix, $name, $content, $asTextNode);
}
/**
* Create a new fDOMElement within given namespace and return it
*
* @param string $namespace Namespace URI for node to create
* @param string $name Name of node to create
* @param null $content Content to set (optional)
* @param bool $asTextNode Create content as textNode rather then setting nodeValue
*
* @throws fDOMException
*
* @return fDOMElement
*/
public function createElementNS($namespace, $name, $content = NULL, $asTextNode = FALSE) {
return $this->ownerDocument->createElementNS($namespace, $name, $content, $asTextNode);
}
/**
* Forward to fDomDocument->query()
*
* @param string $q XPath to use
* @param \DOMNode $ctx \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return \DomNodeList
*/
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->query($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->queryOne()
*
* @param string $q XPath to use
* @param \DOMNode $ctx (optional) \DOMNode to overwrite context
* @param boolean $registerNodeNS Register flag pass thru
*
* @return mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->queryOne($q, $ctx ? $ctx : $this, $registerNodeNS);
}
/**
* Forward to fDomDocument->select()
*
* @param string $selector A CSS Level 3 Selector string
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @return \DOMNodeList
*/
public function select($selector, \DOMNode $ctx = null, $registerNodeNS = true) {
return $this->ownerDocument->select($selector, $ctx, $registerNodeNS);
}
/**
* Check if the given node is in the same document
*
* @param \DomNode $node Node to compare with
*
* @return boolean true on match, false if they differ
*
*/
public function inSameDocument(\DOMNode $node) {
return $this->ownerDocument->inSameDocument($node);
}
/**
* Wrapper to DomDocument::saveXML() with current node as context
*
* @return string
*/
public function saveXML() {
return $this->ownerDocument->saveXML($this);
}
/**
* Wrapper to DomDocument::saveHTML() with current node as context
*
* @return string
*/
public function saveHTML() {
return $this->ownerDocument->saveHTML($this);
}
} // fDOMNode
}
================================================
FILE: src/fDOMXPath.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM {
/**
* fDOMXPath extension to PHP's DOMXPath.
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @access public
*
*/
class fDOMXPath extends \DOMXPath {
/**
* @var \DOMDocument
*/
protected $doc;
/**
* @param \DOMDocument $doc
*/
public function __construct(\DOMDocument $doc) {
parent::__construct($doc);
$this->doc = $doc;
}
/**
* @param string $xpath
* @param array $valueMap
*
* @return string
*/
public function prepare($xpath, array $valueMap) {
if (count($valueMap)==0) {
return $xpath;
}
foreach($valueMap as $key => $value) {
$xpath = str_replace(':'.$key, $this->quote($value), $xpath);
}
return $xpath;
}
/**
* @param string $q
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return \DOMNodeList
*/
#[\ReturnTypeWillChange]
public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
libxml_clear_errors();
if (version_compare(PHP_VERSION, '5.3.3', '<') || strpos(PHP_VERSION, 'hiphop') || strpos(PHP_VERSION, 'hhvm')) {
$rc = parent::query($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement);
} else {
$rc = parent::query($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement, $registerNodeNS);
}
if (libxml_get_last_error()) {
throw new fDOMException('evaluating xpath expression failed.', fDOMException::QueryError);
}
return $rc;
}
/**
* @param string $q
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function evaluate($q, \DOMNode $ctx = null, $registerNodeNS = true) {
libxml_clear_errors();
if (version_compare(PHP_VERSION, '5.3.3', '<') || strpos(PHP_VERSION, 'hiphop') || strpos(PHP_VERSION, 'hhvm')) {
$rc = parent::evaluate($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement);
} else {
$rc = parent::evaluate($q, ($ctx instanceof \DOMNode) ? $ctx : $this->doc->documentElement, $registerNodeNS);
}
if (libxml_get_last_error()) {
throw new fDOMException('evaluating xpath expression failed.', fDOMException::QueryError);
}
return $rc;
}
/**
* @param string $q
* @param \DOMNode $ctx
* @param bool $registerNodeNS
*
* @throws fDOMException
*
* @return \DOMNode|mixed
*/
public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = true) {
$rc = $this->evaluate($q, $ctx, $registerNodeNS);
if ($rc instanceof \DOMNodelist) {
return $rc->item(0);
}
return $rc;
}
/**
* @param string $str
*
* @return string
*/
public function quote($str) {
if (strpos($str, '"') === false) {
return '"'.$str.'"';
}
$parts = explode('"', $str);
return 'concat("' . join('",\'"\',"', $parts).'")';
}
}
}
================================================
FILE: tests/Translator.test.php
================================================
<?php
namespace TheSeer\fDOM\Tests {
use TheSeer\fDOM\CSS\Translator;
class TranslatorTest extends \PHPUnit\Framework\TestCase {
/**
* @dataProvider provider
*/
public function testTranslatingCssSelectorReturnsCorrectXPath($selector, $xpath) {
$translator = new Translator();
$this->assertEquals($xpath, $translator->translate($selector));
}
public function provider() {
return array(
array("div", '//div'),
array("body div", '//body//div'),
array("div p", '//div//p'),
array("div > p", '//div/p'),
array("div + p", '//div/following-sibling::*[1]/self::p'),
array("div ~ p", '//div/following-sibling::p'),
array("div[class^=exa][class$=mple]", '//div[starts-with(@class,"exa") and (substring(@class,string-length(@class)-3)=class)]'),
array("div p a", '//div//p//a'),
array("div, p, a", '//div|//p|//a'),
array(".note", '//*[contains(concat(" ",normalize-space(@class)," ")," note ")]'),
array("div.example", '//div[contains(concat(" ",normalize-space(@class)," ")," example ")]'),
array("ul .tocline2", '//ul//*[contains(concat(" ",normalize-space(@class)," ")," tocline2 ")]'),
array("div.example, div.note", '//div[contains(concat(" ",normalize-space(@class)," ")," example ")]|//div[contains(concat(" ",normalize-space(@class)," ")," note ")]'),
array("#title", '//*[@id="title"]'),
array("h1#title", '//h1[@id="title"]'),
array("div #title", '//div//*[@id="title"]'),
array("ul.toc li.tocline2", '//ul[contains(concat(" ",normalize-space(@class)," ")," toc ")]//li[contains(concat(" ",normalize-space(@class)," ")," tocline2 ")]'),
array("ul.toc > li.tocline2", '//ul[contains(concat(" ",normalize-space(@class)," ")," toc ")]/li[contains(concat(" ",normalize-space(@class)," ")," tocline2 ")]'),
array("h1#title + div > p", '//h1[@id="title"]/following-sibling::*[1]/self::div/p'),
array("h1[id]:contains(Selectors)", '//h1[@id and (contains(string(.),"Selectors"))]'),
array("a[href][lang][class]", '//a[@href and (@lang) and (@class)]'),
array("div[class]", '//div[@class]'),
array("div[class=example]", '//div[@class="example"]'),
array("div[class^=exa]", '//div[starts-with(@class,"exa")]'),
array("div[class$=mple]", '//div[substring(@class,string-length(@class)-3)=class]'),
array("div[class*=e]", '//div[contains(@class,"e")]'),
array("div[class|=dialog]", '//div[@class="dialog" or starts-with(@class,concat("dialog","-"))]'),
array("div[class!=made_up]", '//div[not(@class) or @class!="made_up"]'),
array("div[class~=example]", '//div[contains(concat(" ",normalize-space(@class)," "),concat(" ","example"," "))]'),
array("div:not(.example)", '//div[not(contains(concat(" ",normalize-space(@class)," ")," example "))]'),
array("p:contains(selectors)", '//p[contains(string(.),"selectors")]'),
array("p:nth-child(even)", '//*[position() mod 2=0 and position()>=0]/self::p'),
array("p:nth-child(2n)", '//*[(position()-) mod 2=0 and position()>=]/self::p'),
array("p:nth-child(odd)", '//p[(count(preceding-sibling::*) + 1) mod 2=1]'),
array("p:nth-child(2n+1)", '//*[(position()-1) mod 2=0 and position()>=1]/self::p'),
array("p:nth-child(n)", '//p'),
array("p:only-child", '//*[last()=1]/self::p'),
array("p:last-child", '//p[not(following-sibling::*)]'),
array("p:first-child", '//*[1]/self::p'),
array("foo|bar", '//foo:bar')
);
}
}
}
================================================
FILE: tests/XPathQuery.test.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\Tests {
use TheSeer\fDOM\XPathQuery;
use TheSeer\fDOM\fDOMDocument;
use TheSeer\fDOM\XPathQueryException;
class XPathQueryTest extends \PHPUnit\Framework\TestCase {
private $dom;
protected function setUp(): void {
$this->dom = new fDOMDocument();
$this->dom->loadXML('<?xml version="1.0" ?><root attr="value" />');
}
public function testFindingKeysInQueryWorks() {
$xp = new XPathQuery(':key');
$this->assertEquals(array('key'), $xp->getKeys());
}
public function testTryingToBindNonExistingKeyThrowsException() {
$xp = new XPathQuery(':key');
$this->expectException(XPathQueryException::class);
$xp->bind('other', 123);
}
public function testBoundValueForKeyGetsApplied() {
$xp = new XPathQuery(':key');
$xp->bind('key', 123);
$this->assertEquals('"123"', $xp->generate($this->dom));
}
public function testAppliedValueForKeyIsUsedOnQueryAndReturnsNode() {
$xp = new XPathQuery('//*[@attr = :key]');
$xp->bind('key', 'value');
$res = $xp->query($this->dom);
$this->assertInstanceOf('\DOMNodelist', $res);
$this->assertEquals(1, $res->length);
$this->assertInstanceOf('\DOMNode', $res->item(0));
}
public function testOverwriteValueOnQuery() {
$xp = new XPathQuery('//*[@attr = :key]');
$xp->bind('key', 'first');
$res = $xp->query($this->dom, array('key' => 'value'));
$this->assertEquals(1, $res->length);
$this->assertInstanceOf('\DOMNode', $res->item(0));
}
public function testAppliedValueForKeyIsUsedOnEvaluateAndReturnsNode() {
$xp = new XPathQuery('//*[@attr = :key]');
$xp->bind('key', 'value');
$res = $xp->evaluate($this->dom);
$this->assertInstanceOf('\DOMNodelist', $res);
$this->assertEquals(1, $res->length);
$this->assertInstanceOf('\DOMNode', $res->item(0));
}
public function testOverwriteValueOnEvaluate() {
$xp = new XPathQuery('//*[@attr = :key]');
$xp->bind('key', 'first');
$res = $xp->evaluate($this->dom, array('key' => 'value'));
$this->assertEquals(1, $res->length);
$this->assertInstanceOf('\DOMNode', $res->item(0));
}
public function testCallToQueryOneReturnsOneNode() {
$xp = new XPathQuery('//*[@attr]');
$res = $xp->queryOne($this->dom);
$this->assertInstanceOf('\DOMNode', $res);
}
public function testQueryCanBeRunWithStandardDomDocument() {
$xp = new XPathQuery('/');
$res = $xp->query(new \DomDocument());
$this->assertInstanceOf('\DOMNodelist', $res);
}
}
}
================================================
FILE: tests/_data/broken.xml
================================================
<?xml version="1.0" ?>
<broken>
<!-- This is broken for testing reasons - do not fix ;) ->
================================================
FILE: tests/_data/selector.xml
================================================
<?xml version="1.0" ?>
<root>
<child attr="abc" id="test">
<child attr="other" />
</child>
</root>
================================================
FILE: tests/_data/undefentity.xml
================================================
<?xml version="1.0" ?>
<root>&undefined;</root>
================================================
FILE: tests/_data/valid.xml
================================================
<?xml version="1.0"?>
<test/>
================================================
FILE: tests/fDOMDocument.test.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\Tests {
use TheSeer\fDOM\fDOMDocument;
use TheSeer\fDOM\fDOMException;
/**
*
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
*/
class fDOMDocumentTest extends \PHPUnit\Framework\TestCase {
/**
* @var fDOMDocument
*/
private $dom;
public function setUp(): void {
$this->dom = new fDOMDocument();
}
public function testloadingXMLStringWorks() {
$this->dom->loadXML('<?xml version="1.0" ?><test />');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $this->dom->documentElement);
}
public function testloadingXMLFileWorks() {
$this->dom->load(__DIR__ . '/_data/valid.xml');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $this->dom->documentElement);
}
public function testGetDomXPathReturnsXPathObject() {
$this->dom->loadXML('<?xml version="1.0" ?><test />');
$this->assertInstanceOf('TheSeer\fDOM\fDOMXpath', $this->dom->getDomXPath());
}
public function testAttemptingToLoadAnXMLStringWithAnUndefinedEntityThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->loadXML('<?xml version="1.0" ?><root>&undefined;</root>');
}
public function testAttemptingToLoadAnEmptyXMLStringThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->loadXML('');
}
public function testAttemptingToLoadWithEmptyFilenameThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->load('');
}
public function testAttemptingToLoadHTMLWithAnEmptyFilenameThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->loadHTMLFile('');
}
public function testAttemptingToLoadHMLWithAnEmptyStringThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->loadHTML('');
}
public function testloadingInvalidXMLStringThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->loadXML('<?xml version="1.0" ?><broken>');
}
public function testTryingToLoadNonExistingFileThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->load('_does_not_exist.xml');
}
public function testloadingBrokenXMLFileThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->load(__DIR__ . '/_data/broken.xml');
}
public function testAttemptingToLoadAnXMLFileWithAnUndefinedEntityThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->load(__DIR__ . '/_data/undefentity.xml');
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::query
*/
public function testQueryReturnsNodeList() {
$this->dom->load(__DIR__ . '/_data/valid.xml');
$list = $this->dom->query('/test');
$this->assertInstanceOf('DomNodelist', $list);
$this->assertEquals(1, $list->length);
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::queryOne
*/
public function testQueryOneReturnsElement() {
$this->dom->load(__DIR__ . '/_data/valid.xml');
$node = $this->dom->queryOne('/test');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
}
public function testSaveXMLReturnsCorrectXMLString() {
$xml = file_get_contents(__DIR__ . '/_data/valid.xml');
$this->dom->loadXML($xml);
$this->assertEquals($xml, $this->dom->saveXML());
}
public function testSaveXMLThrowsExceptionWithReferenceToNodeFromOtherDocument() {
$dom = new fDOMDocument();
$this->expectException(fDOMException::class);
$this->dom->saveXML($dom->createElement('foo'));
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::nodeList2FragMent
*/
public function testTransformNodeListToFragmentWorks() {
$this->dom->loadXML('<?xml version="1.0" ?><root><node1/><node2 /></root>');
$frag = $this->dom->nodeList2Fragment($this->dom->query('/root/*'));
$this->assertInstanceOf('TheSeer\fDOM\fDOMDocumentFragment', $frag);
$this->assertEquals(2, $frag->childNodes->length);
}
public function testPrepareQueryReturnsValidXPathString() {
$values = array('key' => 'the "value" of \'values\'');
$xpath = '//some[@value = :key]';
$result = $this->dom->prepareQuery($xpath, $values);
$this->assertEquals('//some[@value = concat("the ",\'"\',"value",\'"\'," of \'values\'")]', $result);
}
public function testRegisteringANamespaceWithPrefixWorks() {
$this->dom->registerNamespace('test', 'test:uri');
$this->assertAttributeEquals(array('test' => 'test:uri'), 'prefixes', $this->dom);
}
public function testCreatingElementWithInvalidNameThrowsException() {
$this->expectException(fDOMException::class);
$node = $this->dom->createElement('in valid');
}
public function testCreatingElementWithoutText() {
$node = $this->dom->createElement('name');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals('name', $node->nodeName);
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::createElementPrefix
*/
public function testCreatingNewElementByprefix() {
$this->dom->registerNamespace('test', 'test:uri');
$node = $this->dom->createElementPrefix('test', 'node');
$this->assertEquals('test:uri', $node->namespaceURI);
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::createElementPrefix
*/
public function testTryingToCreateNewElementByprefixWithUndefinedPrefixThrowsException() {
$this->expectException(fDOMException::class);
$this->dom->createElementPrefix('test', 'node');
}
public function testSettingContentUnescapedForNewElementRemainsIntact() {
$node = $this->dom->createElement('test', "test & demo");
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testSettingContentUnescapedForNewElementThrowsExceptionOnInvalidEntity() {
$this->expectException(fDOMException::class);
$node = $this->dom->createElement('test', "test & demo");
}
public function testSettingContentAsTextNodeForNewElementEncodesEntities() {
$node = $this->dom->createElement('test', "test & demo", TRUE);
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testSettingContentUnescapedForNewElementWithNamespaceRemainsIntact() {
$node = $this->dom->createElementNS('test:uri', 'test', "test & demo");
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testSettingContentUnescapedForNewElementWithNamespaceThrowsExceptionOnInvalidEntity() {
$this->expectException(fDOMException::class);
$node = $this->dom->createElementNS('test:uri', 'test', "test & demo");
}
public function testSettingContentAsTextNodeForNewElementWithNamespaceEncodesEntities() {
$node = $this->dom->createElementNS('test:uri', 'test', "test & demo", TRUE);
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals('test & demo', $node->nodeValue);
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::queryOne
*/
public function testThatTwoNodesAreIdentifiedAsBeingInTheSameDocument() {
$this->dom->loadXML('<?xml version="1.0" ?><root><node /></root>');
$node = $this->dom->queryOne('//node');
$this->assertTrue($this->dom->inSameDocument($node));
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::inSameDocument
*/
public function testThatANodeFromADifferentDocumentIsNotConsideredAsInSameDocument() {
$dom = new fDOMDocument();
$node = $dom->createElement('foo');
$this->dom->loadXML('<?xml version="1.0" ?><root />');
$this->assertFalse($this->dom->documentElement->inSameDocument($node));
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::inSameDocument
*/
public function testInSameDocumentWorksForDOMDocument() {
$dom = new fDOMDocument();
$this->assertFalse($this->dom->inSameDocument($dom));
}
public function testAppendElementCreatesANewNodeAndAttachesIt() {
$node = $this->dom->appendElement('test');
$this->assertSame($node, $this->dom->documentElement);
}
public function testAppendElementNSCreatesANewNodeAndAttachesIt() {
$node = $this->dom->appendElementNS('test:uri', 'test');
$this->assertSame($node, $this->dom->documentElement);
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::__clone
*/
public function testCloningTriggersCreationOfNewDOMXPathInstance() {
$this->dom->loadXML('<?xml version="1.0" ?><test />');
$xp1 = $this->dom->getDOMXPath();
$clone = clone $this->dom;
$xp2 = $clone->getDOMXPath();
$this->assertNotSame($xp2, $xp1);
}
/**
* @covers \TheSeer\fDOM\fDOMDocument::__clone
*/
public function testRegisteredNamespacePrefixesGetCopiedToClonedDocument() {
$this->dom->loadXML('<?xml version="1.0" ?><foo:test xmlns:foo="test:uri" />');
$this->dom->registerNamespace('foo', 'test:uri');
$clone = clone $this->dom;
$node = $clone->queryOne('//foo:test');
$this->assertSame($clone->documentElement, $node);
}
/**
* https://github.com/theseer/fDOMDocument/issues/15
*/
public function testQueryReturnsNodeFromClonedDocument() {
$this->dom->loadXML('<?xml version="1.0" ?><test />');
$clone = clone $this->dom;
$node = $clone->queryOne('/test');
$this->assertNotSame($this->dom->documentElement, $node);
}
public function testCSSSelectorReturnsCorrectNodes() {
$this->dom->load(__DIR__ . '/_data/selector.xml');
$result = $this->dom->select('child');
$this->assertEquals(2, $result->length);
$this->assertEquals('child', $result->item(0)->nodeName);
$this->assertEquals('child', $result->item(1)->nodeName);
}
public function testCSSSelectorHonorsContextNode() {
$this->dom->load(__DIR__ . '/_data/selector.xml');
$ctx = $this->dom->getElementsByTagName('child')->item(0);
$result = $this->dom->select('child', $ctx);
$this->assertEquals(1, $result->length);
$this->assertEquals('child', $result->item(0)->nodeName);
$this->assertEquals('other', $result->item(0)->getAttribute('attr'));
}
}
}
================================================
FILE: tests/fDOMDocumentFragment.test.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\Tests {
use TheSeer\fDOM\fDOMDocument;
use TheSeer\fDOM\fDOMDocumentFragment;
use TheSeer\fDOM\fDOMException;
/**
*
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
*/
class fDOMDocumentFragmentTest extends \PHPUnit\Framework\TestCase {
/**
* @var fDOMDocument
*/
private $dom;
/**
* @var fDOMDocumentFragment
*/
private $frag;
public function setUp(): void {
$this->dom = new fDOMDocument();
$this->frag = $this->dom->createDocumentFragment();
}
public function testAppendedXMLGetsAddedAndIsParsedAsXML() {
$this->frag->appendXML('<some />');
$this->assertEquals('some', $this->frag->firstChild->nodeName);
}
public function testTryingToAppendInvalidXMLToFragmentThrowsException() {
$this->expectException(fDOMException::class);
$this->frag->appendXML('<foo');
}
public function testCheckingInSameDocumentReturnsTrueOnNodeFromFragment() {
$this->frag->appendXML('<some />');
$this->assertTrue($this->frag->inSameDocument($this->frag->firstChild));
}
public function testAppendingANewElement() {
$node = $this->frag->appendElement('append', 'text');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->frag->query('count(append)'));
$this->assertEquals('text', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespace() {
$node = $this->frag->appendElementNS('test:uri', 'append', 'text');
$this->dom->registerNamespace('t', 'test:uri');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->frag->query('count(t:append)'));
$this->assertEquals('text', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespaceByPrefix() {
$this->dom->registerNamespace('t', 'test:uri');
$node = $this->frag->appendElementPrefix('t', 'append', 'text');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->frag->query('count(t:append)'));
$this->assertEquals('text', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespaceAsTextNodeByPrefix() {
$this->dom->registerNamespace('t', 'test:uri');
$node = $this->frag->appendElementPrefix('t', 'append', 'test & demo', true);
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->frag->query('count(t:append)'));
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testAppendingATextAsTextnode() {
$node = $this->frag->appendTextNode('test & demo');
$found = $this->frag->queryOne('text()');
$this->assertSame($node, $found);
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testCSSSelectorReturnsCorrectNodes() {
$node = $this->frag->appendElement('append', 'text');
$result = $this->frag->select('append');
$this->assertSame($node, $result->item(0));
$this->assertEquals(1, $result->length);
}
public function testToStringReturnsSerializedXMLString() {
$this->frag->appendElement('append', 'text');
$this->assertEquals('<append>text</append>', (string)$this->frag);
}
}
}
================================================
FILE: tests/fDOMElement.test.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\Tests {
use TheSeer\fDOM\fDOMDocument;
use TheSeer\fDOM\fDOMElement;
/**
*
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
*/
class fDOMElementTest extends \PHPUnit\Framework\TestCase {
/**
* @var fDOMDocument
*/
private $dom;
/**
* @var fDOMElement
*/
private $node;
public function setUp(): void {
$this->dom = new fDOMDocument();
$this->dom->loadXML('<?xml version="1.0" ?><root><node><child/></node><node /></root>');
$this->node = $this->dom->documentElement;
}
/**
* The query is a forwarder to the DOMDocument, so just checking if the forwarding works is enough
*/
public function testQueryReturnsNodelist() {
$list = $this->node->query('//node');
$this->assertInstanceOf('DOMNodelist', $list);
$this->assertEquals(2, $list->length);
}
/**
* The query is a forwarder to the DOMDocument, so just checking if the forwarding works is enough
*/
public function testQueryOneReturnsNode() {
$node = $this->node->queryOne('//root');
$this->assertSame($this->node, $node);
}
public function testAppendingAnXMLStringCreatesAFragment() {
$frag = $this->node->appendXML('<append />');
$this->assertInstanceOf('TheSeer\fDOM\fDOMDocumentFragment', $frag);
$this->assertEquals(1, $this->node->query('count(append)'));
}
public function testAppendingANewElement() {
$node = $this->node->appendElement('append', 'text');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->node->query('count(append)'));
$this->assertEquals('text', $node->nodeValue);
}
public function testAppendingANewElementAsTextNode() {
$node = $this->node->appendElement('append', 'test & demo', true);
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->node->query('count(append)'));
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespace() {
$node = $this->node->appendElementNS('test:uri', 'append', 'text');
$this->dom->registerNamespace('t','test:uri');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->node->query('count(t:append)'));
$this->assertEquals('text', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespaceAsTextNode() {
$node = $this->node->appendElementNS('test:uri', 'append', 'test & demo', true);
$this->dom->registerNamespace('t','test:uri');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->node->query('count(t:append)'));
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespaceByPrefix() {
$this->dom->registerNamespace('t','test:uri');
$node = $this->node->appendElementPrefix('t', 'append', 'text');
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->node->query('count(t:append)'));
$this->assertEquals('text', $node->nodeValue);
}
public function testAppendingANewElementWithinANamespaceAsTextNodeByPrefix() {
$this->dom->registerNamespace('t','test:uri');
$node = $this->node->appendElementPrefix('t', 'append', 'test & demo', true);
$this->assertInstanceOf('TheSeer\fDOM\fDOMElement', $node);
$this->assertEquals(1, $this->node->query('count(t:append)'));
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testAppendingATextAsTextnode() {
$node = $this->node->appendTextNode('test & demo');
$found = $this->node->queryOne('text()');
$this->assertSame($node, $found);
$this->assertEquals('test & demo', $node->nodeValue);
}
public function testGettingNonExistingAttributeReturnsDefaultValue() {
$res = $this->node->getAttribute('missing','default');
$this->assertEquals('default', $res);
}
public function testGettingNonExistingAttributeWithNamespaceReturnsDefaultValue() {
$res = $this->node->getAttributeNS('some:uri', 'missing','default');
$this->assertEquals('default', $res);
}
public function testSettingAttributeValueWithPlainValue() {
$attr = $this->node->setAttribute('test', 'value');
$this->assertTrue($this->node->hasAttribute('test'));
$this->assertInstanceOf('DOMAttr', $attr);
$this->assertEquals('value', $this->node->getAttribute('test'));
}
public function testSettingAttributeValueWithEntitiesEncodesProperly() {
$attr = $this->node->setAttribute('test', '&');
$this->assertTrue($this->node->hasAttribute('test'));
$this->assertInstanceOf('DOMAttr', $attr);
$this->assertEquals('&', $this->node->getAttribute('test'));
}
public function testSettingAttributeValueWithEntitiesAndKeepEntitiesEnabledDoesNotEncode() {
$attr = $this->node->setAttribute('test', '&', true);
$this->assertTrue($this->node->hasAttribute('test'));
$this->assertInstanceOf('DOMAttr', $attr);
$this->assertEquals('&', $this->node->getAttribute('test'));
}
public function testSettingNamespacedAttributeValueWithPlainValue() {
$this->node->setAttributeNS('some:uri','s:attr', 'value');
$this->assertTrue($this->node->hasAttributeNS('some:uri','attr'));
$this->assertEquals('value', $this->node->getAttributeNS('some:uri','attr'));
}
public function testSettingNamespacedAttributeValueWithEntitiesEncodesProperly() {
$this->node->setAttributeNS('test:uri', 't:test', '&');
$this->assertTrue($this->node->hasAttributeNS('test:uri', 'test'));
$this->assertEquals('&', $this->node->getAttributeNS('test:uri','test'));
}
public function testSettingNamespacedAttributeValueWithEntitiesAndKeepEntitiesEnabledDoesNotEncode() {
$attr = $this->node->setAttributeNS('test:uri', 't:test', '&', true);
$this->assertTrue($this->node->hasAttributeNS('test:uri', 'test'));
$this->assertInstanceOf('DOMAttr', $attr);
$this->assertEquals('&', $this->node->getAttributeNS('test:uri', 'test'));
}
public function testSettingMultipleAttributesFromArray() {
$attrs = array('a1' => 'v1', 'a2' => 'v2');
$this->node->setAttributes($attrs);
$this->assertEquals('v1', $this->node->getAttribute('a1'));
$this->assertEquals('v2', $this->node->getAttribute('a2'));
}
public function testSettingMultipleAttributesWithNamespaceFromArray() {
$attrs = array('a1' => 'v1', 'a2' => 'v2');
$this->node->setAttributesNS('some:uri','s', $attrs);
$this->assertEquals('v1', $this->node->getAttributeNS('some:uri', 'a1'));
$this->assertEquals('v2', $this->node->getAttributeNS('some:uri', 'a2'));
}
public function testGetChildrenByTagnameReturnsCorrectNodelist() {
$list = $this->node->getChildrenByTagName('node');
$this->assertInstanceOf('DOMNodeList', $list);
$this->assertEquals(2, $list->length);
}
public function testGetChildrenByTagnameNSReturnsCorrectNodelist() {
$this->dom->loadXML('<?xml version="1.0" ?><root xmlns="test:uri"><node><child/></node><node /></root>');
$this->node = $this->dom->documentElement;
$list = $this->node->getChildrenByTagNameNS('test:uri', 'node');
$this->assertInstanceOf('DOMNodeList', $list);
$this->assertEquals(2, $list->length);
}
public function testInSameDocumentForwardsToOwnerDocumentAndReturnsCorrectValue() {
$rc = $this->node->inSameDocument($this->node->firstChild);
$this->assertTrue($rc);
}
}
}
================================================
FILE: tests/fDOMXPath.test.php
================================================
<?php
/**
* Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Arne Blankerts nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* @category PHP
* @package TheSeer\fDOM
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://github.com/theseer/fdomdocument
*
*/
namespace TheSeer\fDOM\Tests {
use TheSeer\fDOM\fDOMDocument;
use TheSeer\fDOM\fDOMException;
use TheSeer\fDOM\fDOMXPath;
/**
*
* @author Arne Blankerts <arne@blankerts.de>
* @copyright Arne Blankerts <arne@blankerts.de>, All rights reserved.
*/
class fDOMXPathTest extends \PHPUnit\Framework\TestCase {
/**
* @var TheSeer\fDOM\fDOMDocument
*/
private $dom;
/**
* @var TheSeer\fDOM\fDOMXPath
*/
private $xp;
public function setUp(): void {
$this->dom = new fDOMDocument();
$this->dom->loadXML('<?xml version="1.0" ?><root><node attr="foo" /></root>');
$this->xp = $this->dom->getDOMXPath();
}
/**
* @covers TheSeer\fDOM\fDOMXPath::query
*/
public function testExecutingAQueryWithInvalidXPathThrowsException() {
$this->expectException(fDOMException::class);
$this->xp->query('//[invalid');
}
public function testQueryReturnsNodeList() {
$res = $this->xp->query('//*');
$this->assertInstanceOf('DOMNodeList', $res);
$this->assertEquals(2, $res->length);
}
/**
* @covers TheSeer\fDOM\fDOMXPath::evaluate
*/
public function testExecutingAQueryWithEvaluateWithInvalidXPathThrowsException() {
$this->expectException(fDOMException::class);
$this->xp->evaluate('//[invalid');
}
/**
* @covers TheSeer\fDOM\fDOMXPath::quote
*/
public function testPrepareReturnsStraightStringOnPlainText() {
$this->assertEquals('"test"', $this->xp->quote('test'));
}
public function testQueryOneReturnsANode() {
$this->assertSame($this->dom->documentElement, $this->xp->queryOne('//root'));
}
public function testPrepareReturnsUnmodifiedXPathOnEmptyArray() {
$xpath = 'test';
$this->assertEquals($xpath, $this->xp->prepare($xpath, array()));
}
public function testQueryOneReturnsValueOnNonNodeQuery() {
$this->assertEquals('1', $this->xp->queryOne('count(//root)'));
}
}
}
gitextract_1yhyg96l/
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.xml
├── composer.json
├── fDOMDocument.spec
├── phive.xml
├── phpcs.xml
├── phpunit.xml.dist
├── src/
│ ├── XPathQuery.php
│ ├── XPathQueryException.php
│ ├── autoload.php
│ ├── css/
│ │ ├── DollarEqualRule.php
│ │ ├── NotRule.php
│ │ ├── NthChildRule.php
│ │ ├── RegexRule.php
│ │ ├── RuleInterface.php
│ │ └── Translator.php
│ ├── fDOMDocument.php
│ ├── fDOMDocumentFragment.php
│ ├── fDOMElement.php
│ ├── fDOMException.php
│ ├── fDOMNode.php
│ └── fDOMXPath.php
└── tests/
├── Translator.test.php
├── XPathQuery.test.php
├── _data/
│ ├── broken.xml
│ ├── selector.xml
│ ├── undefentity.xml
│ └── valid.xml
├── fDOMDocument.test.php
├── fDOMDocumentFragment.test.php
├── fDOMElement.test.php
└── fDOMXPath.test.php
SYMBOL INDEX (221 symbols across 20 files)
FILE: src/XPathQuery.php
class XPathQuery (line 49) | class XPathQuery {
method __construct (line 66) | public function __construct($query) {
method setQuery (line 75) | private function setQuery($query) {
method getKeys (line 88) | public function getKeys() {
method bind (line 100) | public function bind($key, $value) {
method generate (line 115) | public function generate(\DOMNode $ctx, array $values = NULL) {
method evaluate (line 130) | public function evaluate(\DOMNode $ctx, array $values = NULL, $registe...
method query (line 146) | public function query(\DOMNode $ctx, array $values = NULL, $registerNo...
method queryOne (line 160) | public function queryOne(\DOMNode $ctx, array $values = NULL, $registe...
method getXPathObjectFor (line 174) | private function getXPathObjectFor(\DOMNode $ctx) {
method buildQuery (line 192) | private function buildQuery(fDOMXPath $xp, array $values = NULL) {
FILE: src/XPathQueryException.php
class XPathQueryException (line 44) | class XPathQueryException extends \Exception {
FILE: src/css/DollarEqualRule.php
class DollarEqualRule (line 4) | class DollarEqualRule implements RuleInterface {
method apply (line 11) | public function apply($selector) {
method callback (line 26) | private function callback(array $matches) {
FILE: src/css/NotRule.php
class NotRule (line 4) | class NotRule implements RuleInterface {
method __construct (line 14) | public function __construct(Translator $translator) {
method apply (line 23) | public function apply($selector) {
method callback (line 36) | private function callback(array $matches) {
FILE: src/css/NthChildRule.php
class NthChildRule (line 4) | class NthChildRule implements RuleInterface {
method apply (line 11) | public function apply($selector) {
method callback (line 24) | private function callback(array $matches) {
FILE: src/css/RegexRule.php
class RegexRule (line 4) | class RegexRule implements RuleInterface {
method __construct (line 20) | public function __construct($regex, $replacement) {
method apply (line 30) | public function apply($selector) {
FILE: src/css/RuleInterface.php
type RuleInterface (line 4) | interface RuleInterface {
method apply (line 11) | public function apply($selector);
FILE: src/css/Translator.php
class Translator (line 52) | class Translator {
method translate (line 64) | public function translate($selector) {
method getRules (line 75) | private function getRules() {
FILE: src/fDOMDocument.php
class fDOMDocument (line 58) | class fDOMDocument extends \DOMDocument {
method __construct (line 82) | public function __construct($version = '1.0', $encoding = 'utf-8', $st...
method __clone (line 98) | public function __clone() {
method __toString (line 109) | public function __toString() {
method setStreamContext (line 120) | public function setStreamContext(array $options) {
method load (line 140) | public function load($fname, $options = LIBXML_NONET) {
method loadXML (line 164) | public function loadXML($source, $options = LIBXML_NONET) {
method loadHTMLFile (line 188) | public function loadHTMLFile($fname, $options = NULL) {
method loadHTML (line 219) | public function loadHTML($source, $options = NULL) {
method save (line 249) | #[\ReturnTypeWillChange]
method saveHTML (line 267) | #[\ReturnTypeWillChange]
method saveHTMLFile (line 289) | #[\ReturnTypeWillChange]
method saveXML (line 308) | #[\ReturnTypeWillChange]
method getDOMXPath (line 336) | public function getDOMXPath() {
method nodeList2Fragment (line 354) | public function nodeList2Fragment(\DOMNodeList $list, $move=FALSE) {
method query (line 372) | public function query($q, \DOMNode $ctx = NULL, $registerNodeNS = TRUE) {
method queryOne (line 388) | public function queryOne($q, \DOMNode $ctx = NULL, $registerNodeNS = T...
method prepareQuery (line 404) | public function prepareQuery($xpath, array $valueMap) {
method select (line 420) | public function select($selector, \DOMNode $ctx = NULL, $registerNodeN...
method registerNamespace (line 439) | public function registerNamespace($prefix, $uri) {
method registerPHPFunctions (line 458) | public function registerPHPFunctions($restrict = NULL) {
method createElementPrefix (line 480) | public function createElementPrefix($prefix, $name, $content = NULL, $...
method createElement (line 498) | public function createElement($name, $content = NULL, $asTextnode = FA...
method createElementNS (line 533) | public function createElementNS($namespace, $name, $content = NULL, $a...
method createDocumentFragment (line 555) | #[\ReturnTypeWillChange]
method inSameDocument (line 568) | public function inSameDocument(\DOMNode $node) {
method appendElement (line 584) | public function appendElement($name, $content = NULL, $asTextNode = FA...
method appendElementNS (line 600) | public function appendElementNS($ns, $name, $content = NULL, $asTextNo...
method ensureIntance (line 614) | private function ensureIntance(\DOMNode $node) {
method registerNodeClasses (line 627) | private function registerNodeClasses() {
FILE: src/fDOMDocumentFragment.php
class fDOMDocumentFragment (line 54) | class fDOMDocumentFragment extends \DOMDocumentFragment {
method __toString (line 59) | public function __toString() {
method appendXML (line 72) | #[\ReturnTypeWillChange]
method appendElement (line 88) | public function appendElement($name, $content = null) {
method appendElementNS (line 103) | public function appendElementNS($ns, $name, $content = null) {
method appendElementPrefix (line 119) | public function appendElementPrefix($prefix, $name, $content = null, $...
method appendTextNode (line 132) | public function appendTextNode($content) {
method inSameDocument (line 146) | public function inSameDocument(\DOMNode $node) {
method query (line 160) | public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
method queryOne (line 173) | public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = t...
method select (line 186) | public function select($selector, \DOMNode $ctx = null, $registerNodeN...
FILE: src/fDOMElement.php
class fDOMElement (line 54) | class fDOMElement extends \DOMElement {
method __toString (line 59) | public function __toString() {
method query (line 72) | public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
method queryOne (line 85) | public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = t...
method select (line 99) | public function select($selector, \DOMNode $ctx = null, $registerNodeN...
method appendXML (line 110) | public function appendXML($str) {
method appendElement (line 126) | public function appendElement($name, $content = null, $asTextnode = FA...
method appendElementNS (line 142) | public function appendElementNS($ns, $name, $content = null, $asTextno...
method appendElementPrefix (line 158) | public function appendElementPrefix($prefix, $name, $content = null, $...
method appendTextNode (line 171) | public function appendTextNode($content) {
method createElement (line 188) | public function createElement($name, $content = NULL, $asTextnode = FA...
method createElementPrefix (line 204) | public function createElementPrefix($prefix, $name, $content = NULL, $...
method createElementNS (line 220) | public function createElementNS($namespace, $name, $content = NULL, $a...
method getAttribute (line 234) | #[\ReturnTypeWillChange]
method getAttributeNS (line 250) | #[\ReturnTypeWillChange]
method setAttribute (line 268) | #[\ReturnTypeWillChange]
method setAttributeNS (line 295) | #[\ReturnTypeWillChange]
method setAttributes (line 317) | public function setAttributes(array $attr, $keepEntities=false) {
method setAttributesNS (line 335) | public function setAttributesNS($ns, $prefix, array $attr, $keepEntiti...
method getChildrenByTagName (line 348) | public function getChildrenByTagName($tagName) {
method getChildrenByTagNameNS (line 360) | public function getChildrenByTagNameNS($ns, $tagName) {
method inSameDocument (line 372) | public function inSameDocument(\DomNode $node) {
method saveXML (line 381) | public function saveXML() {
method saveHTML (line 390) | public function saveHTML() {
FILE: src/fDOMException.php
class fDOMException (line 49) | class fDOMException extends \Exception {
method __construct (line 95) | public function __construct($message, $code = 0, \Exception $chain = N...
method getFullMessage (line 143) | public function getFullMessage() {
method getShortMessage (line 152) | public function getShortMessage() {
method getErrorList (line 161) | public function getErrorList() {
method toggleFullMessage (line 172) | public function toggleFullMessage($full = true) {
method __toString (line 181) | public function __toString() {
FILE: src/fDOMNode.php
class fDOMNode (line 54) | class fDOMNode extends \DOMNode {
method __toString (line 59) | public function __toString() {
method createElement (line 74) | public function createElement($name, $content = NULL, $asTextnode = FA...
method createElementPrefix (line 90) | public function createElementPrefix($prefix, $name, $content = NULL, $...
method createElementNS (line 106) | public function createElementNS($namespace, $name, $content = NULL, $a...
method query (line 119) | public function query($q, \DOMNode $ctx = null, $registerNodeNS = true) {
method queryOne (line 132) | public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = t...
method select (line 146) | public function select($selector, \DOMNode $ctx = null, $registerNodeN...
method inSameDocument (line 158) | public function inSameDocument(\DOMNode $node) {
method saveXML (line 167) | public function saveXML() {
method saveHTML (line 176) | public function saveHTML() {
FILE: src/fDOMXPath.php
class fDOMXPath (line 53) | class fDOMXPath extends \DOMXPath {
method __construct (line 63) | public function __construct(\DOMDocument $doc) {
method prepare (line 74) | public function prepare($xpath, array $valueMap) {
method query (line 93) | #[\ReturnTypeWillChange]
method evaluate (line 117) | #[\ReturnTypeWillChange]
method queryOne (line 140) | public function queryOne($q, \DOMNode $ctx = null, $registerNodeNS = t...
method quote (line 153) | public function quote($str) {
FILE: tests/Translator.test.php
class TranslatorTest (line 7) | class TranslatorTest extends \PHPUnit\Framework\TestCase {
method testTranslatingCssSelectorReturnsCorrectXPath (line 12) | public function testTranslatingCssSelectorReturnsCorrectXPath($selecto...
method provider (line 17) | public function provider() {
FILE: tests/XPathQuery.test.php
class XPathQueryTest (line 48) | class XPathQueryTest extends \PHPUnit\Framework\TestCase {
method setUp (line 52) | protected function setUp(): void {
method testFindingKeysInQueryWorks (line 58) | public function testFindingKeysInQueryWorks() {
method testTryingToBindNonExistingKeyThrowsException (line 63) | public function testTryingToBindNonExistingKeyThrowsException() {
method testBoundValueForKeyGetsApplied (line 69) | public function testBoundValueForKeyGetsApplied() {
method testAppliedValueForKeyIsUsedOnQueryAndReturnsNode (line 75) | public function testAppliedValueForKeyIsUsedOnQueryAndReturnsNode() {
method testOverwriteValueOnQuery (line 84) | public function testOverwriteValueOnQuery() {
method testAppliedValueForKeyIsUsedOnEvaluateAndReturnsNode (line 92) | public function testAppliedValueForKeyIsUsedOnEvaluateAndReturnsNode() {
method testOverwriteValueOnEvaluate (line 101) | public function testOverwriteValueOnEvaluate() {
method testCallToQueryOneReturnsOneNode (line 109) | public function testCallToQueryOneReturnsOneNode() {
method testQueryCanBeRunWithStandardDomDocument (line 115) | public function testQueryCanBeRunWithStandardDomDocument() {
FILE: tests/fDOMDocument.test.php
class fDOMDocumentTest (line 52) | class fDOMDocumentTest extends \PHPUnit\Framework\TestCase {
method setUp (line 59) | public function setUp(): void {
method testloadingXMLStringWorks (line 63) | public function testloadingXMLStringWorks() {
method testloadingXMLFileWorks (line 68) | public function testloadingXMLFileWorks() {
method testGetDomXPathReturnsXPathObject (line 73) | public function testGetDomXPathReturnsXPathObject() {
method testAttemptingToLoadAnXMLStringWithAnUndefinedEntityThrowsException (line 78) | public function testAttemptingToLoadAnXMLStringWithAnUndefinedEntityTh...
method testAttemptingToLoadAnEmptyXMLStringThrowsException (line 83) | public function testAttemptingToLoadAnEmptyXMLStringThrowsException() {
method testAttemptingToLoadWithEmptyFilenameThrowsException (line 88) | public function testAttemptingToLoadWithEmptyFilenameThrowsException() {
method testAttemptingToLoadHTMLWithAnEmptyFilenameThrowsException (line 93) | public function testAttemptingToLoadHTMLWithAnEmptyFilenameThrowsExcep...
method testAttemptingToLoadHMLWithAnEmptyStringThrowsException (line 98) | public function testAttemptingToLoadHMLWithAnEmptyStringThrowsExceptio...
method testloadingInvalidXMLStringThrowsException (line 103) | public function testloadingInvalidXMLStringThrowsException() {
method testTryingToLoadNonExistingFileThrowsException (line 108) | public function testTryingToLoadNonExistingFileThrowsException() {
method testloadingBrokenXMLFileThrowsException (line 113) | public function testloadingBrokenXMLFileThrowsException() {
method testAttemptingToLoadAnXMLFileWithAnUndefinedEntityThrowsException (line 118) | public function testAttemptingToLoadAnXMLFileWithAnUndefinedEntityThro...
method testQueryReturnsNodeList (line 126) | public function testQueryReturnsNodeList() {
method testQueryOneReturnsElement (line 136) | public function testQueryOneReturnsElement() {
method testSaveXMLReturnsCorrectXMLString (line 142) | public function testSaveXMLReturnsCorrectXMLString() {
method testSaveXMLThrowsExceptionWithReferenceToNodeFromOtherDocument (line 148) | public function testSaveXMLThrowsExceptionWithReferenceToNodeFromOther...
method testTransformNodeListToFragmentWorks (line 157) | public function testTransformNodeListToFragmentWorks() {
method testPrepareQueryReturnsValidXPathString (line 164) | public function testPrepareQueryReturnsValidXPathString() {
method testRegisteringANamespaceWithPrefixWorks (line 171) | public function testRegisteringANamespaceWithPrefixWorks() {
method testCreatingElementWithInvalidNameThrowsException (line 176) | public function testCreatingElementWithInvalidNameThrowsException() {
method testCreatingElementWithoutText (line 181) | public function testCreatingElementWithoutText() {
method testCreatingNewElementByprefix (line 190) | public function testCreatingNewElementByprefix() {
method testTryingToCreateNewElementByprefixWithUndefinedPrefixThrowsException (line 199) | public function testTryingToCreateNewElementByprefixWithUndefinedPrefi...
method testSettingContentUnescapedForNewElementRemainsIntact (line 204) | public function testSettingContentUnescapedForNewElementRemainsIntact() {
method testSettingContentUnescapedForNewElementThrowsExceptionOnInvalidEntity (line 210) | public function testSettingContentUnescapedForNewElementThrowsExceptio...
method testSettingContentAsTextNodeForNewElementEncodesEntities (line 215) | public function testSettingContentAsTextNodeForNewElementEncodesEntiti...
method testSettingContentUnescapedForNewElementWithNamespaceRemainsIntact (line 220) | public function testSettingContentUnescapedForNewElementWithNamespaceR...
method testSettingContentUnescapedForNewElementWithNamespaceThrowsExceptionOnInvalidEntity (line 226) | public function testSettingContentUnescapedForNewElementWithNamespaceT...
method testSettingContentAsTextNodeForNewElementWithNamespaceEncodesEntities (line 231) | public function testSettingContentAsTextNodeForNewElementWithNamespace...
method testThatTwoNodesAreIdentifiedAsBeingInTheSameDocument (line 240) | public function testThatTwoNodesAreIdentifiedAsBeingInTheSameDocument() {
method testThatANodeFromADifferentDocumentIsNotConsideredAsInSameDocument (line 249) | public function testThatANodeFromADifferentDocumentIsNotConsideredAsIn...
method testInSameDocumentWorksForDOMDocument (line 260) | public function testInSameDocumentWorksForDOMDocument() {
method testAppendElementCreatesANewNodeAndAttachesIt (line 265) | public function testAppendElementCreatesANewNodeAndAttachesIt() {
method testAppendElementNSCreatesANewNodeAndAttachesIt (line 270) | public function testAppendElementNSCreatesANewNodeAndAttachesIt() {
method testCloningTriggersCreationOfNewDOMXPathInstance (line 278) | public function testCloningTriggersCreationOfNewDOMXPathInstance() {
method testRegisteredNamespacePrefixesGetCopiedToClonedDocument (line 289) | public function testRegisteredNamespacePrefixesGetCopiedToClonedDocume...
method testQueryReturnsNodeFromClonedDocument (line 302) | public function testQueryReturnsNodeFromClonedDocument() {
method testCSSSelectorReturnsCorrectNodes (line 311) | public function testCSSSelectorReturnsCorrectNodes() {
method testCSSSelectorHonorsContextNode (line 319) | public function testCSSSelectorHonorsContextNode() {
FILE: tests/fDOMDocumentFragment.test.php
class fDOMDocumentFragmentTest (line 53) | class fDOMDocumentFragmentTest extends \PHPUnit\Framework\TestCase {
method setUp (line 65) | public function setUp(): void {
method testAppendedXMLGetsAddedAndIsParsedAsXML (line 70) | public function testAppendedXMLGetsAddedAndIsParsedAsXML() {
method testTryingToAppendInvalidXMLToFragmentThrowsException (line 75) | public function testTryingToAppendInvalidXMLToFragmentThrowsException() {
method testCheckingInSameDocumentReturnsTrueOnNodeFromFragment (line 80) | public function testCheckingInSameDocumentReturnsTrueOnNodeFromFragmen...
method testAppendingANewElement (line 85) | public function testAppendingANewElement() {
method testAppendingANewElementWithinANamespace (line 92) | public function testAppendingANewElementWithinANamespace() {
method testAppendingANewElementWithinANamespaceByPrefix (line 100) | public function testAppendingANewElementWithinANamespaceByPrefix() {
method testAppendingANewElementWithinANamespaceAsTextNodeByPrefix (line 108) | public function testAppendingANewElementWithinANamespaceAsTextNodeByPr...
method testAppendingATextAsTextnode (line 116) | public function testAppendingATextAsTextnode() {
method testCSSSelectorReturnsCorrectNodes (line 123) | public function testCSSSelectorReturnsCorrectNodes() {
method testToStringReturnsSerializedXMLString (line 130) | public function testToStringReturnsSerializedXMLString() {
FILE: tests/fDOMElement.test.php
class fDOMElementTest (line 52) | class fDOMElementTest extends \PHPUnit\Framework\TestCase {
method setUp (line 64) | public function setUp(): void {
method testQueryReturnsNodelist (line 73) | public function testQueryReturnsNodelist() {
method testQueryOneReturnsNode (line 82) | public function testQueryOneReturnsNode() {
method testAppendingAnXMLStringCreatesAFragment (line 87) | public function testAppendingAnXMLStringCreatesAFragment() {
method testAppendingANewElement (line 93) | public function testAppendingANewElement() {
method testAppendingANewElementAsTextNode (line 100) | public function testAppendingANewElementAsTextNode() {
method testAppendingANewElementWithinANamespace (line 107) | public function testAppendingANewElementWithinANamespace() {
method testAppendingANewElementWithinANamespaceAsTextNode (line 115) | public function testAppendingANewElementWithinANamespaceAsTextNode() {
method testAppendingANewElementWithinANamespaceByPrefix (line 123) | public function testAppendingANewElementWithinANamespaceByPrefix() {
method testAppendingANewElementWithinANamespaceAsTextNodeByPrefix (line 131) | public function testAppendingANewElementWithinANamespaceAsTextNodeByPr...
method testAppendingATextAsTextnode (line 139) | public function testAppendingATextAsTextnode() {
method testGettingNonExistingAttributeReturnsDefaultValue (line 146) | public function testGettingNonExistingAttributeReturnsDefaultValue() {
method testGettingNonExistingAttributeWithNamespaceReturnsDefaultValue (line 151) | public function testGettingNonExistingAttributeWithNamespaceReturnsDef...
method testSettingAttributeValueWithPlainValue (line 156) | public function testSettingAttributeValueWithPlainValue() {
method testSettingAttributeValueWithEntitiesEncodesProperly (line 163) | public function testSettingAttributeValueWithEntitiesEncodesProperly() {
method testSettingAttributeValueWithEntitiesAndKeepEntitiesEnabledDoesNotEncode (line 170) | public function testSettingAttributeValueWithEntitiesAndKeepEntitiesEn...
method testSettingNamespacedAttributeValueWithPlainValue (line 177) | public function testSettingNamespacedAttributeValueWithPlainValue() {
method testSettingNamespacedAttributeValueWithEntitiesEncodesProperly (line 183) | public function testSettingNamespacedAttributeValueWithEntitiesEncodes...
method testSettingNamespacedAttributeValueWithEntitiesAndKeepEntitiesEnabledDoesNotEncode (line 189) | public function testSettingNamespacedAttributeValueWithEntitiesAndKeep...
method testSettingMultipleAttributesFromArray (line 196) | public function testSettingMultipleAttributesFromArray() {
method testSettingMultipleAttributesWithNamespaceFromArray (line 203) | public function testSettingMultipleAttributesWithNamespaceFromArray() {
method testGetChildrenByTagnameReturnsCorrectNodelist (line 210) | public function testGetChildrenByTagnameReturnsCorrectNodelist() {
method testGetChildrenByTagnameNSReturnsCorrectNodelist (line 216) | public function testGetChildrenByTagnameNSReturnsCorrectNodelist() {
method testInSameDocumentForwardsToOwnerDocumentAndReturnsCorrectValue (line 224) | public function testInSameDocumentForwardsToOwnerDocumentAndReturnsCor...
FILE: tests/fDOMXPath.test.php
class fDOMXPathTest (line 53) | class fDOMXPathTest extends \PHPUnit\Framework\TestCase {
method setUp (line 65) | public function setUp(): void {
method testExecutingAQueryWithInvalidXPathThrowsException (line 74) | public function testExecutingAQueryWithInvalidXPathThrowsException() {
method testQueryReturnsNodeList (line 81) | public function testQueryReturnsNodeList() {
method testExecutingAQueryWithEvaluateWithInvalidXPathThrowsException (line 90) | public function testExecutingAQueryWithEvaluateWithInvalidXPathThrowsE...
method testPrepareReturnsStraightStringOnPlainText (line 98) | public function testPrepareReturnsStraightStringOnPlainText() {
method testQueryOneReturnsANode (line 102) | public function testQueryOneReturnsANode() {
method testPrepareReturnsUnmodifiedXPathOnEmptyArray (line 106) | public function testPrepareReturnsUnmodifiedXPathOnEmptyArray() {
method testQueryOneReturnsValueOnNonNodeQuery (line 111) | public function testQueryOneReturnsValueOnNonNodeQuery() {
Condensed preview — 35 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (151K chars).
[
{
"path": ".gitignore",
"chars": 102,
"preview": ".buildpath\n.project\n.settings\n/fDOMDocument-1.1.0.tgz\n.idea\nbuild\ncomposer.lock\ncomposer.phar\nvendor/\n"
},
{
"path": ".travis.yml",
"chars": 463,
"preview": "language: php\n\nsudo: false\n\nphp:\n - \"5.3\"\n - \"5.4\"\n - \"5.5\"\n - \"5.6\"\n - \"7.0\"\n - \"7.1\"\n - nightly\n#"
},
{
"path": "LICENSE",
"chars": 1523,
"preview": "fDOMDocument\n\nCopyright (c) 2010-2012 Arne Blankerts <arne@blankerts.de>\nAll rights reserved.\n\nRedistribution and use in"
},
{
"path": "README.md",
"chars": 5982,
"preview": "fDOMDocument\n============\n\n> :warning: **This project is Archived**\n> There will be no new features or general bugfixes."
},
{
"path": "build.xml",
"chars": 3108,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Generated by PHP Project Wizard (PPW) 1.0.4 on Fri Mar 11 16:37:31 CET 2011 "
},
{
"path": "composer.json",
"chars": 871,
"preview": "{\n \"name\": \"theseer/fdomdocument\",\n \"description\": \"The classes contained within this repository extend the standa"
},
{
"path": "fDOMDocument.spec",
"chars": 2522,
"preview": "%define _pearDir /usr/share/pear/\n%define _sourcedir src/\n\nSummary: fDOMDocument - An Extension to PHP's standard DOM to"
},
{
"path": "phive.xml",
"chars": 185,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phive xmlns=\"https://phar.io/phive\">\n <phar name=\"phpunit\" version=\"^8.5\" insta"
},
{
"path": "phpcs.xml",
"chars": 1422,
"preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"Arne\">\n <description>Arne Blankerts' coding standard</description>\n\n <rule ref=\"Gen"
},
{
"path": "phpunit.xml.dist",
"chars": 1035,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNam"
},
{
"path": "src/XPathQuery.php",
"chars": 6316,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/XPathQueryException.php",
"chars": 2040,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/autoload.php",
"chars": 1470,
"preview": "<?php\n// @codingStandardsIgnoreFile\n// @codeCoverageIgnoreStart\n// this is an autogenerated file - do not edit\nspl_autol"
},
{
"path": "src/css/DollarEqualRule.php",
"chars": 773,
"preview": "<?php\nnamespace TheSeer\\fDOM\\CSS {\n\n class DollarEqualRule implements RuleInterface {\n\n /**\n * @param "
},
{
"path": "src/css/NotRule.php",
"chars": 1079,
"preview": "<?php\nnamespace TheSeer\\fDOM\\CSS {\n\n class NotRule implements RuleInterface {\n\n /**\n * @var Translator"
},
{
"path": "src/css/NthChildRule.php",
"chars": 1481,
"preview": "<?php\nnamespace TheSeer\\fDOM\\CSS {\n\n class NthChildRule implements RuleInterface {\n\n /**\n * @param $se"
},
{
"path": "src/css/RegexRule.php",
"chars": 711,
"preview": "<?php\nnamespace TheSeer\\fDOM\\CSS {\n\n class RegexRule implements RuleInterface {\n\n /**\n * @var string\n "
},
{
"path": "src/css/RuleInterface.php",
"chars": 208,
"preview": "<?php\nnamespace TheSeer\\fDOM\\CSS {\n\n interface RuleInterface {\n\n /**\n * @param $selector\n *\n "
},
{
"path": "src/css/Translator.php",
"chars": 6017,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/fDOMDocument.php",
"chars": 23684,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/fDOMDocumentFragment.php",
"chars": 7018,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/fDOMElement.php",
"chars": 14675,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/fDOMException.php",
"chars": 5801,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/fDOMNode.php",
"chars": 6362,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "src/fDOMXPath.php",
"chars": 5667,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "tests/Translator.test.php",
"chars": 4012,
"preview": "<?php\n\nnamespace TheSeer\\fDOM\\Tests {\n\n use TheSeer\\fDOM\\CSS\\Translator;\n\n class TranslatorTest extends \\PHPUnit\\F"
},
{
"path": "tests/XPathQuery.test.php",
"chars": 4901,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "tests/_data/broken.xml",
"chars": 91,
"preview": "<?xml version=\"1.0\" ?>\n<broken>\n<!-- This is broken for testing reasons - do not fix ;) ->\n"
},
{
"path": "tests/_data/selector.xml",
"chars": 115,
"preview": "<?xml version=\"1.0\" ?>\n<root>\n <child attr=\"abc\" id=\"test\">\n <child attr=\"other\" />\n </child>\n</root>\n"
},
{
"path": "tests/_data/undefentity.xml",
"chars": 48,
"preview": "<?xml version=\"1.0\" ?>\n<root>&undefined;</root>\n"
},
{
"path": "tests/_data/valid.xml",
"chars": 30,
"preview": "<?xml version=\"1.0\"?>\n<test/>\n"
},
{
"path": "tests/fDOMDocument.test.php",
"chars": 13717,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "tests/fDOMDocumentFragment.test.php",
"chars": 5720,
"preview": "<?php\n/**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n * Redistribution and"
},
{
"path": "tests/fDOMElement.test.php",
"chars": 10767,
"preview": "<?php\n /**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n "
},
{
"path": "tests/fDOMXPath.test.php",
"chars": 4344,
"preview": "<?php\n /**\n * Copyright (c) 2010-2017 Arne Blankerts <arne@blankerts.de>\n * All rights reserved.\n *\n "
}
]
About this extraction
This page contains the full source code of the theseer/fDOMDocument GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 35 files (140.9 KB), approximately 34.9k tokens, and a symbol index with 221 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.