diff --git a/Zend/zend_API.h b/Zend/zend_API.h index c685ae9a48919..cb507132b8400 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -107,12 +107,67 @@ typedef struct _zend_fcall_info_cache { #define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE(type_hint, allow_null), pass_by_ref, 1 }, #define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 1 }, - -#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, classname, allow_null) \ +/** + * Start extended argument information block with an object return type + * declaration. + * + * ## Examples + * ```c + * ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arg_info_f, 0, 0, T, 0) + * ZEND_END_ARG_INFO() + * ``` + * + * Above example argument information applied to a function _f_ would result in + * the following PHP function signature: + * + * ```php + * function f(): T {} + * ``` + * + * @param[in] name + * of the variable where the argument information should be assigned to. + * @param[in] return_reference + * whether the routine returns by reference (`1`) or not (`0`). + * @param[in] required_num_args + * total amount of required (non-optional) arguments of the routine. + * @param[in] class_name + * of the object that this routine must return. + * @param[in] allow_null + * whether the routine's return type is nullable (`1`) or not (`0`). + * @see ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO + * for routines that do not take any arguments. + */ +#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \ static const zend_internal_arg_info name[] = { \ - { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), return_reference, 0 }, + { (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CLASS_CONST(#class_name, allow_null), return_reference, 0 }, + +/** + * Start argument information block with return object return type declaration. + * + * ## Examples + * ```c + * ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(arg_info_f, T, 0) + * ZEND_END_ARG_INFO() + * ``` + * + * Above example argument information applied to a function _f_ would result + * in the following PHP function signature: + * + * ```php + * function f(): T {} + * ``` + * + * @param[in] name + * of the variable where the argument information should be assigned to. + * @param[in] class_name + * of the object that this routine must return. + * @param[in] allow_null + * whether the routine's return type is nullable (`1`) or not (`0`). + * @see ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX + * which allows the specification of the total amount of required arguments. + */ #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(name, class_name, allow_null) \ - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, class_name, allow_null) + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, 0, -1, class_name, allow_null) #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \ static const zend_internal_arg_info name[] = { \ diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 3fea08c43f115..6ae9dbbad06c2 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -2,21 +2,24 @@ ReflectionExtension::getClassNames() method on an extension which actually returns some information --CREDITS-- Felix De Vliegher +Richard Fussenegger --FILE-- getClassNames()); ?> -==DONE== ---EXPECTF-- -array(4) { +--EXPECT-- +array(6) { [0]=> - %s(22) "__PHP_Incomplete_Class" + string(22) "__PHP_Incomplete_Class" [1]=> - %s(15) "php_user_filter" + string(15) "php_user_filter" [2]=> - %s(9) "Directory" + string(9) "Directory" [3]=> - %s(14) "AssertionError" + string(14) "AssertionError" + [4]=> + string(12) "PHP\Std\UUID" + [5]=> + string(26) "PHP\Std\UUIDParseException" } -==DONE== diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 3d911cec47c6d..8a61ee057b02a 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -3707,6 +3707,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(random) + BASIC_MINIT_SUBMODULE(uuid) return SUCCESS; } diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 284e74e967ea7..b60f1a2b8ccaa 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -455,7 +455,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32. http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ filters.c proc_open.c streamsfuncs.c http.c password.c \ - random.c,,, + random.c uuid.c,,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_MAKEFILE_FRAGMENT diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index 00b2166abe876..11d6941a1f478 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -31,7 +31,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ user_filters.c uuencode.c filters.c proc_open.c password.c \ - streamsfuncs.c http.c flock_compat.c random.c", false /* never shared */, + streamsfuncs.c http.c flock_compat.c random.c uuid.c", false /* never shared */, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); PHP_INSTALL_HEADERS("", "ext/standard"); if (PHP_MBREGEX != "no") { diff --git a/ext/standard/php_standard.h b/ext/standard/php_standard.h index 5b0111f143c31..db761aa819ee3 100644 --- a/ext/standard/php_standard.h +++ b/ext/standard/php_standard.h @@ -60,10 +60,14 @@ #include "php_type.h" #include "php_password.h" #include "php_random.h" +#include "php_uuid.h" #include "php_version.h" #define PHP_STANDARD_VERSION PHP_VERSION +/* PHP standard module namespace. */ +#define PHP_STD_NAMESPACE PHP_NAMESPACE PHP_NAMESPACE_SEPARATOR "Std" + #define phpext_standard_ptr basic_functions_module_ptr PHP_MINIT_FUNCTION(standard_filters); PHP_MSHUTDOWN_FUNCTION(standard_filters); diff --git a/ext/standard/php_uuid.h b/ext/standard/php_uuid.h new file mode 100644 index 0000000000000..7e07609882a9c --- /dev/null +++ b/ext/standard/php_uuid.h @@ -0,0 +1,678 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2017 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Richard Fussenegger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/** @file ext/standard/php_uuid.h + * + * Generate and parse RFC 4122/DCE 1.1 Universally Unique Identifiers (UUIDs). + * + * This standard submodule provides generation and parsing capabilities for + * Universally Unique Identifiers (UUIDs, also known as Globally Unique + * Identifiers [GUIDs]) as specified in [RFC 4122]. + * + * UUIDs are 128 bit integers that guarantee uniqueness across space and time. + * They are mainly used to assign identifiers to entities without requiring a + * central authority. They are thus particularly useful in distributed systems. + * They also allow very high allocation rates; up to 10 million per second per + * machine, if necessary. + * + * There are different types of UUIDs, known as variants. This implementation + * generates UUIDs according to the Leach-Salz variant; the one specified in + * [RFC 4122] as variant 1. Textual parsing supports both variant 1 ([RFC 4122]) + * and variant 2 (Microsoft), and construction supports any kind of UUID. + * However, note that the provided functions are **not** guaranteed to provide + * meaningful results if any other variants than the Leach-Salz one is used. + * + * There are also different UUID generation algorithms, known as versions. This + * implementation provides functions to generate version 3, 4, and 5 UUIDs. + * + * Versions 3 and 5 are meant for generating UUIDs from "names" that are drawn + * from, and unique within, some "namespace". They generate the same UUID for + * the same name in the same namespace across all [RFC 4122] compliant + * implementations. + * + * Version 4 UUIDs are random and result in a new UUID upon every generation. + * The randomness is provided by PHP's random bytes implementation, and thus + * uses the best available random source of the operating system. + * + * Versions 1 and 2 are not supported due to privacy/security concerns. Refer + * to the [Wikipedia] article for more information. + * + * This implementation is inspired by many other implementations: + * - [RFC 4122 Sample Implementation](https://tools.ietf.org/html/rfc4122#appendix-A) + * - [Rust UUID](https://github.com/rust-lang-nursery/uuid) + * - [util-linux LibUUID](https://github.com/karelzak/util-linux) + * - [boost Uuid](http://www.boost.org/doc/libs/1_64_0/libs/uuid/) + * - [D std.uuid](https://dlang.org/library/std/uuid.html) + * + * ### Examples + * + * ```c + * php_uuid uuid; + * + * // A random UUID with more randomness than the version 4 implementation. + * // Do NOT be used in real-world applications! + * php_random_bytes_silent(&uuid, PHP_UUID_LEN); + * + * // Set version to future reserved, because we are using a custom UUID. + * // Once more, do NOT use this in real-world applications! + * uuid.bytes = (uuid.bytes[8] & 0x1F) | 0xE0; + * + * php_uuid_create_v3(&uuid, php_uuid_namespace_dns(), "php.net", sizeof("php.net") - 1); + * assert(php_uuid_is_nil(uuid) == FALSE); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_3_NAME_BASED_MD5); + * + * php_uuid_create_v4_silent(&uuid); + * assert(php_uuid_is_nil(uuid) == FALSE); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_4_RANDOM); + * + * php_uuid_create_v5(&uuid, php_uuid_namespace_dns(), "php.net", sizeof("php.net") - 1); + * assert(php_uuid_is_nil(uuid) == FALSE); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_5_NAME_BASED_SHA1); + * + * php_uuid_parse_silent(&uuid, "urn:uuid:123E4567-E89B-12D3-A456-426655440000", sizeof("urn:uuid:123E4567-E89b-12D3-A456-426655440000") - 1); + * assert(php_uuid_is_nil(uuid) == FALSE); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_1_TIME_BASED); + * + * assert(memcmp(&uuid, "\x12\x3E\x45\x67\xE8\x9B\x12\xD3\xA4\x56\x42\x66\x55\x44\x00\x00", PHP_UUID_LEN) == 0); + * + * php_uuid_hex hex; + * php_uuid_to_hex(&hex, uuid); + * assert(memcmp(&hex, "123e4567e89b12d3a456426655440000", PHP_UUID_HEX_LEN) == 0); + * + * php_uuid_string str; + * php_uuid_to_string(&str, uuid); + * assert(memcmp(&str, "123e4567-e89b-12d3-a456-426655440000", PHP_UUID_STRING_LEN) == 0); + * ``` + * + * @author [Richard Fussenegger](mailto:php@fleshgrinder.com) + * @see [Wikipedia: Universally unique identifier][Wikipedia] + * @see [RFC 4122] + * + * [RFC 4122]: https://tools.ietf.org/html/rfc4122 + * [Wikipedia]: https://en.wikipedia.org/wiki/Universally_unique_identifier + */ + +#ifndef PHP_UUID_H +#define PHP_UUID_H + +#include "zend_types.h" +#include "php.h" + +/** `UUID` class entry for usage by other modules. */ +PHPAPI extern zend_class_entry *php_ce_UUID; + +/** `UUIDParseException` class entry for usage by other modules. */ +PHPAPI extern zend_class_entry *php_ce_UUIDParseException; + +/** UUID binary representation length without terminating NUL. */ +#define PHP_UUID_LEN 16 + +/** UUID binary representation to store the 128 bit number in 16 bytes. */ +typedef struct php_uuid { + uint8_t bytes[PHP_UUID_LEN]; +} php_uuid; + +/** UUID hexadecimal representation length with terminating NUL. */ +#define PHP_UUID_HEX_LEN 33 + +/** UUID hexadecimal representation: `000000001111222233334444444444444444\0` */ +typedef struct php_uuid_hex { + char str[PHP_UUID_HEX_LEN]; +} php_uuid_hex; + +/** UUID string representation length with terminating NUL. */ +#define PHP_UUID_STRING_LEN 37 + +/** UUID string representation: `00000000-1111-2222-3333-4444444444444444\0` */ +typedef struct php_uuid_string { + char str[PHP_UUID_STRING_LEN]; +} php_uuid_string; + +/** + * UUID variants as defined in [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.1). + */ +typedef enum php_uuid_variant { + /** Reserved, NCS backward compatibility: `0b0xx` */ + PHP_UUID_VARIANT_NCS = 0, + + /** The variant of this implementation and specified in RFC 4122: `0b10x` */ + PHP_UUID_VARIANT_RFC4122 = 1, + + /** Reserved, Microsoft Corporation backward compatibility: `0b110` */ + PHP_UUID_VARIANT_MICROSOFT = 2, + + /** Reserved for future definition: `0b111` */ + PHP_UUID_VARIANT_FUTURE_RESERVED = 3, +} php_uuid_variant; + +/** + * Version code for date-time and IEEE 802 MAC address UUIDs. + * + * @warning + * Generation of this version is not supported by this implementation due + * to security concerns. Version 4 UUIDs are a good replacement for version + * 1 UUIDs without the privacy/security concerns (see [Wikipedia]). + * + * @see [RFC 4122: Section 4.2](https://tools.ietf.org/html/rfc4122#section-4.2) + * @see [Wikipedia: Universally unique identifier (Version 1)][Wikipedia] + * [Wikipedia]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_.28date-time_and_MAC_address.29 + */ +static const uint8_t PHP_UUID_VERSION_1_TIME_BASED = 1; + +/** + * Version code for date-time and IEEE 802 MAC address UUIDs (DCE security + * algorithm). + * + * @warning + * Generation of this version is not supported by this implementation due + * to security concerns, and uniqueness limitations for applications with + * high allocations. Version 4 UUIDs are a good replacement for version 2 + * UUIDs without the privacy/security concerns (see [Wikipedia]), and they + * support high allocations. + * + * @see [RFC 4122: Section 4.2](https://tools.ietf.org/html/rfc4122#section-4.2) + * @see [Wikipedia: Universally unique identifier (Version 2)][Wikipedia] + * [Wikipedia]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_2_.28date-time_and_MAC_Address.2C_DCE_security_version.29 + */ +static const uint8_t PHP_UUID_VERSION_2_DCE_SECURITY = 2; + +/** + * Version code for namespace/name-based MD5 hashed UUIDs. + * + * @see php_uuid_create_v3() + * @see [RFC 4122: Section 4.3](https://tools.ietf.org/html/rfc4122#section-4.3) + * @see [Wikipedia: Universally unique identifiers (versions 3 and 5)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29) + */ +static const uint8_t PHP_UUID_VERSION_3_NAME_BASED_MD5 = 3; + +/** + * Version code for random UUIDs. + * + * @see php_uuid_create_v4() + * @see [RFC 4122: Section 4.4](https://tools.ietf.org/html/rfc4122#section-4.4) + * @see [Wikipedia: Universally unique identifiers (version 4)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29) + */ +static const uint8_t PHP_UUID_VERSION_4_RANDOM = 4; + +/** + * Version code for namespace/name-based SHA1 hashed UUIDs. + * + * @see php_uuid_create_v5() + * @see [RFC 4122: Section 4.3](https://tools.ietf.org/html/rfc4122#section-4.3) + * @see [Wikipedia: Universally unique identifiers (versions 3 and 5)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29) + */ +static const uint8_t PHP_UUID_VERSION_5_NAME_BASED_SHA1 = 5; + +BEGIN_EXTERN_C() + +/** + * Create version 3 UUID. + * + * @warning + * [RFC 4122] recommends [v5](@ref php_uuid_create_v5()) over this one and + * states that version 3 UUIDs should be used if backwards compatibility is + * required only. This is because MD5 has a higher collision probability + * compared to SHA1, which is used by version 5; regardless of the truncation! + * + * Version 3 UUIDs are generated by [MD5](https://en.wikipedia.org/wiki/MD5) + * hashing the concatenated namespace's byte representation and the given name. + * The namespace itself must be another UUID. This can be any UUID, or one of + * the predefined ones: + * + * - php_uuid_namespace_dns() + * - php_uuid_namespace_oid() + * - php_uuid_namespace_url() + * - php_uuid_namespace_x500() + * + * A particular name within the same namespace always results in the same + * version 3 UUID, across all [RFC 4122] compliant UUID implementations. + * However, the namespace and name cannot be determined from the UUID alone. + * + * ### Examples + * + * ```c + * const php_uuid nsid = php_uuid_namespace_dns(); + * php_uuid uuid; + * + * php_uuid_create_v3(&uuid, &nsid, "php.net", sizeof("php.net") - 1); + * + * assert(php_uuid_is_nil(uuid) == 0); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_3_NAME_BASED_MD5); + * + * php_uuid_string str; + * php_uuid_to_string(&str, uuid); + * assert(memcmp(&str, "11a38b9a-b3da-360f-9353-a5a725514269", PHP_UUID_STRING_LEN) == 0); + * + * php_uuid tmp; + * php_uuid_create_v3(&tmp, &nsid, "php.net", sizeof("php.net") - 1); + * assert(memcmp(&tmp, &uuid, PHP_UUID_LEN) == 0); + * ``` + * + * [RFC 4122]: https://tools.ietf.org/html/rfc4122 + * @see [RFC 4122: Section 4.3](https://tools.ietf.org/html/rfc4122#section-4.3) + * @see [Wikipedia: Universally unique identifier (Versions 3 and 5)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29) + * @param[out] uuid to store the result in. + * @param[in] namespace to create the UUID in. + * @param[in] name to create the UUID from. + * @param[in] name_len + */ +PHPAPI void php_uuid_create_v3(php_uuid *uuid, const php_uuid *namespace, const char *name, const size_t name_len); + +/** + * Create version 4 UUID. + * + * Version 4 UUIDs are randomly generated from the best available random source. + * The selection of that source is determined by PHP's random implementation. + * Some systems may be bad at generating sufficient entropy, e.g. virtual + * machines. This might lead to collisions faster than desired. If this is the + * case, the php_uuid_create_v5() implementation should be used. + * + * ### Examples + * + * ```c + * php_uuid uuid; + * + * php_uuid_create_v4_silent(&uuid); + * + * assert(php_uuid_is_nil(uuid) == 0); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_4_RANDOM); + * + * php_uuid tmp; + * php_uuid_create_v4_silent(&uuid); + * assert(memcmp(&tmp, &uuid, PHP_UUID_LEN) == 0); + * ``` + * + * @see [RFC 4122: Section 4.4](https://tools.ietf.org/html/rfc4122#section-4.4) + * @see [Wikipedia: Universally unique identifier (Version 4)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29) + * @see php_uuid_create_v4_silent() + * @see php_uuid_create_v4_throw() + * @param[out] uuid to store the result. + * @param[in] throw whether to throw a PHP exception (`1`), or not (`0`). + * @return #SUCCESS() if the generation succeeded, #FAILURE() if it was not + * possible to gather sufficient entropy. + * @throws Exception if `throw` is set to true and it was not possible to gather + * sufficient entropy for generating random bytes. + */ +PHPAPI int php_uuid_create_v4(php_uuid *uuid, const zend_bool throw); + +/** + * Silently create version 4 UUID. + * + * @see php_uuid_create_v4() + * @param[out] uuid to store the result. + * @return #SUCCESS() if the generation succeeded, #FAILURE() if it was not + * possible to gather sufficient entropy. + */ +static zend_always_inline int php_uuid_create_v4_silent(php_uuid *uuid) +{ + return php_uuid_create_v4(uuid, 0); +} + +/** + * Create version 4 UUID and throw PHP exception if it is not possible to + * gather sufficient entropy. + * + * @see php_uuid_create_v4() + * @param[out] uuid to store the result. + * @return #SUCCESS() if the generation succeeded, #FAILURE() if it was not + * possible to gather sufficient entropy. + * @throws Exception if `throw` is enabled and it was not possible to gather + * sufficient entropy for generating random bytes. + */ +static zend_always_inline int php_uuid_create_v4_throw(php_uuid *uuid) +{ + return php_uuid_create_v4(uuid, 1); +} + +/** + * Create version 5 UUID. + * + * Version 5 UUIDs are generated by [SHA-1](https://en.wikipedia.org/wiki/SHA-1) + * hashing the concatenated namespace's byte representation and the given name. + * The namespace itself must be another UUID. This can be any UUID, or one of + * the predefined ones: + * + * - php_uuid_namespace_dns() + * - php_uuid_namespace_oid() + * - php_uuid_namespace_url() + * - php_uuid_namespace_x500() + * + * A particular name within the same namespace always results in the same + * version 5 UUID, across all [RFC 4122] compliant UUID implementations. + * However, the namespace and name cannot be determined from the UUID alone. + * + * ### Examples + * + * ```c + * const php_uuid nsid = php_uuid_namespace_dns(); + * php_uuid uuid; + * + * php_uuid_create_v5(&uuid, &nsid, "php.net", sizeof("php.net") - 1); + * + * assert(php_uuid_is_nil(uuid) == 0); + * assert(php_uuid_get_variant(uuid) == PHP_UUID_VARIANT_RFC4122); + * assert(php_uuid_get_version(uuid) == PHP_UUID_VERSION_5_NAME_BASED_SHA1); + * + * php_uuid_string str; + * php_uuid_to_string(&str, uuid); + * assert(memcmp(&str, "c4a760a8-dbcf-5254-a0d9-6a4474bd1b62", PHP_UUID_STRING_LEN) == 0); + * + * php_uuid tmp; + * php_uuid_create_v5(&tmp, &nsid, "php.net", sizeof("php.net") - 1); + * assert(memcmp(&tmp, &uuid, PHP_UUID_LEN) == 0); + * ``` + * + * [RFC 4122]: https://tools.ietf.org/html/rfc4122 + * @see [RFC 4122: Section 4.3](https://tools.ietf.org/html/rfc4122#section-4.3) + * @see [Wikipedia: Universally unique identifier (Versions 3 and 5)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29) + * @param[out] uuid to store the result in. + * @param[in] namespace to create the UUID in. + * @param[in] name to create the UUID from. + * @param[in] name_len + */ +PHPAPI void php_uuid_create_v5(php_uuid *uuid, const php_uuid *namespace, const char *name, const size_t name_len); + +/** + * Get the variant associated with this UUID. + * + * The variant specifies the internal data layout of a UUID. This + * implementation generates #PHP_UUID_VARIANT_RFC4122 UUIDs only, however, + * parsing and construction of other variants is supported. + * + * @see [RFC 4122: Section 4.1.1](http://tools.ietf.org/html/rfc4122#section-4.1.1) + * @see [Wikipedia: Universally unique identifier (Variants)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Variants) + * @see PHP_UUID_VARIANT_NCS + * @see PHP_UUID_VARIANT_RFC4122 + * @see PHP_UUID_VARIANT_MICROSOFT + * @see PHP_UUID_VARIANT_FUTURE_RESERVED + * @param[in] uuid to get the variant from. + * @return The variant of the given UUID. + */ +static const zend_always_inline php_uuid_variant php_uuid_get_variant(const php_uuid *uuid) +{ + if ((uuid->bytes[8] & 0xC0) == 0x80) return PHP_UUID_VARIANT_RFC4122; + if ((uuid->bytes[8] & 0xE0) == 0xC0) return PHP_UUID_VARIANT_MICROSOFT; + if ((uuid->bytes[8] & 0x80) == 0x00) return PHP_UUID_VARIANT_NCS; + return PHP_UUID_VARIANT_FUTURE_RESERVED; +} + +/** + * Get the version associated with this UUID. + * + * The version specifies which algorithm was used to generate the UUID. Note + * that the version might not be meaningful if another variant than the + * #PHP_UUID_VARIANT_RFC4122 was used to generate the UUID. This implementation + * generates #PHP_UUID_VARIANT_RFC4122 UUIDs only, but allows parsing and + * construction of other variants. + * + * @see [RFC 4122: Section 4.1.3](http://tools.ietf.org/html/rfc4122#section-4.1.3) + * @see [Wikipedia: Universally unique identifier (Versions)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions) + * @see PHP_UUID_VERSION_1_TIME_BASED + * @see PHP_UUID_VERSION_2_DCE_SECURITY + * @see PHP_UUID_VERSION_3_NAME_BASED_MD5 + * @see PHP_UUID_VERSION_4_RANDOM + * @see PHP_UUID_VERSION_5_NAME_BASED_SHA1 + * @param[in] uuid to get the version from. + * @return The version of the given UUID, which is an integer in [0, 15], the + * values [1, 5] correspond to one of the version constants. The others are + * not defined in RFC 4122. + */ +static const zend_always_inline uint8_t php_uuid_get_version(const php_uuid *uuid) +{ + return uuid->bytes[6] >> 4; +} + +/** + * Domain Name System (DNS) namespace UUID. + * + * @see [RFC 4122: Appendix C](https://tools.ietf.org/html/rfc4122#appendix-C) + * @see [Wikipedia: Domain Name System](https://en.wikipedia.org/wiki/Domain_Name_System) + */ +static const zend_always_inline php_uuid php_uuid_namespace_dns() +{ + return (php_uuid) { "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" }; +} + +/** +* Object Identifier (OID) namespace UUID. +* + * @see [RFC 4122: Appendix C](https://tools.ietf.org/html/rfc4122#appendix-C) + * @see [Wikipedia: Object identifier](https://en.wikipedia.org/wiki/Object_identifier) + */ +static const zend_always_inline php_uuid php_uuid_namespace_oid() +{ + return (php_uuid) { "\x6b\xa7\xb8\x12\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" }; +} + +/** + * Uniform Resource Locator (URL) namespace UUID. + * + * @see [RFC 4122: Appendix C](https://tools.ietf.org/html/rfc4122#appendix-C) + * @see [Wikipedia: URL](https://en.wikipedia.org/wiki/URL) + */ +static const zend_always_inline php_uuid php_uuid_namespace_url() +{ + return (php_uuid) { "\x6b\xa7\xb8\x11\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" }; +} + +/** + * X.500 Distinguished Names (X.500 DN) namespace UUID. The names that are to + * be hashed in this namespace can be in DER or a text output format. + * + * @see [RFC 4122: Appendix C](https://tools.ietf.org/html/rfc4122#appendix-C) + * @see [Wikipedia: X.500](https://en.wikipedia.org/wiki/X.500) + * @see [Wikipedia: Lightweight Directory Access Protocol](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol) + */ +static const zend_always_inline php_uuid php_uuid_namespace_x500() +{ + return (php_uuid) { "\x6b\xa7\xb8\x14\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8" }; +} + +/** + * Special nil UUID that has all 128 bits set to zero. + * + * @see [RFC 4122: Section 4.1.7](https://tools.ietf.org/html/rfc4122#section-4.1.7) + * @see [Wikipedia: Universally unique identifier (Nil UUID)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID) + */ +static const zend_always_inline php_uuid php_uuid_nil() +{ + return (php_uuid) { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; +} + +/** + * Check if the given UUID is the special nil UUID that has all 128 bits set + * to zero. + * + * @see [RFC 4122: Section 4.1.7](https://tools.ietf.org/html/rfc4122#section-4.1.7) + * @see [Wikipedia: Universally unique identifier (Nil UUID)](https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID) + * @param[in] uuid to check. + * @return `1` if the UUID contains the special nil UUID; `0` otherwise. + */ +static const zend_always_inline int php_uuid_is_nil(const php_uuid *uuid) +{ + const php_uuid nil = php_uuid_nil(); + + return memcmp(uuid->bytes, &nil, PHP_UUID_LEN) == 0; +} + +/** + * Parse the given string as UUID. + * + * The following UUID representations are parsable: + * + * - hexadecimal (`00000000111122223333444444444444`), + * - string (`00000000-1111-2222-3333-444444444444`), + * - URNs (`urn:uuid:00000000-1111-2222-3333-444444444444`), and + * - Microsoft (`{00000000-1111-2222-3333-444444444444}`). + * + * Leading and trailing whitespace, namely spaces (` `) and tabs (`\t`), is + * ignored, so are leading opening braces (`{`) and trailing closing braces + * (`}`). Hyphens (`-`) are ignored everywhere. The parsing algorithm + * follows the [robustness + * principle](https://en.wikipedia.org/wiki/Robustness_principle) and is + * not meant for validation. + * + * ### Examples + * + * ```c + * php_uuid uuid; + * + * // Parsing of canonical representations. + * php_uuid_parse_silent(&uuid, "0123456789abcdef0123456789abcdef", sizeof("0123456789abcdef0123456789abcdef") - 1); + * php_uuid_parse_silent(&uuid, "01234567-89ab-cdef-0123-456789abcdef", sizeof("01234567-89ab-cdef-0123-456789abcdef") - 1); + * php_uuid_parse_silent(&uuid, "urn:uuid:01234567-89ab-cdef-0123-456789abcdef", sizeof("urn:uuid:01234567-89ab-cdef-0123-456789abcdef") - 1); + * php_uuid_parse_silent(&uuid, "{01234567-89ab-cdef-0123-456789abcdef}", sizeof("{01234567-89ab-cdef-0123-456789abcdef}") - 1); + * + * // Leading and trailing garbage is ignored, so are extraneous hyphens. + * php_uuid_parse_silent(&uuid, " \t ---- { urn:uuid:----0123-4567-89ab-cdef-0123-4567-89ab-cdef---- } ---- \t ", sizeof(" \t ---- { urn:uuid:----0123-4567-89ab-cdef-0123-4567-89ab-cdef---- } ---- \t ") - 1); + * + * // However, note that there cannot be whitespace or braces between the URN + * // scheme and the UUID itself. + * assert(php_uuid_parse_silent( + * &uuid, + * "urn:uuid:{01234567-89ab-cdef-0123-456789abcdef", + * sizeof("urn:uuid:{01234567-89ab-cdef-0123-456789abcdef") - 1 + * ) == FAILURE); + * ``` + * + * @see php_uuid_parse_silent + * @see php_uuid_parse_throw + * @param[out] uuid to store the result in. + * @param[in] input to parse as UUID. + * @param[in] input_len + * @param[in] throw whether to throw PHP exceptions (`1`), or not (`0`). + * @return #SUCCESS() if the string was parsed as UUID; #FAILURE() otherwise. + * @throws UUIDParseException if `throw` is enabled and parsing fails. + */ +PHPAPI int php_uuid_parse(php_uuid *uuid, const char *input, const size_t input_len, const zend_bool throw); + +/** + * Silently parse the string as UUID. + * + * @see php_uuid_parse + * @param[out] uuid to store the result in. + * @param[in] input to parse as UUID. + * @param[in] input_len + * @return #SUCCESS() if the string was parsed as UUID; #FAILURE() otherwise. + */ +static zend_always_inline int php_uuid_parse_silent(php_uuid *uuid, const char *input, const size_t input_len) +{ + return php_uuid_parse(uuid, input, input_len, 0); +} + +/** + * Parse the string as UUID and throw PHP exceptions on failures. + * + * @see php_uuid_parse + * @param[out] uuid to store the result in. + * @param[in] input to parse as UUID. + * @param[in] input_len + * @return #SUCCESS() if the string was parsed as UUID; #FAILURE() otherwise. + * @throws UUIDParseException if parsing fails. + */ +static zend_always_inline int php_uuid_parse_throw(php_uuid *uuid, const char *input, const size_t input_len) +{ + return php_uuid_parse(uuid, input, input_len, 1); +} + +/** + * Convert the UUID to its hexadecimal representation. + * + * The hexadecimal representation of a UUID are 32 hexadecimal digits. The + * hexadecimal digits `a` through `f` are always formatted as lower case + * characters, in accordance with RFC 4122. + * + * ### Examples + * + * ```c + * const php_uuid nsid = php_uuid_namespace_dns(); + * php_uuid_hex hex; + * + * php_uuid_to_hex(&hex, &nsid); + * assert(memcmp(&hex, "6ba7b8109dad11d180b400c04fd430c8", PHP_UUID_HEX_LEN) == 0); + * ``` + * + * @param[out] buffer to store the hexadecimal representation in. + * @param[in] uuid to convert. + */ +static zend_always_inline void php_uuid_to_hex(php_uuid_hex *buffer, const php_uuid *uuid) +{ + sprintf( + buffer->str, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + uuid->bytes[0], uuid->bytes[1], uuid->bytes[2], uuid->bytes[3], + uuid->bytes[4], uuid->bytes[5], + uuid->bytes[6], uuid->bytes[7], + uuid->bytes[8], uuid->bytes[9], + uuid->bytes[10], uuid->bytes[11], uuid->bytes[12], uuid->bytes[13], uuid->bytes[14], uuid->bytes[15] + ); +} + +/** + * Convert the UUID to its string representation. + * + * The string representation of a UUID are 32 hexadecimal digits separated + * by a hyphen into five groups of 8, 4, 4, 4, and 12 digits. The + * hexadecimal digits `a` through `f` are always formatted as lower case + * characters, in accordance with RFC 4122. + * + * ### Examples + * + * ```c + * const php_uuid nsid = php_uuid_namespace_dns(); + * php_uuid_string str; + * + * php_uuid_to_hex(&str, &nsid); + * assert(memcmp(&str, "6ba7b810-9dad-11d1-80b4-00c04fd430c8", PHP_UUID_STRING_LEN) == 0); + * ``` + * + * @param[out] buffer to store the string representation in. + * @param[in] uuid to convert. + */ +static zend_always_inline void php_uuid_to_string(php_uuid_string *buffer, const php_uuid *uuid) +{ + sprintf( + buffer->str, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid->bytes[0], uuid->bytes[1], uuid->bytes[2], uuid->bytes[3], + uuid->bytes[4], uuid->bytes[5], + uuid->bytes[6], uuid->bytes[7], + uuid->bytes[8], uuid->bytes[9], + uuid->bytes[10], uuid->bytes[11], uuid->bytes[12], uuid->bytes[13], uuid->bytes[14], uuid->bytes[15] + ); +} + +END_EXTERN_C() + +/** + * Initialization function of the standard UUID submodule. + * + * @see ext/standard/basic_functions.c + * @see ext/standard/php_standard.h + */ +PHP_MINIT_FUNCTION(uuid); + +#endif /* PHP_UUID_H */ diff --git a/ext/standard/stubs/UUID.php b/ext/standard/stubs/UUID.php new file mode 100644 index 0000000000000..1e5d03e525286 --- /dev/null +++ b/ext/standard/stubs/UUID.php @@ -0,0 +1,704 @@ +isNil()); + * assert($uuid->getVariant() === 0); + * assert($uuid->getVersion() === 0); + * + * $uuid = UUID::v3(UUID::NamespaceDNS(), 'php.net'); + * assert($uuid->isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_3_NAME_BASED_MD5); + * + * $uuid = UUID::v4(); + * assert($uuid->isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_4_RANDOM); + * + * $uuid = UUID::v5(UUID::NamespaceDNS(), 'php.net'); + * assert($uuid->isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_5_NAME_BASED_SHA1); + * + * $uuid = UUID::parse('urn:uuid:123E4567-E89B-12D3-A456-426655440000'); + * assert($uuid->isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_1_TIME_BASED); + * + * assert($uuid->toBinary() === "\x12\x3E\x45\x67\xE8\x9B\x12\xD3\xA4\x56\x42\x66\x55\x44\x00\x00"); + * assert($uuid->toHex() === '123e4567e89b12d3a456426655440000'); + * assert($uuid->toString() === '123e4567-e89b-12d3-a456-426655440000'); + * + * ?> + * ``` + * + * Comparison of UUIDs is possible with the default comparison operators of + * PHP. + * + * ``` + * + * ``` + * + * [rfc]: https://tools.ietf.org/html/rfc4122 + * [wiki]: https://en.wikipedia.org/wiki/Universally_unique_identifier + * @since 7.2 + * @see https://php.net/uuid + */ +final class UUID { + /** + * Code for the (reserved) NCS variant of UUIDs. + * + * @since 7.2 + * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 + */ + public const VARIANT_NCS = 0; + + /** + * Code for the RFC 4122 variant of UUIDs. + * + * This implementation generates UUIDs of this variant only. + * + * @since 7.2 + * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 + */ + public const VARIANT_RFC4122 = 1; + + /** + * Code for the (reserved) Microsoft variant of UUIDs, the GUIDs. + * + * @since 7.2 + * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 + */ + public const VARIANT_MICROSOFT = 2; + + /** + * Version code for the future reserved variant of UUIDs. + * + * @since 7.2 + * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 + */ + public const VARIANT_FUTURE_RESERVED = 4; + + /** + * Version code for date-time and IEEE 802 MAC address UUIDs. + * + * Generation of this version is not supported by this implementation due + * to security concerns. Version 4 UUIDs are a good replacement for version + * 1 UUIDs without the privacy/security concerns (see [Wikipedia][wiki]). + * + * [wiki]: https://en.wikipedia.org/wiki/Universally_unique_identifier + * @since 7.2 + * @see https://tools.ietf.org/html/rfc4122#section-4.2 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_.28date-time_and_MAC_address.29 + */ + public const VERSION_1_TIME_BASED = 1; + + /** + * Version code for date-time and IEEE 802 MAC address UUIDs (DCE security + * algorithm). + * + * Generation of this version is not supported by this implementation due + * to security concerns, and uniqueness limitations for applications with + * high allocations. Version 4 UUIDs are a good replacement for version 2 + * UUIDs without the privacy/security concerns (see [Wikipedia][wiki]), and + * they support high allocations. + * + * [wiki]: https://en.wikipedia.org/wiki/Universally_unique_identifier + * @since 7.2 + * @see https://tools.ietf.org/html/rfc4122#section-4.2 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_2_.28date-time_and_MAC_Address.2C_DCE_security_version.29 + */ + public const VERSION_2_DCE_SECURITY = 2; + + /** + * Version code for namespace/name-based MD5 hashed UUIDs. + * + * @since 7.2 + * @see v3 + * @see https://tools.ietf.org/html/rfc4122#section-4.3 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 + */ + public const VERSION_3_NAME_BASED_MD5 = 3; + + /** + * Version code for random UUIDs. + * + * @since 7.2 + * @see v4 + * @see https://tools.ietf.org/html/rfc4122#section-4.4 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 + */ + public const VERSION_4_RANDOM = 4; + + /** + * Version code for namespace/name-based SHA1 hashed UUIDs. + * + * @since 7.2 + * @see v5 + * @see https://tools.ietf.org/html/rfc4122#section-4.3 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 + */ + public const VERSION_5_NAME_BASED_SHA1 = 5; + + /** + * This UUID's 128 bit integer value as 16 byte binary string. + * + * @since 7.2 + * @see toBinary + * @var string + */ + private $bytes; + + /** + * Use {@see fromBinary} or {@see parse} to construct a new instance. + */ + private function __construct() { } + + /** + * Construct new UUID instance from binary string of exactly 16 bytes. + * + * Any string of 16 bytes is accepted by this named constructor. This + * enables the construction of UUIDs of any variant and version, regardless + * of the {@see parse} implementation. + * + * ## Examples + * ``` + * getVariant() === UUID::VARIANT_FUTURE_RESERVED); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.fromBinary + * @see toBinary + * @param string $input string of exactly 16 bytes to construct the + * instance from. + * @return \PHP\Std\UUID UUID constructed from the binary input. + * @throws \ArgumentCountError if less or more than one argument is passed. + * @throws \InvalidArgumentException if the input is not 16 bytes long. + */ + public static function fromBinary(string $input): self { } + + /** + * Parse the given string as UUID. + * + * The following UUID representations are parsable: + * + * - hexadecimal (`00000000111122223333444444444444`), + * - string (`00000000-1111-2222-3333-444444444444`), + * - URNs (`urn:uuid:00000000-1111-2222-3333-444444444444`), and + * - Microsoft (`{00000000-1111-2222-3333-444444444444}`). + * + * Leading and trailing whitespace, namely spaces (` `) and tabs (`\t`), is + * ignored, so are leading opening braces (`{`) and trailing closing braces + * (`}`). Hyphens (`-`) are ignored everywhere. The parsing algorithm + * follows the [robustness principle][wrp] and is not meant for validation. + * + * ## Examples + * ``` + * getMessage() === 'Expected hexadecimal digit, but found '{' (0x7b)'); + * } + * + * ?> + * ``` + * + * [wrp]: https://en.wikipedia.org/wiki/Robustness_principle + * @since 7.2 + * @see https://php.net/uuid.parse + * @see toHex + * @see toString + * @param string $input to parse as UUID and construct the instance from. + * @return \PHP\Std\UUID UUID constructed from the parsed input. + * @throws \ArgumentCountError if less or more than one argument is passed. + * @throws \PHP\Std\UUIDParseException if parsing of the input fails. + */ + public static function parse(string $input): self { } + + /** + * Construct new version 3 PHP\Std\UUID. + * + * > RFC 4122 recommends {@see v5} over this one and states that version 3 + * > UUIDs should be used if backwards compatibility is required only. This + * > is because MD5 has a higher collision probability compared to SHA1, + * > which is used by version 5; regardless of the truncation! + * + * Version 3 UUIDs are generated by MD5 hashing the concatenated + * namespace's byte representation and the given name. The namespace itself + * must be another UUID. This can be any UUID, or one of the predefined + * ones: + * + * - {@see NamespaceDNS} + * - {@see NamespaceOID} + * - {@see NamespaceURL} + * - {@see NamespaceX500} + * + * A particular name within the same namespace always results in the same + * version 3 UUID, across all RFC 4122 compliant UUID implementations. + * However, the namespace and name cannot be determined from the UUID + * alone. + * + * ## Examples + * ``` + * isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_3_NAME_BASED_MD5); + * assert($uuid->toString() === '11a38b9a-b3da-360f-9353-a5a725514269'); + * assert($uuid == UUID::v3(UUID::NamespaceDNS(), 'php.net')); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.v3 + * @see https://tools.ietf.org/html/rfc4122#section-4.3 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 + * @param \PHP\Std\UUID $namespace to construct the UUID in. + * @param string $name to construct the UUID from. + * @return \PHP\Std\UUID UUID constructed from the name in the namespace. + * @throws \ArgumentCountError if less or more than two arguments are passed. + * @throws \Error if the namespace does not encapsulate a valid UUID. + */ + public static function v3(self $namespace, string $name): self { } + + /** + * Construct new version 4 UUID. + * + * Version 4 UUIDs are randomly generated from the best available random + * source. The selection of that source is determined by PHP's + * {@see random_bytes} implementation. Some systems may be bad at + * generating sufficient entropy, e.g. virtual machines. This might lead to + * collisions faster than desired. If this is the case, the {@see v5} + * version should be used. + * + * ## Examples + * ``` + * isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_4_RANDOM); + * assert($uuid != UUID::v4()); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.v4 + * @see https://tools.ietf.org/html/rfc4122#section-4.4 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 + * @return \PHP\Std\UUID UUID constructed from random data. + * @throws \Exception if it was not possible to gather sufficient entropy. + * @throws \ArgumentCountError if arguments are passed. + */ + public static function v4(): self { } + + /** + * Construct new version 5 UUID. + * + * Version 5 UUIDs are generated by MD5 hashing the concatenated + * namespace's byte representation and the given name. The namespace itself + * must be another UUID. This can be any UUID, or one of the predefined + * ones: + * + * - {@see NamespaceDNS} + * - {@see NamespaceOID} + * - {@see NamespaceURL} + * - {@see NamespaceX500} + * + * A particular name within the same namespace always results in the same + * version 5 UUID, across all RFC 4122 compliant UUID implementations. + * However, the namespace and name cannot be determined from the UUID + * alone. + * + * ## Examples + * ``` + * isNil() === false); + * assert($uuid->getVariant() === UUID::VARIANT_RFC4122); + * assert($uuid->getVersion() === UUID::VERSION_5_NAME_BASED_SHA1); + * assert($uuid->toString() === 'c4a760a8-dbcf-5254-a0d9-6a4474bd1b62'); + * assert($uuid == UUID::v5(UUID::NamespaceDNS(), 'php.net')); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.v5 + * @see @see https://tools.ietf.org/html/rfc4122#section-4.3 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_.28namespace_name-based.29 + * @param \PHP\Std\UUID $namespace to construct the UUID in. + * @param string $name to construct the UUID from. + * @return \PHP\Std\UUID UUID constructed from the name in the namespace. + * @throws \ArgumentCountError if less or more than two arguments are passed. + * @throws \Error if the namespace does not encapsulate a valid UUID. + */ + public static function v5(self $namespace, string $name): self { } + + /** + * Construct new Domain Name System (DNS) namespace UUID instance. + * + * @since 7.2 + * @see https://php.net/uuid.NamespaceDNS + * @see https://tools.ietf.org/html/rfc4122#appendix-C + * @see https://en.wikipedia.org/wiki/Domain_Name_System + * @return \PHP\Std\UUID Predefined DNS namespace UUID. + * @throws \ArgumentCountError if arguments are passed. + */ + public static function NamespaceDNS(): self { } + + /** + * Construct new Object Identifier (OID) namespace UUID instance. + * + * @since 7.2 + * @see https://php.net/uuid.NamespaceOID + * @see https://tools.ietf.org/html/rfc4122#appendix-C + * @see https://en.wikipedia.org/wiki/Object_identifier + * @return \PHP\Std\UUID Predefined OID namespace UUID. + * @throws \ArgumentCountError if arguments are passed. + */ + public static function NamespaceOID(): self { } + + /** + * Construct new Uniform Resource Locator (URL) namespace UUID instance. + * + * @since 7.2 + * @see https://php.net/uuid.NamespaceURL + * @see https://tools.ietf.org/html/rfc4122#appendix-C + * @see https://en.wikipedia.org/wiki/URL + * @return \PHP\Std\UUID Predefined URL namespace UUID. + * @throws \ArgumentCountError if arguments are passed. + */ + public static function NamespaceURL(): self { } + + /** + * Construct new X.500 Distinguished Names (X.500 DN) namespace UUID + * instance. The names that are to be hashed in this namespace can be in + * DER or a text output format. + * + * @since 7.2 + * @see https://php.net/uuid.NamespaceX500 + * @see https://tools.ietf.org/html/rfc4122#appendix-C + * @see https://en.wikipedia.org/wiki/X.500 + * @see https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol + * @return \PHP\Std\UUID Predefined X.500 namespace UUID instance. + * @throws \ArgumentCountError if arguments are passed. + */ + public static function NamespaceX500(): self { } + + /** + * Construct special nil UUID that has all 128 bits set to zero. + * + * @since 7.2 + * @see https://php.net/uuid.Nil + * @see https://tools.ietf.org/html/rfc4122#section-4.1.7 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID + * @return \PHP\Std\UUID Predefined special nil UUID. + * @throws \ArgumentCountError if arguments are passed. + */ + public static function Nil(): self { } + + /** + * Callback for dynamic adding of properties which throws an {@see Error} + * upon every invocation, direct or indirect. This is necessary to protect + * the promised immutability of this object. Not doing so could lead to + * problems with the comparison operators, since PHP always compares all + * properties. + * + * @since 7.2 + * @see https://php.net/uuid.__set + * @param mixed $_ + * @param mixed $__ + * @return void + * @throws \Error upon every invocation, direct or indirect. + */ + public function __set($_, $__): void { } + + /** + * Deserialization callback. + * + * @since 7.2 + * @see https://php.net/uuid.__wakeup + * @see unserialize() + * @return void + * @throws \ArgumentCountError if arguments are passed. + * @throws \UnexpectedValueException if the value of the {@see bytes} + * property is not of type string, or not exactly 16 bytes long. + */ + public function __wakeup(): void { } + + /** + * Get the variant associated with this UUID. + * + * The variant specifies the internal data layout of a UUID. This + * implementation generates {@see UUID::VARIANT_RFC4122} UUIDs only, + * however, parsing and construction of other variants is supported. + * + * @since 7.2 + * @see https://php.net/uuid.getVariant + * @see https://tools.ietf.org/html/rfc4122#section-4.1.1 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Variants + * @see UUID::VARIANT_NCS + * @see UUID::VARIANT_RFC4122 + * @see UUID::VARIANT_MICROSOFT + * @see UUID::VARIANT_FUTURE_RESERVED + * @return int An integer in [0, 3] where each value corresponds to one of + * the variant class constants. + * @throws \ArgumentCountError if arguments are passed. + * @throws \Error if this instance does not encapsulate a valid UUID. + */ + public function getVariant(): int { } + + /** + * Get the version associated with this UUID. + * + * The version specifies which algorithm was used to generate the UUID. + * Note that the version might not be meaningful if another variant than + * the {@see UUID::VARIANT_RFC4122} was used to generate the UUID. This + * implementation generates {@see UUID::VARIANT_RFC4122} UUIDs only, but + * allows parsing and construction of other variants. + * + * @since 7.2 + * @see https://php.net/uuid.getVersion + * @see https://tools.ietf.org/html/rfc4122#section-4.1.3 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions + * @see UUID::VERSION_1_TIME_BASED + * @see UUID::VERSION_2_DCE_SECURITY + * @see UUID::VERSION_3_NAME_BASED_MD5 + * @see UUID::VERSION_4_RANDOM + * @see UUID::VERSION_5_NAME_BASED_SHA1 + * @return int An integer in [0, 15], the values [1, 5] correspond to one + * of the version class constants. The others are not defined in + * RFC 4122. + * @throws \ArgumentCountError if arguments are passed. + * @throws \Error if this instance does not encapsulate a valid UUID. + */ + public function getVersion(): int { } + + /** + * Check if this UUID is the special nil UUID that has all 128 bits set to + * zero. + * + * @since 7.2 + * @see https://php.net/uuid.isNil + * @see https://tools.ietf.org/html/rfc4122#section-4.1.7 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID + * @see Nil + * @return bool **TRUE** if this is the special nil UUID; **FALSE** + * otherwise. + * @throws \ArgumentCountError if arguments are passed. + * @throws \Error if this instance does not encapsulate a valid UUID. + */ + public function isNil(): bool { } + + /** + * Convert the UUID to its binary representation. + * + * The binary representation of a UUID is a string of exactly 16 bytes. It + * is the format that is used internally. It is also the format that should + * be used to store UUIDs in a database (e.g. in MySQL as `BINARY(16)` + * column). The resulting string, if generated by this implementation, is + * always in network byte order. + * + * ## Examples + * ``` + * toBinary() === "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.toBinary + * @return string Binary representation of the UUID. + * @throws \ArgumentCountError if arguments are passed. + * @throws \Error if this instance does not encapsulate a valid UUID. + */ + public function toBinary(): string { } + + /** + * Convert the UUID to its hexadecimal representation. + * + * The hexadecimal representation of a UUID are 32 hexadecimal digits. The + * hexadecimal digits `a` through `f` are always formatted as lower case + * characters, in accordance with RFC 4122. + * + * ## Examples + * ``` + * toHex() === '6ba7b8109dad11d180b400c04fd430c8'); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.toHex + * @return string Hexadecimal representation of the UUID. + * @throws \ArgumentCountError if arguments are passed. + * @throws \Error if this instance does not encapsulate a valid UUID. + */ + public function toHex(): string { } + + /** + * Convert the UUID to its string representation. + * + * The string representation of a UUID are 32 hexadecimal digits separated + * by a hyphen into five groups of 8, 4, 4, 4, and 12 digits. The + * hexadecimal digits `a` through `f` are always formatted as lower case + * characters, in accordance with RFC 4122. + * + * ## Examples + * ``` + * toString() === '6ba7b810-9dad-11d1-80b4-00c04fd430c8'); + * + * ?> + * ``` + * + * @since 7.2 + * @see https://php.net/uuid.toString + * @see https://tools.ietf.org/html/rfc4122#page-4 + * @see https://en.wikipedia.org/wiki/Universally_unique_identifier#Format + * @return string String representation of the UUID. + * @throws \ArgumentCountError if arguments are passed. + * @throws \Error if this instance does not encapsulate a valid UUID. + */ + public function toString(): string { } + + /** + * Callback for cloning of objects. This method is private and effectively + * disables cloning of this object, since it makes no sense to clone + * immutable objects. + * + * @return void + * @throws \Error upon every invocation. + */ + private function __clone() { } +} diff --git a/ext/standard/stubs/UUIDParseException.php b/ext/standard/stubs/UUIDParseException.php new file mode 100644 index 0000000000000..ba123545d24ac --- /dev/null +++ b/ext/standard/stubs/UUIDParseException.php @@ -0,0 +1,87 @@ +getMessage(); // Expected at least 32 characters, but got 3 characters + * echo $e->getInput(); // php + * echo $e->getPosition(); // 0 + * } + * + * try { + * UUID::parse('12345678-1234-1234-1234-123456789php'); + * } + * catch (UUIDParseException $e) { + * echo $e->getMessage(); // Expected hexadecimal digit, but found 'p' (0x70) + * echo $e->getInput(); // 12345678-1234-1234-1234-123456789php + * echo $e->getPosition(); // 33 + * } + * + * try { + * UUID::parse('12345678-1234-1234-1234-123456789abcdef'); + * } + * catch (UUIDParseException $e) { + * echo $e->getMessage(); // Expected no more than 32 hexadecimal digits + * echo $e->getInput(); // 12345678-1234-1234-1234-123456789abcdef + * echo $e->getPosition(); // 37 + * } + * + * ?> + * ``` + * + * @see \PHP\Std\UUID::parse() + */ +final class UUIDParseException extends \Exception { + /** @var string */ + private $input; + + /** @var int */ + private $position; + + /** + * Construct new UUID parse exception instance. + * + * @param string $reason why parsing the UUID string failed. + * @param string $input that should be parsed. + * @param int $position at which parsing failed. + * @param \Throwable|null $previous error/exception that lead to this + * failure, if any. + * @throws \ArgumentCountError if less than two or more than four arguments + * are passed. + */ + public function __construct(string $reason, string $input, int $position = 0, ?\Throwable $previous = null) { } + + /** + * Get the original input string that should have been parsed as a UUID. + * + * @return string + * @throws \ArgumentCountError if arguments are passed. + */ + public function getInput(): string { } + + /** + * Get the position in the input string where the parsing failure occurred. + * + * @return int + * @throws \ArgumentCountError if arguments are passed. + */ + public function getPosition(): int { } +} diff --git a/ext/standard/tests/uuid/UUID/NamespaceDNS/basic.phpt b/ext/standard/tests/uuid/UUID/NamespaceDNS/basic.phpt new file mode 100644 index 0000000000000..35c5b18d99e2b --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceDNS/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceDNS +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant() === UUID::VARIANT_RFC4122, + $uuid->getVersion() === UUID::VERSION_1_TIME_BASED, + $uuid->toBinary() === "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", + $uuid->toHex(), + $uuid->toString() +); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +string(32) "6ba7b8109dad11d180b400c04fd430c8" +string(36) "6ba7b810-9dad-11d1-80b4-00c04fd430c8" diff --git a/ext/standard/tests/uuid/UUID/NamespaceDNS/definition.phpt b/ext/standard/tests/uuid/UUID/NamespaceDNS/definition.phpt new file mode 100644 index 0000000000000..7862ddf0029e0 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceDNS/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceDNS method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/NamespaceOID/basic.phpt b/ext/standard/tests/uuid/UUID/NamespaceOID/basic.phpt new file mode 100644 index 0000000000000..5c709304fb0fe --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceOID/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceOID +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant() === UUID::VARIANT_RFC4122, + $uuid->getVersion() === UUID::VERSION_1_TIME_BASED, + $uuid->toBinary() === "\x6b\xa7\xb8\x12\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", + $uuid->toHex(), + $uuid->toString() +); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +string(32) "6ba7b8129dad11d180b400c04fd430c8" +string(36) "6ba7b812-9dad-11d1-80b4-00c04fd430c8" diff --git a/ext/standard/tests/uuid/UUID/NamespaceOID/definition.phpt b/ext/standard/tests/uuid/UUID/NamespaceOID/definition.phpt new file mode 100644 index 0000000000000..5146552bbedba --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceOID/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceOID method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/NamespaceURL/basic.phpt b/ext/standard/tests/uuid/UUID/NamespaceURL/basic.phpt new file mode 100644 index 0000000000000..4afa62242cde0 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceURL/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceURL +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant() === UUID::VARIANT_RFC4122, + $uuid->getVersion() === UUID::VERSION_1_TIME_BASED, + $uuid->toBinary() === "\x6b\xa7\xb8\x11\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", + $uuid->toHex(), + $uuid->toString() +); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +string(32) "6ba7b8119dad11d180b400c04fd430c8" +string(36) "6ba7b811-9dad-11d1-80b4-00c04fd430c8" diff --git a/ext/standard/tests/uuid/UUID/NamespaceURL/definition.phpt b/ext/standard/tests/uuid/UUID/NamespaceURL/definition.phpt new file mode 100644 index 0000000000000..5bc4c270f14d6 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceURL/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceURL method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/NamespaceX500/basic.phpt b/ext/standard/tests/uuid/UUID/NamespaceX500/basic.phpt new file mode 100644 index 0000000000000..c9ec71fcacd79 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceX500/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceX500 +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant() === UUID::VARIANT_RFC4122, + $uuid->getVersion() === UUID::VERSION_1_TIME_BASED, + $uuid->toBinary() === "\x6b\xa7\xb8\x14\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", + $uuid->toHex(), + $uuid->toString() +); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +string(32) "6ba7b8149dad11d180b400c04fd430c8" +string(36) "6ba7b814-9dad-11d1-80b4-00c04fd430c8" diff --git a/ext/standard/tests/uuid/UUID/NamespaceX500/definition.phpt b/ext/standard/tests/uuid/UUID/NamespaceX500/definition.phpt new file mode 100644 index 0000000000000..6860e1f236b0a --- /dev/null +++ b/ext/standard/tests/uuid/UUID/NamespaceX500/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::NamespaceX500 method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/Nil/basic.phpt b/ext/standard/tests/uuid/UUID/Nil/basic.phpt new file mode 100644 index 0000000000000..c4d4dbab03cf2 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/Nil/basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::Nil +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant(), + $uuid->getVersion(), + $uuid->toBinary() === "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", + $uuid->toHex(), + $uuid->toString() +); + +?> +--EXPECT-- +int(0) +int(0) +bool(true) +string(32) "00000000000000000000000000000000" +string(36) "00000000-0000-0000-0000-000000000000" diff --git a/ext/standard/tests/uuid/UUID/Nil/definition.phpt b/ext/standard/tests/uuid/UUID/Nil/definition.phpt new file mode 100644 index 0000000000000..e1a8552f8ee24 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/Nil/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::Nil method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/__clone/definition.phpt b/ext/standard/tests/uuid/UUID/__clone/definition.phpt new file mode 100644 index 0000000000000..2f4a587b86203 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__clone/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::__clone method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + $m->hasReturnType(), + $m->isPrivate(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +bool(false) +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/__clone/error.phpt b/ext/standard/tests/uuid/UUID/__clone/error.phpt new file mode 100644 index 0000000000000..a011af0ac1190 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__clone/error.phpt @@ -0,0 +1,22 @@ +--TEST-- +UUID::__clone invocation leads to Error +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); + +try { + $m->invoke(UUID::Nil()); +} +catch (Error $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- +Cannot clone immutable PHP\Std\UUID object diff --git a/ext/standard/tests/uuid/UUID/__construct/definition.phpt b/ext/standard/tests/uuid/UUID/__construct/definition.phpt new file mode 100644 index 0000000000000..99bce680eb398 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__construct/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::__construct method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + $m->hasReturnType(), + $m->isPrivate(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +bool(false) +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/__set/definition.phpt b/ext/standard/tests/uuid/UUID/__set/definition.phpt new file mode 100644 index 0000000000000..26667cecd8c62 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__set/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::__set method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(2) +int(2) +string(4) "void" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/__set/error.phpt b/ext/standard/tests/uuid/UUID/__set/error.phpt new file mode 100644 index 0000000000000..78f24c30552cf --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__set/error.phpt @@ -0,0 +1,19 @@ +--TEST-- +UUID::__set invocation leads to Error +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +dynamic_property = 'value'; +} +catch (Error $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- +Cannot set dynamic properties on immutable PHP\Std\UUID object diff --git a/ext/standard/tests/uuid/UUID/__wakeup/basic.phpt b/ext/standard/tests/uuid/UUID/__wakeup/basic.phpt new file mode 100644 index 0000000000000..ded43af129bc9 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__wakeup/basic.phpt @@ -0,0 +1,16 @@ +--TEST-- +UUID::__wakeup +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/__wakeup/definition.phpt b/ext/standard/tests/uuid/UUID/__wakeup/definition.phpt new file mode 100644 index 0000000000000..f35e5b80954dc --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__wakeup/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::__wakeup method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "void" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/__wakeup/error-001.phpt b/ext/standard/tests/uuid/UUID/__wakeup/error-001.phpt new file mode 100644 index 0000000000000..6f77a0034984e --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__wakeup/error-001.phpt @@ -0,0 +1,17 @@ +--TEST-- +UUID::__wakeup throws UnexpectedValueException if property value is not of type string +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +Expected PHP\Std\UUID::$bytes value to be of type string, but found null diff --git a/ext/standard/tests/uuid/UUID/__wakeup/error-002.phpt b/ext/standard/tests/uuid/UUID/__wakeup/error-002.phpt new file mode 100644 index 0000000000000..ef8a30b416fb9 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/__wakeup/error-002.phpt @@ -0,0 +1,24 @@ +--TEST-- +UUID::__wakeup throws UnexpectedValueException if binary string is not exactly 16 bytes long after deserialization +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECTF-- +Expected PHP\Std\UUID::$bytes value to be exactly 16 bytes long, but found 0 +Expected PHP\Std\UUID::$bytes value to be exactly 16 bytes long, but found 1 +Expected PHP\Std\UUID::$bytes value to be exactly 16 bytes long, but found 15 +Expected PHP\Std\UUID::$bytes value to be exactly 16 bytes long, but found 17 +Expected PHP\Std\UUID::$bytes value to be exactly 16 bytes long, but found %d diff --git a/ext/standard/tests/uuid/UUID/comparison-001.phpt b/ext/standard/tests/uuid/UUID/comparison-001.phpt new file mode 100644 index 0000000000000..2532b95e02b73 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/comparison-001.phpt @@ -0,0 +1,20 @@ +--TEST-- +UUID comparison reflexivity +--DESCRIPTION-- +𝑎 ≼ 𝑎 + +https://en.wikipedia.org/wiki/Reflexive_relation +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/comparison-002.phpt b/ext/standard/tests/uuid/UUID/comparison-002.phpt new file mode 100644 index 0000000000000..a5e0658857c24 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/comparison-002.phpt @@ -0,0 +1,23 @@ +--TEST-- +UUID comparison antisymmetry +--DESCRIPTION-- +if 𝑎 ≼ 𝑏 and 𝑏 ≼ 𝑎, then 𝑎 = 𝑏 + +https://en.wikipedia.org/wiki/Antisymmetric_relation +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/comparison-003.phpt b/ext/standard/tests/uuid/UUID/comparison-003.phpt new file mode 100644 index 0000000000000..eff14b5584bb5 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/comparison-003.phpt @@ -0,0 +1,24 @@ +--TEST-- +UUID comparison transitivity +--DESCRIPTION-- +if 𝑎 ≼ 𝑏 and 𝑏 ≼ 𝑐, then 𝑎 ≼ 𝑐 + +https://en.wikipedia.org/wiki/Transitive_relation +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/comparison-004.phpt b/ext/standard/tests/uuid/UUID/comparison-004.phpt new file mode 100644 index 0000000000000..5f82436938e17 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/comparison-004.phpt @@ -0,0 +1,38 @@ +--TEST-- +UUID comparison toset +--DESCRIPTION-- +only one of 𝑎 ≺ 𝑏, 𝑎 = 𝑏, or 𝑎 ≻ 𝑏 is true + +https://en.wikipedia.org/wiki/Toset +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + $b), "\n"; + +$a = UUID::fromBinary("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"); +$b = UUID::fromBinary("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"); +echo var_dump($a < $b, $a == $b, $a > $b), "\n"; + +$a = UUID::fromBinary("\2\2\2\2\2\2\2\2\2\2\2\2\2\2\2\2"); +$b = UUID::fromBinary("\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"); +echo var_dump($a < $b, $a == $b, $a > $b), "\n"; + +?> +--EXPECT-- +bool(true) +bool(false) +bool(false) + +bool(false) +bool(true) +bool(false) + +bool(false) +bool(false) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/definition-001.phpt b/ext/standard/tests/uuid/UUID/definition-001.phpt new file mode 100644 index 0000000000000..4a9cfef72010e --- /dev/null +++ b/ext/standard/tests/uuid/UUID/definition-001.phpt @@ -0,0 +1,40 @@ +--TEST-- +UUID class definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getInterfaces() === [], + $c->getParentClass(), + $c->isAbstract(), + $c->isCloneable(), + $c->isFinal(), + $c->isInstantiable(), + $c->isInterface(), + $c->isInternal(), + $c->isIterateable(), + $c->isTrait(), + count($c->getConstants()), + count($c->getProperties()) +); + +?> +--EXPECT-- +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(false) +bool(false) +bool(true) +bool(false) +bool(false) +int(9) +int(1) diff --git a/ext/standard/tests/uuid/UUID/definition-002.phpt b/ext/standard/tests/uuid/UUID/definition-002.phpt new file mode 100644 index 0000000000000..405e3bf9aa7f5 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/definition-002.phpt @@ -0,0 +1,22 @@ +--TEST-- +UUID::VARIANT_* constant definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +int(0) +int(1) +int(2) +int(3) diff --git a/ext/standard/tests/uuid/UUID/definition-003.phpt b/ext/standard/tests/uuid/UUID/definition-003.phpt new file mode 100644 index 0000000000000..ba55e5af9de39 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/definition-003.phpt @@ -0,0 +1,24 @@ +--TEST-- +UUID::VERSION_* constant definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +int(1) +int(2) +int(3) +int(4) +int(5) diff --git a/ext/standard/tests/uuid/UUID/definition-004.phpt b/ext/standard/tests/uuid/UUID/definition-004.phpt new file mode 100644 index 0000000000000..8855e6acf4df7 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/definition-004.phpt @@ -0,0 +1,17 @@ +--TEST-- +UUID::$bytes property definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +isPrivate(), $p->isStatic()); + +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/fromBinary/basic.phpt b/ext/standard/tests/uuid/UUID/fromBinary/basic.phpt new file mode 100644 index 0000000000000..4b2a998e19889 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/fromBinary/basic.phpt @@ -0,0 +1,17 @@ +--TEST-- +UUID::fromBinary random data +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === ' '); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/fromBinary/definition-001.phpt b/ext/standard/tests/uuid/UUID/fromBinary/definition-001.phpt new file mode 100644 index 0000000000000..dd03c676cca56 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/fromBinary/definition-001.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::fromBinary method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(1) +int(1) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/fromBinary/definition-002.phpt b/ext/standard/tests/uuid/UUID/fromBinary/definition-002.phpt new file mode 100644 index 0000000000000..3ccc15f462006 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/fromBinary/definition-002.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUID::fromBinary 1. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[0]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference(), + $p->isVariadic() +); + +?> +--EXPECT-- +string(5) "input" +bool(false) +string(6) "string" +bool(false) +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/fromBinary/error.phpt b/ext/standard/tests/uuid/UUID/fromBinary/error.phpt new file mode 100644 index 0000000000000..cee9254eae9ab --- /dev/null +++ b/ext/standard/tests/uuid/UUID/fromBinary/error.phpt @@ -0,0 +1,25 @@ +--TEST-- +UUID::fromBinary boundaries +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECTF-- +Expected exactly 16 bytes, but got 0 +Expected exactly 16 bytes, but got 1 +Expected exactly 16 bytes, but got 15 +Expected exactly 16 bytes, but got 17 +Expected exactly 16 bytes, but got %d diff --git a/ext/standard/tests/uuid/UUID/getVariant/basic.phpt b/ext/standard/tests/uuid/UUID/getVariant/basic.phpt new file mode 100644 index 0000000000000..f5691a8d49959 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/getVariant/basic.phpt @@ -0,0 +1,22 @@ +--TEST-- +UUID::getVariant +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00")->getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x00")->getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\xE0\x00\x00\x00\x00\x00\x00\x00")->getVariant() +); + +?> +--EXPECT-- +int(0) +int(1) +int(2) +int(3) diff --git a/ext/standard/tests/uuid/UUID/getVariant/definition.phpt b/ext/standard/tests/uuid/UUID/getVariant/definition.phpt new file mode 100644 index 0000000000000..8e06c75ce0a12 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/getVariant/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::getVariant method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(3) "int" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/getVariant/variation-001.phpt b/ext/standard/tests/uuid/UUID/getVariant/variation-001.phpt new file mode 100644 index 0000000000000..3a2c748777035 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/getVariant/variation-001.phpt @@ -0,0 +1,22 @@ +--TEST-- +UUID::getVariant ignores Msb1 and Msb2 of octet 8 if Msb0 is 0 +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00")->getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00")->getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")->getVariant() +); + +?> +--EXPECT-- +int(0) +int(0) +int(0) +int(0) diff --git a/ext/standard/tests/uuid/UUID/getVariant/variation-002.phpt b/ext/standard/tests/uuid/UUID/getVariant/variation-002.phpt new file mode 100644 index 0000000000000..f2961e04a5c55 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/getVariant/variation-002.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::getVariant ignores Msb2 of octet 8 if Msb0 is 1 and Msb1 is 0 +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVariant(), + UUID::fromBinary("\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00")->getVariant() +); + +?> +--EXPECT-- +int(1) +int(1) diff --git a/ext/standard/tests/uuid/UUID/getVersion/basic.phpt b/ext/standard/tests/uuid/UUID/getVersion/basic.phpt new file mode 100644 index 0000000000000..50d241281f1e7 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/getVersion/basic.phpt @@ -0,0 +1,31 @@ +--TEST-- +UUID::getVersion +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getVersion()); +} + +?> +--EXPECT-- +int(0) +int(1) +int(2) +int(3) +int(4) +int(5) +int(6) +int(7) +int(8) +int(9) +int(10) +int(11) +int(12) +int(13) +int(14) +int(15) diff --git a/ext/standard/tests/uuid/UUID/getVersion/definition.phpt b/ext/standard/tests/uuid/UUID/getVersion/definition.phpt new file mode 100644 index 0000000000000..b02c0ac840d5e --- /dev/null +++ b/ext/standard/tests/uuid/UUID/getVersion/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::getVersion method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(3) "int" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/isNil/basic.phpt b/ext/standard/tests/uuid/UUID/isNil/basic.phpt new file mode 100644 index 0000000000000..29383db843d12 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/isNil/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::isNil +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +isNil()); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/isNil/definition.phpt b/ext/standard/tests/uuid/UUID/isNil/definition.phpt new file mode 100644 index 0000000000000..7d7d66fe8b8cb --- /dev/null +++ b/ext/standard/tests/uuid/UUID/isNil/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::isNil method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "bool" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/parse/basic-001.phpt b/ext/standard/tests/uuid/UUID/parse/basic-001.phpt new file mode 100644 index 0000000000000..4d43d507c65bf --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/basic-001.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse hexadecimal representation +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/basic-002.phpt b/ext/standard/tests/uuid/UUID/parse/basic-002.phpt new file mode 100644 index 0000000000000..2bdce8871b7f2 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/basic-002.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse string representation +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/basic-003.phpt b/ext/standard/tests/uuid/UUID/parse/basic-003.phpt new file mode 100644 index 0000000000000..b3eef2522594a --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/basic-003.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse URN +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/basic-004.phpt b/ext/standard/tests/uuid/UUID/parse/basic-004.phpt new file mode 100644 index 0000000000000..c08cfc6b67855 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/basic-004.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse Microsoft +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/definition-001.phpt b/ext/standard/tests/uuid/UUID/parse/definition-001.phpt new file mode 100644 index 0000000000000..3ec28a16a688b --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/definition-001.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::parse method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(1) +int(1) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/definition-002.phpt b/ext/standard/tests/uuid/UUID/parse/definition-002.phpt new file mode 100644 index 0000000000000..2730abbba89f0 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/definition-002.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUID::parse 1. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[0]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference(), + $p->isVariadic() +); + +?> +--EXPECT-- +string(5) "input" +bool(false) +string(6) "string" +bool(false) +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/parse/error-001.phpt b/ext/standard/tests/uuid/UUID/parse/error-001.phpt new file mode 100644 index 0000000000000..62646db26505e --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/error-001.phpt @@ -0,0 +1,34 @@ +--TEST-- +UUID::parse throws UUIDParseException if there are less than 32 chars +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECT-- +Expected at least 32 hexadecimal digits, but got 0 +Expected at least 32 hexadecimal digits, but got 1 +Expected at least 32 hexadecimal digits, but got 31 +Expected at least 32 hexadecimal digits, but got 30 +Expected at least 32 hexadecimal digits, but got 30 diff --git a/ext/standard/tests/uuid/UUID/parse/error-002.phpt b/ext/standard/tests/uuid/UUID/parse/error-002.phpt new file mode 100644 index 0000000000000..a4e16ce90ba73 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/error-002.phpt @@ -0,0 +1,20 @@ +--TEST-- +UUID::parse throws UUIDParseException for non-hexadecimal characters +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Expected hexadecimal digit, but found 'P' (0x50) diff --git a/ext/standard/tests/uuid/UUID/parse/error-003.phpt b/ext/standard/tests/uuid/UUID/parse/error-003.phpt new file mode 100644 index 0000000000000..12e53deeb3930 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/error-003.phpt @@ -0,0 +1,20 @@ +--TEST-- +UUID::parse throws UUIDParseException if too many hexadecimal digits are found +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Expected no more than 32 hexadecimal digits diff --git a/ext/standard/tests/uuid/UUID/parse/variation-001.phpt b/ext/standard/tests/uuid/UUID/parse/variation-001.phpt new file mode 100644 index 0000000000000..f71ba6fdd4797 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/variation-001.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse is case-insensitive +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/variation-002.phpt b/ext/standard/tests/uuid/UUID/parse/variation-002.phpt new file mode 100644 index 0000000000000..5f76d67f671ba --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/variation-002.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse ignores leading spaces ( ), tabs (\t), and opening braces ({) +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/variation-003.phpt b/ext/standard/tests/uuid/UUID/parse/variation-003.phpt new file mode 100644 index 0000000000000..6b70a57ca6925 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/variation-003.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse ignores trailing spaces ( ), tabs (\t), and closing braces (}) +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/variation-004.phpt b/ext/standard/tests/uuid/UUID/parse/variation-004.phpt new file mode 100644 index 0000000000000..d6c265caef9f3 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/variation-004.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse ignores hyphens (-) +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x12\x3e\x45\x67\xe8\x9b\x12\xd3\xa4\x56\x42\x66\x55\x44\x00\x00"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/parse/variation-005.phpt b/ext/standard/tests/uuid/UUID/parse/variation-005.phpt new file mode 100644 index 0000000000000..940c65dfa8507 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/parse/variation-005.phpt @@ -0,0 +1,18 @@ +--TEST-- +UUID::parse accepts URN even with leading and trailing whitespace/braces and extraneous hyphens everywhere +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); +var_dump($p->getValue($uuid) === "\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/sorting.phpt b/ext/standard/tests/uuid/UUID/sorting.phpt new file mode 100644 index 0000000000000..4d0aefeb01433 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/sorting.phpt @@ -0,0 +1,20 @@ +--TEST-- +UUID sorting +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/toBinary/basic.phpt b/ext/standard/tests/uuid/UUID/toBinary/basic.phpt new file mode 100644 index 0000000000000..57258da94097e --- /dev/null +++ b/ext/standard/tests/uuid/UUID/toBinary/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::toBinary +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +toBinary() === "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"); + +?> +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/toBinary/definition.phpt b/ext/standard/tests/uuid/UUID/toBinary/definition.phpt new file mode 100644 index 0000000000000..08e8f1d6b107c --- /dev/null +++ b/ext/standard/tests/uuid/UUID/toBinary/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::toBinary method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(6) "string" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/toHex/basic.phpt b/ext/standard/tests/uuid/UUID/toHex/basic.phpt new file mode 100644 index 0000000000000..214b1085cef14 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/toHex/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::toHex +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +toHex()); + +?> +--EXPECT-- +string(32) "000102030405060708090a0b0c0d0e0f" diff --git a/ext/standard/tests/uuid/UUID/toHex/definition.phpt b/ext/standard/tests/uuid/UUID/toHex/definition.phpt new file mode 100644 index 0000000000000..0e8f7b44f3f2b --- /dev/null +++ b/ext/standard/tests/uuid/UUID/toHex/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::toHex method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(6) "string" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/toString/basic.phpt b/ext/standard/tests/uuid/UUID/toString/basic.phpt new file mode 100644 index 0000000000000..6cda04eee2d66 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/toString/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::toString +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +toString()); + +?> +--EXPECT-- +string(36) "00010203-0405-0607-0809-0a0b0c0d0e0f" diff --git a/ext/standard/tests/uuid/UUID/toString/definition.phpt b/ext/standard/tests/uuid/UUID/toString/definition.phpt new file mode 100644 index 0000000000000..0e8f7b44f3f2b --- /dev/null +++ b/ext/standard/tests/uuid/UUID/toString/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::toHex method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(6) "string" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/v3/basic.phpt b/ext/standard/tests/uuid/UUID/v3/basic.phpt new file mode 100644 index 0000000000000..bc5cb1fd95a4c --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v3/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::v3 +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +toString()); + +?> +--EXPECT-- +string(36) "11a38b9a-b3da-360f-9353-a5a725514269" diff --git a/ext/standard/tests/uuid/UUID/v3/definition-001.phpt b/ext/standard/tests/uuid/UUID/v3/definition-001.phpt new file mode 100644 index 0000000000000..2fa8421b3559f --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v3/definition-001.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::v3 method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(2) +int(2) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/v3/definition-002.phpt b/ext/standard/tests/uuid/UUID/v3/definition-002.phpt new file mode 100644 index 0000000000000..4e3aa0f671eb7 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v3/definition-002.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUID::v3 1. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[0]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference(), + $p->isVariadic() +); + +?> +--EXPECT-- +string(9) "namespace" +bool(false) +string(4) "self" +bool(false) +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/v3/definition-003.phpt b/ext/standard/tests/uuid/UUID/v3/definition-003.phpt new file mode 100644 index 0000000000000..2cdd767f9c708 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v3/definition-003.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUID::v3 2. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[1]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference(), + $p->isVariadic() +); + +?> +--EXPECT-- +string(4) "name" +bool(false) +string(6) "string" +bool(false) +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/v4/basic.phpt b/ext/standard/tests/uuid/UUID/v4/basic.phpt new file mode 100644 index 0000000000000..ca997d867ff29 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v4/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::v4 +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/uuid/UUID/v4/definition.phpt b/ext/standard/tests/uuid/UUID/v4/definition.phpt new file mode 100644 index 0000000000000..2db98346e2141 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v4/definition.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::v4 method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/v5/basic.phpt b/ext/standard/tests/uuid/UUID/v5/basic.phpt new file mode 100644 index 0000000000000..726845b7ecbad --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v5/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUID::v5 +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +toString()); + +?> +--EXPECT-- +string(36) "c4a760a8-dbcf-5254-a0d9-6a4474bd1b62" diff --git a/ext/standard/tests/uuid/UUID/v5/definition-001.phpt b/ext/standard/tests/uuid/UUID/v5/definition-001.phpt new file mode 100644 index 0000000000000..cf67bffbab5e3 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v5/definition-001.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUID::v5 method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(2) +int(2) +string(4) "self" +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUID/v5/definition-002.phpt b/ext/standard/tests/uuid/UUID/v5/definition-002.phpt new file mode 100644 index 0000000000000..47784a2c6487b --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v5/definition-002.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUID::v5 1. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[0]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference(), + $p->isVariadic() +); + +?> +--EXPECT-- +string(9) "namespace" +bool(false) +string(4) "self" +bool(false) +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUID/v5/definition-003.phpt b/ext/standard/tests/uuid/UUID/v5/definition-003.phpt new file mode 100644 index 0000000000000..3bfcb33b6d285 --- /dev/null +++ b/ext/standard/tests/uuid/UUID/v5/definition-003.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUID::v5 2. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[1]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference(), + $p->isVariadic() +); + +?> +--EXPECT-- +string(4) "name" +bool(false) +string(6) "string" +bool(false) +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/definition-001.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-001.phpt new file mode 100644 index 0000000000000..12c28a090f9c3 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-001.phpt @@ -0,0 +1,24 @@ +--TEST-- +UUIDParseException::__construct method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + $m->hasReturnType(), + $m->isPublic() +); + +?> +--EXPECT-- +int(4) +int(2) +bool(false) +bool(true) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/definition-002.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-002.phpt new file mode 100644 index 0000000000000..e98bdc64379ac --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-002.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUIDParseException::__construct 1. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[0]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference() +); + +?> +--EXPECT-- +string(6) "reason" +bool(false) +string(6) "string" +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/definition-003.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-003.phpt new file mode 100644 index 0000000000000..19467c9c66dd8 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-003.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUIDParseException::__construct 2. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[1]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + $p->isPassedByReference() +); + +?> +--EXPECT-- +string(5) "input" +bool(false) +string(6) "string" +bool(false) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/definition-004.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-004.phpt new file mode 100644 index 0000000000000..2f4e0ce1ce14e --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-004.phpt @@ -0,0 +1,27 @@ +--TEST-- +UUIDParseException::__construct 3. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[2]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + //$p->getDefaultValue(), + $p->isPassedByReference() +); + +?> +--EXPECT-- +string(8) "position" +bool(false) +string(3) "int" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/definition-005.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-005.phpt new file mode 100644 index 0000000000000..04a26a7f67564 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/definition-005.phpt @@ -0,0 +1,27 @@ +--TEST-- +UUIDParseException::__construct 4. parameter definition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getParameters()[3]; + +var_dump( + $p->getName(), + $p->allowsNull(), + (string) $p->getType(), + $p->isOptional(), + //$p->getDefaultValue(), + $p->isPassedByReference() +); + +?> +--EXPECT-- +string(8) "previous" +bool(true) +string(9) "Throwable" +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/variation-001.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/variation-001.phpt new file mode 100644 index 0000000000000..0524c99db8e4c --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/variation-001.phpt @@ -0,0 +1,22 @@ +--TEST-- +UUIDParseException::__construct with 2 arguments +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); + var_dump($p->getValue($e)); +} + +?> +--EXPECT-- +string(20) "variation-001-reason" +string(19) "variation-001-input" +int(0) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/variation-002.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/variation-002.phpt new file mode 100644 index 0000000000000..97272cf9c47d4 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/variation-002.phpt @@ -0,0 +1,22 @@ +--TEST-- +UUIDParseException::__construct with 3 arguments +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); + var_dump($p->getValue($e)); +} + +?> +--EXPECT-- +string(20) "variation-002-reason" +string(19) "variation-002-input" +int(42) diff --git a/ext/standard/tests/uuid/UUIDParseException/__construct/variation-003.phpt b/ext/standard/tests/uuid/UUIDParseException/__construct/variation-003.phpt new file mode 100644 index 0000000000000..8b39b3a2c0710 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/__construct/variation-003.phpt @@ -0,0 +1,27 @@ +--TEST-- +UUIDParseException::__construct with 4 arguments +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +setAccessible(true); + var_dump($p->getValue($e)); +} + +$p = new ReflectionProperty(Exception::class, 'previous'); +$p->setAccessible(true); +var_dump($p->getValue($e) === $previous); + +?> +--EXPECT-- +string(20) "variation-003-reason" +string(19) "variation-003-input" +int(84) +bool(true) diff --git a/ext/standard/tests/uuid/UUIDParseException/basic.phpt b/ext/standard/tests/uuid/UUIDParseException/basic.phpt new file mode 100644 index 0000000000000..5a5a5733ab5a6 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/basic.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUIDParseException construction +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getCode(), + $e->getFile() === __FILE__, + $e->getInput(), + $e->getLine(), + $e->getMessage(), + $e->getPosition(), + $e->getPrevious() +); + +?> +--EXPECT-- +int(0) +bool(true) +string(11) "basic-input" +int(3) +string(12) "basic-reason" +int(0) +NULL diff --git a/ext/standard/tests/uuid/UUIDParseException/definition-001.phpt b/ext/standard/tests/uuid/UUIDParseException/definition-001.phpt new file mode 100644 index 0000000000000..e6b58294e2aec --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/definition-001.phpt @@ -0,0 +1,26 @@ +--TEST-- +UUIDParseException class definition. +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +isAbstract(), + $c->isFinal(), + $c->isInstantiable(), + $c->isInternal(), + $c->isSubclassOf(Exception::class) +); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/uuid/UUIDParseException/definition-002.phpt b/ext/standard/tests/uuid/UUIDParseException/definition-002.phpt new file mode 100644 index 0000000000000..dc1fa20485b36 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/definition-002.phpt @@ -0,0 +1,17 @@ +--TEST-- +UUIDParseException input property definition. +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +isPrivate(), $p->isStatic()); + +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/definition-003.phpt b/ext/standard/tests/uuid/UUIDParseException/definition-003.phpt new file mode 100644 index 0000000000000..a39b49783a582 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/definition-003.phpt @@ -0,0 +1,17 @@ +--TEST-- +UUIDParseException position property definition. +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +isPrivate(), $p->isStatic()); + +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/getInput/basic.phpt b/ext/standard/tests/uuid/UUIDParseException/getInput/basic.phpt new file mode 100644 index 0000000000000..7f676fd8d0287 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/getInput/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUIDParseException::getInput +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getInput()); + +?> +--EXPECT-- +string(10) "input-test" diff --git a/ext/standard/tests/uuid/UUIDParseException/getInput/definition.phpt b/ext/standard/tests/uuid/UUIDParseException/getInput/definition.phpt new file mode 100644 index 0000000000000..e2cbbdfce746e --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/getInput/definition.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUIDParseException::getInput method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isAbstract(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(6) "string" +bool(false) +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/getPosition/basic.phpt b/ext/standard/tests/uuid/UUIDParseException/getPosition/basic.phpt new file mode 100644 index 0000000000000..14650aba68808 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/getPosition/basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUIDParseException::getPosition default value +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getPosition()); + +?> +--EXPECT-- +int(0) diff --git a/ext/standard/tests/uuid/UUIDParseException/getPosition/definition.phpt b/ext/standard/tests/uuid/UUIDParseException/getPosition/definition.phpt new file mode 100644 index 0000000000000..ffb275585a177 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/getPosition/definition.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUIDParseException::getPosition method signature +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getNumberOfParameters(), + $m->getNumberOfRequiredParameters(), + (string) $m->getReturnType(), + $m->isAbstract(), + $m->isPublic(), + $m->isStatic() +); + +?> +--EXPECT-- +int(0) +int(0) +string(3) "int" +bool(false) +bool(true) +bool(false) diff --git a/ext/standard/tests/uuid/UUIDParseException/getPosition/variation.phpt b/ext/standard/tests/uuid/UUIDParseException/getPosition/variation.phpt new file mode 100644 index 0000000000000..78d29bbe683ea --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/getPosition/variation.phpt @@ -0,0 +1,14 @@ +--TEST-- +UUIDParseException::getPosition +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getPosition()); + +?> +--EXPECT-- +int(42) diff --git a/ext/standard/tests/uuid/UUIDParseException/variation-001.phpt b/ext/standard/tests/uuid/UUIDParseException/variation-001.phpt new file mode 100644 index 0000000000000..56892f84d14d0 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/variation-001.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUIDParseException construction with custom position +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getCode(), + $e->getFile() === __FILE__, + $e->getInput(), + $e->getLine(), + $e->getMessage(), + $e->getPosition(), + $e->getPrevious() +); + +?> +--EXPECT-- +int(0) +bool(true) +string(19) "variation-001-input" +int(3) +string(20) "variation-001-reason" +int(1) +NULL diff --git a/ext/standard/tests/uuid/UUIDParseException/variation-002.phpt b/ext/standard/tests/uuid/UUIDParseException/variation-002.phpt new file mode 100644 index 0000000000000..d4fdbceaec2cd --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/variation-002.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUIDParseException construction with custom position and previous error +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getCode(), + $e->getFile() === __FILE__, + $e->getInput(), + $e->getLine(), + $e->getMessage(), + $e->getPosition(), + $e->getPrevious() === $previous +); + +?> +--EXPECT-- +int(0) +bool(true) +string(19) "variation-002-input" +int(3) +string(20) "variation-002-reason" +int(2) +bool(true) diff --git a/ext/standard/tests/uuid/UUIDParseException/variation-003.phpt b/ext/standard/tests/uuid/UUIDParseException/variation-003.phpt new file mode 100644 index 0000000000000..041c5c0b15553 --- /dev/null +++ b/ext/standard/tests/uuid/UUIDParseException/variation-003.phpt @@ -0,0 +1,28 @@ +--TEST-- +UUIDParseException construction with custom position and previous exception +--CREDITS-- +Richard Fussenegger php@fleshgrinder.com +--FILE-- +getCode(), + $e->getFile() === __FILE__, + $e->getInput(), + $e->getLine(), + $e->getMessage(), + $e->getPosition(), + $e->getPrevious() === $previous +); + +?> +--EXPECT-- +int(0) +bool(true) +string(19) "variation-003-input" +int(3) +string(20) "variation-003-reason" +int(3) +bool(true) diff --git a/ext/standard/uuid.c b/ext/standard/uuid.c new file mode 100644 index 0000000000000..bf4fa3614b624 --- /dev/null +++ b/ext/standard/uuid.c @@ -0,0 +1,793 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2017 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Richard Fussenegger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php_uuid.h" + +#include "zend_exceptions.h" +#include "php_standard.h" +#include "ext/spl/spl_exceptions.h" +#include "md5.h" +#include "php_random.h" +#include "sha1.h" + +PHPAPI zend_class_entry *php_ce_UUID; +PHPAPI zend_class_entry *php_ce_UUIDParseException; + +/** Minimum legal UUID version value. */ +static const uint8_t UUID_VERSION_MIN = 0; + +/** Maximum legal UUID version value. */ +static const uint8_t UUID_VERSION_MAX = 15; + +/** UUID hexadecimal representation length without terminating NUL. */ +static const uint8_t UUID_HEX_LEN = sizeof(php_uuid_hex) - 1; + +/** UUID string representation length without terminating NUL. */ +static const uint8_t UUID_STRING_LEN = sizeof(php_uuid_string) - 1; + +/** + * Name of the private property of the `UUID` class where the binary + * representation of the UUID is stored in. + */ +static const char UUID_BYTES_PROP[] = "bytes"; + +/** Length of the `UUID::$bytes` property name without terminating NUL. */ +static const uint8_t UUID_BYTES_PROP_LEN = sizeof(UUID_BYTES_PROP) - 1; + +/** + * Name of the private property of the `UUIDParseException` class where the + * original input is stored in that should have been parsed, but failed. + */ +static const char UUID_EX_INPUT_PROP[] = "input"; + +/** + * Length of the `UUIDParseException::$input` property name without terminating + * NUL. + */ +static const uint8_t UUID_EX_INPUT_PROP_LEN = sizeof(UUID_EX_INPUT_PROP) - 1; + +/** + * Name of the private property of the `UUIDParseException` class where the + * position is stored at which the parsing of the UUID failed. + */ +static const char UUID_EX_POSITION_PROP[] = "position"; + +/** + * Length of the `UUIDParseException::$position` property name without + * terminating NUL. + */ +static const uint8_t UUID_EX_POSITON_PROP_LEN = sizeof(UUID_EX_POSITION_PROP) - 1; + +/** URN prefix of a UUID's string representation. */ +static const char URN_PREFIX[] = "urn:uuid:"; + +/** Length of the URN prefix of a UUID without terminating NUL. */ +static const uint8_t URN_PREFIX_LEN = sizeof(URN_PREFIX) - 1; + +/** + * Set UUID variant to RFC 4122, the only supported variant. + * + * @param[out] uuid to set the variant. + */ +static zend_always_inline void set_variant_rfc4122(php_uuid *uuid) +{ + uuid->bytes[8] = (uuid->bytes[8] & 0x3F) | 0x80; +} + +/** + * Set UUID version. + * + * @see PHP_UUID_VERSION_1_TIME_BASED + * @see PHP_UUID_VERSION_2_DCE_SECURITY + * @see PHP_UUID_VERSION_3_NAME_BASED_MD5 + * @see PHP_UUID_VERSION_4_RANDOM + * @see PHP_UUID_VERSION_5_NAME_BASED_SHA1 + * @param[out] uuid to set the version. + * @param[in] version to set. + */ +static zend_always_inline void php_uuid_set_version(php_uuid *uuid, const uint8_t version) +{ + assert(UUID_VERSION_MIN <= version && version <= UUID_VERSION_MAX); + + uuid->bytes[6] = (uuid->bytes[6] & 0x0F) | (version << 4); +} + +PHPAPI void php_uuid_create_v3(php_uuid *uuid, const php_uuid *namespace, const char *name, const size_t name_len) +{ + PHP_MD5_CTX context; + unsigned char digest[16]; + + PHP_MD5Init(&context); + PHP_MD5Update(&context, namespace->bytes, PHP_UUID_LEN); + PHP_MD5Update(&context, name, name_len); + PHP_MD5Final(digest, &context); + memcpy(uuid->bytes, digest, PHP_UUID_LEN); + set_variant_rfc4122(uuid); + php_uuid_set_version(uuid, PHP_UUID_VERSION_3_NAME_BASED_MD5); +} + +PHPAPI int php_uuid_create_v4(php_uuid *uuid, const zend_bool throw) +{ + int result = php_random_bytes(uuid, PHP_UUID_LEN, throw); + + if (result == SUCCESS) { + set_variant_rfc4122(uuid); + php_uuid_set_version(uuid, PHP_UUID_VERSION_4_RANDOM); + } + + return result; +} + +PHPAPI void php_uuid_create_v5(php_uuid *uuid, const php_uuid *namespace, const char *name, const size_t name_len) +{ + PHP_SHA1_CTX context; + unsigned char digest[20]; + + PHP_SHA1Init(&context); + PHP_SHA1Update(&context, (const unsigned char *) namespace->bytes, PHP_UUID_LEN); + PHP_SHA1Update(&context, (const unsigned char *)name, name_len); + PHP_SHA1Final(digest, &context); + memcpy(uuid->bytes, digest, PHP_UUID_LEN); + set_variant_rfc4122(uuid); + php_uuid_set_version(uuid, PHP_UUID_VERSION_5_NAME_BASED_SHA1); +} + +/** + * Throw `UUIDParseException`. + * + * @param[in] input that could not be parsed. + * @param[in] input_len + * @param[in] position at which parsing failed. + * @param[in] reason why parsing failed. + * @param[in] ... arguments to format the reason. + */ +static ZEND_COLD zend_object *throw_uuid_parse_exception(const char *input, const size_t input_len, const size_t position, const char *reason, ...) +{ + va_list format_args; + char *formatted_reason; + zend_object *object; + zval exception; + + va_start(format_args, reason); + zend_vspprintf(&formatted_reason, 0, reason, format_args); + va_end(format_args); + object = zend_throw_exception(php_ce_UUIDParseException, formatted_reason, 0); + efree(formatted_reason); + + ZVAL_OBJ(&exception, object); + zend_update_property_stringl(php_ce_UUIDParseException, &exception, UUID_EX_INPUT_PROP, UUID_EX_INPUT_PROP_LEN, input, input_len); + zend_update_property_long(php_ce_UUIDParseException, &exception, UUID_EX_POSITION_PROP, UUID_EX_POSITON_PROP_LEN, position); + + return object; +} + +/** + * Throw `UUIDParseException` because of insufficient input. + * + * @param[in] input that could not be parsed. + * @param[in] input_len + * @param[in] position at which parsing failed. + * @param[in] actual amount of characters that were found. + */ +static zend_always_inline zend_object *throw_uuid_parse_exception_invalid_len(const char *input, const size_t input_len, const size_t position, const size_t actual) +{ + return throw_uuid_parse_exception( + input, + input_len, + position, + "Expected at least %u hexadecimal digits, but got %u", + UUID_HEX_LEN, + actual + ); +} + +/** + * Throw `UUIDParseException` because of an invalid character. + * + * @param[in] input that could not be parsed. + * @param[in] input_len + * @param[in] position at which parsing failed. + */ +static zend_always_inline zend_object *throw_uuid_parse_exception_invalid_char(const char *input, const size_t input_len, const size_t position) +{ + return throw_uuid_parse_exception( + input, + input_len, + position, + "Expected hexadecimal digit, but found '%c' (0x%02x)", + input[position], + input[position] + ); +} + +PHPAPI int php_uuid_parse(php_uuid *uuid, const char *input, const size_t input_len, const zend_bool throw) +{ + size_t position = 0; + size_t digit = 0; + size_t limit = input_len - 1; + uint8_t byte = 0; + + while (input[position] == '-' || input[position] == ' ' || input[position] == '\t' || input[position] == '{') { + ++position; + } + + if (memcmp(input + position, URN_PREFIX, URN_PREFIX_LEN) == 0) { + position += URN_PREFIX_LEN; + } + + while (input[limit] == '-' || input[limit] == ' ' || input[limit] == '\t' || input[limit] == '}') { + --limit; + } + + if ((limit - position + 1) < UUID_HEX_LEN) { + if (throw) { + throw_uuid_parse_exception_invalid_len(input, input_len, position, limit - position + 1); + } + return FAILURE; + } + + for (; position <= limit; ++position) { + const char chr = input[position]; + + /* First digit of the byte. */ + if (digit % 2 == 0) { + if ('0' <= chr && chr <= '9') { + byte = chr - '0'; + } + else if ('a' <= chr && chr <= 'f') { + byte = chr - 'a' + 10; + } + else if ('A' <= chr && chr <= 'F') { + byte = chr - 'A' + 10; + } + else if (chr == '-') { + continue; + } + else { + if (throw) { + throw_uuid_parse_exception_invalid_char(input, input_len, position); + } + return FAILURE; + } + } + /* Second digit of the byte. */ + else { + /* Shift upper half. */ + byte *= 16; + + if ('0' <= chr && chr <= '9') { + byte += chr - '0'; + } + else if ('a' <= chr && chr <= 'f') { + byte += chr - 'a' + 10; + } + else if ('A' <= chr && chr <= 'F') { + byte += chr - 'A' + 10; + } + else if (chr == '-') { + continue; + } + else { + if (throw) { + throw_uuid_parse_exception_invalid_char(input, input_len, position); + } + return FAILURE; + } + + if (digit > UUID_HEX_LEN) { + if (throw) { + throw_uuid_parse_exception( + input, + input_len, + position, + "Expected no more than %u hexadecimal digits", + UUID_HEX_LEN + ); + } + return FAILURE; + } + + uuid->bytes[digit / 2] = byte; + } + + ++digit; + } + + if (digit < UUID_HEX_LEN) { + if (throw) { + throw_uuid_parse_exception_invalid_len(input, input_len, position, digit); + } + return FAILURE; + } + + return SUCCESS; +} + +/** + * Get the UUID from the given UUID object. + * + * @attention + * We are required to check the type and length of the encapsulated value upon + * every access because users can change the value through various ways (e.g. + * reflection, closures, ...) and there is nothing that prevents them from + * doing so. + * + * @param[in] uuid_object to get the UUID from. + * @return #SUCCESS() if the UUID could be read from the object; #FAILURE() + * otherwise. + * @throws TypeError if the value is not of type string. + * @throws Error if the string value is not exactly 16 bytes long. + */ +static zend_always_inline php_uuid *get_uuid(/*const*/ zval *uuid_object) +{ + zval *bytes = zend_read_property(php_ce_UUID, uuid_object, UUID_BYTES_PROP, UUID_BYTES_PROP_LEN, 1, NULL); + + if (Z_TYPE_P(bytes) != IS_STRING) { + zend_throw_error( + zend_ce_type_error, + "Expected %s::$%s value to be of type %s, but found %s", + ZSTR_VAL(php_ce_UUID->name), + UUID_BYTES_PROP, + zend_get_type_by_const(IS_STRING), + zend_zval_type_name(bytes) + ); + return NULL; + } + + if (Z_STRLEN_P(bytes) != PHP_UUID_LEN) { + zend_throw_error( + zend_ce_error, + "Expected %s::$%s value to be exactly %u bytes long, but found %u", + ZSTR_VAL(php_ce_UUID->name), + UUID_BYTES_PROP, + PHP_UUID_LEN, + Z_STRLEN_P(bytes) + ); + return NULL; + } + + return (php_uuid *) Z_STRVAL_P(bytes); +} + +/** + * Construct new UUID instance. + * + * @param[out] object to store the UUID instance int. + * @param[in] uuid to construct the instance from. + */ +static zend_always_inline void new_uuid(zval *object, const php_uuid *uuid) +{ + object_init_ex(object, php_ce_UUID); + zend_update_property_stringl(php_ce_UUID, object, UUID_BYTES_PROP, UUID_BYTES_PROP_LEN, (const char *) uuid->bytes, PHP_UUID_LEN); +} + +/** `private function UUID::__construct()` */ +PHP_METHOD(UUID, __construct) +{ + /* NOOP */ +} +ZEND_BEGIN_ARG_INFO(UUID___construct_args, NULL) +ZEND_END_ARG_INFO() + +/** `public static function UUID::fromBinary(string $input): self` */ +PHP_METHOD(UUID, fromBinary) +{ + zval *input = NULL; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &input) == FAILURE) { + return; + } + + if (Z_STRLEN_P(input) != PHP_UUID_LEN) { + zend_throw_exception_ex( + spl_ce_InvalidArgumentException, + 0, + "Expected exactly %u bytes, but got %u", + PHP_UUID_LEN, + Z_STRLEN_P(input) + ); + return; + } + + object_init_ex(return_value, php_ce_UUID); + zend_update_property(php_ce_UUID, return_value, UUID_BYTES_PROP, UUID_BYTES_PROP_LEN, input); +} +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(UUID_fromBinary_args, 0, 1, self, 0) + ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** `public static function UUID::parse(string $input): self` */ +PHP_METHOD(UUID, parse) +{ + zval *input = NULL; + php_uuid uuid; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z", &input) == FAILURE) { + return; + } + + if (php_uuid_parse_throw(&uuid, Z_STRVAL_P(input), Z_STRLEN_P(input)) == FAILURE) { + return; + } + + new_uuid(return_value, &uuid); +} +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(UUID_parse_args, 0, 1, self, 0) + ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** `public static function UUID::v3(self $namespace, string $name): self` */ +PHP_METHOD(UUID, v3) +{ + zval *namespace = NULL; + zval *name = NULL; + php_uuid *nsid = NULL; + php_uuid uuid; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zz", &namespace, &name) == FAILURE) { + return; + } + + nsid = get_uuid(namespace); + if (nsid == NULL) { + return; + } + + php_uuid_create_v3(&uuid, nsid, Z_STRVAL_P(name), Z_STRLEN_P(name)); + new_uuid(return_value, &uuid); +} +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(UUID_v3_args, 0, 2, self, 0) + ZEND_ARG_OBJ_INFO(0, namespace, self, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** `public static function UUID::v4(): self` */ +PHP_METHOD(UUID, v4) +{ + php_uuid uuid; + + if (zend_parse_parameters_none_throw() == FAILURE) { + return; + } + + if (php_uuid_create_v4_throw(&uuid) == FAILURE) { + return; + } + + new_uuid(return_value, &uuid); +} +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(UUID_v4_args, self, 0) +ZEND_END_ARG_INFO() + +/** `public static function UUID::v5(self $namespace, string $name): self` */ +PHP_METHOD(UUID, v5) +{ + zval *namespace = NULL; + zval *name = NULL; + php_uuid *nsid = NULL; + php_uuid uuid; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zz", &namespace, &name) == FAILURE) { + return; + } + + nsid = get_uuid(namespace); + if (nsid == NULL) { + return; + } + + php_uuid_create_v5(&uuid, nsid, Z_STRVAL_P(name), Z_STRLEN_P(name)); + new_uuid(return_value, &uuid); +} +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(UUID_v5_args, 0, 2, self, 0) +ZEND_ARG_OBJ_INFO(0, namespace, self, 0) +ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** + * Macro for consistent creation of named UUID class constructors from C + * functions. The output of the macro is as follows: + * + * ```php + * final class UUID { + * public static function php_method_name(): self {} + * } + * ``` + * + * @param[in] php_method_name + * @param[in] c_function_name without the `php_uuid_` prefix. + */ +#define PHP_UUID_NAMED_CONSTRUCTOR(php_method_name, c_function_name) \ + PHP_METHOD(UUID, php_method_name) \ + { \ + const php_uuid c_function_name = php_uuid_##c_function_name(); \ + \ + if (zend_parse_parameters_none_throw() == FAILURE) { \ + return; \ + } \ + \ + new_uuid(return_value, &c_function_name); \ + } \ + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(UUID_##php_method_name##_args, self, 0) \ + ZEND_END_ARG_INFO(); + +PHP_UUID_NAMED_CONSTRUCTOR(NamespaceDNS, namespace_dns) +PHP_UUID_NAMED_CONSTRUCTOR(NamespaceOID, namespace_oid) +PHP_UUID_NAMED_CONSTRUCTOR(NamespaceURL, namespace_url) +PHP_UUID_NAMED_CONSTRUCTOR(NamespaceX500, namespace_x500) +PHP_UUID_NAMED_CONSTRUCTOR(Nil, nil) + +/** `private function UUID::__clone()` */ +PHP_METHOD(UUID, __clone) +{ + zend_throw_error(zend_ce_error, "Cannot clone immutable %s object", ZSTR_VAL(php_ce_UUID->name)); +} +ZEND_BEGIN_ARG_INFO(UUID___clone_args, NULL) +ZEND_END_ARG_INFO() + +/** `public function UUID::__set($_, $__): void` */ +PHP_METHOD(UUID, __set) +{ + zend_throw_error(zend_ce_error, "Cannot set dynamic properties on immutable %s object", ZSTR_VAL(php_ce_UUID->name)); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID___set_args, IS_VOID, 0) + ZEND_ARG_INFO(0, _) + ZEND_ARG_INFO(0, __) +ZEND_END_ARG_INFO() + +/** `public function UUID::__wakeup(): void` */ +PHP_METHOD(UUID, __wakeup) +{ + zval *bytes = zend_read_property(php_ce_UUID, &EX(This), UUID_BYTES_PROP, UUID_BYTES_PROP_LEN, 1, NULL); + + if (zend_parse_parameters_none_throw() == FAILURE) { + return; + } + + if (Z_TYPE_P(bytes) != IS_STRING) { + zend_throw_exception_ex( + spl_ce_UnexpectedValueException, + 0, + "Expected %s::$%s value to be of type %s, but found %s", + ZSTR_VAL(php_ce_UUID->name), + UUID_BYTES_PROP, + zend_get_type_by_const(IS_STRING), + zend_zval_type_name(bytes) + ); + return; + } + + if (Z_STRLEN_P(bytes) != PHP_UUID_LEN) { + zend_throw_exception_ex( + spl_ce_UnexpectedValueException, + 0, + "Expected %s::$%s value to be exactly %u bytes long, but found %u", + ZSTR_VAL(php_ce_UUID->name), + UUID_BYTES_PROP, + PHP_UUID_LEN, + Z_STRLEN_P(bytes) + ); + return; + } +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID___wakeup_args, IS_VOID, 0) +ZEND_END_ARG_INFO() + +/** + * Macro for consistent checking that there were no parameters passed to the + * PHP method, and to fetch the content of the private property that contains + * the byte representation of the UUID into a local variable called `uuid`. + */ +#define PHP_UUID_ACCESSOR \ + const php_uuid *uuid = NULL; \ + if (zend_parse_parameters_none_throw() == FAILURE) return; \ + if ((uuid = get_uuid(&EX(This))) == NULL) return; + +/** `public function UUID::getVariant(): int` */ +PHP_METHOD(UUID, getVariant) +{ + PHP_UUID_ACCESSOR; + RETURN_LONG(php_uuid_get_variant(uuid)); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID_getVariant_args, IS_LONG, 0) +ZEND_END_ARG_INFO() + +/** `public function UUID::getVersion(): int` */ +PHP_METHOD(UUID, getVersion) +{ + PHP_UUID_ACCESSOR; + RETURN_LONG(php_uuid_get_version(uuid)); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID_getVersion_args, IS_LONG, 0) +ZEND_END_ARG_INFO() + +/** `public function UUID::isNil(): bool` */ +PHP_METHOD(UUID, isNil) +{ + PHP_UUID_ACCESSOR; + RETURN_BOOL(php_uuid_is_nil(uuid)); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID_isNil_args, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +/** `public function UUID::toBinary(): string` */ +PHP_METHOD(UUID, toBinary) +{ + PHP_UUID_ACCESSOR; + RETURN_STRINGL((const char *) uuid->bytes, PHP_UUID_LEN); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID_toBinary_args, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** `public function UUID::toHex(): string` */ +PHP_METHOD(UUID, toHex) +{ + php_uuid_hex buffer; + PHP_UUID_ACCESSOR; + php_uuid_to_hex(&buffer, uuid); + RETURN_STRINGL(buffer.str, UUID_HEX_LEN); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID_toHex_args, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** `public function UUID::toString(): string` */ +PHP_METHOD(UUID, toString) +{ + php_uuid_string buffer; + PHP_UUID_ACCESSOR; + php_uuid_to_string(&buffer, uuid); + RETURN_STRINGL(buffer.str, UUID_STRING_LEN); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUID_toString_args, IS_STRING, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry uuid_methods[] = { + PHP_ME(UUID, __construct, UUID___construct_args, ZEND_ACC_PRIVATE) + PHP_ME(UUID, fromBinary, UUID_fromBinary_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, parse, UUID_parse_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, v3, UUID_v3_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, v4, UUID_v4_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, v5, UUID_v5_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, NamespaceDNS, UUID_NamespaceDNS_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, NamespaceOID, UUID_NamespaceOID_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, NamespaceURL, UUID_NamespaceURL_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, NamespaceX500, UUID_NamespaceX500_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, Nil, UUID_Nil_args, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(UUID, __clone, UUID___clone_args, ZEND_ACC_PRIVATE) + PHP_ME(UUID, __set, UUID___set_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, __wakeup, UUID___wakeup_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, getVariant, UUID_getVariant_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, getVersion, UUID_getVersion_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, isNil, UUID_isNil_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, toBinary, UUID_toBinary_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, toHex, UUID_toHex_args, ZEND_ACC_PUBLIC) + PHP_ME(UUID, toString, UUID_toString_args, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/** `public function UUIDParseException::__construct(string $reason, string $input, int $position = 0, ?Throwable $previous = null)` */ +PHP_METHOD(UUIDParseException, __construct) +{ + zval *reason = NULL; + zval *input = NULL; + zval *position = NULL; + zval *previous = NULL; + + if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "zz|zz!", &reason, &input, &position, &previous) == FAILURE) { + return; + } + + zend_update_property_ex(zend_ce_exception, &EX(This), ZSTR_KNOWN(ZEND_STR_MESSAGE), reason); + zend_update_property(php_ce_UUIDParseException, &EX(This), UUID_EX_INPUT_PROP, UUID_EX_INPUT_PROP_LEN, input); + + if (position != NULL) { + zend_update_property(php_ce_UUIDParseException, &EX(This), UUID_EX_POSITION_PROP, UUID_EX_POSITON_PROP_LEN, position); + } + else { + zend_update_property_long(php_ce_UUIDParseException, &EX(This), UUID_EX_POSITION_PROP, UUID_EX_POSITON_PROP_LEN, 0); + } + + if (previous != NULL) { + zend_update_property_ex(zend_ce_exception, &EX(This), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); + } +} +ZEND_BEGIN_ARG_INFO_EX(UUIDParseException___construct_args, NULL, 0, 2) + ZEND_ARG_TYPE_INFO(0, reason, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, position, IS_LONG, 0) + ZEND_ARG_OBJ_INFO(0, previous, Throwable, 1) +ZEND_END_ARG_INFO() + +/** `public function UUIDParseException::getInput(): string` */ +PHP_METHOD(UUIDParseException, getInput) +{ + if (zend_parse_parameters_none_throw() == FAILURE) { + return; + } + + RETURN_ZVAL(zend_read_property( + php_ce_UUIDParseException, + &EX(This), + UUID_EX_INPUT_PROP, + UUID_EX_INPUT_PROP_LEN, + 1, + NULL + ), 1, 0); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUIDParseException_getInput_args, IS_STRING, 0) +ZEND_END_ARG_INFO() + +/** `public function UUIDParseException::getPosition(): int` */ +PHP_METHOD(UUIDParseException, getPosition) +{ + if (zend_parse_parameters_none_throw() == FAILURE) { + return; + } + + RETURN_ZVAL(zend_read_property( + php_ce_UUIDParseException, + &EX(This), + UUID_EX_POSITION_PROP, + UUID_EX_POSITON_PROP_LEN, + 1, + NULL + ), 1, 0); +} +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(UUIDParseException_getPosition_args, IS_LONG, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry uuid_parse_exception_methods[] = { + PHP_ME(UUIDParseException, __construct, UUIDParseException___construct_args, ZEND_ACC_PUBLIC) + PHP_ME(UUIDParseException, getInput, UUIDParseException_getInput_args, ZEND_ACC_PUBLIC) + PHP_ME(UUIDParseException, getPosition, UUIDParseException_getPosition_args, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +PHP_MINIT_FUNCTION(uuid) +{ + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, PHP_STD_NAMESPACE, "UUID", uuid_methods); + php_ce_UUID = zend_register_internal_class(&ce); + php_ce_UUID->ce_flags |= ZEND_ACC_FINAL; + + zend_declare_class_constant_long(php_ce_UUID, "VARIANT_NCS", sizeof("VARIANT_NCS") - 1, PHP_UUID_VARIANT_NCS); + zend_declare_class_constant_long(php_ce_UUID, "VARIANT_RFC4122", sizeof("VARIANT_RFC4122") - 1, PHP_UUID_VARIANT_RFC4122); + zend_declare_class_constant_long(php_ce_UUID, "VARIANT_MICROSOFT", sizeof("VARIANT_MICROSOFT") - 1, PHP_UUID_VARIANT_MICROSOFT); + zend_declare_class_constant_long(php_ce_UUID, "VARIANT_FUTURE_RESERVED", sizeof("VARIANT_FUTURE_RESERVED") - 1, PHP_UUID_VARIANT_FUTURE_RESERVED); + + zend_declare_class_constant_long(php_ce_UUID, "VERSION_1_TIME_BASED", sizeof("VERSION_1_TIME_BASED") - 1, PHP_UUID_VERSION_1_TIME_BASED); + zend_declare_class_constant_long(php_ce_UUID, "VERSION_2_DCE_SECURITY", sizeof("VERSION_2_DCE_SECURITY") - 1, PHP_UUID_VERSION_2_DCE_SECURITY); + zend_declare_class_constant_long(php_ce_UUID, "VERSION_3_NAME_BASED_MD5", sizeof("VERSION_3_NAME_BASED_MD5") - 1, PHP_UUID_VERSION_3_NAME_BASED_MD5); + zend_declare_class_constant_long(php_ce_UUID, "VERSION_4_RANDOM", sizeof("VERSION_4_RANDOM") - 1, PHP_UUID_VERSION_4_RANDOM); + zend_declare_class_constant_long(php_ce_UUID, "VERSION_5_NAME_BASED_SHA1", sizeof("VERSION_5_NAME_BASED_SHA1") - 1, PHP_UUID_VERSION_5_NAME_BASED_SHA1); + + zend_declare_property_null(php_ce_UUID, UUID_BYTES_PROP, UUID_BYTES_PROP_LEN, ZEND_ACC_PRIVATE); + + INIT_NS_CLASS_ENTRY(ce, PHP_STD_NAMESPACE, "UUIDParseException", uuid_parse_exception_methods); + php_ce_UUIDParseException = zend_register_internal_class_ex(&ce, zend_ce_exception); + php_ce_UUIDParseException->ce_flags |= ZEND_ACC_FINAL; + php_ce_UUIDParseException->create_object = zend_ce_exception->create_object; + + zend_declare_property_null(php_ce_UUIDParseException, UUID_EX_INPUT_PROP, UUID_EX_INPUT_PROP_LEN, ZEND_ACC_PRIVATE); + zend_declare_property_null(php_ce_UUIDParseException, UUID_EX_POSITION_PROP, UUID_EX_POSITON_PROP_LEN, ZEND_ACC_PRIVATE); + + return SUCCESS; +} diff --git a/main/php.h b/main/php.h index 7b1a9256bafe2..24be539274279 100644 --- a/main/php.h +++ b/main/php.h @@ -41,7 +41,13 @@ #undef sprintf #define sprintf php_sprintf -/* Operating system family defintion */ +/* PHP vendor namespace. */ +#define PHP_NAMESPACE "PHP" + +/* PHP namespace separator. */ +#define PHP_NAMESPACE_SEPARATOR "\\" + +/* Operating system family definition */ #ifdef PHP_WIN32 # define PHP_OS_FAMILY "Windows" #elif defined(BSD) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)