Skip to content

Repository files navigation

Buy us a treeRun testsScrutinizer Code QualityFOSSA StatusLicense

A simple PHP XML validator.

Installation

composer require hyperized/xml-validator
PackagePHPNotes
2.x^8.3Current. Requires ext-dom and ext-libxml
1.x>=7.2Unmaintained. See Migrating from 1.x

Usage

Validate a document and report why it failed:

useHyperized\Xml\Exceptions\InvalidXml;
useHyperized\Xml\Exceptions\XmlValidatorException;
useHyperized\Xml\Validator;
$validator = newValidator();
try {
$validator->validateXMLFile('/path/to/document.xml', '/path/to/schema.xsd');
} catch (InvalidXml$exception) {
// Malformed, or rejected by the schema. Line and column survive.foreach ($exception->getErrors() as$error) {
printf("line %d column %d: %s\n", $error->line, $error->column, trim($error->message));
}
} catch (XmlValidatorException$exception) {
// Missing, unreadable or empty file.printf("%s: %s\n", $exception::class, $exception->getMessage());
}

The schema is optional, and a document you already hold in memory goes through validateXMLString() instead. Both return void and throw on failure:

$validator->validateXMLFile('/path/to/document.xml');
$validator->validateXMLString($xml);
$validator->validateXMLString($xml, '/path/to/schema.xsd');

Exceptions

Every exception this package throws implements XmlValidatorException, so one catch block covers all of them and keeps working when a new one is added.

ExceptionThrown when
FileDoesNotExistNo file at the given XML or XSD path
FileCouldNotBeOpenedExceptionThe path exists but reading it failed
EmptyFileThe XML file holds nothing
InvalidXmlMalformed, empty once trimmed, or rejected by the schema

All four extend RuntimeException.

InvalidXml::getErrors() returns libxml's own LibXMLError objects, so line, column and level survive. getMessage() is a newline-joined summary of the same errors, which is usually what you want in a log line.

When you only need yes or no

The is*Valid() predicates run the same check, catch the exceptions above and return false instead. They never throw:

$validator->isXMLFileValid('/path/to/document.xml');
$validator->isXMLFileValid('/path/to/document.xml', '/path/to/schema.xsd');
$validator->isXMLStringValid($xml);
$validator->isXMLStringValid($xml, '/path/to/schema.xsd');

Because they are implemented on top of the throwing methods, the two forms can never disagree. Reach for these when a failure needs no explanation; reach for validate*() when it does.

Dependency injection

Validator implements ValidatorInterface, which declares all eight public methods. Type-hint against the interface where you inject or mock it:

useHyperized\Xml\ValidatorInterface;
publicfunction__construct(privatereadonlyValidatorInterface$validator) {}

No error state is kept between calls, so an instance is safe to reuse and to share, including as a container singleton. The only mutable state is the document version and encoding covered below.

Document version and encoding

DOMDocument is constructed with an XML version of 1.0 and an encoding of utf-8. Prefer setting them once, at construction:

$validator = newValidator(version: '1.1', encoding: 'iso-8859-1');
$validator->getVersion(); // '1.1'$validator->getEncoding(); // 'iso-8859-1'

setVersion() and setEncoding() exist too, but they mutate the instance. On a validator you have shared, that change is visible to every other caller, so reach for the constructor instead.

Untrusted input

libxml does not resolve external entities and caps internal entity expansion, so SYSTEM references are not fetched and expansion bombs are rejected rather than exhausting memory. That protection comes from libxml's defaults rather than from this package, and this package does not weaken them: LIBXML_NOENT is never passed.

Schema paths are a different matter. validateXMLFile() and validateXMLString() both read the XSD from the path you give them, so that path should come from your own configuration and never from a request.

Migrating from 1.x

2.0 replaces the error-reporting API. The old throwError() could not report failures from isXMLStringValid() at all, and raised a fatal Error: Typed property ... must not be accessed before initialization when nothing had failed yet.

1.x2.0
$validator->throwError()Catch from validateXMLFile() / validateXMLString()
catch (EmptyFile | FileDoesNotExist | ...)catch (XmlValidatorException)
Exceptions extend ExceptionExceptions extend RuntimeException
Error message string onlyInvalidXml::getErrors() with line and column

Also in 2.0:

  • PHP 8.3 is the minimum, replacing ^8.1|^8.2|^8.3.
  • Nullable parameters are declared ?string, so PHP 8.4 and newer no longer emit implicit-nullable deprecations.
  • libxml_use_internal_errors() is restored to its previous value instead of being left switched on for the rest of the process.

Development

make # install dependencies and run every quality gate
make help# list the available targets

Treeware

You're free to use this package, but if it makes it to your production environment you are required to buy the world a tree.

It's now common knowledge that one of the best tools to tackle the climate crisis and keep our temperatures from rising above 1.5C is to plant trees. If you support this package and contribute to the Treeware forest you'll be creating employment for local families and restoring wildlife habitats.

You can buy trees here offset.earth/treeware

Read more about Treeware at treeware.earth

License

FOSSA Status

About

A simple PHP XML validator

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages