summaryrefslogtreecommitdiff
path: root/vendor/wikimedia
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/wikimedia')
-rw-r--r--vendor/wikimedia/assert/COPYING21
-rw-r--r--vendor/wikimedia/assert/README.md43
-rw-r--r--vendor/wikimedia/assert/composer.json24
-rw-r--r--vendor/wikimedia/assert/phpunit.xml.dist27
-rw-r--r--vendor/wikimedia/assert/src/Assert.php204
-rw-r--r--vendor/wikimedia/assert/src/AssertionException.php16
-rw-r--r--vendor/wikimedia/assert/src/InvariantException.php18
-rw-r--r--vendor/wikimedia/assert/src/ParameterAssertionException.php49
-rw-r--r--vendor/wikimedia/assert/src/ParameterElementTypeException.php43
-rw-r--r--vendor/wikimedia/assert/src/ParameterTypeException.php43
-rw-r--r--vendor/wikimedia/assert/src/PostconditionException.php18
-rw-r--r--vendor/wikimedia/assert/src/PreconditionException.php17
-rw-r--r--vendor/wikimedia/assert/tests/phpunit/AssertTest.php156
-rw-r--r--vendor/wikimedia/avro/LICENSE.txt308
-rw-r--r--vendor/wikimedia/avro/NOTICE.txt9
-rw-r--r--vendor/wikimedia/avro/README.md23
-rw-r--r--vendor/wikimedia/avro/lib/avro.php195
-rw-r--r--vendor/wikimedia/avro/lib/avro/data_file.php535
-rw-r--r--vendor/wikimedia/avro/lib/avro/datum.php984
-rw-r--r--vendor/wikimedia/avro/lib/avro/debug.php194
-rw-r--r--vendor/wikimedia/avro/lib/avro/gmp.php222
-rw-r--r--vendor/wikimedia/avro/lib/avro/io.php494
-rw-r--r--vendor/wikimedia/avro/lib/avro/protocol.php86
-rw-r--r--vendor/wikimedia/avro/lib/avro/schema.php1457
-rw-r--r--vendor/wikimedia/avro/lib/avro/util.php67
-rw-r--r--vendor/wikimedia/cdb/COPYING183
-rw-r--r--vendor/wikimedia/cdb/README.md31
-rw-r--r--vendor/wikimedia/cdb/composer.json26
-rw-r--r--vendor/wikimedia/cdb/doc/README1
-rw-r--r--vendor/wikimedia/cdb/src/Reader.php17
-rw-r--r--vendor/wikimedia/cdb/src/Reader/DBA.php13
-rw-r--r--vendor/wikimedia/cdb/src/Reader/PHP.php286
-rw-r--r--vendor/wikimedia/cdb/src/Writer.php2
-rw-r--r--vendor/wikimedia/cdb/src/Writer/DBA.php1
-rw-r--r--vendor/wikimedia/cdb/src/Writer/PHP.php1
-rw-r--r--vendor/wikimedia/cdb/test/CdbTest.php95
-rw-r--r--vendor/wikimedia/composer-merge-plugin/.arcconfig6
-rw-r--r--vendor/wikimedia/composer-merge-plugin/.arclint13
-rw-r--r--vendor/wikimedia/composer-merge-plugin/LICENSE2
-rw-r--r--vendor/wikimedia/composer-merge-plugin/README.md133
-rw-r--r--vendor/wikimedia/composer-merge-plugin/composer.json18
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Logger.php102
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php487
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php18
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php327
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php181
-rw-r--r--vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php427
-rw-r--r--vendor/wikimedia/ip-set/COPYING339
-rw-r--r--vendor/wikimedia/ip-set/README.md83
-rw-r--r--vendor/wikimedia/ip-set/src/IPSet.php284
-rw-r--r--vendor/wikimedia/utfnormal/COPYING342
-rw-r--r--vendor/wikimedia/utfnormal/Doxyfile (renamed from vendor/wikimedia/cdb/Doxyfile)4
-rw-r--r--vendor/wikimedia/wrappedstring/LICENSE19
-rw-r--r--vendor/wikimedia/wrappedstring/README.md24
-rw-r--r--vendor/wikimedia/wrappedstring/src/WrappedString.php110
55 files changed, 8211 insertions, 617 deletions
diff --git a/vendor/wikimedia/assert/COPYING b/vendor/wikimedia/assert/COPYING
new file mode 100644
index 00000000..56f0386f
--- /dev/null
+++ b/vendor/wikimedia/assert/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Wikimedia Deutschland e.V.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/wikimedia/assert/README.md b/vendor/wikimedia/assert/README.md
new file mode 100644
index 00000000..6fa9b4d2
--- /dev/null
+++ b/vendor/wikimedia/assert/README.md
@@ -0,0 +1,43 @@
+This package provides an alternative to PHP's `assert()` that allows for an simple and reliable way
+to check preconditions and postconditions in PHP code. It was proposed [as a MediaWiki RFC](https://www.mediawiki.org/wiki/Requests_for_comment/Assert),
+but is completely generic and can be used by any PHP program or library. It is published under the
+MIT license, see the COPYING file.
+
+Usage
+-------
+
+The Assert class provides several static methods for checking various kinds of assertions.
+The most common kind is to check the type of a parameter, typically in a constructor or a
+setter method:
+
+ function setFoo( $foo ) {
+ Assert::parameterType( 'integer', $foo, 'foo' );
+ Assert::parameter( $foo > 0, 'foo', 'must be greater than 0' );
+ }
+
+ function __construct( $bar, array $bazz ) {
+ Assert::parameterType( 'Me\MyApp\SomeClass', $bar );
+ Assert::parameterElementType( 'int', $bazz );
+ }
+
+Checking parameters, or other assertions such as pre- or postconditions, is not recommended for
+performance critical regions of the code, since evaluating expressions and calling the assertion
+functions costs time.
+
+
+Rationale
+-----------
+The background of this proposal is the recurring discussions about whether PHP's `assert()`
+can and should be used in MediaWiki code. Two relevant threads:
+* [Using PHP's assert in MediaWiki code](http://www.gossamer-threads.com/lists/wiki/wikitech/275737)
+* [Is assert() allowed?](http://www.gossamer-threads.com/lists/wiki/wikitech/378676)
+
+The outcome appears to be that
+* assertions are generally a good way to improve code quality
+* but PHP's ''assert()'' is broken by design
+
+Following a [suggestion by Tim Starling](http://www.gossamer-threads.com/lists/wiki/wikitech/378815#378815),
+this package provides an alternative to PHP's built in `assert()`.
+
+[![Build Status](https://secure.travis-ci.org/wmde/Assert.svg)](https://travis-ci.org/wmde/Assert)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/wmde/Assert/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/wmde/Assert/?branch=master)
diff --git a/vendor/wikimedia/assert/composer.json b/vendor/wikimedia/assert/composer.json
new file mode 100644
index 00000000..121f7162
--- /dev/null
+++ b/vendor/wikimedia/assert/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "wikimedia/assert",
+ "description": "Provides runtime assertions",
+ "keywords": ["PHP", "QA", "assert", "assertions", "precondition", "postcondition"],
+ "homepage": "https://github.com/wmde/Assert",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Daniel Kinzler"
+ }
+ ],
+
+ "require-dev": {
+ "phpunit/phpunit": "3.7.*"
+ },
+
+ "autoload": {
+ "psr-4": {
+ "Wikimedia\\Assert\\": "src/",
+ "Wikimedia\\Assert\\Test\\": "tests/phpunit/"
+ }
+ }
+
+}
diff --git a/vendor/wikimedia/assert/phpunit.xml.dist b/vendor/wikimedia/assert/phpunit.xml.dist
new file mode 100644
index 00000000..49a2f87d
--- /dev/null
+++ b/vendor/wikimedia/assert/phpunit.xml.dist
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<phpunit bootstrap="./vendor/autoload.php"
+ backupGlobals="false"
+ backupStaticAttributes="false"
+ cacheTokens="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ stopOnError="false"
+ stopOnFailure="false"
+ stopOnIncomplete="false"
+ stopOnSkipped="false"
+ verbose="true">
+
+ <testsuites>
+ <testsuite name="Assert">
+ <directory>./tests/phpunit</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">src</directory>
+ </whitelist>
+ </filter>
+
+</phpunit> \ No newline at end of file
diff --git a/vendor/wikimedia/assert/src/Assert.php b/vendor/wikimedia/assert/src/Assert.php
new file mode 100644
index 00000000..53b438a5
--- /dev/null
+++ b/vendor/wikimedia/assert/src/Assert.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+/**
+ * Assert provides functions for assorting preconditions (such as parameter types) and
+ * postconditions. It is intended as a safer alternative to PHP's assert() function.
+ *
+ * Note that assertions evaluate expressions and add function calls, so using assertions
+ * may have a negative impact on performance when used in performance hotspots. The idea
+ * if this class is to have a neat tool for assertions if and when they are needed.
+ * It is not recommended to place assertions all over the code indiscriminately.
+ *
+ * For more information, see the the README file.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class Assert {
+
+ /**
+ * Checks a precondition, that is, throws a PreconditionException if $condition is false.
+ * For checking call parameters, use Assert::parameter() instead.
+ *
+ * This is provided for completeness, most preconditions should be covered by
+ * Assert::parameter() and related assertions.
+ *
+ * @see parameter()
+ *
+ * @note This is intended mostly for checking preconditions in constructors and setters,
+ * or before using parameters in complex computations.
+ * Checking preconditions in every function call is not recommended, since it may have a
+ * negative impact on performance.
+ *
+ * @param bool $condition
+ * @param string $description The message to include in the exception if the condition fails.
+ *
+ * @throws PreconditionException if $condition is not true.
+ */
+ public static function precondition( $condition, $description ) {
+ if ( !$condition ) {
+ throw new PreconditionException( "Precondition failed: $description" );
+ }
+ }
+
+ /**
+ * Checks a parameter, that is, throws a ParameterAssertionException if $condition is false.
+ * This is similar to Assert::precondition().
+ *
+ * @note This is intended for checking parameters in constructors and setters.
+ * Checking parameters in every function call is not recommended, since it may have a
+ * negative impact on performance.
+ *
+ * @param bool $condition
+ * @param string $name The name of the parameter that was checked.
+ * @param string $description The message to include in the exception if the condition fails.
+ *
+ * @throws ParameterAssertionException if $condition is not true.
+ */
+ public static function parameter( $condition, $name, $description ) {
+ if ( !$condition ) {
+ throw new ParameterAssertionException( $name, $description );
+ }
+ }
+
+ /**
+ * Checks an parameter's type, that is, throws a InvalidArgumentException if $condition is false.
+ * This is really a special case of Assert::precondition().
+ *
+ * @note This is intended for checking parameters in constructors and setters.
+ * Checking parameters in every function call is not recommended, since it may have a
+ * negative impact on performance.
+ *
+ * @note If possible, type hints should be used instead of calling this function.
+ * It is intended for cases where type hints to not work, e.g. for checking primitive types.
+ *
+ * @param string $type The parameter's expected type. Can be the name of a native type or a
+ * class or interface. If multiple types are allowed, they can be given separated by
+ * a pipe character ("|").
+ * @param mixed $value The parameter's actual value.
+ * @param string $name The name of the parameter that was checked.
+ *
+ * @throws ParameterTypeException if $value is not of type (or, for objects, is not an
+ * instance of) $type.
+ */
+ public static function parameterType( $type, $value, $name ) {
+ if ( !self::hasType( $value, explode( '|', $type ) ) ) {
+ throw new ParameterTypeException( $name, $type );
+ }
+ }
+
+ /**
+ * Checks the type of all elements of an parameter, assuming the parameter is an array,
+ * that is, throws a ParameterElementTypeException if $value
+ *
+ * @note This is intended for checking parameters in constructors and setters.
+ * Checking parameters in every function call is not recommended, since it may have a
+ * negative impact on performance.
+ *
+ * @param string $type The elements' expected type. Can be the name of a native type or a
+ * class or interface. If multiple types are allowed, they can be given separated by
+ * a pipe character ("|").
+ * @param mixed $value The parameter's actual value. If this is not an array,
+ * a ParameterTypeException is raised.
+ * @param string $name The name of the parameter that was checked.
+ *
+ * @throws ParameterTypeException If $value is not an array.
+ * @throws ParameterElementTypeException If an element of $value is not of type
+ * (or, for objects, is not an instance of) $type.
+ */
+ public static function parameterElementType( $type, $value, $name ) {
+ self::parameterType( 'array', $value, $name );
+
+ $allowedTypes = explode( '|', $type );
+
+ foreach ( $value as $element ) {
+ if ( !self::hasType( $element, $allowedTypes ) ) {
+ throw new ParameterElementTypeException( $name, $type );
+ }
+ }
+ }
+
+ /**
+ * Checks a postcondition, that is, throws a PostconditionException if $condition is false.
+ * This is very similar Assert::invariant() but is intended for use only after a computation
+ * is complete.
+ *
+ * @note This is intended for sanity-checks in the implementation of complex algorithms.
+ * Note however that it should not be used in performance hotspots, since evaluating
+ * $condition and calling postcondition() costs time.
+ *
+ * @param bool $condition
+ * @param string $description The message to include in the exception if the condition fails.
+ *
+ * @throws PostconditionException
+ */
+ public static function postcondition( $condition, $description ) {
+ if ( !$condition ) {
+ throw new PostconditionException( "Postcondition failed: $description" );
+ }
+ }
+
+ /**
+ * Checks an invariant, that is, throws a InvariantException if $condition is false.
+ * This is very similar Assert::postcondition() but is intended for use throughout the code.
+ *
+ * @note This is intended for sanity-checks in the implementation of complex algorithms.
+ * Note however that it should not be used in performance hotspots, since evaluating
+ * $condition and calling postcondition() costs time.
+ *
+ * @param bool $condition
+ * @param string $description The message to include in the exception if the condition fails.
+ *
+ * @throws InvariantException
+ */
+ public static function invariant( $condition, $description ) {
+ if ( !$condition ) {
+ throw new InvariantException( "Invariant failed: $description" );
+ }
+ }
+
+ /**
+ * @param mixed $value
+ * @param array $allowedTypes
+ *
+ * @return bool
+ */
+ private static function hasType( $value, array $allowedTypes ) {
+ // Apply strtolower because gettype returns "NULL" for null values.
+ $type = strtolower( gettype( $value ) );
+
+ if ( in_array( $type, $allowedTypes ) ) {
+ return true;
+ }
+
+ if ( is_callable( $value ) && in_array( 'callable', $allowedTypes ) ) {
+ return true;
+ }
+
+ if ( is_object( $value ) && self::isInstanceOf( $value, $allowedTypes ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param mixed $value
+ * @param array $allowedTypes
+ *
+ * @return bool
+ */
+ private static function isInstanceOf( $value, array $allowedTypes ) {
+ foreach ( $allowedTypes as $type ) {
+ if ( $value instanceof $type ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/vendor/wikimedia/assert/src/AssertionException.php b/vendor/wikimedia/assert/src/AssertionException.php
new file mode 100644
index 00000000..488c3135
--- /dev/null
+++ b/vendor/wikimedia/assert/src/AssertionException.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+/**
+ * Marker interface for exceptions thrown by Assert. Since the exceptions thrown by Assert
+ * use different standard exceptions as base classes, the marker interface is needed to be
+ * able to catch them all at once.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+interface AssertionException {
+
+}
diff --git a/vendor/wikimedia/assert/src/InvariantException.php b/vendor/wikimedia/assert/src/InvariantException.php
new file mode 100644
index 00000000..870ce1a0
--- /dev/null
+++ b/vendor/wikimedia/assert/src/InvariantException.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+use LogicException;
+
+/**
+ * Exception indicating that an invariant assertion failed.
+ * This generally means an error in the internal logic of a function, or a serious problem
+ * in the runtime environment.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class InvariantException extends LogicException implements AssertionException {
+
+}
diff --git a/vendor/wikimedia/assert/src/ParameterAssertionException.php b/vendor/wikimedia/assert/src/ParameterAssertionException.php
new file mode 100644
index 00000000..cb42cc6f
--- /dev/null
+++ b/vendor/wikimedia/assert/src/ParameterAssertionException.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+use InvalidArgumentException;
+
+/**
+ * Exception indicating that an parameter assertion failed.
+ * This generally means a disagreement between the caller and the implementation of a function.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class ParameterAssertionException extends InvalidArgumentException implements AssertionException {
+
+ /**
+ * @var string
+ */
+ private $parameterName;
+
+ /**
+ * @param string $parameterName
+ * @param string $description
+ *
+ * @throws ParameterTypeException
+ */
+ public function __construct( $parameterName, $description ) {
+ if ( !is_string( $parameterName ) ) {
+ throw new ParameterTypeException( 'parameterName', 'string' );
+ }
+
+ if ( !is_string( $description ) ) {
+ throw new ParameterTypeException( 'description', 'string' );
+ }
+
+ parent::__construct( "Bad value for parameter $parameterName: $description" );
+
+ $this->parameterName = $parameterName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getParameterName() {
+ return $this->parameterName;
+ }
+
+}
diff --git a/vendor/wikimedia/assert/src/ParameterElementTypeException.php b/vendor/wikimedia/assert/src/ParameterElementTypeException.php
new file mode 100644
index 00000000..eaaa663f
--- /dev/null
+++ b/vendor/wikimedia/assert/src/ParameterElementTypeException.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+/**
+ * Exception indicating that a parameter element type assertion failed.
+ * This generally means a disagreement between the caller and the implementation of a function.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class ParameterElementTypeException extends ParameterAssertionException {
+
+ /**
+ * @var string
+ */
+ private $elementType;
+
+ /**
+ * @param string $parameterName
+ * @param string $elementType
+ *
+ * @throws ParameterTypeException
+ */
+ public function __construct( $parameterName, $elementType ) {
+ if ( !is_string( $elementType ) ) {
+ throw new ParameterTypeException( 'elementType', 'string' );
+ }
+
+ parent::__construct( $parameterName, "all elements must be $elementType" );
+
+ $this->elementType = $elementType;
+ }
+
+ /**
+ * @return string
+ */
+ public function getElementType() {
+ return $this->elementType;
+ }
+
+}
diff --git a/vendor/wikimedia/assert/src/ParameterTypeException.php b/vendor/wikimedia/assert/src/ParameterTypeException.php
new file mode 100644
index 00000000..943f6c08
--- /dev/null
+++ b/vendor/wikimedia/assert/src/ParameterTypeException.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+/**
+ * Exception indicating that a parameter type assertion failed.
+ * This generally means a disagreement between the caller and the implementation of a function.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class ParameterTypeException extends ParameterAssertionException {
+
+ /**
+ * @var string
+ */
+ private $parameterType;
+
+ /**
+ * @param string $parameterName
+ * @param string $parameterType
+ *
+ * @throws ParameterTypeException
+ */
+ public function __construct( $parameterName, $parameterType ) {
+ if ( !is_string( $parameterType ) ) {
+ throw new ParameterTypeException( 'parameterType', 'string' );
+ }
+
+ parent::__construct( $parameterName, "must be a $parameterType" );
+
+ $this->parameterType = $parameterType;
+ }
+
+ /**
+ * @return string
+ */
+ public function getParameterType() {
+ return $this->parameterType;
+ }
+
+}
diff --git a/vendor/wikimedia/assert/src/PostconditionException.php b/vendor/wikimedia/assert/src/PostconditionException.php
new file mode 100644
index 00000000..a7753ef0
--- /dev/null
+++ b/vendor/wikimedia/assert/src/PostconditionException.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+use LogicException;
+
+/**
+ * Exception indicating that a postcondition assertion failed.
+ * This generally means an error in the internal logic of a function, or a serious problem
+ * in the runtime environment.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class PostconditionException extends LogicException implements AssertionException {
+
+}
diff --git a/vendor/wikimedia/assert/src/PreconditionException.php b/vendor/wikimedia/assert/src/PreconditionException.php
new file mode 100644
index 00000000..97e98035
--- /dev/null
+++ b/vendor/wikimedia/assert/src/PreconditionException.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Wikimedia\Assert;
+
+use RuntimeException;
+
+/**
+ * Exception indicating that an precondition assertion failed.
+ * This generally means a disagreement between the caller and the implementation of a function.
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+class PreconditionException extends RuntimeException implements AssertionException {
+
+}
diff --git a/vendor/wikimedia/assert/tests/phpunit/AssertTest.php b/vendor/wikimedia/assert/tests/phpunit/AssertTest.php
new file mode 100644
index 00000000..038bc1c1
--- /dev/null
+++ b/vendor/wikimedia/assert/tests/phpunit/AssertTest.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Wikimedia\Assert\Test;
+
+use LogicException;
+use PHPUnit_Framework_TestCase;
+use RuntimeException;
+use Wikimedia\Assert\Assert;
+use Wikimedia\Assert\AssertionException;
+use Wikimedia\Assert\ParameterAssertionException;
+use Wikimedia\Assert\ParameterElementTypeException;
+use Wikimedia\Assert\ParameterTypeException;
+
+/**
+ * @covers Wikimedia\Assert\Assert
+ *
+ * @license MIT
+ * @author Daniel Kinzler
+ * @copyright Wikimedia Deutschland e.V.
+ */
+
+class AssertTest extends PHPUnit_Framework_TestCase {
+
+ public function testPrecondition_pass() {
+ Assert::precondition( true, 'test' );
+ }
+
+ public function testPrecondition_fail() {
+ $this->setExpectedException( 'Wikimedia\Assert\PreconditionException' );
+ Assert::precondition( false, 'test' );
+ }
+
+ public function testParameter_pass() {
+ Assert::parameter( true, 'foo', 'test' );
+ }
+
+ public function testParameter_fail() {
+ try {
+ Assert::parameter( false, 'test', 'testing' );
+ } catch ( ParameterAssertionException $ex ) {
+ $this->assertEquals( 'test', $ex->getParameterName() );
+ }
+ }
+
+ public function validParameterTypeProvider() {
+ return array(
+ 'simple' => array( 'string', 'hello' ),
+ 'class' => array( 'RuntimeException', new RuntimeException() ),
+ 'multi' => array( 'string|array|Closure', function() {} ),
+ 'null' => array( 'integer|null', null ),
+ 'callable' => array( 'null|callable', 'time' ),
+ );
+ }
+
+ /**
+ * @dataProvider validParameterTypeProvider
+ */
+ public function testParameterType_pass( $type, $value ) {
+ Assert::parameterType( $type, $value, 'test' );
+ }
+
+ public function invalidParameterTypeProvider() {
+ return array(
+ 'simple' => array( 'string', 5 ),
+ 'class' => array( 'RuntimeException', new LogicException() ),
+ 'multi' => array( 'string|integer|Closure', array() ),
+ 'null' => array( 'integer|string', null ),
+ 'callable' => array( 'null|callable', array() ),
+ );
+ }
+
+ /**
+ * @dataProvider invalidParameterTypeProvider
+ */
+ public function testParameterType_fail( $type, $value ) {
+ try {
+ Assert::parameterType( $type, $value, 'test' );
+ $this->fail( 'Expected ParameterTypeException' );
+ } catch ( ParameterTypeException $ex ) {
+ $this->assertEquals( $type, $ex->getParameterType() );
+ $this->assertEquals( 'test', $ex->getParameterName() );
+ }
+ }
+
+ public function testParameterType_catch() {
+ try {
+ Assert::parameterType( 'string', 17, 'test' );
+ $this->fail( 'Expected exception' );
+ } catch ( AssertionException $ex ) {
+ // ok
+ }
+ }
+
+ public function validParameterElementTypeProvider() {
+ return array(
+ 'empty' => array( 'string', array() ),
+ 'simple' => array( 'string', array( 'hello', 'world' ) ),
+ 'class' => array( 'RuntimeException', array( new RuntimeException() ) ),
+ 'multi' => array( 'string|array|Closure', array( array(), function() {} ) ),
+ 'null' => array( 'integer|null', array( null, 3, null ) ),
+ );
+ }
+
+ /**
+ * @dataProvider validParameterElementTypeProvider
+ */
+ public function testParameterElementType_pass( $type, $value ) {
+ Assert::parameterElementType( $type, $value, 'test' );
+ }
+
+ public function invalidParameterElementTypeProvider() {
+ return array(
+ 'simple' => array( 'string', array( 'hello', 5 ) ),
+ 'class' => array( 'RuntimeException', array( new LogicException() ) ),
+ 'multi' => array( 'string|array|Closure', array( array(), function() {}, 5 ) ),
+ 'null' => array( 'integer|string', array( null, 3, null ) ),
+ );
+ }
+
+ /**
+ * @dataProvider invalidParameterElementTypeProvider
+ */
+ public function testParameterElementType_fail( $type, $value ) {
+ try {
+ Assert::parameterElementType( $type, $value, 'test' );
+ $this->fail( 'Expected ParameterElementTypeException' );
+ } catch ( ParameterElementTypeException $ex ) {
+ $this->assertEquals( $type, $ex->getElementType() );
+ $this->assertEquals( 'test', $ex->getParameterName() );
+ }
+ }
+
+ public function testParameterElementType_bad() {
+ $this->setExpectedException( 'Wikimedia\Assert\ParameterTypeException' );
+ Assert::parameterElementType( 'string', 'foo', 'test' );
+ }
+
+ public function testInvariant_pass() {
+ Assert::invariant( true, 'test' );
+ }
+
+ public function testInvariant_fail() {
+ $this->setExpectedException( 'Wikimedia\Assert\InvariantException' );
+ Assert::invariant( false, 'test' );
+ }
+
+ public function testPostcondition_pass() {
+ Assert::postcondition( true, 'test' );
+ }
+
+ public function testPostcondition_fail() {
+ $this->setExpectedException( 'Wikimedia\Assert\PostconditionException' );
+ Assert::postcondition( false, 'test' );
+ }
+
+}
diff --git a/vendor/wikimedia/avro/LICENSE.txt b/vendor/wikimedia/avro/LICENSE.txt
new file mode 100644
index 00000000..6d3f211b
--- /dev/null
+++ b/vendor/wikimedia/avro/LICENSE.txt
@@ -0,0 +1,308 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+----------------------------------------------------------------------
+License for the Jansson C JSON parser used in the C implementation:
+
+Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+----------------------------------------------------------------------
+License for the Json.NET used in the C# implementation:
+
+Copyright (c) 2007 James Newton-King
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
+License for msinttypes used in the C implementation:
+Source from:
+http://code.google.com/p/msinttypes/downloads/detail?name=msinttypes-r26.zip
+
+Copyright (c) 2006-2008 Alexander Chemeris
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. The name of the author may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+----------------------------------------------------------------------
+License for Dirent API for Microsoft Visual Studio used in the C implementation:
+Source from:
+http://www.softagalleria.net/download/dirent/dirent-1.11.zip
+
+Copyright (C) 2006 Toni Ronkko
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+----------------------------------------------------------------------
diff --git a/vendor/wikimedia/avro/NOTICE.txt b/vendor/wikimedia/avro/NOTICE.txt
new file mode 100644
index 00000000..e601a8e9
--- /dev/null
+++ b/vendor/wikimedia/avro/NOTICE.txt
@@ -0,0 +1,9 @@
+Apache Avro
+Copyright 2010 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+C JSON parsing provided by Jansson and
+written by Petri Lehtinen. The original software is
+available from http://www.digip.org/jansson/.
diff --git a/vendor/wikimedia/avro/README.md b/vendor/wikimedia/avro/README.md
new file mode 100644
index 00000000..5604baf2
--- /dev/null
+++ b/vendor/wikimedia/avro/README.md
@@ -0,0 +1,23 @@
+What the Avro PHP library is
+============================
+
+A library for using [Avro](http://avro.apache.org/) with PHP.
+
+Requirements
+------------
+ * PHP 5
+ * On 32-bit platforms, the [GMP PHP extension](http://php.net/gmp)
+ * For testing, [PHPUnit](http://www.phpunit.de/)
+
+Both GMP and PHPUnit are often available via package management
+systems as `php5-gmp` and `phpunit`, respectively.
+
+Getting started
+---------------
+```
+$ composer require wikimedia/avro
+```
+
+History
+-------
+Extracted from https://github.com/apache/avro using `git subtree`.
diff --git a/vendor/wikimedia/avro/lib/avro.php b/vendor/wikimedia/avro/lib/avro.php
new file mode 100644
index 00000000..4805fb7a
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Avro library top-level file.
+ *
+ * This file in turn includes all files supporting the
+ * Avro implementation.
+ *
+ * @package Avro
+ */
+
+/**
+ * General Avro exceptions.
+ * @package Avro
+ */
+class AvroException extends Exception {}
+
+/**
+ * Library-level class for PHP Avro port.
+ *
+ * Contains library details such as version number and platform checks.
+ *
+ * This port is an implementation of the
+ * {@link http://avro.apache.org/docs/1.3.3/spec.html Avro 1.3.3 Specification}
+ *
+ * @package Avro
+ *
+ */
+class Avro
+{
+ /**
+ * @var string version number of Avro specification to which
+ * this implemenation complies
+ */
+ const SPEC_VERSION = '1.3.3';
+
+ /**#@+
+ * Constant to enumerate endianness.
+ * @access private
+ * @var int
+ */
+ const BIG_ENDIAN = 0x00;
+ const LITTLE_ENDIAN = 0x01;
+ /**#@-*/
+
+ /**
+ * Memoized result of self::set_endianness()
+ * @var int self::BIG_ENDIAN or self::LITTLE_ENDIAN
+ * @see self::set_endianness()
+ */
+ private static $endianness;
+
+ /**#@+
+ * Constant to enumerate biginteger handling mode.
+ * GMP is used, if available, on 32-bit platforms.
+ */
+ const PHP_BIGINTEGER_MODE = 0x00;
+ const GMP_BIGINTEGER_MODE = 0x01;
+ /**#@-*/
+
+ /**
+ * @var int
+ * Mode used to handle bigintegers. After Avro::check_64_bit() has been called,
+ * (usually via a call to Avro::check_platform(), set to
+ * self::GMP_BIGINTEGER_MODE on 32-bit platforms that have GMP available,
+ * and to self::PHP_BIGINTEGER_MODE otherwise.
+ */
+ private static $biginteger_mode;
+
+ /**
+ * Wrapper method to call each required check.
+ *
+ */
+ public static function check_platform()
+ {
+ self::check_64_bit();
+ self::check_little_endian();
+ }
+
+ /**
+ * Determines if the host platform can encode and decode long integer data.
+ *
+ * @throws AvroException if the platform cannot handle long integers.
+ */
+ private static function check_64_bit()
+ {
+ if (8 != PHP_INT_SIZE)
+ if (extension_loaded('gmp'))
+ self::$biginteger_mode = self::GMP_BIGINTEGER_MODE;
+ else
+ throw new AvroException('This platform cannot handle a 64-bit operations. '
+ . 'Please install the GMP PHP extension.');
+ else
+ self::$biginteger_mode = self::PHP_BIGINTEGER_MODE;
+
+ }
+
+ /**
+ * @returns boolean true if the PHP GMP extension is used and false otherwise.
+ * @internal Requires Avro::check_64_bit() (exposed via Avro::check_platform())
+ * to have been called to set Avro::$biginteger_mode.
+ */
+ static function uses_gmp()
+ {
+ return (self::GMP_BIGINTEGER_MODE == self::$biginteger_mode);
+ }
+
+ /**
+ * Determines if the host platform is little endian,
+ * required for processing double and float data.
+ *
+ * @throws AvroException if the platform is not little endian.
+ */
+ private static function check_little_endian()
+ {
+ if (!self::is_little_endian_platform())
+ throw new AvroException('This is not a little-endian platform');
+ }
+
+ /**
+ * Determines the endianness of the host platform and memoizes
+ * the result to Avro::$endianness.
+ *
+ * Based on a similar check perfomed in http://pear.php.net/package/Math_BinaryUtils
+ *
+ * @throws AvroException if the endianness cannot be determined.
+ */
+ private static function set_endianness()
+ {
+ $packed = pack('d', 1);
+ switch ($packed)
+ {
+ case "\77\360\0\0\0\0\0\0":
+ self::$endianness = self::BIG_ENDIAN;
+ break;
+ case "\0\0\0\0\0\0\360\77":
+ self::$endianness = self::LITTLE_ENDIAN;
+ break;
+ default:
+ throw new AvroException(
+ sprintf('Error determining platform endianness: %s',
+ AvroDebug::hex_string($packed)));
+ }
+ }
+
+ /**
+ * @returns boolean true if the host platform is big endian
+ * and false otherwise.
+ * @uses self::set_endianness()
+ */
+ private static function is_big_endian_platform()
+ {
+ if (is_null(self::$endianness))
+ self::set_endianness();
+
+ return (self::BIG_ENDIAN == self::$endianness);
+ }
+
+ /**
+ * @returns boolean true if the host platform is little endian,
+ * and false otherwise.
+ * @uses self::is_bin_endian_platform()
+ */
+ private static function is_little_endian_platform()
+ {
+ return !self::is_big_endian_platform();
+ }
+
+}
+
+require_once('avro/util.php');
+require_once('avro/debug.php');
+require_once('avro/schema.php');
+require_once('avro/io.php');
+require_once('avro/gmp.php');
+require_once('avro/datum.php');
+require_once('avro/data_file.php');
+require_once('avro/protocol.php');
diff --git a/vendor/wikimedia/avro/lib/avro/data_file.php b/vendor/wikimedia/avro/lib/avro/data_file.php
new file mode 100644
index 00000000..e8e089f5
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/data_file.php
@@ -0,0 +1,535 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes handling reading and writing from and to AvroIO objects
+ * @package Avro
+ */
+
+/**
+ * Raised when something unkind happens with respect to AvroDataIO.
+ * @package Avro
+ */
+class AvroDataIOException extends AvroException {}
+
+/**
+ * @package Avro
+ */
+class AvroDataIO
+{
+ /**
+ * @var int used in file header
+ */
+ const VERSION = 1;
+
+ /**
+ * @var int count of bytes in synchronization marker
+ */
+ const SYNC_SIZE = 16;
+
+ /**
+ * @var int count of items per block, arbitrarily set to 4000 * SYNC_SIZE
+ * @todo make this value configurable
+ */
+ const SYNC_INTERVAL = 64000;
+
+ /**
+ * @var string map key for datafile metadata codec value
+ */
+ const METADATA_CODEC_ATTR = 'avro.codec';
+
+ /**
+ * @var string map key for datafile metadata schema value
+ */
+ const METADATA_SCHEMA_ATTR = 'avro.schema';
+ /**
+ * @var string JSON for datafile metadata schema
+ */
+ const METADATA_SCHEMA_JSON = '{"type":"map","values":"bytes"}';
+
+ /**
+ * @var string codec value for NULL codec
+ */
+ const NULL_CODEC = 'null';
+
+ /**
+ * @var string codec value for deflate codec
+ */
+ const DEFLATE_CODEC = 'deflate';
+
+ /**
+ * @var array array of valid codec names
+ * @todo Avro implementations are required to implement deflate codec as well,
+ * so implement it already!
+ */
+ private static $valid_codecs = array(self::NULL_CODEC);
+
+ /**
+ * @var AvroSchema cached version of metadata schema object
+ */
+ private static $metadata_schema;
+
+ /**
+ * @returns the initial "magic" segment of an Avro container file header.
+ */
+ public static function magic() { return ('Obj' . pack('c', self::VERSION)); }
+
+ /**
+ * @returns int count of bytes in the initial "magic" segment of the
+ * Avro container file header
+ */
+ public static function magic_size() { return strlen(self::magic()); }
+
+
+ /**
+ * @returns AvroSchema object of Avro container file metadata.
+ */
+ public static function metadata_schema()
+ {
+ if (is_null(self::$metadata_schema))
+ self::$metadata_schema = AvroSchema::parse(self::METADATA_SCHEMA_JSON);
+ return self::$metadata_schema;
+ }
+
+ /**
+ * @param string $file_path file_path of file to open
+ * @param string $mode one of AvroFile::READ_MODE or AvroFile::WRITE_MODE
+ * @param string $schema_json JSON of writer's schema
+ * @returns AvroDataIOWriter instance of AvroDataIOWriter
+ *
+ * @throws AvroDataIOException if $writers_schema is not provided
+ * or if an invalid $mode is given.
+ */
+ public static function open_file($file_path, $mode=AvroFile::READ_MODE,
+ $schema_json=null)
+ {
+ $schema = !is_null($schema_json)
+ ? AvroSchema::parse($schema_json) : null;
+
+ $io = false;
+ switch ($mode)
+ {
+ case AvroFile::WRITE_MODE:
+ if (is_null($schema))
+ throw new AvroDataIOException('Writing an Avro file requires a schema.');
+ $file = new AvroFile($file_path, AvroFile::WRITE_MODE);
+ $io = self::open_writer($file, $schema);
+ break;
+ case AvroFile::READ_MODE:
+ $file = new AvroFile($file_path, AvroFile::READ_MODE);
+ $io = self::open_reader($file, $schema);
+ break;
+ default:
+ throw new AvroDataIOException(
+ sprintf("Only modes '%s' and '%s' allowed. You gave '%s'.",
+ AvroFile::READ_MODE, AvroFile::WRITE_MODE, $mode));
+ }
+ return $io;
+ }
+
+ /**
+ * @returns array array of valid codecs
+ */
+ private static function valid_codecs()
+ {
+ return self::$valid_codecs;
+ }
+
+ /**
+ * @param string $codec
+ * @returns boolean true if $codec is a valid codec value and false otherwise
+ */
+ public static function is_valid_codec($codec)
+ {
+ return in_array($codec, self::valid_codecs());
+ }
+
+ /**
+ * @param AvroIO $io
+ * @param AvroSchema $schema
+ * @returns AvroDataIOWriter
+ */
+ protected function open_writer($io, $schema)
+ {
+ $writer = new AvroIODatumWriter($schema);
+ return new AvroDataIOWriter($io, $writer, $schema);
+ }
+
+ /**
+ * @param AvroIO $io
+ * @param AvroSchema $schema
+ * @returns AvroDataIOReader
+ */
+ protected function open_reader($io, $schema)
+ {
+ $reader = new AvroIODatumReader(null, $schema);
+ return new AvroDataIOReader($io, $reader);
+ }
+
+}
+
+/**
+ *
+ * Reads Avro data from an AvroIO source using an AvroSchema.
+ * @package Avro
+ */
+class AvroDataIOReader
+{
+ /**
+ * @var AvroIO
+ */
+ private $io;
+
+ /**
+ * @var AvroIOBinaryDecoder
+ */
+ private $decoder;
+
+ /**
+ * @var AvroIODatumReader
+ */
+ private $datum_reader;
+
+ /**
+ * @var string
+ */
+ private $sync_marker;
+
+ /**
+ * @var array object container metadata
+ */
+ private $metadata;
+
+ /**
+ * @var int count of items in block
+ */
+ private $block_count;
+
+ /**
+ * @param AvroIO $io source from which to read
+ * @param AvroIODatumReader $datum_reader reader that understands
+ * the data schema
+ * @throws AvroDataIOException if $io is not an instance of AvroIO
+ * @uses read_header()
+ */
+ public function __construct($io, $datum_reader)
+ {
+
+ if (!($io instanceof AvroIO))
+ throw new AvroDataIOException('io must be instance of AvroIO');
+
+ $this->io = $io;
+ $this->decoder = new AvroIOBinaryDecoder($this->io);
+ $this->datum_reader = $datum_reader;
+ $this->read_header();
+
+ $codec = AvroUtil::array_value($this->metadata,
+ AvroDataIO::METADATA_CODEC_ATTR);
+ if ($codec && !AvroDataIO::is_valid_codec($codec))
+ throw new AvroDataIOException(sprintf('Uknown codec: %s', $codec));
+
+ $this->block_count = 0;
+ // FIXME: Seems unsanitary to set writers_schema here.
+ // Can't constructor take it as an argument?
+ $this->datum_reader->set_writers_schema(
+ AvroSchema::parse($this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR]));
+ }
+
+ /**
+ * Reads header of object container
+ * @throws AvroDataIOException if the file is not an Avro data file.
+ */
+ private function read_header()
+ {
+ $this->seek(0, AvroIO::SEEK_SET);
+
+ $magic = $this->read(AvroDataIO::magic_size());
+
+ if (strlen($magic) < AvroDataIO::magic_size())
+ throw new AvroDataIOException(
+ 'Not an Avro data file: shorter than the Avro magic block');
+
+ if (AvroDataIO::magic() != $magic)
+ throw new AvroDataIOException(
+ sprintf('Not an Avro data file: %s does not match %s',
+ $magic, AvroDataIO::magic()));
+
+ $this->metadata = $this->datum_reader->read_data(AvroDataIO::metadata_schema(),
+ AvroDataIO::metadata_schema(),
+ $this->decoder);
+ $this->sync_marker = $this->read(AvroDataIO::SYNC_SIZE);
+ }
+
+ /**
+ * @internal Would be nice to implement data() as an iterator, I think
+ * @returns array of data from object container.
+ */
+ public function data()
+ {
+ $data = array();
+ while (true)
+ {
+ if (0 == $this->block_count)
+ {
+ if ($this->is_eof())
+ break;
+
+ if ($this->skip_sync())
+ if ($this->is_eof())
+ break;
+
+ $this->read_block_header();
+ }
+ $data []= $this->datum_reader->read($this->decoder);
+ $this->block_count -= 1;
+ }
+ return $data;
+ }
+
+ /**
+ * Closes this writer (and its AvroIO object.)
+ * @uses AvroIO::close()
+ */
+ public function close() { return $this->io->close(); }
+
+ /**
+ * @uses AvroIO::seek()
+ */
+ private function seek($offset, $whence)
+ {
+ return $this->io->seek($offset, $whence);
+ }
+
+ /**
+ * @uses AvroIO::read()
+ */
+ private function read($len) { return $this->io->read($len); }
+
+ /**
+ * @uses AvroIO::is_eof()
+ */
+ private function is_eof() { return $this->io->is_eof(); }
+
+ private function skip_sync()
+ {
+ $proposed_sync_marker = $this->read(AvroDataIO::SYNC_SIZE);
+ if ($proposed_sync_marker != $this->sync_marker)
+ {
+ $this->seek(-AvroDataIO::SYNC_SIZE, AvroIO::SEEK_CUR);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Reads the block header (which includes the count of items in the block
+ * and the length in bytes of the block)
+ * @returns int length in bytes of the block.
+ */
+ private function read_block_header()
+ {
+ $this->block_count = $this->decoder->read_long();
+ return $this->decoder->read_long();
+ }
+
+}
+
+/**
+ * Writes Avro data to an AvroIO source using an AvroSchema
+ * @package Avro
+ */
+class AvroDataIOWriter
+{
+ /**
+ * @returns string a new, unique sync marker.
+ */
+ private static function generate_sync_marker()
+ {
+ // From http://php.net/manual/en/function.mt-rand.php comments
+ return pack('S8',
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff),
+ mt_rand(0, 0xffff) | 0x4000,
+ mt_rand(0, 0xffff) | 0x8000,
+ mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
+ }
+
+ /**
+ * @var AvroIO object container where data is written
+ */
+ private $io;
+
+ /**
+ * @var AvroIOBinaryEncoder encoder for object container
+ */
+ private $encoder;
+
+ /**
+ * @var AvroDatumWriter
+ */
+ private $datum_writer;
+
+ /**
+ * @var AvroStringIO buffer for writing
+ */
+ private $buffer;
+
+ /**
+ * @var AvroIOBinaryEncoder encoder for buffer
+ */
+ private $buffer_encoder; // AvroIOBinaryEncoder
+
+ /**
+ * @var int count of items written to block
+ */
+ private $block_count;
+
+ /**
+ * @var array map of object container metadata
+ */
+ private $metadata;
+
+ /**
+ * @param AvroIO $io
+ * @param AvroIODatumWriter $datum_writer
+ * @param AvroSchema $writers_schema
+ */
+ public function __construct($io, $datum_writer, $writers_schema=null)
+ {
+ if (!($io instanceof AvroIO))
+ throw new AvroDataIOException('io must be instance of AvroIO');
+
+ $this->io = $io;
+ $this->encoder = new AvroIOBinaryEncoder($this->io);
+ $this->datum_writer = $datum_writer;
+ $this->buffer = new AvroStringIO();
+ $this->buffer_encoder = new AvroIOBinaryEncoder($this->buffer);
+ $this->block_count = 0;
+ $this->metadata = array();
+
+ if ($writers_schema)
+ {
+ $this->sync_marker = self::generate_sync_marker();
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = AvroDataIO::NULL_CODEC;
+ $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = strval($writers_schema);
+ $this->write_header();
+ }
+ else
+ {
+ $dfr = new AvroDataIOReader($this->io, new AvroIODatumReader());
+ $this->sync_marker = $dfr->sync_marker;
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = $dfr->metadata[AvroDataIO::METADATA_CODEC_ATTR];
+
+ $schema_from_file = $dfr->metadata[AvroDataIO::METADATA_SCHEMA_ATTR];
+ $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = $schema_from_file;
+ $this->datum_writer->writers_schema = AvroSchema::parse($schema_from_file);
+ $this->seek(0, SEEK_END);
+ }
+ }
+
+ /**
+ * @param mixed $datum
+ */
+ public function append($datum)
+ {
+ $this->datum_writer->write($datum, $this->buffer_encoder);
+ $this->block_count++;
+
+ if ($this->buffer->length() >= AvroDataIO::SYNC_INTERVAL)
+ $this->write_block();
+ }
+
+ /**
+ * Flushes buffer to AvroIO object container and closes it.
+ * @return mixed value of $io->close()
+ * @see AvroIO::close()
+ */
+ public function close()
+ {
+ $this->flush();
+ return $this->io->close();
+ }
+
+ /**
+ * Flushes biffer to AvroIO object container.
+ * @returns mixed value of $io->flush()
+ * @see AvroIO::flush()
+ */
+ private function flush()
+ {
+ $this->write_block();
+ return $this->io->flush();
+ }
+
+ /**
+ * Writes a block of data to the AvroIO object container.
+ * @throws AvroDataIOException if the codec provided by the encoder
+ * is not supported
+ * @internal Should the codec check happen in the constructor?
+ * Why wait until we're writing data?
+ */
+ private function write_block()
+ {
+ if ($this->block_count > 0)
+ {
+ $this->encoder->write_long($this->block_count);
+ $to_write = strval($this->buffer);
+ $this->encoder->write_long(strlen($to_write));
+
+ if (AvroDataIO::is_valid_codec(
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR]))
+ $this->write($to_write);
+ else
+ throw new AvroDataIOException(
+ sprintf('codec %s is not supported',
+ $this->metadata[AvroDataIO::METADATA_CODEC_ATTR]));
+
+ $this->write($this->sync_marker);
+ $this->buffer->truncate();
+ $this->block_count = 0;
+ }
+ }
+
+ /**
+ * Writes the header of the AvroIO object container
+ */
+ private function write_header()
+ {
+ $this->write(AvroDataIO::magic());
+ $this->datum_writer->write_data(AvroDataIO::metadata_schema(),
+ $this->metadata, $this->encoder);
+ $this->write($this->sync_marker);
+ }
+
+ /**
+ * @param string $bytes
+ * @uses AvroIO::write()
+ */
+ private function write($bytes) { return $this->io->write($bytes); }
+
+ /**
+ * @param int $offset
+ * @param int $whence
+ * @uses AvroIO::seek()
+ */
+ private function seek($offset, $whence)
+ {
+ return $this->io->seek($offset, $whence);
+ }
+}
diff --git a/vendor/wikimedia/avro/lib/avro/datum.php b/vendor/wikimedia/avro/lib/avro/datum.php
new file mode 100644
index 00000000..ea275faf
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/datum.php
@@ -0,0 +1,984 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for reading and writing Avro data to AvroIO objects.
+ *
+ * @package Avro
+ *
+ * @todo Implement JSON encoding, as is required by the Avro spec.
+ */
+
+/**
+ * Exceptions arising from writing or reading Avro data.
+ *
+ * @package Avro
+ */
+class AvroIOTypeException extends AvroException
+{
+ /**
+ * @param AvroSchema $expected_schema
+ * @param mixed $datum
+ */
+ public function __construct($expected_schema, $datum)
+ {
+ parent::__construct(sprintf('The datum %s is not an example of schema %s',
+ var_export($datum, true), $expected_schema));
+ }
+}
+
+/**
+ * Exceptions arising from incompatibility between
+ * reader and writer schemas.
+ *
+ * @package Avro
+ */
+class AvroIOSchemaMatchException extends AvroException
+{
+ /**
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ */
+ function __construct($writers_schema, $readers_schema)
+ {
+ parent::__construct(
+ sprintf("Writer's schema %s and Reader's schema %s do not match.",
+ $writers_schema, $readers_schema));
+ }
+}
+
+/**
+ * Handles schema-specific writing of data to the encoder.
+ *
+ * Ensures that each datum written is consistent with the writer's schema.
+ *
+ * @package Avro
+ */
+class AvroIODatumWriter
+{
+ /**
+ * Schema used by this instance to write Avro data.
+ * @var AvroSchema
+ */
+ private $writers_schema;
+
+ /**
+ * @param AvroSchema $writers_schema
+ */
+ function __construct($writers_schema=null)
+ {
+ $this->writers_schema = $writers_schema;
+ }
+
+ /**
+ * @param AvroSchema $writers_schema
+ * @param $datum
+ * @param AvroIOBinaryEncoder $encoder
+ * @returns mixed
+ *
+ * @throws AvroIOTypeException if $datum is invalid for $writers_schema
+ */
+ function write_data($writers_schema, $datum, $encoder)
+ {
+ if (!AvroSchema::is_valid_datum($writers_schema, $datum))
+ throw new AvroIOTypeException($writers_schema, $datum);
+
+ switch ($writers_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return $encoder->write_null($datum);
+ case AvroSchema::BOOLEAN_TYPE:
+ return $encoder->write_boolean($datum);
+ case AvroSchema::INT_TYPE:
+ return $encoder->write_int($datum);
+ case AvroSchema::LONG_TYPE:
+ return $encoder->write_long($datum);
+ case AvroSchema::FLOAT_TYPE:
+ return $encoder->write_float($datum);
+ case AvroSchema::DOUBLE_TYPE:
+ return $encoder->write_double($datum);
+ case AvroSchema::STRING_TYPE:
+ return $encoder->write_string($datum);
+ case AvroSchema::BYTES_TYPE:
+ return $encoder->write_bytes($datum);
+ case AvroSchema::ARRAY_SCHEMA:
+ return $this->write_array($writers_schema, $datum, $encoder);
+ case AvroSchema::MAP_SCHEMA:
+ return $this->write_map($writers_schema, $datum, $encoder);
+ case AvroSchema::FIXED_SCHEMA:
+ return $this->write_fixed($writers_schema, $datum, $encoder);
+ case AvroSchema::ENUM_SCHEMA:
+ return $this->write_enum($writers_schema, $datum, $encoder);
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ case AvroSchema::REQUEST_SCHEMA:
+ return $this->write_record($writers_schema, $datum, $encoder);
+ case AvroSchema::UNION_SCHEMA:
+ return $this->write_union($writers_schema, $datum, $encoder);
+ default:
+ throw new AvroException(sprintf('Uknown type: %s',
+ $writers_schema->type));
+ }
+ }
+
+ /**
+ * @param $datum
+ * @param AvroIOBinaryEncoder $encoder
+ */
+ function write($datum, $encoder)
+ {
+ $this->write_data($this->writers_schema, $datum, $encoder);
+ }
+
+ /**#@+
+ * @param AvroSchema $writers_schema
+ * @param null|boolean|int|float|string|array $datum item to be written
+ * @param AvroIOBinaryEncoder $encoder
+ */
+ private function write_array($writers_schema, $datum, $encoder)
+ {
+ $datum_count = count($datum);
+ if (0 < $datum_count)
+ {
+ $encoder->write_long($datum_count);
+ $items = $writers_schema->items();
+ foreach ($datum as $item)
+ $this->write_data($items, $item, $encoder);
+ }
+ return $encoder->write_long(0);
+ }
+
+ private function write_map($writers_schema, $datum, $encoder)
+ {
+ $datum_count = count($datum);
+ if ($datum_count > 0)
+ {
+ $encoder->write_long($datum_count);
+ foreach ($datum as $k => $v)
+ {
+ $encoder->write_string($k);
+ $this->write_data($writers_schema->values(), $v, $encoder);
+ }
+ }
+ $encoder->write_long(0);
+ }
+
+ private function write_union($writers_schema, $datum, $encoder)
+ {
+ $datum_schema_index = -1;
+ $datum_schema = null;
+ foreach ($writers_schema->schemas() as $index => $schema)
+ if (AvroSchema::is_valid_datum($schema, $datum))
+ {
+ $datum_schema_index = $index;
+ $datum_schema = $schema;
+ break;
+ }
+
+ if (is_null($datum_schema))
+ throw new AvroIOTypeException($writers_schema, $datum);
+
+ $encoder->write_long($datum_schema_index);
+ $this->write_data($datum_schema, $datum, $encoder);
+ }
+
+ private function write_enum($writers_schema, $datum, $encoder)
+ {
+ $datum_index = $writers_schema->symbol_index($datum);
+ return $encoder->write_int($datum_index);
+ }
+
+ private function write_fixed($writers_schema, $datum, $encoder)
+ {
+ /**
+ * NOTE Unused $writers_schema parameter included for consistency
+ * with other write_* methods.
+ */
+ return $encoder->write($datum);
+ }
+
+ private function write_record($writers_schema, $datum, $encoder)
+ {
+ foreach ($writers_schema->fields() as $field)
+ $this->write_data($field->type(), $datum[$field->name()], $encoder);
+ }
+
+ /**#@-*/
+}
+
+/**
+ * Encodes and writes Avro data to an AvroIO object using
+ * Avro binary encoding.
+ *
+ * @package Avro
+ */
+class AvroIOBinaryEncoder
+{
+ /**
+ * Performs encoding of the given float value to a binary string
+ *
+ * XXX: This is <b>not</b> endian-aware! The {@link Avro::check_platform()}
+ * called in {@link AvroIOBinaryEncoder::__construct()} should ensure the
+ * library is only used on little-endian platforms, which ensure the little-endian
+ * encoding required by the Avro spec.
+ *
+ * @param float $float
+ * @returns string bytes
+ * @see Avro::check_platform()
+ */
+ static function float_to_int_bits($float)
+ {
+ return pack('f', (float) $float);
+ }
+
+ /**
+ * Performs encoding of the given double value to a binary string
+ *
+ * XXX: This is <b>not</b> endian-aware! See comments in
+ * {@link AvroIOBinaryEncoder::float_to_int_bits()} for details.
+ *
+ * @param double $double
+ * @returns string bytes
+ */
+ static function double_to_long_bits($double)
+ {
+ return pack('d', (double) $double);
+ }
+
+ /**
+ * @param int|string $n
+ * @returns string long $n encoded as bytes
+ * @internal This relies on 64-bit PHP.
+ */
+ static public function encode_long($n)
+ {
+ $n = (int) $n;
+ $n = ($n << 1) ^ ($n >> 63);
+ $str = '';
+ while (0 != ($n & ~0x7F))
+ {
+ $str .= chr(($n & 0x7F) | 0x80);
+ $n >>= 7;
+ }
+ $str .= chr($n);
+ return $str;
+ }
+
+ /**
+ * @var AvroIO
+ */
+ private $io;
+
+ /**
+ * @param AvroIO $io object to which data is to be written.
+ *
+ */
+ function __construct($io)
+ {
+ Avro::check_platform();
+ $this->io = $io;
+ }
+
+ /**
+ * @param null $datum actual value is ignored
+ */
+ function write_null($datum) { return null; }
+
+ /**
+ * @param boolean $datum
+ */
+ function write_boolean($datum)
+ {
+ $byte = $datum ? chr(1) : chr(0);
+ $this->write($byte);
+ }
+
+ /**
+ * @param int $datum
+ */
+ function write_int($datum) { $this->write_long($datum); }
+
+ /**
+ * @param int $n
+ */
+ function write_long($n)
+ {
+ if (Avro::uses_gmp())
+ $this->write(AvroGMP::encode_long($n));
+ else
+ $this->write(self::encode_long($n));
+ }
+
+ /**
+ * @param float $datum
+ * @uses self::float_to_int_bits()
+ */
+ public function write_float($datum)
+ {
+ $this->write(self::float_to_int_bits($datum));
+ }
+
+ /**
+ * @param float $datum
+ * @uses self::double_to_long_bits()
+ */
+ public function write_double($datum)
+ {
+ $this->write(self::double_to_long_bits($datum));
+ }
+
+ /**
+ * @param string $str
+ * @uses self::write_bytes()
+ */
+ function write_string($str) { $this->write_bytes($str); }
+
+ /**
+ * @param string $bytes
+ */
+ function write_bytes($bytes)
+ {
+ $this->write_long(strlen($bytes));
+ $this->write($bytes);
+ }
+
+ /**
+ * @param string $datum
+ */
+ function write($datum) { $this->io->write($datum); }
+}
+
+/**
+ * Handles schema-specifc reading of data from the decoder.
+ *
+ * Also handles schema resolution between the reader and writer
+ * schemas (if a writer's schema is provided).
+ *
+ * @package Avro
+ */
+class AvroIODatumReader
+{
+ /**
+ *
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ * @returns boolean true if the schemas are consistent with
+ * each other and false otherwise.
+ */
+ static function schemas_match($writers_schema, $readers_schema)
+ {
+ $writers_schema_type = $writers_schema->type;
+ $readers_schema_type = $readers_schema->type;
+
+ if (AvroSchema::UNION_SCHEMA == $writers_schema_type
+ || AvroSchema::UNION_SCHEMA == $readers_schema_type)
+ return true;
+
+ if ($writers_schema_type == $readers_schema_type)
+ {
+ if (AvroSchema::is_primitive_type($writers_schema_type))
+ return true;
+
+ switch ($readers_schema_type)
+ {
+ case AvroSchema::MAP_SCHEMA:
+ return self::attributes_match($writers_schema->values(),
+ $readers_schema->values(),
+ array(AvroSchema::TYPE_ATTR));
+ case AvroSchema::ARRAY_SCHEMA:
+ return self::attributes_match($writers_schema->items(),
+ $readers_schema->items(),
+ array(AvroSchema::TYPE_ATTR));
+ case AvroSchema::ENUM_SCHEMA:
+ return self::attributes_match($writers_schema, $readers_schema,
+ array(AvroSchema::FULLNAME_ATTR));
+ case AvroSchema::FIXED_SCHEMA:
+ return self::attributes_match($writers_schema, $readers_schema,
+ array(AvroSchema::FULLNAME_ATTR,
+ AvroSchema::SIZE_ATTR));
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ return self::attributes_match($writers_schema, $readers_schema,
+ array(AvroSchema::FULLNAME_ATTR));
+ case AvroSchema::REQUEST_SCHEMA:
+ // XXX: This seems wrong
+ return true;
+ // XXX: no default
+ }
+
+ if (AvroSchema::INT_TYPE == $writers_schema_type
+ && in_array($readers_schema_type, array(AvroSchema::LONG_TYPE,
+ AvroSchema::FLOAT_TYPE,
+ AvroSchema::DOUBLE_TYPE)))
+ return true;
+
+ if (AvroSchema::LONG_TYPE == $writers_schema_type
+ && in_array($readers_schema_type, array(AvroSchema::FLOAT_TYPE,
+ AvroSchema::DOUBLE_TYPE)))
+ return true;
+
+ if (AvroSchema::FLOAT_TYPE == $writers_schema_type
+ && AvroSchema::DOUBLE_TYPE == $readers_schema_type)
+ return true;
+
+ return false;
+ }
+
+ }
+
+ /**
+ * Checks equivalence of the given attributes of the two given schemas.
+ *
+ * @param AvroSchema $schema_one
+ * @param AvroSchema $schema_two
+ * @param string[] $attribute_names array of string attribute names to compare
+ *
+ * @returns boolean true if the attributes match and false otherwise.
+ */
+ static function attributes_match($schema_one, $schema_two, $attribute_names)
+ {
+ foreach ($attribute_names as $attribute_name)
+ if ($schema_one->attribute($attribute_name)
+ != $schema_two->attribute($attribute_name))
+ return false;
+ return true;
+ }
+
+ /**
+ * @var AvroSchema
+ */
+ private $writers_schema;
+
+ /**
+ * @var AvroSchema
+ */
+ private $readers_schema;
+
+ /**
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ */
+ function __construct($writers_schema=null, $readers_schema=null)
+ {
+ $this->writers_schema = $writers_schema;
+ $this->readers_schema = $readers_schema;
+ }
+
+ /**
+ * @param AvroSchema $readers_schema
+ */
+ public function set_writers_schema($readers_schema)
+ {
+ $this->writers_schema = $readers_schema;
+ }
+
+ /**
+ * @param AvroIOBinaryDecoder $decoder
+ * @returns string
+ */
+ public function read($decoder)
+ {
+ if (is_null($this->readers_schema))
+ $this->readers_schema = $this->writers_schema;
+ return $this->read_data($this->writers_schema, $this->readers_schema,
+ $decoder);
+ }
+
+ /**#@+
+ * @param AvroSchema $writers_schema
+ * @param AvroSchema $readers_schema
+ * @param AvroIOBinaryDecoder $decoder
+ */
+ /**
+ * @returns mixed
+ */
+ public function read_data($writers_schema, $readers_schema, $decoder)
+ {
+ if (!self::schemas_match($writers_schema, $readers_schema))
+ throw new AvroIOSchemaMatchException($writers_schema, $readers_schema);
+
+ // Schema resolution: reader's schema is a union, writer's schema is not
+ if (AvroSchema::UNION_SCHEMA == $readers_schema->type()
+ && AvroSchema::UNION_SCHEMA != $writers_schema->type())
+ {
+ foreach ($readers_schema->schemas() as $schema)
+ if (self::schemas_match($writers_schema, $schema))
+ return $this->read_data($writers_schema, $schema, $decoder);
+ throw new AvroIOSchemaMatchException($writers_schema, $readers_schema);
+ }
+
+ switch ($writers_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return $decoder->read_null();
+ case AvroSchema::BOOLEAN_TYPE:
+ return $decoder->read_boolean();
+ case AvroSchema::INT_TYPE:
+ return $decoder->read_int();
+ case AvroSchema::LONG_TYPE:
+ return $decoder->read_long();
+ case AvroSchema::FLOAT_TYPE:
+ return $decoder->read_float();
+ case AvroSchema::DOUBLE_TYPE:
+ return $decoder->read_double();
+ case AvroSchema::STRING_TYPE:
+ return $decoder->read_string();
+ case AvroSchema::BYTES_TYPE:
+ return $decoder->read_bytes();
+ case AvroSchema::ARRAY_SCHEMA:
+ return $this->read_array($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::MAP_SCHEMA:
+ return $this->read_map($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::UNION_SCHEMA:
+ return $this->read_union($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::ENUM_SCHEMA:
+ return $this->read_enum($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::FIXED_SCHEMA:
+ return $this->read_fixed($writers_schema, $readers_schema, $decoder);
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ case AvroSchema::REQUEST_SCHEMA:
+ return $this->read_record($writers_schema, $readers_schema, $decoder);
+ default:
+ throw new AvroException(sprintf("Cannot read unknown schema type: %s",
+ $writers_schema->type()));
+ }
+ }
+
+ /**
+ * @returns array
+ */
+ public function read_array($writers_schema, $readers_schema, $decoder)
+ {
+ $items = array();
+ $block_count = $decoder->read_long();
+ while (0 != $block_count)
+ {
+ if ($block_count < 0)
+ {
+ $block_count = -$block_count;
+ $block_size = $decoder->read_long(); // Read (and ignore) block size
+ }
+ for ($i = 0; $i < $block_count; $i++)
+ $items []= $this->read_data($writers_schema->items(),
+ $readers_schema->items(),
+ $decoder);
+ $block_count = $decoder->read_long();
+ }
+ return $items;
+ }
+
+ /**
+ * @returns array
+ */
+ public function read_map($writers_schema, $readers_schema, $decoder)
+ {
+ $items = array();
+ $pair_count = $decoder->read_long();
+ while (0 != $pair_count)
+ {
+ if ($pair_count < 0)
+ {
+ $pair_count = -$pair_count;
+ // Note: we're not doing anything with block_size other than skipping it
+ $block_size = $decoder->read_long();
+ }
+
+ for ($i = 0; $i < $pair_count; $i++)
+ {
+ $key = $decoder->read_string();
+ $items[$key] = $this->read_data($writers_schema->values(),
+ $readers_schema->values(),
+ $decoder);
+ }
+ $pair_count = $decoder->read_long();
+ }
+ return $items;
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function read_union($writers_schema, $readers_schema, $decoder)
+ {
+ $schema_index = $decoder->read_long();
+ $selected_writers_schema = $writers_schema->schema_by_index($schema_index);
+ return $this->read_data($selected_writers_schema, $readers_schema, $decoder);
+ }
+
+ /**
+ * @returns string
+ */
+ public function read_enum($writers_schema, $readers_schema, $decoder)
+ {
+ $symbol_index = $decoder->read_int();
+ $symbol = $writers_schema->symbol_by_index($symbol_index);
+ if (!$readers_schema->has_symbol($symbol))
+ null; // FIXME: unset wrt schema resolution
+ return $symbol;
+ }
+
+ /**
+ * @returns string
+ */
+ public function read_fixed($writers_schema, $readers_schema, $decoder)
+ {
+ return $decoder->read($writers_schema->size());
+ }
+
+ /**
+ * @returns array
+ */
+ public function read_record($writers_schema, $readers_schema, $decoder)
+ {
+ $readers_fields = $readers_schema->fields_hash();
+ $record = array();
+ foreach ($writers_schema->fields() as $writers_field)
+ {
+ $type = $writers_field->type();
+ if (isset($readers_fields[$writers_field->name()]))
+ $record[$writers_field->name()]
+ = $this->read_data($type,
+ $readers_fields[$writers_field->name()]->type(),
+ $decoder);
+ else
+ $this->skip_data($type, $decoder);
+ }
+ // Fill in default values
+ if (count($readers_fields) > count($record))
+ {
+ $writers_fields = $writers_schema->fields_hash();
+ foreach ($readers_fields as $field_name => $field)
+ {
+ if (!isset($writers_fields[$field_name]))
+ {
+ if ($field->has_default_value())
+ $record[$field->name()]
+ = $this->read_default_value($field->type(),
+ $field->default_value());
+ else
+ null; // FIXME: unset
+ }
+ }
+ }
+
+ return $record;
+ }
+ /**#@-*/
+
+ /**
+ * @param AvroSchema $field_schema
+ * @param null|boolean|int|float|string|array $default_value
+ * @returns null|boolean|int|float|string|array
+ *
+ * @throws AvroException if $field_schema type is unknown.
+ */
+ public function read_default_value($field_schema, $default_value)
+ {
+ switch($field_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return null;
+ case AvroSchema::BOOLEAN_TYPE:
+ return $default_value;
+ case AvroSchema::INT_TYPE:
+ case AvroSchema::LONG_TYPE:
+ return (int) $default_value;
+ case AvroSchema::FLOAT_TYPE:
+ case AvroSchema::DOUBLE_TYPE:
+ return (float) $default_value;
+ case AvroSchema::STRING_TYPE:
+ case AvroSchema::BYTES_TYPE:
+ return $default_value;
+ case AvroSchema::ARRAY_SCHEMA:
+ $array = array();
+ foreach ($default_value as $json_val)
+ {
+ $val = $this->read_default_value($field_schema->items(), $json_val);
+ $array []= $val;
+ }
+ return $array;
+ case AvroSchema::MAP_SCHEMA:
+ $map = array();
+ foreach ($default_value as $key => $json_val)
+ $map[$key] = $this->read_default_value($field_schema->values(),
+ $json_val);
+ return $map;
+ case AvroSchema::UNION_SCHEMA:
+ return $this->read_default_value($field_schema->schema_by_index(0),
+ $default_value);
+ case AvroSchema::ENUM_SCHEMA:
+ case AvroSchema::FIXED_SCHEMA:
+ return $default_value;
+ case AvroSchema::RECORD_SCHEMA:
+ $record = array();
+ foreach ($field_schema->fields() as $field)
+ {
+ $field_name = $field->name();
+ if (!$json_val = $default_value[$field_name])
+ $json_val = $field->default_value();
+
+ $record[$field_name] = $this->read_default_value($field->type(),
+ $json_val);
+ }
+ return $record;
+ default:
+ throw new AvroException(sprintf('Unknown type: %s', $field_schema->type()));
+ }
+ }
+
+ /**
+ * @param AvroSchema $writers_schema
+ * @param AvroIOBinaryDecoder $decoder
+ */
+ private function skip_data($writers_schema, $decoder)
+ {
+ switch ($writers_schema->type())
+ {
+ case AvroSchema::NULL_TYPE:
+ return $decoder->skip_null();
+ case AvroSchema::BOOLEAN_TYPE:
+ return $decoder->skip_boolean();
+ case AvroSchema::INT_TYPE:
+ return $decoder->skip_int();
+ case AvroSchema::LONG_TYPE:
+ return $decoder->skip_long();
+ case AvroSchema::FLOAT_TYPE:
+ return $decoder->skip_float();
+ case AvroSchema::DOUBLE_TYPE:
+ return $decoder->skip_double();
+ case AvroSchema::STRING_TYPE:
+ return $decoder->skip_string();
+ case AvroSchema::BYTES_TYPE:
+ return $decoder->skip_bytes();
+ case AvroSchema::ARRAY_SCHEMA:
+ return $decoder->skip_array($writers_schema, $decoder);
+ case AvroSchema::MAP_SCHEMA:
+ return $decoder->skip_map($writers_schema, $decoder);
+ case AvroSchema::UNION_SCHEMA:
+ return $decoder->skip_union($writers_schema, $decoder);
+ case AvroSchema::ENUM_SCHEMA:
+ return $decoder->skip_enum($writers_schema, $decoder);
+ case AvroSchema::FIXED_SCHEMA:
+ return $decoder->skip_fixed($writers_schema, $decoder);
+ case AvroSchema::RECORD_SCHEMA:
+ case AvroSchema::ERROR_SCHEMA:
+ case AvroSchema::REQUEST_SCHEMA:
+ return $decoder->skip_record($writers_schema, $decoder);
+ default:
+ throw new AvroException(sprintf('Uknown schema type: %s',
+ $writers_schema->type()));
+ }
+ }
+}
+
+/**
+ * Decodes and reads Avro data from an AvroIO object encoded using
+ * Avro binary encoding.
+ *
+ * @package Avro
+ */
+class AvroIOBinaryDecoder
+{
+
+ /**
+ * @param int[] array of byte ascii values
+ * @returns long decoded value
+ * @internal Requires 64-bit platform
+ */
+ public static function decode_long_from_array($bytes)
+ {
+ $b = array_shift($bytes);
+ $n = $b & 0x7f;
+ $shift = 7;
+ while (0 != ($b & 0x80))
+ {
+ $b = array_shift($bytes);
+ $n |= (($b & 0x7f) << $shift);
+ $shift += 7;
+ }
+ return (($n >> 1) ^ -($n & 1));
+ }
+
+ /**
+ * Performs decoding of the binary string to a float value.
+ *
+ * XXX: This is <b>not</b> endian-aware! See comments in
+ * {@link AvroIOBinaryEncoder::float_to_int_bits()} for details.
+ *
+ * @param string $bits
+ * @returns float
+ */
+ static public function int_bits_to_float($bits)
+ {
+ $float = unpack('f', $bits);
+ return (float) $float[1];
+ }
+
+ /**
+ * Performs decoding of the binary string to a double value.
+ *
+ * XXX: This is <b>not</b> endian-aware! See comments in
+ * {@link AvroIOBinaryEncoder::float_to_int_bits()} for details.
+ *
+ * @param string $bits
+ * @returns float
+ */
+ static public function long_bits_to_double($bits)
+ {
+ $double = unpack('d', $bits);
+ return (double) $double[1];
+ }
+
+ /**
+ * @var AvroIO
+ */
+ private $io;
+
+ /**
+ * @param AvroIO $io object from which to read.
+ */
+ public function __construct($io)
+ {
+ Avro::check_platform();
+ $this->io = $io;
+ }
+
+ /**
+ * @returns string the next byte from $this->io.
+ * @throws AvroException if the next byte cannot be read.
+ */
+ private function next_byte() { return $this->read(1); }
+
+ /**
+ * @returns null
+ */
+ public function read_null() { return null; }
+
+ /**
+ * @returns boolean
+ */
+ public function read_boolean()
+ {
+ return (boolean) (1 == ord($this->next_byte()));
+ }
+
+ /**
+ * @returns int
+ */
+ public function read_int() { return (int) $this->read_long(); }
+
+ /**
+ * @returns long
+ */
+ public function read_long()
+ {
+ $byte = ord($this->next_byte());
+ $bytes = array($byte);
+ while (0 != ($byte & 0x80))
+ {
+ $byte = ord($this->next_byte());
+ $bytes []= $byte;
+ }
+
+ if (Avro::uses_gmp())
+ return AvroGMP::decode_long_from_array($bytes);
+
+ return self::decode_long_from_array($bytes);
+ }
+
+ /**
+ * @returns float
+ */
+ public function read_float()
+ {
+ return self::int_bits_to_float($this->read(4));
+ }
+
+ /**
+ * @returns double
+ */
+ public function read_double()
+ {
+ return self::long_bits_to_double($this->read(8));
+ }
+
+ /**
+ * A string is encoded as a long followed by that many bytes
+ * of UTF-8 encoded character data.
+ * @returns string
+ */
+ public function read_string() { return $this->read_bytes(); }
+
+ /**
+ * @returns string
+ */
+ public function read_bytes() { return $this->read($this->read_long()); }
+
+ /**
+ * @param int $len count of bytes to read
+ * @returns string
+ */
+ public function read($len) { return $this->io->read($len); }
+
+ public function skip_null() { return null; }
+
+ public function skip_boolean() { return $this->skip(1); }
+
+ public function skip_int() { return $this->skip_long(); }
+
+ protected function skip_long()
+ {
+ $b = $this->next_byte();
+ while (0 != ($b & 0x80))
+ $b = $this->next_byte();
+ }
+
+ public function skip_float() { return $this->skip(4); }
+
+ public function skip_double() { return $this->skip(8); }
+
+ public function skip_bytes() { return $this->skip($this->read_long()); }
+
+ public function skip_string() { return $this->skip_bytes(); }
+
+ /**
+ * @param int $len count of bytes to skip
+ * @uses AvroIO::seek()
+ */
+ public function skip($len) { $this->seek($len, AvroIO::SEEK_CUR); }
+
+ /**
+ * @returns int position of pointer in AvroIO instance
+ * @uses AvroIO::tell()
+ */
+ private function tell() { return $this->io->tell(); }
+
+ /**
+ * @param int $offset
+ * @param int $whence
+ * @returns boolean true upon success
+ * @uses AvroIO::seek()
+ */
+ private function seek($offset, $whence)
+ {
+ return $this->io->seek($offset, $whence);
+ }
+}
+
diff --git a/vendor/wikimedia/avro/lib/avro/debug.php b/vendor/wikimedia/avro/lib/avro/debug.php
new file mode 100644
index 00000000..2278f19b
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/debug.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Avro library code debugging functions
+ * @package Avro
+ */
+class AvroDebug
+{
+
+ /**
+ * @var int high debug level
+ */
+ const DEBUG5 = 5;
+ /**
+ * @var int low debug level
+ */
+ const DEBUG1 = 1;
+ /**
+ * @var int current debug level
+ */
+ const DEBUG_LEVEL = self::DEBUG1;
+
+ /**
+ * @var int $debug_level
+ * @returns boolean true if the given $debug_level is equivalent
+ * or more verbose than than the current debug level
+ * and false otherwise.
+ */
+ static function is_debug($debug_level=self::DEBUG1)
+ {
+ return (self::DEBUG_LEVEL >= $debug_level);
+ }
+
+ /**
+ * @param string $format format string for the given arguments. Passed as is
+ * to <code>vprintf</code>.
+ * @param array $args array of arguments to pass to vsprinf.
+ * @param int $debug_level debug level at which to print this statement
+ * @returns boolean true
+ */
+ static function debug($format, $args, $debug_level=self::DEBUG1)
+ {
+ if (self::is_debug($debug_level))
+ vprintf($format . "\n", $args);
+ return true;
+ }
+
+ /**
+ * @param string $str
+ * @returns string[] array of hex representation of each byte of $str
+ */
+ static function hex_array($str) { return self::bytes_array($str); }
+
+ /**
+ * @param string $str
+ * @param string $joiner string used to join
+ * @returns string hex-represented bytes of each byte of $str
+ joined by $joiner
+ */
+ static function hex_string($str, $joiner=' ')
+ {
+ return join($joiner, self::hex_array($str));
+ }
+
+ /**
+ * @param string $str
+ * @param string $format format to represent bytes
+ * @returns string[] array of each byte of $str formatted using $format
+ */
+ static function bytes_array($str, $format='x%02x')
+ {
+ $x = array();
+ foreach (str_split($str) as $b)
+ $x []= sprintf($format, ord($b));
+ return $x;
+ }
+
+ /**
+ * @param string $str
+ * @returns string[] array of bytes of $str represented in decimal format ('%3d')
+ */
+ static function dec_array($str) { return self::bytes_array($str, '%3d'); }
+
+ /**
+ * @param string $str
+ * @param string $joiner string to join bytes of $str
+ * @returns string of bytes of $str represented in decimal format
+ * @uses dec_array()
+ */
+ static function dec_string($str, $joiner = ' ')
+ {
+ return join($joiner, self::dec_array($str));
+ }
+
+ /**
+ * @param string $str
+ * @param string $format one of 'ctrl', 'hex', or 'dec' for control,
+ hexadecimal, or decimal format for bytes.
+ - ctrl: ASCII control characters represented as text.
+ For example, the null byte is represented as 'NUL'.
+ Visible ASCII characters represent themselves, and
+ others are represented as a decimal ('%03d')
+ - hex: bytes represented in hexadecimal ('%02X')
+ - dec: bytes represented in decimal ('%03d')
+ * @returns string[] array of bytes represented in the given format.
+ */
+ static function ascii_array($str, $format='ctrl')
+ {
+ if (!in_array($format, array('ctrl', 'hex', 'dec')))
+ throw new AvroException('Unrecognized format specifier');
+
+ $ctrl_chars = array('NUL', 'SOH', 'STX', 'ETX', 'EOT', 'ENQ', 'ACK', 'BEL',
+ 'BS', 'HT', 'LF', 'VT', 'FF', 'CR', 'SO', 'SI',
+ 'DLE', 'DC1', 'DC2', 'DC3', 'DC4', 'NAK', 'SYN', 'ETB',
+ 'CAN', 'EM', 'SUB', 'ESC', 'FS', 'GS', 'RS', 'US');
+ $x = array();
+ foreach (str_split($str) as $b)
+ {
+ $db = ord($b);
+ if ($db < 32)
+ {
+ switch ($format)
+ {
+ case 'ctrl':
+ $x []= str_pad($ctrl_chars[$db], 3, ' ', STR_PAD_LEFT);
+ break;
+ case 'hex':
+ $x []= sprintf("x%02X", $db);
+ break;
+ case 'dec':
+ $x []= str_pad($db, 3, '0', STR_PAD_LEFT);
+ break;
+ }
+ }
+ else if ($db < 127)
+ $x []= " $b";
+ else if ($db == 127)
+ {
+ switch ($format)
+ {
+ case 'ctrl':
+ $x []= 'DEL';
+ break;
+ case 'hex':
+ $x []= sprintf("x%02X", $db);
+ break;
+ case 'dec':
+ $x []= str_pad($db, 3, '0', STR_PAD_LEFT);
+ break;
+ }
+ }
+ else
+ if ('hex' == $format)
+ $x []= sprintf("x%02X", $db);
+ else
+ $x []= str_pad($db, 3, '0', STR_PAD_LEFT);
+ }
+ return $x;
+ }
+
+ /**
+ * @param string $str
+ * @param string $format one of 'ctrl', 'hex', or 'dec'.
+ * See {@link self::ascii_array()} for more description
+ * @param string $joiner
+ * @returns string of bytes joined by $joiner
+ * @uses ascii_array()
+ */
+ static function ascii_string($str, $format='ctrl', $joiner = ' ')
+ {
+ return join($joiner, self::ascii_array($str, $format));
+ }
+}
diff --git a/vendor/wikimedia/avro/lib/avro/gmp.php b/vendor/wikimedia/avro/lib/avro/gmp.php
new file mode 100644
index 00000000..3d41d034
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/gmp.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Methods for handling 64-bit operations using the GMP extension.
+ *
+ * This is a naive and hackish implementation that is intended
+ * to work well enough to support Avro. It has not been tested
+ * beyond what's needed to decode and encode long values.
+ *
+ * @package Avro
+ */
+class AvroGMP {
+
+ /**
+ * @var resource memoized GMP resource for zero
+ */
+ private static $gmp_0;
+
+ /**
+ * @returns resource GMP resource for zero
+ */
+ private static function gmp_0()
+ {
+ if (!isset(self::$gmp_0))
+ self::$gmp_0 = gmp_init('0');
+ return self::$gmp_0;
+ }
+
+ /**
+ * @var resource memoized GMP resource for one (1)
+ */
+ private static $gmp_1;
+
+ /**
+ * @returns resource GMP resource for one (1)
+ */
+ private static function gmp_1()
+ {
+ if (!isset(self::$gmp_1))
+ self::$gmp_1 = gmp_init('1');
+ return self::$gmp_1;
+ }
+
+ /**
+ * @var resource memoized GMP resource for two (2)
+ */
+ private static $gmp_2;
+
+ /**
+ * @returns resource GMP resource for two (2)
+ */
+ private static function gmp_2()
+ {
+ if (!isset(self::$gmp_2))
+ self::$gmp_2 = gmp_init('2');
+ return self::$gmp_2;
+ }
+
+ /**
+ * @var resource memoized GMP resource for 0x7f
+ */
+ private static $gmp_0x7f;
+
+ /**
+ * @returns resource GMP resource for 0x7f
+ */
+ private static function gmp_0x7f()
+ {
+ if (!isset(self::$gmp_0x7f))
+ self::$gmp_0x7f = gmp_init('0x7f');
+ return self::$gmp_0x7f;
+ }
+
+ /**
+ * @var resource memoized GMP resource for 64-bit ~0x7f
+ */
+ private static $gmp_n0x7f;
+
+ /**
+ * @returns resource GMP resource for 64-bit ~0x7f
+ */
+ private static function gmp_n0x7f()
+ {
+ if (!isset(self::$gmp_n0x7f))
+ self::$gmp_n0x7f = gmp_init('0xffffffffffffff80');
+ return self::$gmp_n0x7f;
+ }
+
+ /**
+ * @var resource memoized GMP resource for 64-bits of 1
+ */
+ private static $gmp_0xfs;
+
+ /**
+ * @returns resource GMP resource for 64-bits of 1
+ */
+ private static function gmp_0xfs()
+ {
+ if (!isset(self::$gmp_0xfs))
+ self::$gmp_0xfs = gmp_init('0xffffffffffffffff');
+ return self::$gmp_0xfs;
+ }
+
+ /**
+ * @param GMP resource
+ * @returns GMP resource 64-bit two's complement of input.
+ */
+ static function gmp_twos_complement($g)
+ {
+ return gmp_neg(gmp_sub(gmp_pow(self::gmp_2(), 64), $g));
+ }
+
+ /**
+ * @interal Only works up to shift 63 (doesn't wrap bits around).
+ * @param resource|int|string $g
+ * @param int $shift number of bits to shift left
+ * @returns resource $g shifted left
+ */
+ static function shift_left($g, $shift)
+ {
+ if (0 == $shift)
+ return $g;
+
+ if (0 > gmp_sign($g))
+ $g = self::gmp_twos_complement($g);
+
+ $m = gmp_mul($g, gmp_pow(self::gmp_2(), $shift));
+ $m = gmp_and($m, self::gmp_0xfs());
+ if (gmp_testbit($m, 63))
+ $m = gmp_neg(gmp_add(gmp_and(gmp_com($m), self::gmp_0xfs()),
+ self::gmp_1()));
+ return $m;
+ }
+
+ /**
+ * Arithmetic right shift
+ * @param resource|int|string $g
+ * @param int $shift number of bits to shift right
+ * @returns resource $g shifted right $shift bits
+ */
+ static function shift_right($g, $shift)
+ {
+ if (0 == $shift)
+ return $g;
+
+ if (0 <= gmp_sign($g))
+ $m = gmp_div($g, gmp_pow(self::gmp_2(), $shift));
+ else // negative
+ {
+ $g = gmp_and($g, self::gmp_0xfs());
+ $m = gmp_div($g, gmp_pow(self::gmp_2(), $shift));
+ $m = gmp_and($m, self::gmp_0xfs());
+ for ($i = 63; $i >= (63 - $shift); $i--)
+ gmp_setbit($m, $i);
+
+ $m = gmp_neg(gmp_add(gmp_and(gmp_com($m), self::gmp_0xfs()),
+ self::gmp_1()));
+ }
+
+ return $m;
+ }
+
+ /**
+ * @param int|str $n integer (or string representation of integer) to encode
+ * @return string $bytes of the long $n encoded per the Avro spec
+ */
+ static function encode_long($n)
+ {
+ $g = gmp_init($n);
+ $g = gmp_xor(self::shift_left($g, 1),
+ self::shift_right($g, 63));
+ $bytes = '';
+ while (0 != gmp_cmp(self::gmp_0(), gmp_and($g, self::gmp_n0x7f())))
+ {
+ $bytes .= chr(gmp_intval(gmp_and($g, self::gmp_0x7f())) | 0x80);
+ $g = self::shift_right($g, 7);
+ }
+ $bytes .= chr(gmp_intval($g));
+ return $bytes;
+ }
+
+ /**
+ * @param int[] $bytes array of ascii codes of bytes to decode
+ * @return string represenation of decoded long.
+ */
+ static function decode_long_from_array($bytes)
+ {
+ $b = array_shift($bytes);
+ $g = gmp_init($b & 0x7f);
+ $shift = 7;
+ while (0 != ($b & 0x80))
+ {
+ $b = array_shift($bytes);
+ $g = gmp_or($g, self::shift_left(($b & 0x7f), $shift));
+ $shift += 7;
+ }
+ $val = gmp_xor(self::shift_right($g, 1), gmp_neg(gmp_and($g, 1)));
+ return gmp_strval($val);
+ }
+
+}
diff --git a/vendor/wikimedia/avro/lib/avro/io.php b/vendor/wikimedia/avro/lib/avro/io.php
new file mode 100644
index 00000000..239e53d8
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/io.php
@@ -0,0 +1,494 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Avro IO object classes
+ * @package Avro
+ */
+
+/**
+ * Exceptions associated with AvroIO instances.
+ * @package Avro
+ */
+class AvroIOException extends AvroException {}
+
+/**
+ * Barebones IO base class to provide common interface for file and string
+ * access within the Avro classes.
+ *
+ * @package Avro
+ */
+class AvroIO
+{
+
+ /**
+ * @var string general read mode
+ */
+ const READ_MODE = 'r';
+ /**
+ * @var string general write mode.
+ */
+ const WRITE_MODE = 'w';
+
+ /**
+ * @var int set position equal to $offset bytes
+ */
+ const SEEK_CUR = SEEK_CUR;
+ /**
+ * @var int set position to current index + $offset bytes
+ */
+ const SEEK_SET = SEEK_SET;
+ /**
+ * @var int set position to end of file + $offset bytes
+ */
+ const SEEK_END = SEEK_END;
+
+ /**
+ * Read $len bytes from AvroIO instance
+ * @var int $len
+ * @return string bytes read
+ */
+ public function read($len)
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Append bytes to this buffer. (Nothing more is needed to support Avro.)
+ * @param str $arg bytes to write
+ * @returns int count of bytes written.
+ * @throws AvroIOException if $args is not a string value.
+ */
+ public function write($arg)
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Return byte offset within AvroIO instance
+ * @return int
+ */
+ public function tell()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Set the position indicator. The new position, measured in bytes
+ * from the beginning of the file, is obtained by adding $offset to
+ * the position specified by $whence.
+ *
+ * @param int $offset
+ * @param int $whence one of AvroIO::SEEK_SET, AvroIO::SEEK_CUR,
+ * or Avro::SEEK_END
+ * @returns boolean true
+ *
+ * @throws AvroIOException
+ */
+ public function seek($offset, $whence=self::SEEK_SET)
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Flushes any buffered data to the AvroIO object.
+ * @returns boolean true upon success.
+ */
+ public function flush()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Returns whether or not the current position at the end of this AvroIO
+ * instance.
+ *
+ * Note is_eof() is <b>not</b> like eof in C or feof in PHP:
+ * it returns TRUE if the *next* read would be end of file,
+ * rather than if the *most recent* read read end of file.
+ * @returns boolean true if at the end of file, and false otherwise
+ */
+ public function is_eof()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+ /**
+ * Closes this AvroIO instance.
+ */
+ public function close()
+ {
+ throw new AvroNotImplementedException('Not implemented');
+ }
+
+}
+
+/**
+ * AvroIO wrapper for string access
+ * @package Avro
+ */
+class AvroStringIO extends AvroIO
+{
+ /**
+ * @var string
+ */
+ private $string_buffer;
+ /**
+ * @var int current position in string
+ */
+ private $current_index;
+ /**
+ * @var boolean whether or not the string is closed.
+ */
+ private $is_closed;
+
+ /**
+ * @param string $str initial value of AvroStringIO buffer. Regardless
+ * of the initial value, the pointer is set to the
+ * beginning of the buffer.
+ * @throws AvroIOException if a non-string value is passed as $str
+ */
+ public function __construct($str = '')
+ {
+ $this->is_closed = false;
+ $this->string_buffer = '';
+ $this->current_index = 0;
+
+ if (is_string($str))
+ $this->string_buffer .= $str;
+ else
+ throw new AvroIOException(
+ sprintf('constructor argument must be a string: %s', gettype($str)));
+ }
+
+ /**
+ * Append bytes to this buffer.
+ * (Nothing more is needed to support Avro.)
+ * @param str $arg bytes to write
+ * @returns int count of bytes written.
+ * @throws AvroIOException if $args is not a string value.
+ */
+ public function write($arg)
+ {
+ $this->check_closed();
+ if (is_string($arg))
+ return $this->append_str($arg);
+ throw new AvroIOException(
+ sprintf('write argument must be a string: (%s) %s',
+ gettype($arg), var_export($arg, true)));
+ }
+
+ /**
+ * @returns string bytes read from buffer
+ * @todo test for fencepost errors wrt updating current_index
+ */
+ public function read($len)
+ {
+ $this->check_closed();
+ $read='';
+ for($i=$this->current_index; $i<($this->current_index+$len); $i++)
+ $read .= $this->string_buffer[$i];
+ if (strlen($read) < $len)
+ $this->current_index = $this->length();
+ else
+ $this->current_index += $len;
+ return $read;
+ }
+
+ /**
+ * @returns boolean true if successful
+ * @throws AvroIOException if the seek failed.
+ */
+ public function seek($offset, $whence=self::SEEK_SET)
+ {
+ if (!is_int($offset))
+ throw new AvroIOException('Seek offset must be an integer.');
+ // Prevent seeking before BOF
+ switch ($whence)
+ {
+ case self::SEEK_SET:
+ if (0 > $offset)
+ throw new AvroIOException('Cannot seek before beginning of file.');
+ $this->current_index = $offset;
+ break;
+ case self::SEEK_CUR:
+ if (0 > $this->current_index + $whence)
+ throw new AvroIOException('Cannot seek before beginning of file.');
+ $this->current_index += $offset;
+ break;
+ case self::SEEK_END:
+ if (0 > $this->length() + $offset)
+ throw new AvroIOException('Cannot seek before beginning of file.');
+ $this->current_index = $this->length() + $offset;
+ break;
+ default:
+ throw new AvroIOException(sprintf('Invalid seek whence %d', $whence));
+ }
+
+ return true;
+ }
+
+ /**
+ * @returns int
+ * @see AvroIO::tell()
+ */
+ public function tell() { return $this->current_index; }
+
+ /**
+ * @returns boolean
+ * @see AvroIO::is_eof()
+ */
+ public function is_eof()
+ {
+ return ($this->current_index >= $this->length());
+ }
+
+ /**
+ * No-op provided for compatibility with AvroIO interface.
+ * @returns boolean true
+ */
+ public function flush() { return true; }
+
+ /**
+ * Marks this buffer as closed.
+ * @returns boolean true
+ */
+ public function close()
+ {
+ $this->check_closed();
+ $this->is_closed = true;
+ return true;
+ }
+
+ /**
+ * @throws AvroIOException if the buffer is closed.
+ */
+ private function check_closed()
+ {
+ if ($this->is_closed())
+ throw new AvroIOException('Buffer is closed');
+ }
+
+ /**
+ * Appends bytes to this buffer.
+ * @param string $str
+ * @returns integer count of bytes written.
+ */
+ private function append_str($str)
+ {
+ $this->check_closed();
+ $this->string_buffer .= $str;
+ $len = strlen($str);
+ $this->current_index += $len;
+ return $len;
+ }
+
+ /**
+ * Truncates the truncate buffer to 0 bytes and returns the pointer
+ * to the beginning of the buffer.
+ * @returns boolean true
+ */
+ public function truncate()
+ {
+ $this->check_closed();
+ $this->string_buffer = '';
+ $this->current_index = 0;
+ return true;
+ }
+
+ /**
+ * @returns int count of bytes in the buffer
+ * @internal Could probably memoize length for performance, but
+ * no need do this yet.
+ */
+ public function length() { return strlen($this->string_buffer); }
+
+ /**
+ * @returns string
+ */
+ public function __toString() { return $this->string_buffer; }
+
+
+ /**
+ * @returns string
+ * @uses self::__toString()
+ */
+ public function string() { return $this->__toString(); }
+
+ /**
+ * @returns boolean true if this buffer is closed and false
+ * otherwise.
+ */
+ public function is_closed() { return $this->is_closed; }
+}
+
+/**
+ * AvroIO wrapper for PHP file access functions
+ * @package Avro
+ */
+class AvroFile extends AvroIO
+{
+ /**
+ * @var string fopen read mode value. Used internally.
+ */
+ const FOPEN_READ_MODE = 'rb';
+
+ /**
+ * @var string fopen write mode value. Used internally.
+ */
+ const FOPEN_WRITE_MODE = 'wb';
+
+ /**
+ * @var string
+ */
+ private $file_path;
+
+ /**
+ * @var resource file handle for AvroFile instance
+ */
+ private $file_handle;
+
+ public function __construct($file_path, $mode = self::READ_MODE)
+ {
+ /**
+ * XXX: should we check for file existence (in case of reading)
+ * or anything else about the provided file_path argument?
+ */
+ $this->file_path = $file_path;
+ switch ($mode)
+ {
+ case self::WRITE_MODE:
+ $this->file_handle = fopen($this->file_path, self::FOPEN_WRITE_MODE);
+ if (false == $this->file_handle)
+ throw new AvroIOException('Could not open file for writing');
+ break;
+ case self::READ_MODE:
+ $this->file_handle = fopen($this->file_path, self::FOPEN_READ_MODE);
+ if (false == $this->file_handle)
+ throw new AvroIOException('Could not open file for reading');
+ break;
+ default:
+ throw new AvroIOException(
+ sprintf("Only modes '%s' and '%s' allowed. You provided '%s'.",
+ self::READ_MODE, self::WRITE_MODE, $mode));
+ }
+ }
+
+ /**
+ * @returns int count of bytes written
+ * @throws AvroIOException if write failed.
+ */
+ public function write($str)
+ {
+ $len = fwrite($this->file_handle, $str);
+ if (false === $len)
+ throw new AvroIOException(sprintf('Could not write to file'));
+ return $len;
+ }
+
+ /**
+ * @param int $len count of bytes to read.
+ * @returns string bytes read
+ * @throws AvroIOException if length value is negative or if the read failed
+ */
+ public function read($len)
+ {
+ if (0 > $len)
+ throw new AvroIOException(
+ sprintf("Invalid length value passed to read: %d", $len));
+
+ if (0 == $len)
+ return '';
+
+ $bytes = fread($this->file_handle, $len);
+ if (false === $bytes)
+ throw new AvroIOException('Could not read from file');
+ return $bytes;
+ }
+
+ /**
+ * @returns int current position within the file
+ * @throws AvroFileExcpetion if tell failed.
+ */
+ public function tell()
+ {
+ $position = ftell($this->file_handle);
+ if (false === $position)
+ throw new AvroIOException('Could not execute tell on reader');
+ return $position;
+ }
+
+ /**
+ * @param int $offset
+ * @param int $whence
+ * @returns boolean true upon success
+ * @throws AvroIOException if seek failed.
+ * @see AvroIO::seek()
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ $res = fseek($this->file_handle, $offset, $whence);
+ // Note: does not catch seeking beyond end of file
+ if (-1 === $res)
+ throw new AvroIOException(
+ sprintf("Could not execute seek (offset = %d, whence = %d)",
+ $offset, $whence));
+ return true;
+ }
+
+ /**
+ * Closes the file.
+ * @returns boolean true if successful.
+ * @throws AvroIOException if there was an error closing the file.
+ */
+ public function close()
+ {
+ $res = fclose($this->file_handle);
+ if (false === $res)
+ throw new AvroIOException('Error closing file.');
+ return $res;
+ }
+
+ /**
+ * @returns boolean true if the pointer is at the end of the file,
+ * and false otherwise.
+ * @see AvroIO::is_eof() as behavior differs from feof()
+ */
+ public function is_eof()
+ {
+ $this->read(1);
+ if (feof($this->file_handle))
+ return true;
+ $this->seek(-1, self::SEEK_CUR);
+ return false;
+ }
+
+ /**
+ * @returns boolean true if the flush was successful.
+ * @throws AvroIOException if there was an error flushing the file.
+ */
+ public function flush()
+ {
+ $res = fflush($this->file_handle);
+ if (false === $res)
+ throw new AvroIOException('Could not flush file.');
+ return true;
+ }
+
+}
diff --git a/vendor/wikimedia/avro/lib/avro/protocol.php b/vendor/wikimedia/avro/lib/avro/protocol.php
new file mode 100644
index 00000000..a558e66b
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/protocol.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Avro library for protocols
+ * @package Avro
+ */
+class AvroProtocol
+{
+ public $name;
+ public $namespace;
+ public $schemata;
+
+ public static function parse($json)
+ {
+ if (is_null($json))
+ throw new AvroProtocolParseException( "Protocol can't be null");
+
+ $protocol = new AvroProtocol();
+ $protocol->real_parse(json_decode($json, true));
+ return $protocol;
+ }
+
+ function real_parse($avro) {
+ $this->protocol = $avro["protocol"];
+ $this->namespace = $avro["namespace"];
+ $this->schemata = new AvroNamedSchemata();
+ $this->name = $avro["protocol"];
+
+ if (!is_null($avro["types"])) {
+ $types = AvroSchema::real_parse($avro["types"], $this->namespace, $this->schemata);
+ }
+
+ if (!is_null($avro["messages"])) {
+ foreach ($avro["messages"] as $messageName => $messageAvro) {
+ $message = new AvroProtocolMessage($messageName, $messageAvro, $this);
+ $this->messages{$messageName} = $message;
+ }
+ }
+ }
+}
+
+class AvroProtocolMessage
+{
+ /**
+ * @var AvroRecordSchema $request
+ */
+
+ public $request;
+
+ public $response;
+
+ public function __construct($name, $avro, $protocol)
+ {
+ $this->name = $name;
+ $this->request = new AvroRecordSchema(new AvroName($name, null, $protocol->namespace), null, $avro{'request'}, $protocol->schemata, AvroSchema::REQUEST_SCHEMA);
+
+ if (array_key_exists('response', $avro)) {
+ $this->response = $protocol->schemata->schema_by_name(new AvroName($avro{'response'}, $protocol->namespace, $protocol->namespace));
+ if ($this->response == null)
+ $this->response = new AvroPrimitiveSchema($avro{'response'});
+ }
+ }
+}
+
+class AvroProtocolParseException extends AvroException {};
diff --git a/vendor/wikimedia/avro/lib/avro/schema.php b/vendor/wikimedia/avro/lib/avro/schema.php
new file mode 100644
index 00000000..3d7fbbb8
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/schema.php
@@ -0,0 +1,1457 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Avro Schema and and Avro Schema support classes.
+ * @package Avro
+ */
+
+/** TODO
+ * - ARRAY have only type and item attributes (what about metadata?)
+ * - MAP keys are (assumed?) to be strings
+ * - FIXED size must be integer (must be positive? less than MAXINT?)
+ * - primitive type names cannot have a namespace (so throw an error? or ignore?)
+ * - schema may contain multiple definitions of a named schema
+ * if definitions are equivalent (?)
+ * - Cleanup default namespace and named schemata handling.
+ * - For one, it appears to be *too* global. According to the spec,
+ * we should only be referencing schemas that are named within the
+ * *enclosing* schema, so those in sibling schemas (say, unions or fields)
+ * shouldn't be referenced, if I understand the spec correctly.
+ * - Also, if a named schema is defined more than once in the same schema,
+ * it must have the same definition: so it appears we *do* need to keep
+ * track of named schemata globally as well. (And does this play well
+ * with the requirements regarding enclosing schema?
+ * - default values for bytes and fixed fields are JSON strings,
+ * where unicode code points 0-255 are mapped to unsigned 8-bit byte values 0-255
+ * - make sure other default values for other schema are of appropriate type
+ * - Should AvroField really be an AvroSchema object? Avro Fields have a name
+ * attribute, but not a namespace attribute (and the name can't be namespace
+ * qualified). It also has additional attributes such as doc, which named schemas
+ * enum and record have (though not fixed schemas, which also have names), and
+ * fields also have default and order attributes, shared by no other schema type.
+ */
+
+/**
+ * Exceptions associated with parsing JSON schema represenations
+ * @package Avro
+ */
+class AvroSchemaParseException extends AvroException {};
+
+/**
+ * @package Avro
+ */
+class AvroSchema
+{
+ /**
+ * @var int lower bound of integer values: -(1 << 31)
+ */
+ const INT_MIN_VALUE = -2147483648;
+
+ /**
+ * @var int upper bound of integer values: (1 << 31) - 1
+ */
+ const INT_MAX_VALUE = 2147483647;
+
+ /**
+ * @var long lower bound of long values: -(1 << 63)
+ */
+ const LONG_MIN_VALUE = -9223372036854775808;
+
+ /**
+ * @var long upper bound of long values: (1 << 63) - 1
+ */
+ const LONG_MAX_VALUE = 9223372036854775807;
+
+ /**
+ * @var string null schema type name
+ */
+ const NULL_TYPE = 'null';
+
+ /**
+ * @var string boolean schema type name
+ */
+ const BOOLEAN_TYPE = 'boolean';
+
+ /**
+ * int schema type value is a 32-bit signed int
+ * @var string int schema type name.
+ */
+ const INT_TYPE = 'int';
+
+ /**
+ * long schema type value is a 64-bit signed int
+ * @var string long schema type name
+ */
+ const LONG_TYPE = 'long';
+
+ /**
+ * float schema type value is a 32-bit IEEE 754 floating-point number
+ * @var string float schema type name
+ */
+ const FLOAT_TYPE = 'float';
+
+ /**
+ * double schema type value is a 64-bit IEEE 754 floating-point number
+ * @var string double schema type name
+ */
+ const DOUBLE_TYPE = 'double';
+
+ /**
+ * string schema type value is a Unicode character sequence
+ * @var string string schema type name
+ */
+ const STRING_TYPE = 'string';
+
+ /**
+ * bytes schema type value is a sequence of 8-bit unsigned bytes
+ * @var string bytes schema type name
+ */
+ const BYTES_TYPE = 'bytes';
+
+ // Complex Types
+ // Unnamed Schema
+ /**
+ * @var string array schema type name
+ */
+ const ARRAY_SCHEMA = 'array';
+
+ /**
+ * @var string map schema type name
+ */
+ const MAP_SCHEMA = 'map';
+
+ /**
+ * @var string union schema type name
+ */
+ const UNION_SCHEMA = 'union';
+
+ /**
+ * Unions of error schemas are used by Avro messages
+ * @var string error_union schema type name
+ */
+ const ERROR_UNION_SCHEMA = 'error_union';
+
+ // Named Schema
+
+ /**
+ * @var string enum schema type name
+ */
+ const ENUM_SCHEMA = 'enum';
+
+ /**
+ * @var string fixed schema type name
+ */
+ const FIXED_SCHEMA = 'fixed';
+
+ /**
+ * @var string record schema type name
+ */
+ const RECORD_SCHEMA = 'record';
+ // Other Schema
+
+ /**
+ * @var string error schema type name
+ */
+ const ERROR_SCHEMA = 'error';
+
+ /**
+ * @var string request schema type name
+ */
+ const REQUEST_SCHEMA = 'request';
+
+
+ // Schema attribute names
+ /**
+ * @var string schema type name attribute name
+ */
+ const TYPE_ATTR = 'type';
+
+ /**
+ * @var string named schema name attribute name
+ */
+ const NAME_ATTR = 'name';
+
+ /**
+ * @var string named schema namespace attribute name
+ */
+ const NAMESPACE_ATTR = 'namespace';
+
+ /**
+ * @var string derived attribute: doesn't appear in schema
+ */
+ const FULLNAME_ATTR = 'fullname';
+
+ /**
+ * @var string array schema size attribute name
+ */
+ const SIZE_ATTR = 'size';
+
+ /**
+ * @var string record fields attribute name
+ */
+ const FIELDS_ATTR = 'fields';
+
+ /**
+ * @var string array schema items attribute name
+ */
+ const ITEMS_ATTR = 'items';
+
+ /**
+ * @var string enum schema symbols attribute name
+ */
+ const SYMBOLS_ATTR = 'symbols';
+
+ /**
+ * @var string map schema values attribute name
+ */
+ const VALUES_ATTR = 'values';
+
+ /**
+ * @var string document string attribute name
+ */
+ const DOC_ATTR = 'doc';
+
+ /**
+ * @var array list of primitive schema type names
+ */
+ private static $primitive_types = array(self::NULL_TYPE, self::BOOLEAN_TYPE,
+ self::STRING_TYPE, self::BYTES_TYPE,
+ self::INT_TYPE, self::LONG_TYPE,
+ self::FLOAT_TYPE, self::DOUBLE_TYPE);
+
+ /**
+ * @var array list of named schema type names
+ */
+ private static $named_types = array(self::FIXED_SCHEMA, self::ENUM_SCHEMA,
+ self::RECORD_SCHEMA, self::ERROR_SCHEMA);
+
+ /**
+ * @param string $type a schema type name
+ * @returns boolean true if the given type name is a named schema type name
+ * and false otherwise.
+ */
+ public static function is_named_type($type)
+ {
+ return in_array($type, self::$named_types);
+ }
+
+ /**
+ * @param string $type a schema type name
+ * @returns boolean true if the given type name is a primitive schema type
+ * name and false otherwise.
+ */
+ public static function is_primitive_type($type)
+ {
+ return in_array($type, self::$primitive_types);
+ }
+
+ /**
+ * @param string $type a schema type name
+ * @returns boolean true if the given type name is a valid schema type
+ * name and false otherwise.
+ */
+ public static function is_valid_type($type)
+ {
+ return (self::is_primitive_type($type)
+ || self::is_named_type($type)
+ || in_array($type, array(self::ARRAY_SCHEMA,
+ self::MAP_SCHEMA,
+ self::UNION_SCHEMA,
+ self::REQUEST_SCHEMA,
+ self::ERROR_UNION_SCHEMA)));
+ }
+
+ /**
+ * @var array list of names of reserved attributes
+ */
+ private static $reserved_attrs = array(self::TYPE_ATTR,
+ self::NAME_ATTR,
+ self::NAMESPACE_ATTR,
+ self::FIELDS_ATTR,
+ self::ITEMS_ATTR,
+ self::SIZE_ATTR,
+ self::SYMBOLS_ATTR,
+ self::VALUES_ATTR);
+
+ /**
+ * @param string $json JSON-encoded schema
+ * @uses self::real_parse()
+ * @returns AvroSchema
+ */
+ public static function parse($json)
+ {
+ $schemata = new AvroNamedSchemata();
+ return self::real_parse(json_decode($json, true), null, $schemata);
+ }
+
+ /**
+ * @param mixed $avro JSON-decoded schema
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata reference to named schemas
+ * @returns AvroSchema
+ * @throws AvroSchemaParseException
+ */
+ static function real_parse($avro, $default_namespace=null, &$schemata=null)
+ {
+ if (is_null($schemata))
+ $schemata = new AvroNamedSchemata();
+
+ if (is_array($avro))
+ {
+ $type = AvroUtil::array_value($avro, self::TYPE_ATTR);
+
+ if (self::is_primitive_type($type))
+ return new AvroPrimitiveSchema($type);
+
+ elseif (self::is_named_type($type))
+ {
+ $name = AvroUtil::array_value($avro, self::NAME_ATTR);
+ $namespace = AvroUtil::array_value($avro, self::NAMESPACE_ATTR);
+ $new_name = new AvroName($name, $namespace, $default_namespace);
+ $doc = AvroUtil::array_value($avro, self::DOC_ATTR);
+ switch ($type)
+ {
+ case self::FIXED_SCHEMA:
+ $size = AvroUtil::array_value($avro, self::SIZE_ATTR);
+ return new AvroFixedSchema($new_name, $doc,
+ $size,
+ $schemata);
+ case self::ENUM_SCHEMA:
+ $symbols = AvroUtil::array_value($avro, self::SYMBOLS_ATTR);
+ return new AvroEnumSchema($new_name, $doc,
+ $symbols,
+ $schemata);
+ case self::RECORD_SCHEMA:
+ case self::ERROR_SCHEMA:
+ $fields = AvroUtil::array_value($avro, self::FIELDS_ATTR);
+ return new AvroRecordSchema($new_name, $doc,
+ $fields,
+ $schemata, $type);
+ default:
+ throw new AvroSchemaParseException(
+ sprintf('Unknown named type: %s', $type));
+ }
+ }
+ elseif (self::is_valid_type($type))
+ {
+ switch ($type)
+ {
+ case self::ARRAY_SCHEMA:
+ return new AvroArraySchema($avro[self::ITEMS_ATTR],
+ $default_namespace,
+ $schemata);
+ case self::MAP_SCHEMA:
+ return new AvroMapSchema($avro[self::VALUES_ATTR],
+ $default_namespace,
+ $schemata);
+ default:
+ throw new AvroSchemaParseException(
+ sprintf('Unknown valid type: %s', $type));
+ }
+ }
+ elseif (!array_key_exists(self::TYPE_ATTR, $avro)
+ && AvroUtil::is_list($avro))
+ return new AvroUnionSchema($avro, $default_namespace, $schemata);
+ else
+ throw new AvroSchemaParseException(sprintf('Undefined type: %s',
+ $type));
+ }
+ elseif (self::is_primitive_type($avro))
+ return new AvroPrimitiveSchema($avro);
+ else
+ throw new AvroSchemaParseException(
+ sprintf('%s is not a schema we know about.',
+ print_r($avro, true)));
+ }
+
+ /**
+ * @returns boolean true if $datum is valid for $expected_schema
+ * and false otherwise.
+ * @throws AvroSchemaParseException
+ */
+ public static function is_valid_datum($expected_schema, $datum)
+ {
+ switch($expected_schema->type)
+ {
+ case self::NULL_TYPE:
+ return is_null($datum);
+ case self::BOOLEAN_TYPE:
+ return is_bool($datum);
+ case self::STRING_TYPE:
+ case self::BYTES_TYPE:
+ return is_string($datum);
+ case self::INT_TYPE:
+ return (is_int($datum)
+ && (self::INT_MIN_VALUE <= $datum)
+ && ($datum <= self::INT_MAX_VALUE));
+ case self::LONG_TYPE:
+ return (is_int($datum)
+ && (self::LONG_MIN_VALUE <= $datum)
+ && ($datum <= self::LONG_MAX_VALUE));
+ case self::FLOAT_TYPE:
+ case self::DOUBLE_TYPE:
+ return (is_float($datum) || is_int($datum));
+ case self::ARRAY_SCHEMA:
+ if (is_array($datum))
+ {
+ foreach ($datum as $d)
+ if (!self::is_valid_datum($expected_schema->items(), $d))
+ return false;
+ return true;
+ }
+ return false;
+ case self::MAP_SCHEMA:
+ if (is_array($datum))
+ {
+ foreach ($datum as $k => $v)
+ if (!is_string($k)
+ || !self::is_valid_datum($expected_schema->values(), $v))
+ return false;
+ return true;
+ }
+ return false;
+ case self::UNION_SCHEMA:
+ foreach ($expected_schema->schemas() as $schema)
+ if (self::is_valid_datum($schema, $datum))
+ return true;
+ return false;
+ case self::ENUM_SCHEMA:
+ return in_array($datum, $expected_schema->symbols());
+ case self::FIXED_SCHEMA:
+ return (is_string($datum)
+ && (strlen($datum) == $expected_schema->size()));
+ case self::RECORD_SCHEMA:
+ case self::ERROR_SCHEMA:
+ case self::REQUEST_SCHEMA:
+ if (is_array($datum))
+ {
+ foreach ($expected_schema->fields() as $field)
+ if (!array_key_exists($field->name(), $datum) || !self::is_valid_datum($field->type(), $datum[$field->name()]))
+ return false;
+ return true;
+ }
+ return false;
+ default:
+ throw new AvroSchemaParseException(
+ sprintf('%s is not allowed.', $expected_schema));
+ }
+ }
+
+ /**
+ * @internal Should only be called from within the constructor of
+ * a class which extends AvroSchema
+ * @param string $type a schema type name
+ */
+ public function __construct($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * @param mixed $avro
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ * @returns AvroSchema
+ * @uses AvroSchema::real_parse()
+ * @throws AvroSchemaParseException
+ */
+ protected static function subparse($avro, $default_namespace, &$schemata=null)
+ {
+ try
+ {
+ return self::real_parse($avro, $default_namespace, $schemata);
+ }
+ catch (AvroSchemaParseException $e)
+ {
+ throw $e;
+ }
+ catch (Exception $e)
+ {
+ throw new AvroSchemaParseException(
+ sprintf('Sub-schema is not a valid Avro schema. Bad schema: %s',
+ print_r($avro, true)));
+ }
+
+ }
+
+ /**
+ * @returns string schema type name of this schema
+ */
+ public function type() { return $this->type; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ return array(self::TYPE_ATTR => $this->type);
+ }
+
+ /**
+ * @returns string the JSON-encoded representation of this Avro schema.
+ */
+ public function __toString() { return json_encode($this->to_avro()); }
+
+ /**
+ * @returns mixed value of the attribute with the given attribute name
+ */
+ public function attribute($attribute) { return $this->$attribute(); }
+
+}
+
+/**
+ * Avro schema for basic types such as null, int, long, string.
+ * @package Avro
+ */
+class AvroPrimitiveSchema extends AvroSchema
+{
+
+ /**
+ * @param string $type the primitive schema type name
+ * @throws AvroSchemaParseException if the given $type is not a
+ * primitive schema type name
+ */
+ public function __construct($type)
+ {
+ if (self::is_primitive_type($type))
+ return parent::__construct($type);
+ throw new AvroSchemaParseException(
+ sprintf('%s is not a valid primitive type.', $type));
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ // FIXME: Is this if really necessary? When *wouldn't* this be the case?
+ if (1 == count($avro))
+ return $this->type;
+ return $avro;
+ }
+}
+
+/**
+ * Avro array schema, consisting of items of a particular
+ * Avro schema type.
+ * @package Avro
+ */
+class AvroArraySchema extends AvroSchema
+{
+ /**
+ * @var AvroName|AvroSchema named schema name or AvroSchema of
+ * array element
+ */
+ private $items;
+
+ /**
+ * @var boolean true if the items schema
+ * FIXME: couldn't we derive this from whether or not $this->items
+ * is an AvroName or an AvroSchema?
+ */
+ private $is_items_schema_from_schemata;
+
+ /**
+ * @param string|mixed $items AvroNamedSchema name or object form
+ * of decoded JSON schema representation.
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($items, $default_namespace, &$schemata=null)
+ {
+ parent::__construct(AvroSchema::ARRAY_SCHEMA);
+
+ $this->is_items_schema_from_schemata = false;
+ $items_schema = null;
+ if (is_string($items)
+ && $items_schema = $schemata->schema_by_name(
+ new AvroName($items, null, $default_namespace)))
+ $this->is_items_schema_from_schemata = true;
+ else
+ $items_schema = AvroSchema::subparse($items, $default_namespace, $schemata);
+
+ $this->items = $items_schema;
+ }
+
+
+ /**
+ * @returns AvroName|AvroSchema named schema name or AvroSchema
+ * of this array schema's elements.
+ */
+ public function items() { return $this->items; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::ITEMS_ATTR] = $this->is_items_schema_from_schemata
+ ? $this->items->qualified_name() : $this->items->to_avro();
+ return $avro;
+ }
+}
+
+/**
+ * Avro map schema consisting of named values of defined
+ * Avro Schema types.
+ * @package Avro
+ */
+class AvroMapSchema extends AvroSchema
+{
+ /**
+ * @var string|AvroSchema named schema name or AvroSchema
+ * of map schema values.
+ */
+ private $values;
+
+ /**
+ * @var boolean true if the named schema
+ * XXX Couldn't we derive this based on whether or not
+ * $this->values is a string?
+ */
+ private $is_values_schema_from_schemata;
+
+ /**
+ * @param string|AvroSchema $values
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($values, $default_namespace, &$schemata=null)
+ {
+ parent::__construct(AvroSchema::MAP_SCHEMA);
+
+ $this->is_values_schema_from_schemata = false;
+ $values_schema = null;
+ if (is_string($values)
+ && $values_schema = $schemata->schema_by_name(
+ new AvroName($values, null, $default_namespace)))
+ $this->is_values_schema_from_schemata = true;
+ else
+ $values_schema = AvroSchema::subparse($values, $default_namespace,
+ $schemata);
+
+ $this->values = $values_schema;
+ }
+
+ /**
+ * @returns XXX|AvroSchema
+ */
+ public function values() { return $this->values; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::VALUES_ATTR] = $this->is_values_schema_from_schemata
+ ? $this->values->qualified_name() : $this->values->to_avro();
+ return $avro;
+ }
+}
+
+/**
+ * Union of Avro schemas, of which values can be of any of the schema in
+ * the union.
+ * @package Avro
+ */
+class AvroUnionSchema extends AvroSchema
+{
+ /**
+ * @var AvroSchema[] list of schemas of this union
+ */
+ private $schemas;
+
+ /**
+ * @var int[] list of indices of named schemas which
+ * are defined in $schemata
+ */
+ public $schema_from_schemata_indices;
+
+ /**
+ * @param AvroSchema[] $schemas list of schemas in the union
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($schemas, $default_namespace, &$schemata=null)
+ {
+ parent::__construct(AvroSchema::UNION_SCHEMA);
+
+ $this->schema_from_schemata_indices = array();
+ $schema_types = array();
+ foreach ($schemas as $index => $schema)
+ {
+ $is_schema_from_schemata = false;
+ $new_schema = null;
+ if (is_string($schema)
+ && ($new_schema = $schemata->schema_by_name(
+ new AvroName($schema, null, $default_namespace))))
+ $is_schema_from_schemata = true;
+ else
+ $new_schema = self::subparse($schema, $default_namespace, $schemata);
+
+ $schema_type = $new_schema->type;
+ if (self::is_valid_type($schema_type)
+ && !self::is_named_type($schema_type)
+ && in_array($schema_type, $schema_types))
+ throw new AvroSchemaParseException(
+ sprintf('"%s" is already in union', $schema_type));
+ elseif (AvroSchema::UNION_SCHEMA == $schema_type)
+ throw new AvroSchemaParseException('Unions cannot contain other unions');
+ else
+ {
+ $schema_types []= $schema_type;
+ $this->schemas []= $new_schema;
+ if ($is_schema_from_schemata)
+ $this->schema_from_schemata_indices []= $index;
+ }
+ }
+
+ }
+
+ /**
+ * @returns AvroSchema[]
+ */
+ public function schemas() { return $this->schemas; }
+
+ /**
+ * @returns AvroSchema the particular schema from the union for
+ * the given (zero-based) index.
+ * @throws AvroSchemaParseException if the index is invalid for this schema.
+ */
+ public function schema_by_index($index)
+ {
+ if (count($this->schemas) > $index)
+ return $this->schemas[$index];
+
+ throw new AvroSchemaParseException('Invalid union schema index');
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = array();
+
+ foreach ($this->schemas as $index => $schema)
+ $avro []= (in_array($index, $this->schema_from_schemata_indices))
+ ? $schema->qualified_name() : $schema->to_avro();
+
+ return $avro;
+ }
+}
+
+/**
+ * Parent class of named Avro schema
+ * @package Avro
+ * @todo Refactor AvroNamedSchema to use an AvroName instance
+ * to store name information.
+ */
+class AvroNamedSchema extends AvroSchema
+{
+ /**
+ * @var AvroName $name
+ */
+ private $name;
+
+ /**
+ * @var string documentation string
+ */
+ private $doc;
+
+ /**
+ * @param string $type
+ * @param AvroName $name
+ * @param string $doc documentation string
+ * @param AvroNamedSchemata &$schemata
+ * @throws AvroSchemaParseException
+ */
+ public function __construct($type, $name, $doc=null, &$schemata=null)
+ {
+ parent::__construct($type);
+ $this->name = $name;
+
+ if ($doc && !is_string($doc))
+ throw new AvroSchemaParseException('Schema doc attribute must be a string');
+ $this->doc = $doc;
+
+ if (!is_null($schemata))
+ $schemata = $schemata->clone_with_new_schema($this);
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ list($name, $namespace) = AvroName::extract_namespace($this->qualified_name());
+ $avro[AvroSchema::NAME_ATTR] = $name;
+ if ($namespace)
+ $avro[AvroSchema::NAMESPACE_ATTR] = $namespace;
+ if (!is_null($this->doc))
+ $avro[AvroSchema::DOC_ATTR] = $this->doc;
+ return $avro;
+ }
+
+ /**
+ * @returns string
+ */
+ public function fullname() { return $this->name->fullname(); }
+
+ public function qualified_name() { return $this->name->qualified_name(); }
+
+}
+
+/**
+ * @package Avro
+ */
+class AvroName
+{
+ /**
+ * @var string character used to separate names comprising the fullname
+ */
+ const NAME_SEPARATOR = '.';
+
+ /**
+ * @var string regular expression to validate name values
+ */
+ const NAME_REGEXP = '/^[A-Za-z_][A-Za-z0-9_]*$/';
+
+ /**
+ * @returns string[] array($name, $namespace)
+ */
+ public static function extract_namespace($name, $namespace=null)
+ {
+ $parts = explode(self::NAME_SEPARATOR, $name);
+ if (count($parts) > 1)
+ {
+ $name = array_pop($parts);
+ $namespace = join(self::NAME_SEPARATOR, $parts);
+ }
+ return array($name, $namespace);
+ }
+
+ /**
+ * @returns boolean true if the given name is well-formed
+ * (is a non-null, non-empty string) and false otherwise
+ */
+ public static function is_well_formed_name($name)
+ {
+ return (is_string($name) && !empty($name)
+ && preg_match(self::NAME_REGEXP, $name));
+ }
+
+ /**
+ * @param string $namespace
+ * @returns boolean true if namespace is composed of valid names
+ * @throws AvroSchemaParseException if any of the namespace components
+ * are invalid.
+ */
+ private static function check_namespace_names($namespace)
+ {
+ foreach (explode(self::NAME_SEPARATOR, $namespace) as $n)
+ {
+ if (empty($n) || (0 == preg_match(self::NAME_REGEXP, $n)))
+ throw new AvroSchemaParseException(sprintf('Invalid name "%s"', $n));
+ }
+ return true;
+ }
+
+ /**
+ * @param string $name
+ * @param string $namespace
+ * @returns string
+ * @throws AvroSchemaParseException if any of the names are not valid.
+ */
+ private static function parse_fullname($name, $namespace)
+ {
+ if (!is_string($namespace) || empty($namespace))
+ throw new AvroSchemaParseException('Namespace must be a non-empty string.');
+ self::check_namespace_names($namespace);
+ return $namespace . '.' . $name;
+ }
+
+ /**
+ * @var string valid names are matched by self::NAME_REGEXP
+ */
+ private $name;
+
+ /**
+ * @var string
+ */
+ private $namespace;
+
+ /**
+ * @var string
+ */
+ private $fullname;
+
+ /**
+ * @var string Name qualified as necessary given its default namespace.
+ */
+ private $qualified_name;
+
+ /**
+ * @param string $name
+ * @param string $namespace
+ * @param string $default_namespace
+ */
+ public function __construct($name, $namespace, $default_namespace)
+ {
+ if (!is_string($name) || empty($name))
+ throw new AvroSchemaParseException('Name must be a non-empty string.');
+
+ if (strpos($name, self::NAME_SEPARATOR)
+ && self::check_namespace_names($name))
+ $this->fullname = $name;
+ elseif (0 == preg_match(self::NAME_REGEXP, $name))
+ throw new AvroSchemaParseException(sprintf('Invalid name "%s"', $name));
+ elseif (!is_null($namespace))
+ $this->fullname = self::parse_fullname($name, $namespace);
+ elseif (!is_null($default_namespace))
+ $this->fullname = self::parse_fullname($name, $default_namespace);
+ else
+ $this->fullname = $name;
+
+ list($this->name, $this->namespace) = self::extract_namespace($this->fullname);
+ $this->qualified_name = (is_null($this->namespace)
+ || $this->namespace == $default_namespace)
+ ? $this->name : $this->fullname;
+ }
+
+ /**
+ * @returns array array($name, $namespace)
+ */
+ public function name_and_namespace()
+ {
+ return array($this->name, $this->namespace);
+ }
+
+ /**
+ * @returns string
+ */
+ public function fullname() { return $this->fullname; }
+
+ /**
+ * @returns string fullname
+ * @uses $this->fullname()
+ */
+ public function __toString() { return $this->fullname(); }
+
+ /**
+ * @returns string name qualified for its context
+ */
+ public function qualified_name() { return $this->qualified_name; }
+
+}
+
+/**
+ * Keeps track of AvroNamedSchema which have been observed so far,
+ * as well as the default namespace.
+ *
+ * @package Avro
+ */
+class AvroNamedSchemata
+{
+ /**
+ * @var AvroNamedSchema[]
+ */
+ private $schemata;
+
+ /**
+ * @param AvroNamedSchemata[]
+ */
+ public function __construct($schemata=array())
+ {
+ $this->schemata = $schemata;
+ }
+
+ public function list_schemas() {
+ var_export($this->schemata);
+ foreach($this->schemata as $sch)
+ print('Schema '.$sch->__toString()."\n");
+ }
+
+ /**
+ * @param string $fullname
+ * @returns boolean true if there exists a schema with the given name
+ * and false otherwise.
+ */
+ public function has_name($fullname)
+ {
+ return array_key_exists($fullname, $this->schemata);
+ }
+
+ /**
+ * @param string $fullname
+ * @returns AvroSchema|null the schema which has the given name,
+ * or null if there is no schema with the given name.
+ */
+ public function schema($fullname)
+ {
+ if (isset($this->schemata[$fullname]))
+ return $this->schemata[$fullname];
+ return null;
+ }
+
+ /**
+ * @param AvroName $name
+ * @returns AvroSchema|null
+ */
+ public function schema_by_name($name)
+ {
+ return $this->schema($name->fullname());
+ }
+
+ /**
+ * Creates a new AvroNamedSchemata instance of this schemata instance
+ * with the given $schema appended.
+ * @param AvroNamedSchema schema to add to this existing schemata
+ * @returns AvroNamedSchemata
+ */
+ public function clone_with_new_schema($schema)
+ {
+ $name = $schema->fullname();
+ if (AvroSchema::is_valid_type($name))
+ throw new AvroSchemaParseException(
+ sprintf('Name "%s" is a reserved type name', $name));
+ else if ($this->has_name($name))
+ throw new AvroSchemaParseException(
+ sprintf('Name "%s" is already in use', $name));
+ $schemata = new AvroNamedSchemata($this->schemata);
+ $schemata->schemata[$name] = $schema;
+ return $schemata;
+ }
+}
+
+/**
+ * @package Avro
+ */
+class AvroEnumSchema extends AvroNamedSchema
+{
+ /**
+ * @var string[] array of symbols
+ */
+ private $symbols;
+
+ /**
+ * @param AvroName $name
+ * @param string $doc
+ * @param string[] $symbols
+ * @param AvroNamedSchemata &$schemata
+ * @throws AvroSchemaParseException
+ */
+ public function __construct($name, $doc, $symbols, &$schemata=null)
+ {
+ if (!AvroUtil::is_list($symbols))
+ throw new AvroSchemaParseException('Enum Schema symbols are not a list');
+
+ if (count(array_unique($symbols)) > count($symbols))
+ throw new AvroSchemaParseException(
+ sprintf('Duplicate symbols: %s', $symbols));
+
+ foreach ($symbols as $symbol)
+ if (!is_string($symbol) || empty($symbol))
+ throw new AvroSchemaParseException(
+ sprintf('Enum schema symbol must be a string %',
+ print_r($symbol, true)));
+
+ parent::__construct(AvroSchema::ENUM_SCHEMA, $name, $doc, $schemata);
+ $this->symbols = $symbols;
+ }
+
+ /**
+ * @returns string[] this enum schema's symbols
+ */
+ public function symbols() { return $this->symbols; }
+
+ /**
+ * @param string $symbol
+ * @returns boolean true if the given symbol exists in this
+ * enum schema and false otherwise
+ */
+ public function has_symbol($symbol)
+ {
+ return in_array($symbol, $this->symbols);
+ }
+
+ /**
+ * @param int $index
+ * @returns string enum schema symbol with the given (zero-based) index
+ */
+ public function symbol_by_index($index)
+ {
+ if (array_key_exists($index, $this->symbols))
+ return $this->symbols[$index];
+ throw new AvroException(sprintf('Invalid symbol index %d', $index));
+ }
+
+ /**
+ * @param string $symbol
+ * @returns int the index of the given $symbol in the enum schema
+ */
+ public function symbol_index($symbol)
+ {
+ $idx = array_search($symbol, $this->symbols, true);
+ if (false !== $idx)
+ return $idx;
+ throw new AvroException(sprintf("Invalid symbol value '%s'", $symbol));
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::SYMBOLS_ATTR] = $this->symbols;
+ return $avro;
+ }
+}
+
+/**
+ * AvroNamedSchema with fixed-length data values
+ * @package Avro
+ */
+class AvroFixedSchema extends AvroNamedSchema
+{
+
+ /**
+ * @var int byte count of this fixed schema data value
+ */
+ private $size;
+
+ /**
+ * @param AvroName $name
+ * @param string $doc Set to null, as fixed schemas don't have doc strings
+ * @param int $size byte count of this fixed schema data value
+ * @param AvroNamedSchemata &$schemata
+ */
+ public function __construct($name, $doc, $size, &$schemata=null)
+ {
+ $doc = null; // Fixed schemas don't have doc strings.
+ if (!is_integer($size))
+ throw new AvroSchemaParseException(
+ 'Fixed Schema requires a valid integer for "size" attribute');
+ parent::__construct(AvroSchema::FIXED_SCHEMA, $name, $doc, $schemata);
+ return $this->size = $size;
+ }
+
+ /**
+ * @returns int byte count of this fixed schema data value
+ */
+ public function size() { return $this->size; }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+ $avro[AvroSchema::SIZE_ATTR] = $this->size;
+ return $avro;
+ }
+}
+
+/**
+ * @package Avro
+ */
+class AvroRecordSchema extends AvroNamedSchema
+{
+ /**
+ * @param mixed $field_data
+ * @param string $default_namespace namespace of enclosing schema
+ * @param AvroNamedSchemata &$schemata
+ * @returns AvroField[]
+ * @throws AvroSchemaParseException
+ */
+ static function parse_fields($field_data, $default_namespace, &$schemata)
+ {
+ $fields = array();
+ $field_names = array();
+ foreach ($field_data as $index => $field)
+ {
+ $name = AvroUtil::array_value($field, AvroField::FIELD_NAME_ATTR);
+ $type = AvroUtil::array_value($field, AvroSchema::TYPE_ATTR);
+ $order = AvroUtil::array_value($field, AvroField::ORDER_ATTR);
+
+ $default = null;
+ $has_default = false;
+ if (array_key_exists(AvroField::DEFAULT_ATTR, $field))
+ {
+ $default = $field[AvroField::DEFAULT_ATTR];
+ $has_default = true;
+ }
+
+ if (in_array($name, $field_names))
+ throw new AvroSchemaParseException(
+ sprintf("Field name %s is already in use", $name));
+
+ $is_schema_from_schemata = false;
+ $field_schema = null;
+ if (is_string($type)
+ && $field_schema = $schemata->schema_by_name(
+ new AvroName($type, null, $default_namespace)))
+ $is_schema_from_schemata = true;
+ else
+ $field_schema = self::subparse($type, $default_namespace, $schemata);
+
+ $new_field = new AvroField($name, $field_schema, $is_schema_from_schemata,
+ $has_default, $default, $order);
+ $field_names []= $name;
+ $fields []= $new_field;
+ }
+ return $fields;
+ }
+
+ /**
+ * @var AvroSchema[] array of AvroNamedSchema field definitions of
+ * this AvroRecordSchema
+ */
+ private $fields;
+
+ /**
+ * @var array map of field names to field objects.
+ * @internal Not called directly. Memoization of AvroRecordSchema->fields_hash()
+ */
+ private $fields_hash;
+
+ /**
+ * @param string $name
+ * @param string $namespace
+ * @param string $doc
+ * @param array $fields
+ * @param AvroNamedSchemata &$schemata
+ * @param string $schema_type schema type name
+ * @throws AvroSchemaParseException
+ */
+ public function __construct($name, $doc, $fields, &$schemata=null,
+ $schema_type=AvroSchema::RECORD_SCHEMA)
+ {
+ if (is_null($fields))
+ throw new AvroSchemaParseException(
+ 'Record schema requires a non-empty fields attribute');
+
+ if (AvroSchema::REQUEST_SCHEMA == $schema_type)
+ parent::__construct($schema_type, $name);
+ else
+ parent::__construct($schema_type, $name, $doc, $schemata);
+
+ list($x, $namespace) = $name->name_and_namespace();
+ $this->fields = self::parse_fields($fields, $namespace, $schemata);
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = parent::to_avro();
+
+ $fields_avro = array();
+ foreach ($this->fields as $field)
+ $fields_avro [] = $field->to_avro();
+
+ if (AvroSchema::REQUEST_SCHEMA == $this->type)
+ return $fields_avro;
+
+ $avro[AvroSchema::FIELDS_ATTR] = $fields_avro;
+
+ return $avro;
+ }
+
+ /**
+ * @returns array the schema definitions of the fields of this AvroRecordSchema
+ */
+ public function fields() { return $this->fields; }
+
+ /**
+ * @returns array a hash table of the fields of this AvroRecordSchema fields
+ * keyed by each field's name
+ */
+ public function fields_hash()
+ {
+ if (is_null($this->fields_hash))
+ {
+ $hash = array();
+ foreach ($this->fields as $field)
+ $hash[$field->name()] = $field;
+ $this->fields_hash = $hash;
+ }
+ return $this->fields_hash;
+ }
+}
+
+/**
+ * Field of an {@link AvroRecordSchema}
+ * @package Avro
+ */
+class AvroField extends AvroSchema
+{
+
+ /**
+ * @var string fields name attribute name
+ */
+ const FIELD_NAME_ATTR = 'name';
+
+ /**
+ * @var string
+ */
+ const DEFAULT_ATTR = 'default';
+
+ /**
+ * @var string
+ */
+ const ORDER_ATTR = 'order';
+
+ /**
+ * @var string
+ */
+ const ASC_SORT_ORDER = 'ascending';
+
+ /**
+ * @var string
+ */
+ const DESC_SORT_ORDER = 'descending';
+
+ /**
+ * @var string
+ */
+ const IGNORE_SORT_ORDER = 'ignore';
+
+ /**
+ * @var array list of valid field sort order values
+ */
+ private static $valid_field_sort_orders = array(self::ASC_SORT_ORDER,
+ self::DESC_SORT_ORDER,
+ self::IGNORE_SORT_ORDER);
+
+
+ /**
+ * @param string $order
+ * @returns boolean
+ */
+ private static function is_valid_field_sort_order($order)
+ {
+ return in_array($order, self::$valid_field_sort_orders);
+ }
+
+ /**
+ * @param string $order
+ * @throws AvroSchemaParseException if $order is not a valid
+ * field order value.
+ */
+ private static function check_order_value($order)
+ {
+ if (!is_null($order) && !self::is_valid_field_sort_order($order))
+ throw new AvroSchemaParseException(
+ sprintf('Invalid field sort order %s', $order));
+ }
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var boolean whether or no there is a default value
+ */
+ private $has_default;
+
+ /**
+ * @var string field default value
+ */
+ private $default;
+
+ /**
+ * @var string sort order of this field
+ */
+ private $order;
+
+ /**
+ * @var boolean whether or not the AvroNamedSchema of this field is
+ * defined in the AvroNamedSchemata instance
+ */
+ private $is_type_from_schemata;
+
+ /**
+ * @param string $type
+ * @param string $name
+ * @param AvroSchema $schema
+ * @param boolean $is_type_from_schemata
+ * @param string $default
+ * @param string $order
+ * @todo Check validity of $default value
+ * @todo Check validity of $order value
+ */
+ public function __construct($name, $schema, $is_type_from_schemata,
+ $has_default, $default, $order=null)
+ {
+ if (!AvroName::is_well_formed_name($name))
+ throw new AvroSchemaParseException('Field requires a "name" attribute');
+
+ $this->type = $schema;
+ $this->is_type_from_schemata = $is_type_from_schemata;
+ $this->name = $name;
+ $this->has_default = $has_default;
+ if ($this->has_default)
+ $this->default = $default;
+ $this->check_order_value($order);
+ $this->order = $order;
+ }
+
+ /**
+ * @returns mixed
+ */
+ public function to_avro()
+ {
+ $avro = array(AvroField::FIELD_NAME_ATTR => $this->name);
+
+ $avro[AvroSchema::TYPE_ATTR] = ($this->is_type_from_schemata)
+ ? $this->type->qualified_name() : $this->type->to_avro();
+
+ if (isset($this->default))
+ $avro[AvroField::DEFAULT_ATTR] = $this->default;
+
+ if ($this->order)
+ $avro[AvroField::ORDER_ATTR] = $this->order;
+
+ return $avro;
+ }
+
+ /**
+ * @returns string the name of this field
+ */
+ public function name() { return $this->name; }
+
+ /**
+ * @returns mixed the default value of this field
+ */
+ public function default_value() { return $this->default; }
+
+ /**
+ * @returns boolean true if the field has a default and false otherwise
+ */
+ public function has_default_value() { return $this->has_default; }
+}
diff --git a/vendor/wikimedia/avro/lib/avro/util.php b/vendor/wikimedia/avro/lib/avro/util.php
new file mode 100644
index 00000000..a43613e9
--- /dev/null
+++ b/vendor/wikimedia/avro/lib/avro/util.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @package Avro
+ */
+
+/**
+ * Class for static utility methods used in Avro.
+ *
+ * @package Avro
+ */
+class AvroUtil
+{
+ /**
+ * Determines whether the given array is an associative array
+ * (what is termed a map, hash, or dictionary in other languages)
+ * or a list (an array with monotonically increasing integer indicies
+ * starting with zero).
+ *
+ * @param array $ary array to test
+ * @returns true if the array is a list and false otherwise.
+ *
+ */
+ static function is_list($ary)
+ {
+ if (is_array($ary))
+ {
+ $i = 0;
+ foreach ($ary as $k => $v)
+ {
+ if ($i !== $k)
+ return false;
+ $i++;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param array $ary
+ * @param string $key
+ * @returns mixed the value of $ary[$key] if it is set,
+ * and null otherwise.
+ */
+ static function array_value($ary, $key)
+ {
+ return isset($ary[$key]) ? $ary[$key] : null;
+ }
+}
diff --git a/vendor/wikimedia/cdb/COPYING b/vendor/wikimedia/cdb/COPYING
index 019694a9..d159169d 100644
--- a/vendor/wikimedia/cdb/COPYING
+++ b/vendor/wikimedia/cdb/COPYING
@@ -1,65 +1,65 @@
-== GNU GENERAL PUBLIC LICENSE ==
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
-Version 2, June 1991
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-Everyone is permitted to copy and distribute verbatim copies
-of this license document, but changing it is not allowed.
+ Preamble
-=== Preamble ===
-
-The licenses for most software are designed to take away your
+ The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
+the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
-When we speak of free software, we are referring to freedom, not
+ When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
-To protect your rights, we need to make restrictions that forbid
+ To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
-For example, if you distribute copies of such a program, whether
+ For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
-We protect your rights with two steps: (1) copyright the software, and
+ We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
-Also, for each author's protection and ours, we want to make certain
+ Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
-Finally, any free program is threatened constantly by software
+ Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
-The precise terms and conditions for copying, distribution and
+ The precise terms and conditions for copying, distribution and
modification follow.
-== TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ==
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-'''0.''' This License applies to any program or other work which contains
+ 0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
@@ -76,7 +76,7 @@ is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
-'''1.''' You may copy and distribute verbatim copies of the Program's
+ 1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
@@ -87,29 +87,29 @@ along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
-'''2.''' You may modify your copy or copies of the Program or any portion
+ 2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
- '''a)''' You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- '''b)''' You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- '''c)''' If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
@@ -131,26 +131,26 @@ with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
-'''3.''' You may copy and distribute the Program (or a work based on it,
+ 3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
- '''a)''' Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
- '''b)''' Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
- '''c)''' Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
@@ -169,7 +169,7 @@ access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
-'''4.''' You may not copy, modify, sublicense, or distribute the Program
+ 4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
@@ -177,7 +177,7 @@ However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
-'''5.''' You are not required to accept this License, since you have not
+ 5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
@@ -186,7 +186,7 @@ Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
-'''6.''' Each time you redistribute the Program (or any work based on the
+ 6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
@@ -194,7 +194,7 @@ restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
-'''7.''' If, as a consequence of a court judgment or allegation of patent
+ 7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
@@ -226,7 +226,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
-'''8.''' If the distribution and/or use of the Program is restricted in
+ 8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
@@ -234,7 +234,7 @@ those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
-'''9.''' The Free Software Foundation may publish revised and/or new versions
+ 9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
@@ -247,7 +247,7 @@ Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
-'''10.''' If you wish to incorporate parts of the Program into other free
+ 10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
@@ -255,9 +255,9 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
-=== NO WARRANTY ===
+ NO WARRANTY
-'''11.''' BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
@@ -267,7 +267,7 @@ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
-'''12.''' IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
@@ -277,47 +277,45 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
- '''END OF TERMS AND CONDITIONS'''
+ END OF TERMS AND CONDITIONS
-== How to Apply These Terms to Your New Programs ==
+ How to Apply These Terms to Your New Programs
-If you develop a new program, and you want it to be of the greatest
+ If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
-To do so, attach the following notices to the program. It is safest
+ To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
- <one line to give the program's name and a brief idea of what it does.>
-
- Copyright (C) <year> <name of author>
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
@@ -328,15 +326,14 @@ You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
- Ty Coon, President of Vice
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
+library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
diff --git a/vendor/wikimedia/cdb/README.md b/vendor/wikimedia/cdb/README.md
index e4ef4498..02c1b94c 100644
--- a/vendor/wikimedia/cdb/README.md
+++ b/vendor/wikimedia/cdb/README.md
@@ -1,4 +1,4 @@
-[![Latest Stable Version](https://poser.pugx.org/cdb/cdb/v/stable.svg)](https://packagist.org/packages/cdb/cdb) [![License](https://poser.pugx.org/cdb/cdb/license.svg)](https://packagist.org/packages/cdb/cdb)
+[![Latest Stable Version]](https://packagist.org/packages/wikimedia/cdb) [![License]](https://packagist.org/packages/wikimedia/cdb)
CDB functions for PHP
=====================
@@ -9,30 +9,28 @@ library wraps the CDB functionality exposed in PHP via the `dba_*` functions.
In cases where `dba_*` functions are not present or are not compiled with CDB
support, a pure-PHP implementation is provided for falling back.
-Additional documentation about the library can be found on [MediaWiki.org](https://www.mediawiki.org/wiki/CDB).
+Additional documentation about the library can be found on
+[MediaWiki.org](https://www.mediawiki.org/wiki/CDB).
Usage
-----
-```
-// Reading a CDB file
-$cdb = \Cdb\Reader::open( 'db.cdb' );
-$foo = $cdb->get( 'somekey' );
+ // Reading a CDB file
+ $cdb = \Cdb\Reader::open( 'db.cdb' );
+ $foo = $cdb->get( 'somekey' );
+
+ // Writing to a CDB file
+ $cdb = \Cdb\Writer::open( 'anotherdb.cdb' );
+ $cdb->set( 'somekey', $foo );
-// Writing to a CDB file
-$cdb = \Cdb\Writer::open( 'anotherdb.cdb' );
-$cdb->set( 'somekey', $foo );
-```
Running tests
-------------
-```
-composer install --prefer-dist
-cd test
-../vendor/phpunit/phpunit/phpunit .
-```
+ composer install --prefer-dist
+ composer test
+
History
-------
@@ -41,8 +39,11 @@ This library was first introduced in [MediaWiki 1.16][] ([r52203][]). It was
split out of the MediaWiki codebase and published as an independent library
during the [MediaWiki 1.25][] development cycle.
+
---
[CDB]: https://en.wikipedia.org/wiki/cdb_(software)
[MediaWiki 1.16]: https://www.mediawiki.org/wiki/MediaWiki_1.16
[r52203]: https://www.mediawiki.org/wiki/Special:Code/MediaWiki/52203
[MediaWiki 1.25]: https://www.mediawiki.org/wiki/MediaWiki_1.25
+[Latest Stable Version]: https://poser.pugx.org/wikimedia/cdb/v/stable.svg
+[License]: https://poser.pugx.org/wikimedia/cdb/license.svg
diff --git a/vendor/wikimedia/cdb/composer.json b/vendor/wikimedia/cdb/composer.json
deleted file mode 100644
index 2134d2f7..00000000
--- a/vendor/wikimedia/cdb/composer.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "name": "wikimedia/cdb",
- "description": "Constant Database (CDB) wrapper library for PHP. Provides pure-PHP fallback when dba_* functions are absent.",
- "license": "GPL-2.0",
- "homepage": "https://www.mediawiki.org/wiki/CDB",
- "authors": [
- {
- "name": "Tim Starling",
- "email": "tstarling@wikimedia.org"
- },
- {
- "name": "Chad Horohoe",
- "email": "chad@wikimedia.org"
- }
- ],
- "minimum-stability": "dev",
- "require": {
- "php": ">=5.3.2"
- },
- "require-dev": {
- "phpunit/phpunit": "*"
- },
- "autoload": {
- "classmap": ["src/"]
- }
-}
diff --git a/vendor/wikimedia/cdb/doc/README b/vendor/wikimedia/cdb/doc/README
deleted file mode 100644
index 15ee3b71..00000000
--- a/vendor/wikimedia/cdb/doc/README
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder for doxygen documentation
diff --git a/vendor/wikimedia/cdb/src/Reader.php b/vendor/wikimedia/cdb/src/Reader.php
index 2df06ab3..b1aaa022 100644
--- a/vendor/wikimedia/cdb/src/Reader.php
+++ b/vendor/wikimedia/cdb/src/Reader.php
@@ -82,4 +82,21 @@ abstract class Reader {
* @param string $key
*/
abstract public function get( $key );
+
+ /**
+ * Check whether key exists
+ *
+ * @param string $key
+ */
+ abstract public function exists( $key );
+
+ /**
+ * Fetch first key
+ */
+ abstract public function firstkey();
+
+ /**
+ * Fetch next key
+ */
+ abstract public function nextkey();
}
diff --git a/vendor/wikimedia/cdb/src/Reader/DBA.php b/vendor/wikimedia/cdb/src/Reader/DBA.php
index 838bf6e0..62f1ad99 100644
--- a/vendor/wikimedia/cdb/src/Reader/DBA.php
+++ b/vendor/wikimedia/cdb/src/Reader/DBA.php
@@ -1,6 +1,7 @@
<?php
namespace Cdb\Reader;
+
use Cdb\Exception;
use Cdb\Reader;
@@ -46,4 +47,16 @@ class DBA extends Reader {
public function get( $key ) {
return dba_fetch( $key, $this->handle );
}
+
+ public function exists( $key ) {
+ return dba_exists( $key, $this->handle );
+ }
+
+ public function firstkey() {
+ return dba_firstkey( $this->handle );
+ }
+
+ public function nextkey() {
+ return dba_nextkey( $this->handle );
+ }
}
diff --git a/vendor/wikimedia/cdb/src/Reader/PHP.php b/vendor/wikimedia/cdb/src/Reader/PHP.php
index 57b7d8ca..0f0d36ef 100644
--- a/vendor/wikimedia/cdb/src/Reader/PHP.php
+++ b/vendor/wikimedia/cdb/src/Reader/PHP.php
@@ -1,16 +1,14 @@
<?php
namespace Cdb\Reader;
+
use Cdb\Exception;
use Cdb\Reader;
use Cdb\Util;
/**
* This is a port of D.J. Bernstein's CDB to PHP. It's based on the copy that
- * appears in PHP 5.3. Changes are:
- * * Error returns replaced with exceptions
- * * Exception thrown if sizes or offsets are between 2GB and 4GB
- * * Some variables renamed
+ * appears in PHP 5.3.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,33 +32,37 @@ use Cdb\Util;
* CDB reader class
*/
class PHP extends Reader {
- /** The filename */
+
+ /** @var string The file name of the CDB file. **/
protected $fileName;
- /* number of hash slots searched under this key */
- protected $loop;
+ /** @var string First 2048b of CDB file, containing the hash table. **/
+ protected $hashTable;
- /* initialized if loop is nonzero */
- protected $khash;
+ /** @var int Offset in file where value of found key starts. **/
+ protected $dataPos;
- /* initialized if loop is nonzero */
- protected $kpos;
+ /** @var int Byte length of found key's value. **/
+ protected $dataLen;
- /* initialized if loop is nonzero */
- protected $hpos;
+ /** @var int File position indicator when iterating over keys. **/
+ protected $keyIterPos = 2048;
- /* initialized if loop is nonzero */
- protected $hslots;
+ /** @var string Read buffer for CDB file. **/
+ protected $buf;
- /* initialized if findNext() returns true */
- protected $dpos;
+ /** @var int File offset where read buffer starts. **/
+ protected $bufStart;
- /* initialized if cdb_findnext() returns 1 */
- protected $dlen;
+ /** @var int File handle position indicator **/
+ protected $filePos = 2048;
/**
+ * Constructor.
+ *
* @param string $fileName
- * @throws Exception
+ * @throws Exception If CDB file cannot be opened or if it contains fewer
+ * than 2048 bytes of data.
*/
public function __construct( $fileName ) {
$this->fileName = $fileName;
@@ -68,9 +70,15 @@ class PHP extends Reader {
if ( !$this->handle ) {
throw new Exception( 'Unable to open CDB file "' . $this->fileName . '".' );
}
- $this->findStart();
+ $this->hashTable = fread( $this->handle, 2048 );
+ if ( strlen( $this->hashTable ) !== 2048 ) {
+ throw new Exception( 'CDB file contains fewer than 2048 bytes of data.' );
+ }
}
+ /**
+ * Close the handle on the CDB file.
+ */
public function close() {
if ( isset( $this->handle ) ) {
fclose( $this->handle );
@@ -79,127 +87,180 @@ class PHP extends Reader {
}
/**
+ * Get the value of a key.
+ *
* @param mixed $key
- * @return bool|string
+ * @return bool|string The key's value or false if not found.
*/
public function get( $key ) {
// strval is required
if ( $this->find( strval( $key ) ) ) {
- return $this->read( $this->dlen, $this->dpos );
+ return $this->read( $this->dataPos, $this->dataLen );
}
return false;
}
/**
- * @param string $key
- * @param int $pos
- * @return bool
+ * Read data from the CDB file.
+ *
+ * @throws Exception When attempting to read past the end of the file.
+ * @param int $start Start reading from this position.
+ * @param int $len Number of bytes to read.
+ * @return string Read data.
*/
- protected function match( $key, $pos ) {
- $buf = $this->read( strlen( $key ), $pos );
+ protected function read( $start, $len ) {
+ $end = $start + $len;
- return $buf === $key;
- }
+ // The first 2048 bytes are the lookup table, which is read into
+ // memory on initialization.
+ if ( $end <= 2048 ) {
+ return substr( $this->hashTable, $start, $len );
+ }
- protected function findStart() {
- $this->loop = 0;
- }
+ // Read data from the internal buffer first.
+ $bytes = '';
+ if ( $this->buf && $start >= $this->bufStart ) {
+ $bytes .= substr( $this->buf, $start - $this->bufStart, $len );
+ $bytesRead = strlen( $bytes );
+ $len -= $bytesRead;
+ $start += $bytesRead;
+ } else {
+ $bytesRead = 0;
+ }
- /**
- * @throws Exception
- * @param int $length
- * @param int $pos
- * @return string
- */
- protected function read( $length, $pos ) {
- if ( fseek( $this->handle, $pos ) == -1 ) {
- // This can easily happen if the internal pointers are incorrect
- throw new Exception(
- 'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
+ if ( !$len ) {
+ return $bytes;
+ }
+
+ // Many reads are sequential, so the file position indicator may
+ // already be in the right place, in which case we can avoid the
+ // call to fseek().
+ if ( $start !== $this->filePos ) {
+ if ( fseek( $this->handle, $start ) === -1 ) {
+ // This can easily happen if the internal pointers are incorrect
+ throw new Exception(
+ 'Seek failed, file "' . $this->fileName . '" may be corrupted.' );
+ }
}
- if ( $length == 0 ) {
- return '';
+ $buf = fread( $this->handle, max( $len, 1024 ) );
+ if ( $buf === false ) {
+ $buf = '';
}
- $buf = fread( $this->handle, $length );
- if ( $buf === false || strlen( $buf ) !== $length ) {
+ $bytes .= substr( $buf, 0, $len );
+ if ( strlen( $bytes ) !== $len + $bytesRead ) {
throw new Exception(
'Read from CDB file failed, file "' . $this->fileName . '" may be corrupted.' );
}
- return $buf;
+ $this->filePos = $end;
+ $this->bufStart = $start;
+ $this->buf = $buf;
+
+ return $bytes;
}
/**
- * Unpack an unsigned integer and throw an exception if it needs more than 31 bits
- * @param string $s
- * @throws Exception
- * @return mixed
+ * Unpack an unsigned integer and throw an exception if it needs more than 31 bits.
+ *
+ * @param int $pos Position to read from.
+ * @throws Exception When the integer cannot be represented in 31 bits.
+ * @return int
*/
- protected function unpack31( $s ) {
- $data = unpack( 'V', $s );
- if ( $data[1] > 0x7fffffff ) {
+ protected function readInt31( $pos = 0 ) {
+ $uint31 = $this->readInt32( $pos );
+ if ( $uint31 > 0x7fffffff ) {
throw new Exception(
'Error in CDB file "' . $this->fileName . '", integer too big.' );
}
- return $data[1];
+ return $uint31;
}
/**
- * Unpack a 32-bit signed integer
- * @param string $s
+ * Unpack a 32-bit integer.
+ *
+ * @param int $pos
* @return int
*/
- protected function unpackSigned( $s ) {
- $data = unpack( 'va/vb', $s );
+ protected function readInt32( $pos = 0 ) {
+ static $lookups;
+
+ if ( !$lookups ) {
+ $lookups = array();
+ for ( $i = 1; $i < 256; $i++ ) {
+ $lookups[ chr( $i ) ] = $i;
+ }
+ }
- return $data['a'] | ( $data['b'] << 16 );
+ $buf = $this->read( $pos, 4 );
+
+ $rv = 0;
+
+ if ( $buf[0] !== "\x0" ) {
+ $rv = $lookups[ $buf[0] ];
+ }
+ if ( $buf[1] !== "\x0" ) {
+ $rv |= ( $lookups[ $buf[1] ] << 8 );
+ }
+ if ( $buf[2] !== "\x0" ) {
+ $rv |= ( $lookups[ $buf[2] ] << 16 );
+ }
+ if ( $buf[3] !== "\x0" ) {
+ $rv |= ( $lookups[ $buf[3] ] << 24 );
+ }
+
+ return $rv;
}
/**
+ * Search the CDB file for a key.
+ *
+ * Sets `dataLen` and `dataPos` properties if successful.
+ *
* @param string $key
- * @return bool
+ * @return bool Whether the key was found.
*/
- protected function findNext( $key ) {
- if ( !$this->loop ) {
- $u = Util::hash( $key );
- $buf = $this->read( 8, ( $u << 3 ) & 2047 );
- $this->hslots = $this->unpack31( substr( $buf, 4 ) );
- if ( !$this->hslots ) {
- return false;
- }
- $this->hpos = $this->unpack31( substr( $buf, 0, 4 ) );
- $this->khash = $u;
- $u = Util::unsignedShiftRight( $u, 8 );
- $u = Util::unsignedMod( $u, $this->hslots );
- $u <<= 3;
- $this->kpos = $this->hpos + $u;
- }
+ protected function find( $key ) {
+ $keyLen = strlen( $key );
- while ( $this->loop < $this->hslots ) {
- $buf = $this->read( 8, $this->kpos );
- $pos = $this->unpack31( substr( $buf, 4 ) );
+ $u = Util::hash( $key );
+ $upos = ( $u << 3 ) & 2047;
+ $hashSlots = $this->readInt31( $upos + 4 );
+ if ( !$hashSlots ) {
+ return false;
+ }
+ $hashPos = $this->readInt31( $upos );
+ $keyHash = $u;
+ $u = Util::unsignedShiftRight( $u, 8 );
+ $u = Util::unsignedMod( $u, $hashSlots );
+ $u <<= 3;
+ $keyPos = $hashPos + $u;
+
+ for ( $i = 0; $i < $hashSlots; $i++ ) {
+ $hash = $this->readInt32( $keyPos );
+ $pos = $this->readInt31( $keyPos + 4 );
if ( !$pos ) {
return false;
}
- $this->loop += 1;
- $this->kpos += 8;
- if ( $this->kpos == $this->hpos + ( $this->hslots << 3 ) ) {
- $this->kpos = $this->hpos;
+ $keyPos += 8;
+ if ( $keyPos == $hashPos + ( $hashSlots << 3 ) ) {
+ $keyPos = $hashPos;
}
- $u = $this->unpackSigned( substr( $buf, 0, 4 ) );
- if ( $u === $this->khash ) {
- $buf = $this->read( 8, $pos );
- $keyLen = $this->unpack31( substr( $buf, 0, 4 ) );
- if ( $keyLen == strlen( $key ) && $this->match( $key, $pos + 8 ) ) {
- // Found
- $this->dlen = $this->unpack31( substr( $buf, 4 ) );
- $this->dpos = $pos + 8 + $keyLen;
-
- return true;
+ if ( $hash === $keyHash ) {
+ if ( $keyLen === $this->readInt31( $pos ) ) {
+ $dataLen = $this->readInt31( $pos + 4 );
+ $dataPos = $pos + 8 + $keyLen;
+ $foundKey = $this->read( $pos + 8, $keyLen );
+ if ( $foundKey === $key ) {
+ // Found
+ $this->dataLen = $dataLen;
+ $this->dataPos = $dataPos;
+
+ return true;
+ }
}
}
}
@@ -208,13 +269,36 @@ class PHP extends Reader {
}
/**
- * @param mixed $key
- * @return bool
+ * Check if a key exists in the CDB file.
+ *
+ * @param string $key
+ * @return bool Whether the key exists.
*/
- protected function find( $key ) {
- $this->findStart();
+ public function exists( $key ) {
+ return $this->find( strval( $key ) );
+ }
+
+ /**
+ * Get the first key from the CDB file and reset the key iterator.
+ *
+ * @return string Key.
+ */
+ public function firstkey() {
+ $this->keyIterPos = 2048;
+ return $this->nextkey();
+ }
+
+ /**
+ * Get the next key from the CDB file.
+ *
+ * @return string Key.
+ */
+ public function nextkey() {
+ $keyLen = $this->readInt31( $this->keyIterPos );
+ $dataLen = $this->readInt31( $this->keyIterPos + 4 );
+ $key = $this->read( $this->keyIterPos + 8, $keyLen );
+ $this->keyIterPos += 8 + $keyLen + $dataLen;
- return $this->findNext( $key );
+ return $key;
}
}
-
diff --git a/vendor/wikimedia/cdb/src/Writer.php b/vendor/wikimedia/cdb/src/Writer.php
index b994ec67..53216041 100644
--- a/vendor/wikimedia/cdb/src/Writer.php
+++ b/vendor/wikimedia/cdb/src/Writer.php
@@ -94,6 +94,6 @@ abstract class Writer {
* @return bool
*/
protected function isWindows() {
- return substr( php_uname(), 0, 7 ) == 'Windows';
+ return strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
}
}
diff --git a/vendor/wikimedia/cdb/src/Writer/DBA.php b/vendor/wikimedia/cdb/src/Writer/DBA.php
index eb525f4c..20d0adfb 100644
--- a/vendor/wikimedia/cdb/src/Writer/DBA.php
+++ b/vendor/wikimedia/cdb/src/Writer/DBA.php
@@ -1,6 +1,7 @@
<?php
namespace Cdb\Writer;
+
use Cdb\Exception;
use Cdb\Writer;
diff --git a/vendor/wikimedia/cdb/src/Writer/PHP.php b/vendor/wikimedia/cdb/src/Writer/PHP.php
index 68c6b760..4676b5e4 100644
--- a/vendor/wikimedia/cdb/src/Writer/PHP.php
+++ b/vendor/wikimedia/cdb/src/Writer/PHP.php
@@ -1,6 +1,7 @@
<?php
namespace Cdb\Writer;
+
use Cdb\Exception;
use Cdb\Util;
use Cdb\Writer;
diff --git a/vendor/wikimedia/cdb/test/CdbTest.php b/vendor/wikimedia/cdb/test/CdbTest.php
deleted file mode 100644
index 920fc111..00000000
--- a/vendor/wikimedia/cdb/test/CdbTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-namespace Cdb\Test;
-use Cdb\Reader;
-use Cdb\Writer;
-
-/**
- * Test the CDB reader/writer
- * @covers Cdb\Writer\PHP
- * @covers Cdb\Writer\DBA
- */
-class CdbTest extends \PHPUnit_Framework_TestCase {
- /** @var string */
- private $phpCdbFile, $dbaCdbFile;
-
- protected function setUp() {
- parent::setUp();
- if ( !Reader::haveExtension() ) {
- $this->markTestSkipped( 'Native CDB support is not available.' );
- }
- $temp = sys_get_temp_dir();
- if ( !is_writable( $temp ) ) {
- $this->markTestSkipped( "Temp dir [$temp] isn't writable." );
- }
- $this->phpCdbFile = tempnam( $temp, get_class( $this ) . '_' );
- $this->dbaCdbFile = tempnam( $temp, get_class( $this ) . '_' );
- }
-
- /**
- * Make a random-ish string
- * @return string
- */
- private static function randomString() {
- $len = mt_rand( 0, 10 );
- $s = '';
- for ( $j = 0; $j < $len; $j++ ) {
- $s .= chr( mt_rand( 0, 255 ) );
- }
-
- return $s;
- }
-
- public function testCdbWrite() {
- $w1 = new Writer\PHP( $this->phpCdbFile );
- $w2 = new Writer\DBA( $this->dbaCdbFile );
-
- $data = array();
- for ( $i = 0; $i < 1000; $i++ ) {
- $key = self::randomString();
- $value = self::randomString();
- $w1->set( $key, $value );
- $w2->set( $key, $value );
-
- if ( !isset( $data[$key] ) ) {
- $data[$key] = $value;
- }
- }
-
- $w1->close();
- $w2->close();
-
- $this->assertEquals(
- md5_file( $this->phpCdbFile ),
- md5_file( $this->dbaCdbFile ),
- 'same hash'
- );
-
- $r1 = new Reader\PHP( $this->phpCdbFile );
- $r2 = new Reader\DBA( $this->dbaCdbFile );
-
- foreach ( $data as $key => $value ) {
- if ( $key === '' ) {
- // Known bug
- continue;
- }
- $v1 = $r1->get( $key );
- $v2 = $r2->get( $key );
-
- $v1 = $v1 === false ? '(not found)' : $v1;
- $v2 = $v2 === false ? '(not found)' : $v2;
-
- # cdbAssert( 'Mismatch', $key, $v1, $v2 );
- $this->cdbAssert( "PHP error", $key, $v1, $value );
- $this->cdbAssert( "DBA error", $key, $v2, $value );
- }
- }
-
- private function cdbAssert( $msg, $key, $v1, $v2 ) {
- $this->assertEquals(
- $v2,
- $v1,
- $msg . ', k=' . bin2hex( $key )
- );
- }
-}
diff --git a/vendor/wikimedia/composer-merge-plugin/.arcconfig b/vendor/wikimedia/composer-merge-plugin/.arcconfig
new file mode 100644
index 00000000..3dfae3d9
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/.arcconfig
@@ -0,0 +1,6 @@
+{
+ "phabricator.uri" : "https://phabricator.wikimedia.org/",
+ "repository.callsign" : "GCMP",
+ "history.immutable" : false,
+ "unit.engine": "PhpunitTestEngine"
+}
diff --git a/vendor/wikimedia/composer-merge-plugin/.arclint b/vendor/wikimedia/composer-merge-plugin/.arclint
new file mode 100644
index 00000000..da42f29a
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/.arclint
@@ -0,0 +1,13 @@
+{
+ "exclude": "(^vendor/)",
+ "linters": {
+ "php": {
+ "type": "php",
+ "include": "(\\.php$)"
+ },
+ "json": {
+ "type": "json",
+ "include": "(\\.json$)"
+ }
+ }
+}
diff --git a/vendor/wikimedia/composer-merge-plugin/LICENSE b/vendor/wikimedia/composer-merge-plugin/LICENSE
index 55e376b4..3c9804a6 100644
--- a/vendor/wikimedia/composer-merge-plugin/LICENSE
+++ b/vendor/wikimedia/composer-merge-plugin/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014 Bryan Davis, Wikimedia Foundation, and contributors
+Copyright (c) 2015 Bryan Davis, Wikimedia Foundation, and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/wikimedia/composer-merge-plugin/README.md b/vendor/wikimedia/composer-merge-plugin/README.md
index 53d64579..f020d40a 100644
--- a/vendor/wikimedia/composer-merge-plugin/README.md
+++ b/vendor/wikimedia/composer-merge-plugin/README.md
@@ -1,18 +1,34 @@
-[![Latest Stable Version](https://img.shields.io/packagist/v/wikimedia/composer-merge-plugin.svg?style=flat)](https://packagist.org/packages/wikimedia/composer-merge-plugin) [![License](https://img.shields.io/packagist/l/wikimedia/composer-merge-plugin.svg?style=flat)](https://github.com/wikimedia/composer-merge-plugin/blob/master/LICENSE)
-[![Build Status](https://img.shields.io/travis/wikimedia/composer-merge-plugin.svg?style=flat)](https://travis-ci.org/wikimedia/composer-merge-plugin)
-[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/wikimedia/composer-merge-plugin/master.svg?style=flat)](https://scrutinizer-ci.com/g/wikimedia/composer-merge-plugin/?branch=master)
+[![Latest Stable Version]](https://packagist.org/packages/wikimedia/composer-merge-plugin) [![License]](https://github.com/wikimedia/composer-merge-plugin/blob/master/LICENSE)
+[![Build Status]](https://travis-ci.org/wikimedia/composer-merge-plugin)
+[![Code Coverage]](https://scrutinizer-ci.com/g/wikimedia/composer-merge-plugin/?branch=master)
Composer Merge Plugin
=====================
-Merge one or more additional composer.json files at runtime.
+Merge multiple composer.json files at [Composer] runtime.
+
+Composer Merge Plugin is intended to allow easier dependency management for
+applications which ship a composer.json file and expect some deployments to
+install additional Composer managed libraries. It does this by allowing the
+application's top level `composer.json` file to provide a list of optional
+additional configuration files. When Composer is run it will parse these files
+and merge their configuration settings into the base configuration. This
+combined configuration will then be used when downloading additional libraries
+and generating the autoloader.
+
+Composer Merge Plugin was created to help with installation of [MediaWiki]
+which has core library requirements as well as optional libraries and
+extensions which may be managed via Composer.
+
Installation
------------
+
```
$ composer require wikimedia/composer-merge-plugin
```
+
Usage
-----
@@ -26,33 +42,111 @@ Usage
"include": [
"composer.local.json",
"extensions/*/composer.json"
- ]
+ ],
+ "require": [
+ "submodule/composer.json"
+ ],
+ "recurse": true,
+ "replace": false,
+ "merge-dev": true,
+ "merge-extra": false
}
}
}
```
-The `include` key can specify either a single value or an array of values.
-Each value is treated as a glob() pattern identifying additional composer.json
-style configuration files to merge into the configuration for the current
-Composer execution. By default the merge plugin is recursive, if an included
-file also has a "merge-plugin" section it will also be processed. This
-functionality can be disabled by setting `"recurse": false` inside the
-"merge-plugin" section.
-The "require", "require-dev", "repositories" and "suggest" sections of the
-found configuration files will be merged into the root package configuration
-as though they were directly included in the top-level composer.json file.
+Plugin configuration
+--------------------
+
+The plugin reads its configuration from the `merge-plugin` section of your
+composer.json's `extra` section. An `include` setting is required to tell
+Composer Merge Plugin which file(s) to merge.
+
+
+### include
+
+The `include` setting can specify either a single value or an array of values.
+Each value is treated as a PHP `glob()` pattern identifying additional
+composer.json style configuration files to merge into the root package
+configuration for the current Composer execution.
+
+The following sections of the found configuration files will be merged into
+the Composer root package configuration as though they were directly included
+in the top-level composer.json file:
+
+* [autoload](https://getcomposer.org/doc/04-schema.md#autoload)
+* [autoload-dev](https://getcomposer.org/doc/04-schema.md#autoload-dev)
+ (optional, see [merge-dev](#merge-dev) below)
+* [conflict](https://getcomposer.org/doc/04-schema.md#conflict)
+* [provide](https://getcomposer.org/doc/04-schema.md#provide)
+* [replace](https://getcomposer.org/doc/04-schema.md#replace)
+* [repositories](https://getcomposer.org/doc/04-schema.md#repositories)
+* [require](https://getcomposer.org/doc/04-schema.md#require)
+* [require-dev](https://getcomposer.org/doc/04-schema.md#require-dev)
+ (optional, see [merge-dev](#merge-dev) below)
+* [suggest](https://getcomposer.org/doc/04-schema.md#suggest)
+* [extra](https://getcomposer.org/doc/04-schema.md#extra)
+ (optional, see [merge-extra](#merge-extra) below)
+
+
+### require
+
+The `require` setting is identical to `[include](#include)` except when
+a pattern fails to match at least one file then it will cause an error.
+
+### recurse
+
+By default the merge plugin is recursive; if an included file has
+a `merge-plugin` section it will also be processed. This functionality can be
+disabled by adding a `"recurse": false` setting.
+
+
+### replace
+
+By default, Composer's conflict resolution engine is used to determine which
+version of a package should be installed when multiple files specify the same
+package. A `"replace": true` setting can be provided to change to a "last
+version specified wins" conflict resolution strategy. In this mode, duplicate
+package declarations found in merged files will overwrite the declarations
+made by earlier files. Files are loaded in the order specified by the
+`include` setting with globbed files being processed in alphabetical order.
+
+
+### merge-dev
+
+By default, `autoload-dev` and `require-dev` sections of included files are
+merged. A `"merge-dev": false` setting will disable this behavior.
+
+
+### merge-extra
+
+A `"merge-extra": true` setting enables the merging the contents of the
+`extra` section of included files as well. The normal merge mode for the extra
+section is to accept the first version of any key found (e.g. a key in the
+master config wins over the version found in any imported config). If
+`replace` mode is active ([see above](#replace)) then this behavior changes
+and the last key found will win (e.g. the key in the master config is replaced
+by the key in the imported config). The usefulness of merging the extra
+section will vary depending on the Composer plugins being used and the order
+in which they are processed by Composer.
+
+Note that `merge-plugin` sections are excluded from the merge process, but are
+always processed by the plugin unless [recursion](#recurse) is disabled.
+
Running tests
-------------
+
```
$ composer install
$ composer test
```
+
Contributing
------------
+
Bug, feature requests and other issues should be reported to the [GitHub
project]. We accept code and documentation contributions via Pull Requests on
GitHub as well.
@@ -69,12 +163,21 @@ GitHub as well.
easier for you to make sure you have updated the necessary tests and
documentation.
+
License
-------
+
Composer Merge plugin is licensed under the MIT license. See the `LICENSE`
file for more details.
+
---
+[Composer]: https://getcomposer.org/
+[MediaWiki]: https://www.mediawiki.org/wiki/MediaWiki
[GitHub project]: https://github.com/wikimedia/composer-merge-plugin
[PSR-2 Coding Standard]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
[PHP Code Sniffer]: http://pear.php.net/package/PHP_CodeSniffer
+[Latest Stable Version]: https://img.shields.io/packagist/v/wikimedia/composer-merge-plugin.svg?style=flat
+[License]: https://img.shields.io/packagist/l/wikimedia/composer-merge-plugin.svg?style=flat
+[Build Status]: https://img.shields.io/travis/wikimedia/composer-merge-plugin.svg?style=flat
+[Code Coverage]: https://img.shields.io/scrutinizer/coverage/g/wikimedia/composer-merge-plugin/master.svg?style=flat
diff --git a/vendor/wikimedia/composer-merge-plugin/composer.json b/vendor/wikimedia/composer-merge-plugin/composer.json
index 5ef429ad..dce70e7d 100644
--- a/vendor/wikimedia/composer-merge-plugin/composer.json
+++ b/vendor/wikimedia/composer-merge-plugin/composer.json
@@ -3,18 +3,23 @@
"description": "Composer plugin to merge multiple composer.json files",
"type": "composer-plugin",
"license": "MIT",
+ "authors": [
+ {
+ "name": "Bryan Davis",
+ "email": "bd808@wikimedia.org"
+ }
+ ],
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
- "php": ">=5.3.2",
- "composer-plugin-api": "1.0.0"
+ "composer-plugin-api": "^1.0",
+ "php": ">=5.3.2"
},
"require-dev": {
"composer/composer": "1.0.*@dev",
- "phpunit/phpunit": "~4.0",
"jakub-onderka/php-parallel-lint": "~0.8",
- "squizlabs/php_codesniffer": "~2.1.0",
- "phpspec/prophecy-phpunit": "~1.0"
+ "phpunit/phpunit": "~4.8|~5.0",
+ "squizlabs/php_codesniffer": "~2.1.0"
},
"autoload": {
"psr-4": {
@@ -22,6 +27,9 @@
}
},
"extra": {
+ "branch-alias": {
+ "dev-master": "1.3.x-dev"
+ },
"class": "Wikimedia\\Composer\\MergePlugin"
},
"config": {
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Logger.php b/vendor/wikimedia/composer-merge-plugin/src/Logger.php
new file mode 100644
index 00000000..1635a2b0
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/src/Logger.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * This file is part of the Composer Merge plugin.
+ *
+ * Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
+ *
+ * This software may be modified and distributed under the terms of the MIT
+ * license. See the LICENSE file for details.
+ */
+
+namespace Wikimedia\Composer;
+
+use Composer\IO\IOInterface;
+
+/**
+ * Simple logging wrapper for Composer\IO\IOInterface
+ *
+ * @author Bryan Davis <bd808@bd808.com>
+ */
+class Logger
+{
+ /**
+ * @var string $name
+ */
+ protected $name;
+
+ /**
+ * @var IOInterface $inputOutput
+ */
+ protected $inputOutput;
+
+ /**
+ * @param string $name
+ * @param IOInterface $io
+ */
+ public function __construct($name, IOInterface $io)
+ {
+ $this->name = $name;
+ $this->inputOutput = $io;
+ }
+
+ /**
+ * Log a debug message
+ *
+ * Messages will be output at the "very verbose" logging level (eg `-vv`
+ * needed on the Composer command).
+ *
+ * @param string $message
+ */
+ public function debug($message)
+ {
+ if ($this->inputOutput->isVeryVerbose()) {
+ $message = " <info>[{$this->name}]</info> {$message}";
+ $this->log($message);
+ }
+ }
+
+ /**
+ * Log an informative message
+ *
+ * Messages will be output at the "verbose" logging level (eg `-v` needed
+ * on the Composer command).
+ *
+ * @param string $message
+ */
+ public function info($message)
+ {
+ if ($this->inputOutput->isVerbose()) {
+ $message = " <info>[{$this->name}]</info> {$message}";
+ $this->log($message);
+ }
+ }
+
+ /**
+ * Log a warning message
+ *
+ * @param string $message
+ */
+ public function warning($message)
+ {
+ $message = " <error>[{$this->name}]</error> {$message}";
+ $this->log($message);
+ }
+
+ /**
+ * Write a message
+ *
+ * @param string $message
+ */
+ protected function log($message)
+ {
+ if (method_exists($this->inputOutput, 'writeError')) {
+ $this->inputOutput->writeError($message);
+ } else {
+ // @codeCoverageIgnoreStart
+ // Backwards compatiblity for Composer before cb336a5
+ $this->inputOutput->write($message);
+ // @codeCoverageIgnoreEnd
+ }
+ }
+}
+// vim:sw=4:ts=4:sts=4:et:
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php
new file mode 100644
index 00000000..ebecdff5
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php
@@ -0,0 +1,487 @@
+<?php
+/**
+ * This file is part of the Composer Merge plugin.
+ *
+ * Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
+ *
+ * This software may be modified and distributed under the terms of the MIT
+ * license. See the LICENSE file for details.
+ */
+
+namespace Wikimedia\Composer\Merge;
+
+use Wikimedia\Composer\Logger;
+
+use Composer\Composer;
+use Composer\Json\JsonFile;
+use Composer\Package\BasePackage;
+use Composer\Package\CompletePackage;
+use Composer\Package\Link;
+use Composer\Package\Loader\ArrayLoader;
+use Composer\Package\RootAliasPackage;
+use Composer\Package\RootPackage;
+use Composer\Package\RootPackageInterface;
+use Composer\Package\Version\VersionParser;
+use UnexpectedValueException;
+
+/**
+ * Processing for a composer.json file that will be merged into
+ * a RootPackageInterface
+ *
+ * @author Bryan Davis <bd808@bd808.com>
+ */
+class ExtraPackage
+{
+
+ /**
+ * @var Composer $composer
+ */
+ protected $composer;
+
+ /**
+ * @var Logger $logger
+ */
+ protected $logger;
+
+ /**
+ * @var string $path
+ */
+ protected $path;
+
+ /**
+ * @var array $json
+ */
+ protected $json;
+
+ /**
+ * @var CompletePackage $package
+ */
+ protected $package;
+
+ /**
+ * @param string $path Path to composer.json file
+ * @param Composer $composer
+ * @param Logger $logger
+ */
+ public function __construct($path, Composer $composer, Logger $logger)
+ {
+ $this->path = $path;
+ $this->composer = $composer;
+ $this->logger = $logger;
+ $this->json = $this->readPackageJson($path);
+ $this->package = $this->loadPackage($this->json);
+ }
+
+ /**
+ * Get list of additional packages to include if precessing recursively.
+ *
+ * @return array
+ */
+ public function getIncludes()
+ {
+ return isset($this->json['extra']['merge-plugin']['include']) ?
+ $this->json['extra']['merge-plugin']['include'] : array();
+ }
+
+ /**
+ * Get list of additional packages to require if precessing recursively.
+ *
+ * @return array
+ */
+ public function getRequires()
+ {
+ return isset($this->json['extra']['merge-plugin']['require']) ?
+ $this->json['extra']['merge-plugin']['require'] : array();
+ }
+
+ /**
+ * Read the contents of a composer.json style file into an array.
+ *
+ * The package contents are fixed up to be usable to create a Package
+ * object by providing dummy "name" and "version" values if they have not
+ * been provided in the file. This is consistent with the default root
+ * package loading behavior of Composer.
+ *
+ * @param string $path
+ * @return array
+ */
+ protected function readPackageJson($path)
+ {
+ $file = new JsonFile($path);
+ $json = $file->read();
+ if (!isset($json['name'])) {
+ $json['name'] = 'merge-plugin/' .
+ strtr($path, DIRECTORY_SEPARATOR, '-');
+ }
+ if (!isset($json['version'])) {
+ $json['version'] = '1.0.0';
+ }
+ return $json;
+ }
+
+ /**
+ * @return CompletePackage
+ */
+ protected function loadPackage($json)
+ {
+ $loader = new ArrayLoader();
+ $package = $loader->load($json);
+ // @codeCoverageIgnoreStart
+ if (!$package instanceof CompletePackage) {
+ throw new UnexpectedValueException(
+ 'Expected instance of CompletePackage, got ' .
+ get_class($package)
+ );
+ }
+ // @codeCoverageIgnoreEnd
+ return $package;
+ }
+
+ /**
+ * Merge this package into a RootPackageInterface
+ *
+ * @param RootPackageInterface $root
+ * @param PluginState $state
+ */
+ public function mergeInto(RootPackageInterface $root, PluginState $state)
+ {
+ $this->addRepositories($root);
+
+ $this->mergeRequires('require', $root, $state);
+ if ($state->isDevMode()) {
+ $this->mergeRequires('require-dev', $root, $state);
+ }
+
+ $this->mergePackageLinks('conflict', $root);
+ $this->mergePackageLinks('replace', $root);
+ $this->mergePackageLinks('provide', $root);
+
+ $this->mergeSuggests($root);
+
+ $this->mergeAutoload('autoload', $root);
+ if ($state->isDevMode()) {
+ $this->mergeAutoload('devAutoload', $root);
+ }
+
+ $this->mergeExtra($root, $state);
+ }
+
+ /**
+ * Add a collection of repositories described by the given configuration
+ * to the given package and the global repository manager.
+ *
+ * @param RootPackageInterface $root
+ */
+ protected function addRepositories(RootPackageInterface $root)
+ {
+ if (!isset($this->json['repositories'])) {
+ return;
+ }
+ $repoManager = $this->composer->getRepositoryManager();
+ $newRepos = array();
+
+ foreach ($this->json['repositories'] as $repoJson) {
+ if (!isset($repoJson['type'])) {
+ continue;
+ }
+ $this->logger->info("Adding {$repoJson['type']} repository");
+ $repo = $repoManager->createRepository(
+ $repoJson['type'],
+ $repoJson
+ );
+ $repoManager->addRepository($repo);
+ $newRepos[] = $repo;
+ }
+
+ $unwrapped = self::unwrapIfNeeded($root, 'setRepositories');
+ $unwrapped->setRepositories(array_merge(
+ $newRepos,
+ $root->getRepositories()
+ ));
+ }
+
+ /**
+ * Merge require or require-dev into a RootPackageInterface
+ *
+ * @param string $type 'require' or 'require-dev'
+ * @param RootPackageInterface $root
+ * @param PluginState $state
+ */
+ protected function mergeRequires(
+ $type,
+ RootPackageInterface $root,
+ PluginState $state
+ ) {
+ $linkType = BasePackage::$supportedLinkTypes[$type];
+ $getter = 'get' . ucfirst($linkType['method']);
+ $setter = 'set' . ucfirst($linkType['method']);
+
+ $requires = $this->package->{$getter}();
+ if (empty($requires)) {
+ return;
+ }
+
+ $this->mergeStabilityFlags($root, $requires);
+
+ $requires = $this->replaceSelfVersionDependencies(
+ $type,
+ $requires,
+ $root
+ );
+
+ $root->{$setter}($this->mergeOrDefer(
+ $type,
+ $root->{$getter}(),
+ $requires,
+ $state
+ ));
+ }
+
+ /**
+ * Merge two collections of package links and collect duplicates for
+ * subsequent processing.
+ *
+ * @param string $type 'require' or 'require-dev'
+ * @param array $origin Primary collection
+ * @param array $merge Additional collection
+ * @param PluginState $state
+ * @return array Merged collection
+ */
+ protected function mergeOrDefer(
+ $type,
+ array $origin,
+ array $merge,
+ $state
+ ) {
+ $dups = array();
+ foreach ($merge as $name => $link) {
+ if (!isset($origin[$name]) || $state->replaceDuplicateLinks()) {
+ $this->logger->info("Merging <comment>{$name}</comment>");
+ $origin[$name] = $link;
+ } else {
+ // Defer to solver.
+ $this->logger->info(
+ "Deferring duplicate <comment>{$name}</comment>"
+ );
+ $dups[] = $link;
+ }
+ }
+ $state->addDuplicateLinks($type, $dups);
+ return $origin;
+ }
+
+ /**
+ * Merge autoload or autoload-dev into a RootPackageInterface
+ *
+ * @param string $type 'autoload' or 'devAutoload'
+ * @param RootPackageInterface $root
+ */
+ protected function mergeAutoload($type, RootPackageInterface $root)
+ {
+ $getter = 'get' . ucfirst($type);
+ $setter = 'set' . ucfirst($type);
+
+ $autoload = $this->package->{$getter}();
+ if (empty($autoload)) {
+ return;
+ }
+
+ $unwrapped = self::unwrapIfNeeded($root, $setter);
+ $unwrapped->{$setter}(array_merge_recursive(
+ $root->{$getter}(),
+ $this->fixRelativePaths($autoload)
+ ));
+ }
+
+ /**
+ * Fix a collection of paths that are relative to this package to be
+ * relative to the base package.
+ *
+ * @param array $paths
+ * @return array
+ */
+ protected function fixRelativePaths(array $paths)
+ {
+ $base = dirname($this->path);
+ $base = ($base === '.') ? '' : "{$base}/";
+
+ array_walk_recursive(
+ $paths,
+ function (&$path) use ($base) {
+ $path = "{$base}{$path}";
+ }
+ );
+ return $paths;
+ }
+
+ /**
+ * Extract and merge stability flags from the given collection of
+ * requires and merge them into a RootPackageInterface
+ *
+ * @param RootPackageInterface $root
+ * @param array $requires
+ */
+ protected function mergeStabilityFlags(
+ RootPackageInterface $root,
+ array $requires
+ ) {
+ $flags = $root->getStabilityFlags();
+ $sf = new StabilityFlags($flags, $root->getMinimumStability());
+
+ $unwrapped = self::unwrapIfNeeded($root, 'setStabilityFlags');
+ $unwrapped->setStabilityFlags(array_merge(
+ $flags,
+ $sf->extractAll($requires)
+ ));
+ }
+
+ /**
+ * Merge package links of the given type into a RootPackageInterface
+ *
+ * @param string $type 'conflict', 'replace' or 'provide'
+ * @param RootPackageInterface $root
+ */
+ protected function mergePackageLinks($type, RootPackageInterface $root)
+ {
+ $linkType = BasePackage::$supportedLinkTypes[$type];
+ $getter = 'get' . ucfirst($linkType['method']);
+ $setter = 'set' . ucfirst($linkType['method']);
+
+ $links = $this->package->{$getter}();
+ if (!empty($links)) {
+ $unwrapped = self::unwrapIfNeeded($root, $setter);
+ if ($root !== $unwrapped) {
+ $this->logger->warning(
+ 'This Composer version does not support ' .
+ "'{$type}' merging for aliased packages."
+ );
+ }
+ $unwrapped->{$setter}(array_merge(
+ $root->{$getter}(),
+ $this->replaceSelfVersionDependencies($type, $links, $root)
+ ));
+ }
+ }
+
+ /**
+ * Merge suggested packages into a RootPackageInterface
+ *
+ * @param RootPackageInterface $root
+ */
+ protected function mergeSuggests(RootPackageInterface $root)
+ {
+ $suggests = $this->package->getSuggests();
+ if (!empty($suggests)) {
+ $unwrapped = self::unwrapIfNeeded($root, 'setSuggests');
+ $unwrapped->setSuggests(array_merge(
+ $root->getSuggests(),
+ $suggests
+ ));
+ }
+ }
+
+ /**
+ * Merge extra config into a RootPackageInterface
+ *
+ * @param RootPackageInterface $root
+ * @param PluginState $state
+ */
+ public function mergeExtra(RootPackageInterface $root, PluginState $state)
+ {
+ $extra = $this->package->getExtra();
+ unset($extra['merge-plugin']);
+ if (!$state->shouldMergeExtra() || empty($extra)) {
+ return;
+ }
+
+ $rootExtra = $root->getExtra();
+ $unwrapped = self::unwrapIfNeeded($root, 'setExtra');
+
+ if ($state->replaceDuplicateLinks()) {
+ $unwrapped->setExtra(
+ array_merge($rootExtra, $extra)
+ );
+
+ } else {
+ foreach (array_intersect(
+ array_keys($extra),
+ array_keys($rootExtra)
+ ) as $key) {
+ $this->logger->info(
+ "Ignoring duplicate <comment>{$key}</comment> in ".
+ "<comment>{$this->path}</comment> extra config."
+ );
+ }
+ $unwrapped->setExtra(
+ array_merge($extra, $rootExtra)
+ );
+ }
+ }
+
+ /**
+ * Update Links with a 'self.version' constraint with the root package's
+ * version.
+ *
+ * @param string $type Link type
+ * @param array $links
+ * @param RootPackageInterface $root
+ * @return array
+ */
+ protected function replaceSelfVersionDependencies(
+ $type,
+ array $links,
+ RootPackageInterface $root
+ ) {
+ $linkType = BasePackage::$supportedLinkTypes[$type];
+ $version = $root->getVersion();
+ $prettyVersion = $root->getPrettyVersion();
+ $vp = new VersionParser();
+
+ return array_map(
+ function ($link) use ($linkType, $version, $prettyVersion, $vp) {
+ if ('self.version' === $link->getPrettyConstraint()) {
+ return new Link(
+ $link->getSource(),
+ $link->getTarget(),
+ $vp->parseConstraints($version),
+ $linkType['description'],
+ $prettyVersion
+ );
+ }
+ return $link;
+ },
+ $links
+ );
+ }
+
+ /**
+ * Get a full featured Package from a RootPackageInterface.
+ *
+ * In Composer versions before 599ad77 the RootPackageInterface only
+ * defines a sub-set of operations needed by composer-merge-plugin and
+ * RootAliasPackage only implemented those methods defined by the
+ * interface. Most of the unimplemented methods in RootAliasPackage can be
+ * worked around because the getter methods that are implemented proxy to
+ * the aliased package which we can modify by unwrapping. The exception
+ * being modifying the 'conflicts', 'provides' and 'replaces' collections.
+ * We have no way to actually modify those collections unfortunately in
+ * older versions of Composer.
+ *
+ * @param RootPackageInterface $root
+ * @param string $method Method needed
+ * @return RootPackageInterface|RootPackage
+ */
+ public static function unwrapIfNeeded(
+ RootPackageInterface $root,
+ $method = 'setExtra'
+ ) {
+ if ($root instanceof RootAliasPackage &&
+ !method_exists($root, $method)
+ ) {
+ // Unwrap and return the aliased RootPackage.
+ $root = $root->getAliasOf();
+ }
+ return $root;
+ }
+}
+// vim:sw=4:ts=4:sts=4:et:
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php
new file mode 100644
index 00000000..873719d7
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * This file is part of the Composer Merge plugin.
+ *
+ * Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
+ *
+ * This software may be modified and distributed under the terms of the MIT
+ * license. See the LICENSE file for details.
+ */
+
+namespace Wikimedia\Composer\Merge;
+
+/**
+ * @author Bryan Davis <bd808@bd808.com>
+ */
+class MissingFileException extends \RuntimeException
+{
+}
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php
new file mode 100644
index 00000000..a191c1e8
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/PluginState.php
@@ -0,0 +1,327 @@
+<?php
+/**
+ * This file is part of the Composer Merge plugin.
+ *
+ * Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
+ *
+ * This software may be modified and distributed under the terms of the MIT
+ * license. See the LICENSE file for details.
+ */
+
+namespace Wikimedia\Composer\Merge;
+
+use Composer\Composer;
+
+/**
+ * Mutable plugin state
+ *
+ * @author Bryan Davis <bd808@bd808.com>
+ */
+class PluginState
+{
+ /**
+ * @var Composer $composer
+ */
+ protected $composer;
+
+ /**
+ * @var array $includes
+ */
+ protected $includes = array();
+
+ /**
+ * @var array $requires
+ */
+ protected $requires = array();
+
+ /**
+ * @var array $duplicateLinks
+ */
+ protected $duplicateLinks = array();
+
+ /**
+ * @var bool $devMode
+ */
+ protected $devMode = false;
+
+ /**
+ * @var bool $recurse
+ */
+ protected $recurse = true;
+
+ /**
+ * @var bool $replace
+ */
+ protected $replace = false;
+
+ /**
+ * Whether to merge the -dev sections.
+ * @var bool $mergeDev
+ */
+ protected $mergeDev = true;
+
+ /**
+ * Whether to merge the extra section.
+ *
+ * By default, the extra section is not merged and there will be many
+ * cases where the merge of the extra section is performed too late
+ * to be of use to other plugins. When enabled, merging uses one of
+ * two strategies - either 'first wins' or 'last wins'. When enabled,
+ * 'first wins' is the default behaviour. If Replace mode is activated
+ * then 'last wins' is used.
+ *
+ * @var bool $mergeExtra
+ */
+ protected $mergeExtra = false;
+
+ /**
+ * @var bool $firstInstall
+ */
+ protected $firstInstall = false;
+
+ /**
+ * @var bool $locked
+ */
+ protected $locked = false;
+
+ /**
+ * @var bool $dumpAutoloader
+ */
+ protected $dumpAutoloader = false;
+
+ /**
+ * @var bool $optimizeAutoloader
+ */
+ protected $optimizeAutoloader = false;
+
+ /**
+ * @param Composer $composer
+ */
+ public function __construct(Composer $composer)
+ {
+ $this->composer = $composer;
+ }
+
+ /**
+ * Load plugin settings
+ */
+ public function loadSettings()
+ {
+ $extra = $this->composer->getPackage()->getExtra();
+ $config = array_merge(
+ array(
+ 'include' => array(),
+ 'require' => array(),
+ 'recurse' => true,
+ 'replace' => false,
+ 'merge-dev' => true,
+ 'merge-extra' => false,
+ ),
+ isset($extra['merge-plugin']) ? $extra['merge-plugin'] : array()
+ );
+
+ $this->includes = (is_array($config['include'])) ?
+ $config['include'] : array($config['include']);
+ $this->requires = (is_array($config['require'])) ?
+ $config['require'] : array($config['require']);
+ $this->recurse = (bool)$config['recurse'];
+ $this->replace = (bool)$config['replace'];
+ $this->mergeDev = (bool)$config['merge-dev'];
+ $this->mergeExtra = (bool)$config['merge-extra'];
+ }
+
+ /**
+ * Get list of filenames and/or glob patterns to include
+ *
+ * @return array
+ */
+ public function getIncludes()
+ {
+ return $this->includes;
+ }
+
+ /**
+ * Get list of filenames and/or glob patterns to require
+ *
+ * @return array
+ */
+ public function getRequires()
+ {
+ return $this->requires;
+ }
+
+ /**
+ * Set the first install flag
+ *
+ * @param bool $flag
+ */
+ public function setFirstInstall($flag)
+ {
+ $this->firstInstall = (bool)$flag;
+ }
+
+ /**
+ * Is this the first time that the plugin has been installed?
+ *
+ * @return bool
+ */
+ public function isFirstInstall()
+ {
+ return $this->firstInstall;
+ }
+
+ /**
+ * Set the locked flag
+ *
+ * @param bool $flag
+ */
+ public function setLocked($flag)
+ {
+ $this->locked = (bool)$flag;
+ }
+
+ /**
+ * Was a lockfile present when the plugin was installed?
+ *
+ * @return bool
+ */
+ public function isLocked()
+ {
+ return $this->locked;
+ }
+
+ /**
+ * Should an update be forced?
+ *
+ * @return true If packages are not locked
+ */
+ public function forceUpdate()
+ {
+ return !$this->locked;
+ }
+
+ /**
+ * Set the devMode flag
+ *
+ * @param bool $flag
+ */
+ public function setDevMode($flag)
+ {
+ $this->devMode = (bool)$flag;
+ }
+
+ /**
+ * Should devMode settings be processed?
+ *
+ * @return bool
+ */
+ public function isDevMode()
+ {
+ return $this->mergeDev && $this->devMode;
+ }
+
+ /**
+ * Set the dumpAutoloader flag
+ *
+ * @param bool $flag
+ */
+ public function setDumpAutoloader($flag)
+ {
+ $this->dumpAutoloader = (bool)$flag;
+ }
+
+ /**
+ * Is the autoloader file supposed to be written out?
+ *
+ * @return bool
+ */
+ public function shouldDumpAutoloader()
+ {
+ return $this->dumpAutoloader;
+ }
+
+ /**
+ * Set the optimizeAutoloader flag
+ *
+ * @param bool $flag
+ */
+ public function setOptimizeAutoloader($flag)
+ {
+ $this->optimizeAutoloader = (bool)$flag;
+ }
+
+ /**
+ * Should the autoloader be optimized?
+ *
+ * @return bool
+ */
+ public function shouldOptimizeAutoloader()
+ {
+ return $this->optimizeAutoloader;
+ }
+
+ /**
+ * Add duplicate packages
+ *
+ * @param string $type Package type
+ * @param array $packages
+ */
+ public function addDuplicateLinks($type, array $packages)
+ {
+ if (!isset($this->duplicateLinks[$type])) {
+ $this->duplicateLinks[$type] = array();
+ }
+ $this->duplicateLinks[$type] =
+ array_merge($this->duplicateLinks[$type], $packages);
+ }
+
+ /**
+ * Get duplicate packages
+ *
+ * @param string $type Package type
+ * @return array
+ */
+ public function getDuplicateLinks($type)
+ {
+ return isset($this->duplicateLinks[$type]) ?
+ $this->duplicateLinks[$type] : array();
+ }
+
+ /**
+ * Should includes be recursively processed?
+ *
+ * @return bool
+ */
+ public function recurseIncludes()
+ {
+ return $this->recurse;
+ }
+
+ /**
+ * Should duplicate links be replaced in a 'last definition wins' order?
+ *
+ * @return bool
+ */
+ public function replaceDuplicateLinks()
+ {
+ return $this->replace;
+ }
+
+ /**
+ * Should the extra section be merged?
+ *
+ * By default, the extra section is not merged and there will be many
+ * cases where the merge of the extra section is performed too late
+ * to be of use to other plugins. When enabled, merging uses one of
+ * two strategies - either 'first wins' or 'last wins'. When enabled,
+ * 'first wins' is the default behaviour. If Replace mode is activated
+ * then 'last wins' is used.
+ *
+ * @return bool
+ */
+ public function shouldMergeExtra()
+ {
+ return $this->mergeExtra;
+ }
+}
+// vim:sw=4:ts=4:sts=4:et:
diff --git a/vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php b/vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php
new file mode 100644
index 00000000..1c106e02
--- /dev/null
+++ b/vendor/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * This file is part of the Composer Merge plugin.
+ *
+ * Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
+ *
+ * This software may be modified and distributed under the terms of the MIT
+ * license. See the LICENSE file for details.
+ */
+
+namespace Wikimedia\Composer\Merge;
+
+use Composer\Package\BasePackage;
+use Composer\Package\Version\VersionParser;
+
+/**
+ * Adapted from Composer's RootPackageLoader::extractStabilityFlags
+ * @author Bryan Davis <bd808@bd808.com>
+ */
+class StabilityFlags
+{
+
+ /**
+ * @var array Current package name => stability mappings
+ */
+ protected $stabilityFlags;
+
+ /**
+ * @var int Current default minimum stability
+ */
+ protected $minimumStability;
+
+ /**
+ * @var string Regex to extract an explict stability flag (eg '@dev')
+ */
+ protected $explicitStabilityRe;
+
+
+ /**
+ * @param array $stabilityFlags Current package name => stability mappings
+ * @param int $minimumStability Current default minimum stability
+ */
+ public function __construct(
+ array $stabilityFlags = array(),
+ $minimumStability = BasePackage::STABILITY_STABLE
+ ) {
+ $this->stabilityFlags = $stabilityFlags;
+ $this->minimumStability = $this->getStabilityInt($minimumStability);
+ $this->explicitStabilityRe = '/^[^@]*?@(' .
+ implode('|', array_keys(BasePackage::$stabilities)) .
+ ')$/i';
+ }
+
+ /**
+ * Get the stability value for a given string.
+ *
+ * @param string $name Stability name
+ * @return int Stability value
+ */
+ protected function getStabilityInt($name)
+ {
+ $name = VersionParser::normalizeStability($name);
+ return isset(BasePackage::$stabilities[$name]) ?
+ BasePackage::$stabilities[$name] :
+ BasePackage::STABILITY_STABLE;
+ }
+
+ /**
+ * Extract and merge stability flags from the given collection of
+ * requires with another collection of stability flags.
+ *
+ * @param array $requires New package name => link mappings
+ * @return array Unified package name => stability mappings
+ */
+ public function extractAll(array $requires)
+ {
+ $flags = array();
+
+ foreach ($requires as $name => $link) {
+ $name = strtolower($name);
+ $version = $link->getPrettyConstraint();
+
+ $stability = $this->getExplicitStability($version);
+
+ if ($stability === null) {
+ $stability = $this->getParsedStability($version);
+ }
+
+ $flags[$name] = max($stability, $this->getCurrentStability($name));
+ }
+
+ // Filter out null stability values
+ return array_filter($flags, function ($v) {
+ return $v !== null;
+ });
+ }
+
+
+ /**
+ * Extract the most unstable explicit stability (eg '@dev') from a version
+ * specification.
+ *
+ * @param string $version
+ * @return int|null Stability or null if no explict stability found
+ */
+ protected function getExplicitStability($version)
+ {
+ $found = null;
+ $constraints = $this->splitConstraints($version);
+ foreach ($constraints as $constraint) {
+ if (preg_match($this->explicitStabilityRe, $constraint, $match)) {
+ $stability = $this->getStabilityInt($match[1]);
+ $found = max($stability, $found);
+ }
+ }
+ return $found;
+ }
+
+
+ /**
+ * Split a version specification into a list of version constraints.
+ *
+ * @param string $version
+ * @return array
+ */
+ protected function splitConstraints($version)
+ {
+ $found = array();
+ $orConstraints = preg_split('/\s*\|\|?\s*/', trim($version));
+ foreach ($orConstraints as $constraints) {
+ $andConstraints = preg_split(
+ '/(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)/',
+ $constraints
+ );
+ foreach ($andConstraints as $constraint) {
+ $found[] = $constraint;
+ }
+ }
+ return $found;
+ }
+
+
+ /**
+ * Get the stability of a version
+ *
+ * @param string $version
+ * @return int|null Stability or null if STABLE or less than minimum
+ */
+ protected function getParsedStability($version)
+ {
+ // Drop aliasing if used
+ $version = preg_replace('/^([^,\s@]+) as .+$/', '$1', $version);
+ $stability = $this->getStabilityInt(
+ VersionParser::parseStability($version)
+ );
+
+ if ($stability === BasePackage::STABILITY_STABLE ||
+ $this->minimumStability > $stability
+ ) {
+ // Ignore if 'stable' or more stable than the global
+ // minimum
+ $stability = null;
+ }
+
+ return $stability;
+ }
+
+
+ /**
+ * Get the current stability of a given package.
+ *
+ * @param string $name
+ * @return int|null Stability of null if not set
+ */
+ protected function getCurrentStability($name)
+ {
+ return isset($this->stabilityFlags[$name]) ?
+ $this->stabilityFlags[$name] : null;
+ }
+}
+// vim:sw=4:ts=4:sts=4:et:
diff --git a/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php b/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
index 04c55886..ea41d41a 100644
--- a/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
+++ b/vendor/wikimedia/composer-merge-plugin/src/MergePlugin.php
@@ -2,7 +2,7 @@
/**
* This file is part of the Composer Merge plugin.
*
- * Copyright (C) 2014 Bryan Davis, Wikimedia Foundation, and contributors
+ * Copyright (C) 2015 Bryan Davis, Wikimedia Foundation, and contributors
*
* This software may be modified and distributed under the terms of the MIT
* license. See the LICENSE file for details.
@@ -10,39 +10,56 @@
namespace Wikimedia\Composer;
+use Wikimedia\Composer\Merge\ExtraPackage;
+use Wikimedia\Composer\Merge\MissingFileException;
+use Wikimedia\Composer\Merge\PluginState;
+
use Composer\Composer;
-use Composer\Config;
+use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\EventDispatcher\EventSubscriberInterface;
+use Composer\Factory;
+use Composer\Installer;
use Composer\Installer\InstallerEvent;
use Composer\Installer\InstallerEvents;
+use Composer\Installer\PackageEvent;
+use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
-use Composer\Json\JsonFile;
-use Composer\Package\BasePackage;
-use Composer\Package\CompletePackage;
-use Composer\Package\Loader\ArrayLoader;
use Composer\Package\RootPackageInterface;
-use Composer\Package\Version\VersionParser;
use Composer\Plugin\PluginInterface;
-use Composer\Script\CommandEvent;
+use Composer\Script\Event;
use Composer\Script\ScriptEvents;
/**
* Composer plugin that allows merging multiple composer.json files.
*
- * When installed, this plugin will look for a "merge-patterns" key in the
- * composer configuration's "extra" section. The value of this setting can be
- * either a single value or an array of values. Each value is treated as
- * a glob() pattern identifying additional composer.json style configuration
- * files to merge into the configuration for the current compser execution.
+ * When installed, this plugin will look for a "merge-plugin" key in the
+ * composer configuration's "extra" section. The value for this key is
+ * a set of options configuring the plugin.
+ *
+ * An "include" setting is required. The value of this setting can be either
+ * a single value or an array of values. Each value is treated as a glob()
+ * pattern identifying additional composer.json style configuration files to
+ * merge into the configuration for the current compser execution.
*
- * The "require", "require-dev", "repositories" and "suggest" sections of the
+ * The "autoload", "autoload-dev", "conflict", "provide", "replace",
+ * "repositories", "require", "require-dev", and "suggest" sections of the
* found configuration files will be merged into the root package
* configuration as though they were directly included in the top-level
* composer.json file.
*
* If included files specify conflicting package versions for "require" or
* "require-dev", the normal Composer dependency solver process will be used
- * to attempt to resolve the conflict.
+ * to attempt to resolve the conflict. Specifying the 'replace' key as true will
+ * change this default behaviour so that the last-defined version of a package
+ * will win, allowing for force-overrides of package defines.
+ *
+ * By default the "extra" section is not merged. This can be enabled by
+ * setitng the 'merge-extra' key to true. In normal mode, when the same key is
+ * found in both the original and the imported extra section, the version in
+ * the original config is used and the imported version is skipped. If
+ * 'replace' mode is active, this behaviour changes so the imported version of
+ * the key is used, replacing the version in the original config.
+ *
*
* @code
* {
@@ -65,36 +82,24 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
{
/**
- * @var Composer $composer
+ * Offical package name
*/
- protected $composer;
+ const PACKAGE_NAME = 'wikimedia/composer-merge-plugin';
/**
- * @var IOInterface $inputOutput
- */
- protected $inputOutput;
-
- /**
- * @var ArrayLoader $loader
- */
- protected $loader;
-
- /**
- * @var array $duplicateLinks
+ * @var Composer $composer
*/
- protected $duplicateLinks;
+ protected $composer;
/**
- * @var bool $devMode
+ * @var PluginState $state
*/
- protected $devMode;
+ protected $state;
/**
- * Whether to recursively include dependencies
- *
- * @var bool $recurse
+ * @var Logger $logger
*/
- protected $recurse = true;
+ protected $logger;
/**
* Files that have already been processed
@@ -109,7 +114,8 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
public function activate(Composer $composer, IOInterface $io)
{
$this->composer = $composer;
- $this->inputOutput = $io;
+ $this->state = new PluginState($this->composer);
+ $this->logger = new Logger('merge-plugin', $io);
}
/**
@@ -119,69 +125,66 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
{
return array(
InstallerEvents::PRE_DEPENDENCIES_SOLVING => 'onDependencySolve',
- ScriptEvents::PRE_INSTALL_CMD => 'onInstallOrUpdate',
- ScriptEvents::PRE_UPDATE_CMD => 'onInstallOrUpdate',
+ PackageEvents::POST_PACKAGE_INSTALL => 'onPostPackageInstall',
+ ScriptEvents::POST_INSTALL_CMD => 'onPostInstallOrUpdate',
+ ScriptEvents::POST_UPDATE_CMD => 'onPostInstallOrUpdate',
+ ScriptEvents::PRE_AUTOLOAD_DUMP => 'onInstallUpdateOrDump',
+ ScriptEvents::PRE_INSTALL_CMD => 'onInstallUpdateOrDump',
+ ScriptEvents::PRE_UPDATE_CMD => 'onInstallUpdateOrDump',
);
}
/**
- * Handle an event callback for an install or update command by checking
- * for "merge-patterns" in the "extra" data and merging package contents
- * if found.
+ * Handle an event callback for an install, update or dump command by
+ * checking for "merge-plugin" in the "extra" data and merging package
+ * contents if found.
*
- * @param CommandEvent $event
+ * @param Event $event
*/
- public function onInstallOrUpdate(CommandEvent $event)
+ public function onInstallUpdateOrDump(Event $event)
{
- $config = $this->readConfig($this->composer->getPackage());
- if (isset($config['recurse'])) {
- $this->recurse = (bool)$config['recurse'];
- }
- if ($config['include']) {
- $this->loader = new ArrayLoader();
- $this->duplicateLinks = array(
- 'require' => array(),
- 'require-dev' => array(),
- );
- $this->devMode = $event->isDevMode();
- $this->mergePackages($config);
- }
- }
-
- /**
- * @param RootPackageInterface $package
- * @return array
- */
- protected function readConfig(RootPackageInterface $package)
- {
- $config = array(
- 'include' => array(),
- );
- $extra = $package->getExtra();
- if (isset($extra['merge-plugin'])) {
- $config = array_merge($config, $extra['merge-plugin']);
- if (!is_array($config['include'])) {
- $config['include'] = array($config['include']);
+ $this->state->loadSettings();
+ $this->state->setDevMode($event->isDevMode());
+ $this->mergeFiles($this->state->getIncludes(), false);
+ $this->mergeFiles($this->state->getRequires(), true);
+
+ if ($event->getName() === ScriptEvents::PRE_AUTOLOAD_DUMP) {
+ $this->state->setDumpAutoloader(true);
+ $flags = $event->getFlags();
+ if (isset($flags['optimize'])) {
+ $this->state->setOptimizeAutoloader($flags['optimize']);
}
}
- return $config;
}
/**
* Find configuration files matching the configured glob patterns and
* merge their contents with the master package.
*
- * @param array $config
+ * @param array $patterns List of files/glob patterns
+ * @param bool $required Are the patterns required to match files?
+ * @throws MissingFileException when required and a pattern returns no
+ * results
*/
- protected function mergePackages(array $config)
+ protected function mergeFiles(array $patterns, $required = false)
{
$root = $this->composer->getPackage();
- foreach (array_reduce(
- array_map('glob', $config['include']),
- 'array_merge',
- array()
- ) as $path) {
- $this->loadFile($root, $path);
+
+ $files = array_map(
+ function ($files, $pattern) use ($required) {
+ if ($required && !$files) {
+ throw new MissingFileException(
+ "merge-plugin: No files matched required '{$pattern}'"
+ );
+ }
+ return $files;
+ },
+ array_map('glob', $patterns),
+ $patterns
+ );
+
+ foreach (array_reduce($files, 'array_merge', array()) as $path) {
+ $this->mergeFile($root, $path);
}
}
@@ -191,221 +194,121 @@ class MergePlugin implements PluginInterface, EventSubscriberInterface
* @param RootPackageInterface $root
* @param string $path
*/
- protected function loadFile($root, $path)
+ protected function mergeFile(RootPackageInterface $root, $path)
{
- if (in_array($path, $this->loadedFiles)) {
- $this->debug("Skipping duplicate <comment>$path</comment>...");
+ if (isset($this->loadedFiles[$path])) {
+ $this->logger->debug("Already merged <comment>$path</comment>");
return;
} else {
- $this->loadedFiles[] = $path;
+ $this->loadedFiles[$path] = true;
}
- $this->debug("Loading <comment>{$path}</comment>...");
- $json = $this->readPackageJson($path);
- $package = $this->loader->load($json);
-
- $this->mergeRequires($root, $package);
- $this->mergeDevRequires($root, $package);
+ $this->logger->info("Loading <comment>{$path}</comment>...");
- if (isset($json['repositories'])) {
- $this->addRepositories($json['repositories'], $root);
- }
+ $package = new ExtraPackage($path, $this->composer, $this->logger);
+ $package->mergeInto($root, $this->state);
- if ($package->getSuggests()) {
- $root->setSuggests(array_merge(
- $root->getSuggests(),
- $package->getSuggests()
- ));
- }
-
- if ($this->recurse && isset($json['extra']['merge-plugin'])) {
- $this->mergePackages($json['extra']['merge-plugin']);
+ if ($this->state->recurseIncludes()) {
+ $this->mergeFiles($package->getIncludes(), false);
+ $this->mergeFiles($package->getRequires(), true);
}
}
/**
- * Read the contents of a composer.json style file into an array.
- *
- * The package contents are fixed up to be usable to create a Package
- * object by providing dummy "name" and "version" values if they have not
- * been provided in the file. This is consistent with the default root
- * package loading behavior of Composer.
+ * Handle an event callback for pre-dependency solving phase of an install
+ * or update by adding any duplicate package dependencies found during
+ * initial merge processing to the request that will be processed by the
+ * dependency solver.
*
- * @param string $path
- * @return array
+ * @param InstallerEvent $event
*/
- protected function readPackageJson($path)
+ public function onDependencySolve(InstallerEvent $event)
{
- $file = new JsonFile($path);
- $json = $file->read();
- if (!isset($json['name'])) {
- $json['name'] = 'merge-plugin/' .
- strtr($path, DIRECTORY_SEPARATOR, '-');
- }
- if (!isset($json['version'])) {
- $json['version'] = '1.0.0';
- }
- return $json;
- }
-
- /**
- * @param RootPackageInterface $root
- * @param CompletePackage $package
- */
- protected function mergeRequires(
- RootPackageInterface $root,
- CompletePackage $package
- ) {
- $requires = $package->getRequires();
- if (!$requires) {
- return;
+ $request = $event->getRequest();
+ foreach ($this->state->getDuplicateLinks('require') as $link) {
+ $this->logger->info(
+ "Adding dependency <comment>{$link}</comment>"
+ );
+ $request->install($link->getTarget(), $link->getConstraint());
}
-
- $this->mergeStabilityFlags($root, $requires);
-
- $root->setRequires($this->mergeLinks(
- $root->getRequires(),
- $requires,
- $this->duplicateLinks['require']
- ));
- }
-
- /**
- * @param RootPackageInterface $root
- * @param CompletePackage $package
- */
- protected function mergeDevRequires(
- RootPackageInterface $root,
- CompletePackage $package
- ) {
- $requires = $package->getDevRequires();
- if (!$requires) {
- return;
+ if ($this->state->isDevMode()) {
+ foreach ($this->state->getDuplicateLinks('require-dev') as $link) {
+ $this->logger->info(
+ "Adding dev dependency <comment>{$link}</comment>"
+ );
+ $request->install($link->getTarget(), $link->getConstraint());
+ }
}
-
- $this->mergeStabilityFlags($root, $requires);
-
- $root->setDevRequires($this->mergeLinks(
- $root->getDevRequires(),
- $requires,
- $this->duplicateLinks['require-dev']
- ));
}
/**
- * Extract and merge stability flags from the given collection of
- * requires.
+ * Handle an event callback following installation of a new package by
+ * checking to see if the package that was installed was our plugin.
*
- * @param RootPackageInterface $root
- * @param array $requires
+ * @param PackageEvent $event
*/
- protected function mergeStabilityFlags(
- RootPackageInterface $root,
- array $requires
- ) {
- $flags = $root->getStabilityFlags();
- foreach ($requires as $name => $link) {
- $name = strtolower($name);
- $version = $link->getPrettyConstraint();
- $stability = VersionParser::parseStability($version);
- $flags[$name] = BasePackage::$stabilities[$stability];
+ public function onPostPackageInstall(PackageEvent $event)
+ {
+ $op = $event->getOperation();
+ if ($op instanceof InstallOperation) {
+ $package = $op->getPackage()->getName();
+ if ($package === self::PACKAGE_NAME) {
+ $this->logger->info('composer-merge-plugin installed');
+ $this->state->setFirstInstall(true);
+ $this->state->setLocked(
+ $event->getComposer()->getLocker()->isLocked()
+ );
+ }
}
- $root->setStabilityFlags($flags);
}
/**
- * Add a collection of repositories described by the given configuration
- * to the given package and the global repository manager.
+ * Handle an event callback following an install or update command. If our
+ * plugin was installed during the run then trigger an update command to
+ * process any merge-patterns in the current config.
*
- * @param array $repositories
- * @param RootPackageInterface $root
+ * @param Event $event
*/
- protected function addRepositories(
- array $repositories,
- RootPackageInterface $root
- ) {
- $repoManager = $this->composer->getRepositoryManager();
- $newRepos = array();
-
- foreach ($repositories as $repoJson) {
- $this->debug("Adding {$repoJson['type']} repository");
- $repo = $repoManager->createRepository(
- $repoJson['type'],
- $repoJson
+ public function onPostInstallOrUpdate(Event $event)
+ {
+ // @codeCoverageIgnoreStart
+ if ($this->state->isFirstInstall()) {
+ $this->state->setFirstInstall(false);
+ $this->logger->info(
+ '<comment>' .
+ 'Running additional update to apply merge settings' .
+ '</comment>'
);
- $repoManager->addRepository($repo);
- $newRepos[] = $repo;
- }
- $root->setRepositories(array_merge(
- $newRepos,
- $root->getRepositories()
- ));
- }
+ $config = $this->composer->getConfig();
- /**
- * Merge two collections of package links and collect duplicates for
- * subsequent processing.
- *
- * @param array $origin Primary collection
- * @param array $merge Additional collection
- * @param array &dups Duplicate storage
- * @return array Merged collection
- */
- protected function mergeLinks(array $origin, array $merge, array &$dups)
- {
- foreach ($merge as $name => $link) {
- if (!isset($origin[$name])) {
- $this->debug("Merging <comment>{$name}</comment>");
- $origin[$name] = $link;
- } else {
- // Defer to solver.
- $this->debug("Deferring duplicate <comment>{$name}</comment>");
- $dups[] = $link;
- }
- }
- return $origin;
- }
+ $preferSource = $config->get('preferred-install') == 'source';
+ $preferDist = $config->get('preferred-install') == 'dist';
- /**
- * Handle an event callback for pre-dependency solving phase of an install
- * or update by adding any duplicate package dependencies found during
- * initial merge processing to the request that will be processed by the
- * dependency solver.
- *
- * @param InstallerEvent $event
- */
- public function onDependencySolve(InstallerEvent $event)
- {
- if (!$this->duplicateLinks) {
- return;
- }
+ $installer = Installer::create(
+ $event->getIO(),
+ // Create a new Composer instance to ensure full processing of
+ // the merged files.
+ Factory::create($event->getIO(), null, false)
+ );
- $request = $event->getRequest();
- foreach ($this->duplicateLinks['require'] as $link) {
- $this->debug("Adding dependency <comment>{$link}</comment>");
- $request->install($link->getTarget(), $link->getConstraint());
- }
- if ($this->devMode) {
- foreach ($this->duplicateLinks['require-dev'] as $link) {
- $this->debug("Adding dev dependency <comment>{$link}</comment>");
- $request->install($link->getTarget(), $link->getConstraint());
+ $installer->setPreferSource($preferSource);
+ $installer->setPreferDist($preferDist);
+ $installer->setDevMode($event->isDevMode());
+ $installer->setDumpAutoloader($this->state->shouldDumpAutoloader());
+ $installer->setOptimizeAutoloader(
+ $this->state->shouldOptimizeAutoloader()
+ );
+
+ if ($this->state->forceUpdate()) {
+ // Force update mode so that new packages are processed rather
+ // than just telling the user that composer.json and
+ // composer.lock don't match.
+ $installer->setUpdate(true);
}
- }
- }
- /**
- * Log a debug message
- *
- * Messages will be output at the "verbose" logging level (eg `-v` needed
- * on the Composer command).
- *
- * @param string $message
- */
- protected function debug($message)
- {
- if ($this->inputOutput->isVerbose()) {
- $this->inputOutput->write(" <info>[merge]</info> {$message}");
+ $installer->run();
}
+ // @codeCoverageIgnoreEnd
}
}
// vim:sw=4:ts=4:sts=4:et:
diff --git a/vendor/wikimedia/ip-set/COPYING b/vendor/wikimedia/ip-set/COPYING
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/vendor/wikimedia/ip-set/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/vendor/wikimedia/ip-set/README.md b/vendor/wikimedia/ip-set/README.md
new file mode 100644
index 00000000..480e1678
--- /dev/null
+++ b/vendor/wikimedia/ip-set/README.md
@@ -0,0 +1,83 @@
+IPSet
+=====
+
+IPSet is a PHP library for matching IP addresses against a set of CIDR
+specifications.
+
+Here is how you use it:
+
+<pre lang="php">
+// At startup, calculate the optimized data structure for the set:
+$ipset = new IPSet( array(
+ '208.80.154.0/26',
+ '2620:0:861:1::/64',
+ '10.64.0.0/22',
+) );
+
+// Runtime check against cached set (returns bool):
+if ( $ipset->match( $ip ) ) {
+ // ...
+}
+</pre>
+
+In rough benchmarking, this takes about 80% more time than `in_array()` checks
+on a short (a couple hundred at most) array of addresses. It's fast either way
+at those levels, though, and IPSet would scale better than in_array if the
+array were much larger.
+
+For mixed-family CIDR sets, however, this code gives well over 100x speedup vs
+iterating `IP::isInRange()` over an array of CIDR specs.
+
+The basic implementation is two separate binary trees (IPv4 and IPv6) as nested
+php arrays with keys named 0 and 1. The values false and true are terminal
+match-fail and match-success, otherwise the value is a deeper node in the tree.
+
+A simple depth-compression scheme is also implemented: whole-byte tree
+compression at whole-byte boundaries only, where no branching occurs during
+that whole byte of depth. A compressed node has keys 'comp' (the byte to
+compare) and 'next' (the next node to recurse into if 'comp' matched successfully).
+
+For example, given these inputs:
+
+<pre>
+25.0.0.0/9
+25.192.0.0/10
+</pre>
+
+The v4 tree would look like:
+
+<pre lang="php">
+root4 => array(
+ 'comp' => 25,
+ 'next' => array(
+ 0 => true,
+ 1 => array(
+ 0 => false,
+ 1 => true,
+ ),
+ ),
+);
+</pre>
+
+(multi-byte compression nodes were attempted as well, but were
+a net loss in my test scenarios due to additional match complexity)
+
+
+License
+-------
+Copyright 2014, 2015 Brandon Black <blblack@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+<http://www.gnu.org/copyleft/gpl.html>
diff --git a/vendor/wikimedia/ip-set/src/IPSet.php b/vendor/wikimedia/ip-set/src/IPSet.php
new file mode 100644
index 00000000..1c79f067
--- /dev/null
+++ b/vendor/wikimedia/ip-set/src/IPSet.php
@@ -0,0 +1,284 @@
+<?php
+/**
+ * Copyright 2014, 2015 Brandon Black <blblack@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @author Brandon Black <blblack@gmail.com>
+ */
+namespace IPSet;
+
+/**
+ * Matches IP addresses against a set of CIDR specifications
+ *
+ * Usage:
+ *
+ * // At startup, calculate the optimized data structure for the set:
+ * $ipset = new IPSet( array(
+ * '208.80.154.0/26',
+ * '2620:0:861:1::/64',
+ * '10.64.0.0/22',
+ * ) );
+ *
+ * // Runtime check against cached set (returns bool):
+ * $allowme = $ipset->match( $ip );
+ *
+ * In rough benchmarking, this takes about 80% more time than
+ * in_array() checks on a short (a couple hundred at most) array
+ * of addresses. It's fast either way at those levels, though,
+ * and IPSet would scale better than in_array if the array were
+ * much larger.
+ *
+ * For mixed-family CIDR sets, however, this code gives well over
+ * 100x speedup vs iterating IP::isInRange() over an array
+ * of CIDR specs.
+ *
+ * The basic implementation is two separate binary trees
+ * (IPv4 and IPv6) as nested php arrays with keys named 0 and 1.
+ * The values false and true are terminal match-fail and match-success,
+ * otherwise the value is a deeper node in the tree.
+ *
+ * A simple depth-compression scheme is also implemented: whole-byte
+ * tree compression at whole-byte boundaries only, where no branching
+ * occurs during that whole byte of depth. A compressed node has
+ * keys 'comp' (the byte to compare) and 'next' (the next node to
+ * recurse into if 'comp' matched successfully).
+ *
+ * For example, given these inputs:
+ *
+ * 25.0.0.0/9
+ * 25.192.0.0/10
+ *
+ * The v4 tree would look like:
+ *
+ * root4 => array(
+ * 'comp' => 25,
+ * 'next' => array(
+ * 0 => true,
+ * 1 => array(
+ * 0 => false,
+ * 1 => true,
+ * ),
+ * ),
+ * );
+ *
+ * (multi-byte compression nodes were attempted as well, but were
+ * a net loss in my test scenarios due to additional match complexity)
+ */
+class IPSet {
+ /** @var array $root4 The root of the IPv4 matching tree */
+ private $root4 = array( false, false );
+
+ /** @var array $root6 The root of the IPv6 matching tree */
+ private $root6 = array( false, false );
+
+ /**
+ * Instantiate the object from an array of CIDR specs
+ *
+ * Invalid input network/mask values in $cfg will result in issuing
+ * E_WARNING and/or E_USER_WARNING and the bad values being ignored.
+ *
+ * @param array $cfg Array of IPv[46] CIDR specs as strings
+ */
+ public function __construct( array $cfg ) {
+ foreach ( $cfg as $cidr ) {
+ $this->addCidr( $cidr );
+ }
+
+ self::recOptimize( $this->root4 );
+ self::recCompress( $this->root4, 0, 24 );
+ self::recOptimize( $this->root6 );
+ self::recCompress( $this->root6, 0, 120 );
+ }
+
+ /**
+ * Add a single CIDR spec to the internal matching trees
+ *
+ * @param string $cidr String CIDR spec, IPv[46], optional /mask (def all-1's)
+ */
+ private function addCidr( $cidr ) {
+ // v4 or v6 check
+ if ( strpos( $cidr, ':' ) === false ) {
+ $node =& $this->root4;
+ $defMask = '32';
+ } else {
+ $node =& $this->root6;
+ $defMask = '128';
+ }
+
+ // Default to all-1's mask if no netmask in the input
+ if ( strpos( $cidr, '/' ) === false ) {
+ $net = $cidr;
+ $mask = $defMask;
+ } else {
+ list( $net, $mask ) = explode( '/', $cidr, 2 );
+ if ( !ctype_digit( $mask ) || intval( $mask ) > $defMask ) {
+ trigger_error( "IPSet: Bad mask '$mask' from '$cidr', ignored", E_USER_WARNING );
+ return;
+ }
+ }
+ $mask = intval( $mask ); // explicit integer convert, checked above
+
+ // convert $net to an array of integer bytes, length 4 or 16:
+ $raw = inet_pton( $net );
+ if ( $raw === false ) {
+ return; // inet_pton() sends an E_WARNING for us
+ }
+ $rawOrd = array_map( 'ord', str_split( $raw ) );
+
+ // special-case: zero mask overwrites the whole tree with a pair of terminal successes
+ if ( $mask == 0 ) {
+ $node = array( true, true );
+ return;
+ }
+
+ // iterate the bits of the address while walking the tree structure for inserts
+ $curBit = 0;
+ while ( 1 ) {
+ $maskShift = 7 - ( $curBit & 7 );
+ $node =& $node[( $rawOrd[$curBit >> 3] & ( 1 << $maskShift ) ) >> $maskShift];
+ ++$curBit;
+ if ( $node === true ) {
+ // already added a larger supernet, no need to go deeper
+ return;
+ } elseif ( $curBit == $mask ) {
+ // this may wipe out deeper subnets from earlier
+ $node = true;
+ return;
+ } elseif ( $node === false ) {
+ // create new subarray to go deeper
+ $node = array( false, false );
+ }
+ }
+ }
+
+ /**
+ * Match an IP address against the set
+ *
+ * If $ip is unparseable, inet_pton may issue an E_WARNING to that effect
+ *
+ * @param string $ip string IPv[46] address
+ * @return bool True is match success, false is match failure
+ */
+ public function match( $ip ) {
+ $raw = inet_pton( $ip );
+ if ( $raw === false ) {
+ return false; // inet_pton() sends an E_WARNING for us
+ }
+
+ $rawOrd = array_map( 'ord', str_split( $raw ) );
+ if ( count( $rawOrd ) == 4 ) {
+ $node =& $this->root4;
+ } else {
+ $node =& $this->root6;
+ }
+
+ $curBit = 0;
+ while ( 1 ) {
+ if ( isset( $node['comp'] ) ) {
+ // compressed node, matches 1 whole byte on a byte boundary
+ if ( $rawOrd[$curBit >> 3] != $node['comp'] ) {
+ return false;
+ }
+ $curBit += 8;
+ $node =& $node['next'];
+ } else {
+ // uncompressed node, walk in the correct direction for the current bit-value
+ $maskShift = 7 - ( $curBit & 7 );
+ $node =& $node[( $rawOrd[$curBit >> 3] & ( 1 << $maskShift ) ) >> $maskShift];
+ ++$curBit;
+ }
+
+ if ( $node === true || $node === false ) {
+ return $node;
+ }
+ }
+ }
+
+ /**
+ * Recursively merges adjacent nets into larger supernets
+ *
+ * @param array &$node Tree node to optimize, by-reference
+ *
+ * e.g.: 8.0.0.0/8 + 9.0.0.0/8 -> 8.0.0.0/7
+ */
+ private static function recOptimize( &$node ) {
+ if ( $node[0] !== false && $node[0] !== true && self::recOptimize( $node[0] ) ) {
+ $node[0] = true;
+ }
+ if ( $node[1] !== false && $node[1] !== true && self::recOptimize( $node[1] ) ) {
+ $node[1] = true;
+ }
+ if ( $node[0] === true && $node[1] === true ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Recursively compresses a tree
+ *
+ * @param array &$node Tree node to compress, by-reference
+ * @param integer $curBit current depth in the tree
+ * @param integer $maxCompStart maximum depth at which compression can start, family-specific
+ *
+ * This is a very simplistic compression scheme: if we go through a whole
+ * byte of address starting at a byte boundary with no real branching
+ * other than immediate false-vs-(node|true), compress that subtree down to a single
+ * byte-matching node.
+ * The $maxCompStart check elides recursing the final 7 levels of depth (family-dependent)
+ */
+ private static function recCompress( &$node, $curBit, $maxCompStart ) {
+ if ( !( $curBit & 7 ) ) { // byte boundary, check for depth-8 single path(s)
+ $byte = 0;
+ $cnode =& $node;
+ $i = 8;
+ while ( $i-- ) {
+ if ( $cnode[0] === false ) {
+ $byte |= 1 << $i;
+ $cnode =& $cnode[1];
+ } elseif ( $cnode[1] === false ) {
+ $cnode =& $cnode[0];
+ } else {
+ // partial-byte branching, give up
+ break;
+ }
+ }
+ if ( $i == -1 ) { // means we did not exit the while() via break
+ $node = array(
+ 'comp' => $byte,
+ 'next' => &$cnode,
+ );
+ $curBit += 8;
+ if ( $cnode !== true ) {
+ self::recCompress( $cnode, $curBit, $maxCompStart );
+ }
+ return;
+ }
+ }
+
+ ++$curBit;
+ if ( $curBit <= $maxCompStart ) {
+ if ( $node[0] !== false && $node[0] !== true ) {
+ self::recCompress( $node[0], $curBit, $maxCompStart );
+ }
+ if ( $node[1] !== false && $node[1] !== true ) {
+ self::recCompress( $node[1], $curBit, $maxCompStart );
+ }
+ }
+ }
+}
diff --git a/vendor/wikimedia/utfnormal/COPYING b/vendor/wikimedia/utfnormal/COPYING
new file mode 100644
index 00000000..019694a9
--- /dev/null
+++ b/vendor/wikimedia/utfnormal/COPYING
@@ -0,0 +1,342 @@
+== GNU GENERAL PUBLIC LICENSE ==
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+=== Preamble ===
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+== TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ==
+
+'''0.''' This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+'''1.''' You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+'''2.''' You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ '''a)''' You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ '''b)''' You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ '''c)''' If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+'''3.''' You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ '''a)''' Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ '''b)''' Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ '''c)''' Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+'''4.''' You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+'''5.''' You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+'''6.''' Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+'''7.''' If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+'''8.''' If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+'''9.''' The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+'''10.''' If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+=== NO WARRANTY ===
+
+'''11.''' BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+'''12.''' IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ '''END OF TERMS AND CONDITIONS'''
+
+== How to Apply These Terms to Your New Programs ==
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/vendor/wikimedia/cdb/Doxyfile b/vendor/wikimedia/utfnormal/Doxyfile
index e77e75ae..c091ecb7 100644
--- a/vendor/wikimedia/cdb/Doxyfile
+++ b/vendor/wikimedia/utfnormal/Doxyfile
@@ -1,7 +1,7 @@
# Configuration file for Doxygen
-PROJECT_NAME = CDB
-PROJECT_BRIEF = CDB functions for PHP
+PROJECT_NAME = utfnormal
+PROJECT_BRIEF = "Unicode normalization for PHP"
OUTPUT_DIRECTORY = doc
diff --git a/vendor/wikimedia/wrappedstring/LICENSE b/vendor/wikimedia/wrappedstring/LICENSE
new file mode 100644
index 00000000..5d34a4b3
--- /dev/null
+++ b/vendor/wikimedia/wrappedstring/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Timo Tijhof <krinklemail@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/wikimedia/wrappedstring/README.md b/vendor/wikimedia/wrappedstring/README.md
new file mode 100644
index 00000000..5c8b8fb0
--- /dev/null
+++ b/vendor/wikimedia/wrappedstring/README.md
@@ -0,0 +1,24 @@
+WrappedString
+=============
+
+WrappedString is a small PHP library for compacting redundant string-wrapping
+code in text output. The most common use-case is to eliminate redundant runs of
+HTML open/close tags and JavaScript boilerplate.
+
+Here is how you use it:
+
+<pre lang="php">
+use WrappedString\WrappedString;
+
+$buffer = array(
+ new WrappedString( '[foo]', '[', ']' ),
+ new WrappedString( '[bar]', '[', ']' ),
+);
+$output = WrappedString::join( "\n", $buffer );
+// Result: '[foobar]'
+</pre>
+
+License
+-------
+
+The project is licensed under the MIT license.
diff --git a/vendor/wikimedia/wrappedstring/src/WrappedString.php b/vendor/wikimedia/wrappedstring/src/WrappedString.php
new file mode 100644
index 00000000..ed2593de
--- /dev/null
+++ b/vendor/wikimedia/wrappedstring/src/WrappedString.php
@@ -0,0 +1,110 @@
+<?php
+/**
+ * Copyright (c) 2015 Timo Tijhof <krinklemail@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @file
+ */
+
+namespace WrappedString;
+
+class WrappedString {
+ /** @var string */
+ protected $value;
+
+ /** @var string */
+ protected $prefix;
+
+ /** @var string */
+ protected $suffix;
+
+ /**
+ * @param string $value
+ * @param string $prefix
+ * @param string $suffix
+ */
+ public function __construct( $value, $prefix = null, $suffix = null ) {
+ $this->value = $value;
+ $this->prefix = $prefix;
+ $this->suffix = $suffix;
+ }
+
+ /**
+ * @param string $content
+ * @return WrappedString Newly wrapped string
+ */
+ protected function extend( $value ) {
+ $wrap = clone $this;
+ $suffixlen = strlen( $this->suffix );
+ if ( $suffixlen ) {
+ $wrap->value = substr( $this->value, 0, -$suffixlen );
+ }
+ $wrap->value .= substr( $value, strlen( $this->prefix ) );
+ return $wrap;
+ }
+
+ /**
+ * Merge consecutive wrapped strings with the same before/after values.
+ *
+ * Does not modify the array or the WrappedString objects.
+ *
+ * @param WrappedString[] $wraps
+ * @return WrappedString[]
+ */
+ protected static function compact( array &$wraps ) {
+ $consolidated = array();
+ $prev = current( $wraps );
+ while ( ( $wrap = next( $wraps ) ) !== false ) {
+ if ( $prev instanceof WrappedString
+ && $wrap instanceof WrappedString
+ && $prev->prefix !== null
+ && $prev->prefix === $wrap->prefix
+ && $prev->suffix !== null
+ && $prev->suffix === $wrap->suffix
+ ) {
+ $prev = $prev->extend( $wrap->value );
+ } else {
+ $consolidated[] = $prev;
+ $prev = $wrap;
+ }
+ }
+ // Add last one
+ $consolidated[] = $prev;
+
+ return $consolidated;
+ }
+
+ /**
+ * Join a several wrapped strings with a separator between each.
+ *
+ * @param string $sep
+ * @param WrappedString[] $wraps
+ * @return string
+ */
+ public static function join( $sep, array $wraps ) {
+ return implode( $sep, self::compact( $wraps ) );
+ }
+
+ /** @return string */
+ public function __toString() {
+ return $this->value;
+ }
+}