add plugins
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
||||
|
||||
## 1.2.0 - 2019-01-22
|
||||
|
||||
### Added
|
||||
|
||||
- [#6](https://github.com/zendframework/zendxml/pull/6) adds the following method:
|
||||
|
||||
```php
|
||||
Security::scanHtml(
|
||||
string $html,
|
||||
DOMDocument $dom = null,
|
||||
int $libXmlConstants = 0
|
||||
) : SimpleXMLElement|DOMDocument|bool
|
||||
```
|
||||
|
||||
This method allows scanning markup known to be HTML, versus assuming the
|
||||
markup is generic XML.
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.1.1 - 2019-01-22
|
||||
|
||||
### Added
|
||||
|
||||
- [#16](https://github.com/zendframework/ZendXml/pull/16) adds support for PHP 7.3.
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#17](https://github.com/zendframework/ZendXml/pull/17) properly enables heuristic security checks for PHP 5.6.0 - 5.6.5 when PHP
|
||||
is running as PHP-FPM.
|
||||
|
||||
## 1.1.0 - 2018-04-30
|
||||
|
||||
### Added
|
||||
|
||||
- [#13](https://github.com/zendframework/ZendXml/pull/13) adds support for PHP 7.1 and 7.2.
|
||||
|
||||
### Changed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- [#13](https://github.com/zendframework/ZendXml/pull/13) removes support for PHP 5.3, 5.4, and 5.5.
|
||||
|
||||
- [#13](https://github.com/zendframework/ZendXml/pull/13) removes support for HHVM.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Nothing.
|
||||
|
||||
## 1.0.2 - 2016-02-04
|
||||
|
||||
### Added
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Removed
|
||||
|
||||
- Nothing.
|
||||
|
||||
### Fixed
|
||||
|
||||
- [#11](https://github.com/zendframework/ZendXml/pull/11) updates the
|
||||
dependencies to PHP `^5.3.3 || ^7.0` and PHPUnit `^3.7 || ^4.0`, ensuring
|
||||
better compatibility with other components, and with PHP 7. The test matrix
|
||||
was also expanded to add PHP 7 as a required platform.
|
||||
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014-2018, Zend Technologies USA, Inc.
|
||||
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 Zend Technologies USA, Inc. nor the names of its
|
||||
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 OWNER OR CONTRIBUTORS 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.
|
||||
@@ -0,0 +1,48 @@
|
||||
# ZendXml
|
||||
|
||||
[](https://secure.travis-ci.org/zendframework/ZendXml)
|
||||
[](https://coveralls.io/github/zendframework/ZendXml?branch=master)
|
||||
|
||||
An utility component for XML usage and best practices in PHP
|
||||
|
||||
## Installation
|
||||
|
||||
You can install using:
|
||||
|
||||
```
|
||||
curl -s https://getcomposer.org/installer | php
|
||||
php composer.phar install
|
||||
```
|
||||
|
||||
Notice that this library doesn't have any external dependencies, the usage of composer is for autoloading and standard purpose.
|
||||
|
||||
|
||||
## ZendXml\Security
|
||||
|
||||
This is a security component to prevent [XML eXternal Entity](https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing) (XXE) and [XML Entity Expansion](http://projects.webappsec.org/w/page/13247002/XML%20Entity%20Expansion) (XEE) attacks on XML documents.
|
||||
|
||||
The XXE attack is prevented disabling the load of external entities in the libxml library used by PHP, using the function [libxml_disable_entity_loader](http://www.php.net/manual/en/function.libxml-disable-entity-loader.php).
|
||||
|
||||
The XEE attack is prevented looking inside the XML document for ENTITY usage. If the XML document uses ENTITY the library throw an Exception.
|
||||
|
||||
We have two static methods to scan and load XML document from a string (scan) and from a file (scanFile). You can decide to get a SimpleXMLElement or DOMDocument as result, using the following use cases:
|
||||
|
||||
```php
|
||||
use ZendXml\Security as XmlSecurity;
|
||||
|
||||
$xml = <<<XML
|
||||
<?xml version="1.0"?>
|
||||
<results>
|
||||
<result>test</result>
|
||||
</results>
|
||||
XML;
|
||||
|
||||
// SimpleXML use case
|
||||
$simplexml = XmlSecurity::scan($xml);
|
||||
printf ("SimpleXMLElement: %s\n", ($simplexml instanceof \SimpleXMLElement) ? 'yes' : 'no');
|
||||
|
||||
// DOMDocument use case
|
||||
$dom = new \DOMDocument('1.0');
|
||||
$dom = XmlSecurity::scan($xml, $dom);
|
||||
printf ("DOMDocument: %s\n", ($dom instanceof \DOMDocument) ? 'yes' : 'no');
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "zendframework/zendxml",
|
||||
"description": "Utility library for XML usage, best practices, and security in PHP",
|
||||
"license": "BSD-3-Clause",
|
||||
"keywords": [
|
||||
"zf",
|
||||
"zendframework",
|
||||
"xml",
|
||||
"security"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/zendframework/ZendXml/issues",
|
||||
"source": "https://github.com/zendframework/ZendXml",
|
||||
"rss": "https://github.com/zendframework/ZendXml/releases.atom",
|
||||
"chat": "https://zendframework-slack.herokuapp.com",
|
||||
"forum": "https://discourse.zendframework.com/c/questions/components"
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"zendframework/zend-coding-standard": "~1.0.0",
|
||||
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ZendXml\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ZendXmlTest\\": "test/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev",
|
||||
"dev-develop": "1.3.x-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"check": [
|
||||
"@cs-check",
|
||||
"@test"
|
||||
],
|
||||
"cs-check": "phpcs",
|
||||
"cs-fix": "phpcbf",
|
||||
"test": "phpunit --colors=always",
|
||||
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* @see https://github.com/zendframework/ZendXml for the canonical source repository
|
||||
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
|
||||
* @license https://github.com/zendframework/ZendXml/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace ZendXml\Exception;
|
||||
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* @see https://github.com/zendframework/ZendXml for the canonical source repository
|
||||
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
|
||||
* @license https://github.com/zendframework/ZendXml/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace ZendXml\Exception;
|
||||
|
||||
/**
|
||||
* Invalid argument exception
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* @see https://github.com/zendframework/ZendXml for the canonical source repository
|
||||
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
|
||||
* @license https://github.com/zendframework/ZendXml/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace ZendXml\Exception;
|
||||
|
||||
/**
|
||||
* Runtime exception
|
||||
*/
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
/**
|
||||
* @see https://github.com/zendframework/ZendXml for the canonical source repository
|
||||
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
|
||||
* @license https://github.com/zendframework/ZendXml/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace ZendXml;
|
||||
|
||||
use DOMDocument;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class Security
|
||||
{
|
||||
const ENTITY_DETECT = 'Detected use of ENTITY in XML, disabled to prevent XXE/XEE attacks';
|
||||
|
||||
/**
|
||||
* Heuristic scan to detect entity in XML
|
||||
*
|
||||
* @param string $xml
|
||||
* @throws Exception\RuntimeException If entity expansion or external entity declaration was discovered.
|
||||
*/
|
||||
protected static function heuristicScan($xml)
|
||||
{
|
||||
foreach (self::getEntityComparison($xml) as $compare) {
|
||||
if (strpos($xml, $compare) !== false) {
|
||||
throw new Exception\RuntimeException(self::ENTITY_DETECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan XML string for potential XXE and XEE attacks
|
||||
*
|
||||
* @param string $xml
|
||||
* @param DomDocument $dom
|
||||
* @param int $libXmlConstants additional libxml constants to pass in
|
||||
* @param callable $callback the callback to use to create the dom element
|
||||
* @throws Exception\RuntimeException
|
||||
* @return SimpleXMLElement|DomDocument|boolean
|
||||
*/
|
||||
private static function scanString($xml, DOMDocument $dom = null, $libXmlConstants, callable $callback)
|
||||
{
|
||||
// If running with PHP-FPM we perform an heuristic scan
|
||||
// We cannot use libxml_disable_entity_loader because of this bug
|
||||
// @see https://bugs.php.net/bug.php?id=64938
|
||||
if (self::isPhpFpm()) {
|
||||
self::heuristicScan($xml);
|
||||
}
|
||||
|
||||
if (null === $dom) {
|
||||
$simpleXml = true;
|
||||
$dom = new DOMDocument();
|
||||
}
|
||||
|
||||
if (! self::isPhpFpm()) {
|
||||
$loadEntities = libxml_disable_entity_loader(true);
|
||||
$useInternalXmlErrors = libxml_use_internal_errors(true);
|
||||
}
|
||||
|
||||
// Load XML with network access disabled (LIBXML_NONET)
|
||||
// error disabled with @ for PHP-FPM scenario
|
||||
set_error_handler(function ($errno, $errstr) {
|
||||
if (substr_count($errstr, 'DOMDocument::loadXML()') > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, E_WARNING);
|
||||
|
||||
$result = $callback($xml, $dom, LIBXML_NONET | $libXmlConstants);
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
if (! $result) {
|
||||
// Entity load to previous setting
|
||||
if (! self::isPhpFpm()) {
|
||||
libxml_disable_entity_loader($loadEntities);
|
||||
libxml_use_internal_errors($useInternalXmlErrors);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scan for potential XEE attacks using ENTITY, if not PHP-FPM
|
||||
if (! self::isPhpFpm()) {
|
||||
foreach ($dom->childNodes as $child) {
|
||||
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
|
||||
if ($child->entities->length > 0) {
|
||||
throw new Exception\RuntimeException(self::ENTITY_DETECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entity load to previous setting
|
||||
if (! self::isPhpFpm()) {
|
||||
libxml_disable_entity_loader($loadEntities);
|
||||
libxml_use_internal_errors($useInternalXmlErrors);
|
||||
}
|
||||
|
||||
if (isset($simpleXml)) {
|
||||
$result = simplexml_import_dom($dom);
|
||||
if (! $result instanceof SimpleXMLElement) {
|
||||
return false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return $dom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan XML string for potential XXE and XEE attacks
|
||||
*
|
||||
* @param string $xml
|
||||
* @param DomDocument $dom
|
||||
* @param int $libXmlConstants additional libxml constants to pass in
|
||||
* @throws Exception\RuntimeException
|
||||
* @return SimpleXMLElement|DomDocument|boolean
|
||||
*/
|
||||
public static function scan($xml, DOMDocument $dom = null, $libXmlConstants = 0)
|
||||
{
|
||||
$callback = function ($xml, $dom, $constants) {
|
||||
return $dom->loadXml($xml, $constants);
|
||||
};
|
||||
return self::scanString($xml, $dom, $libXmlConstants, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan HTML string for potential XXE and XEE attacks
|
||||
*
|
||||
* @param string $xml
|
||||
* @param DomDocument $dom
|
||||
* @param int $libXmlConstants additional libxml constants to pass in
|
||||
* @throws Exception\RuntimeException
|
||||
* @return SimpleXMLElement|DomDocument|boolean
|
||||
*/
|
||||
public static function scanHtml($html, DOMDocument $dom = null, $libXmlConstants = 0)
|
||||
{
|
||||
$callback = function ($html, $dom, $constants) {
|
||||
return $dom->loadHtml($html, $constants);
|
||||
};
|
||||
return self::scanString($html, $dom, $libXmlConstants, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan XML file for potential XXE/XEE attacks
|
||||
*
|
||||
* @param string $file
|
||||
* @param DOMDocument $dom
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return SimpleXMLElement|DomDocument
|
||||
*/
|
||||
public static function scanFile($file, DOMDocument $dom = null)
|
||||
{
|
||||
if (! file_exists($file)) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
"The file $file specified doesn't exist"
|
||||
);
|
||||
}
|
||||
return self::scan(file_get_contents($file), $dom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if PHP is running with PHP-FPM
|
||||
*
|
||||
* This method is mainly used to determine whether or not heuristic checks
|
||||
* (vs libxml checks) should be made, due to threading issues in libxml;
|
||||
* under php-fpm, threading becomes a concern.
|
||||
*
|
||||
* However, PHP versions 5.6.6+ contain a patch to the
|
||||
* libxml support in PHP that makes the libxml checks viable; in such
|
||||
* versions, this method will return false to enforce those checks, which
|
||||
* are more strict and accurate than the heuristic checks.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isPhpFpm()
|
||||
{
|
||||
$isVulnerableVersion = version_compare(PHP_VERSION, '5.6', 'ge')
|
||||
&& version_compare(PHP_VERSION, '5.6.6', 'lt');
|
||||
|
||||
if (0 === strpos(php_sapi_name(), 'fpm') && $isVulnerableVersion) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine and return the string(s) to use for the <!ENTITY comparison.
|
||||
*
|
||||
* @param string $xml
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function getEntityComparison($xml)
|
||||
{
|
||||
$encodingMap = self::getAsciiEncodingMap();
|
||||
return array_map(function ($encoding) use ($encodingMap) {
|
||||
$generator = isset($encodingMap[$encoding]) ? $encodingMap[$encoding] : $encodingMap['UTF-8'];
|
||||
return $generator('<!ENTITY');
|
||||
}, self::detectXmlEncoding($xml, self::detectStringEncoding($xml)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the string encoding.
|
||||
*
|
||||
* Determines string encoding from either a detected BOM or a
|
||||
* heuristic.
|
||||
*
|
||||
* @param string $xml
|
||||
* @return string File encoding
|
||||
*/
|
||||
protected static function detectStringEncoding($xml)
|
||||
{
|
||||
return self::detectBom($xml) ?: self::detectXmlStringEncoding($xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to match a known BOM.
|
||||
*
|
||||
* Iterates through the return of getBomMap(), comparing the initial bytes
|
||||
* of the provided string to the BOM of each; if a match is determined,
|
||||
* it returns the encoding.
|
||||
*
|
||||
* @param string $string
|
||||
* @return false|string Returns encoding on success.
|
||||
*/
|
||||
protected static function detectBom($string)
|
||||
{
|
||||
foreach (self::getBomMap() as $criteria) {
|
||||
if (0 === strncmp($string, $criteria['bom'], $criteria['length'])) {
|
||||
return $criteria['encoding'];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to detect the string encoding of an XML string.
|
||||
*
|
||||
* @param string $xml
|
||||
* @return string Encoding
|
||||
*/
|
||||
protected static function detectXmlStringEncoding($xml)
|
||||
{
|
||||
foreach (self::getAsciiEncodingMap() as $encoding => $generator) {
|
||||
$prefix = $generator('<' . '?xml');
|
||||
if (0 === strncmp($xml, $prefix, strlen($prefix))) {
|
||||
return $encoding;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return 'UTF-8';
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to detect the specified XML encoding.
|
||||
*
|
||||
* Using the file's encoding, determines if an "encoding" attribute is
|
||||
* present and well-formed in the XML declaration; if so, it returns a
|
||||
* list with both the ASCII representation of that declaration and the
|
||||
* original file encoding.
|
||||
*
|
||||
* If not, a list containing only the provided file encoding is returned.
|
||||
*
|
||||
* @param string $xml
|
||||
* @param string $fileEncoding
|
||||
* @return string[] Potential XML encodings
|
||||
*/
|
||||
protected static function detectXmlEncoding($xml, $fileEncoding)
|
||||
{
|
||||
$encodingMap = self::getAsciiEncodingMap();
|
||||
$generator = $encodingMap[$fileEncoding];
|
||||
$encAttr = $generator('encoding="');
|
||||
$quote = $generator('"');
|
||||
$close = $generator('>');
|
||||
|
||||
$closePos = strpos($xml, $close);
|
||||
if (false === $closePos) {
|
||||
return [$fileEncoding];
|
||||
}
|
||||
|
||||
$encPos = strpos($xml, $encAttr);
|
||||
if (false === $encPos
|
||||
|| $encPos > $closePos
|
||||
) {
|
||||
return [$fileEncoding];
|
||||
}
|
||||
|
||||
$encPos += strlen($encAttr);
|
||||
$quotePos = strpos($xml, $quote, $encPos);
|
||||
if (false === $quotePos) {
|
||||
return [$fileEncoding];
|
||||
}
|
||||
|
||||
$encoding = self::substr($xml, $encPos, $quotePos);
|
||||
return [
|
||||
// Following line works because we're only supporting 8-bit safe encodings at this time.
|
||||
str_replace('\0', '', $encoding), // detected encoding
|
||||
$fileEncoding, // file encoding
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of BOM maps.
|
||||
*
|
||||
* Returns a list of common encoding -> BOM maps, along with the character
|
||||
* length to compare against.
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/Byte_order_mark
|
||||
* @return array
|
||||
*/
|
||||
protected static function getBomMap()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'encoding' => 'UTF-32BE',
|
||||
'bom' => pack('CCCC', 0x00, 0x00, 0xfe, 0xff),
|
||||
'length' => 4,
|
||||
],
|
||||
[
|
||||
'encoding' => 'UTF-32LE',
|
||||
'bom' => pack('CCCC', 0xff, 0xfe, 0x00, 0x00),
|
||||
'length' => 4,
|
||||
],
|
||||
[
|
||||
'encoding' => 'GB-18030',
|
||||
'bom' => pack('CCCC', 0x84, 0x31, 0x95, 0x33),
|
||||
'length' => 4,
|
||||
],
|
||||
[
|
||||
'encoding' => 'UTF-16BE',
|
||||
'bom' => pack('CC', 0xfe, 0xff),
|
||||
'length' => 2,
|
||||
],
|
||||
[
|
||||
'encoding' => 'UTF-16LE',
|
||||
'bom' => pack('CC', 0xff, 0xfe),
|
||||
'length' => 2,
|
||||
],
|
||||
[
|
||||
'encoding' => 'UTF-8',
|
||||
'bom' => pack('CCC', 0xef, 0xbb, 0xbf),
|
||||
'length' => 3,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map of encoding => generator pairs.
|
||||
*
|
||||
* Returns a map of encoding => generator pairs, where the generator is a
|
||||
* callable that accepts a string and returns the appropriate byte order
|
||||
* sequence of that string for the encoding.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function getAsciiEncodingMap()
|
||||
{
|
||||
return [
|
||||
'UTF-32BE' => function ($ascii) {
|
||||
return preg_replace('/(.)/', "\0\0\0\\1", $ascii);
|
||||
},
|
||||
'UTF-32LE' => function ($ascii) {
|
||||
return preg_replace('/(.)/', "\\1\0\0\0", $ascii);
|
||||
},
|
||||
'UTF-32odd1' => function ($ascii) {
|
||||
return preg_replace('/(.)/', "\0\\1\0\0", $ascii);
|
||||
},
|
||||
'UTF-32odd2' => function ($ascii) {
|
||||
return preg_replace('/(.)/', "\0\0\\1\0", $ascii);
|
||||
},
|
||||
'UTF-16BE' => function ($ascii) {
|
||||
return preg_replace('/(.)/', "\0\\1", $ascii);
|
||||
},
|
||||
'UTF-16LE' => function ($ascii) {
|
||||
return preg_replace('/(.)/', "\\1\0", $ascii);
|
||||
},
|
||||
'UTF-8' => function ($ascii) {
|
||||
return $ascii;
|
||||
},
|
||||
'GB-18030' => function ($ascii) {
|
||||
return $ascii;
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary-safe substr.
|
||||
*
|
||||
* substr() is not binary-safe; this method loops by character to ensure
|
||||
* multi-byte characters are aggregated correctly.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
* @return string
|
||||
*/
|
||||
protected static function substr($string, $start, $end)
|
||||
{
|
||||
$substr = '';
|
||||
for ($i = $start; $i < $end; $i += 1) {
|
||||
$substr .= $string[$i];
|
||||
}
|
||||
return $substr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user