diff options
Diffstat (limited to 'vendor/wikimedia/avro')
-rw-r--r-- | vendor/wikimedia/avro/LICENSE.txt | 308 | ||||
-rw-r--r-- | vendor/wikimedia/avro/NOTICE.txt | 9 | ||||
-rw-r--r-- | vendor/wikimedia/avro/README.md | 23 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro.php | 195 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/data_file.php | 535 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/datum.php | 984 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/debug.php | 194 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/gmp.php | 222 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/io.php | 494 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/protocol.php | 86 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/schema.php | 1457 | ||||
-rw-r--r-- | vendor/wikimedia/avro/lib/avro/util.php | 67 |
12 files changed, 4574 insertions, 0 deletions
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; + } +} |