├── README.md ├── config.m4 ├── config.w32 ├── php_qolfuncs.h ├── qolfuncs.c └── tests ├── 001_ext_enabled.phpt ├── 002_str_splice.phpt ├── 003_str_realloc.phpt ├── 004_explode_substr.phpt ├── 005_str_split_substr.phpt ├── 006_fread_mem.phpt ├── 007_fwrite_substr.phpt ├── 008_hash_substr.phpt ├── 009_hash_hmac_substr.phpt ├── 010_imagexportpixels.phpt ├── 011_imageimportpixels.phpt ├── 012_mat_add.phpt ├── 013_mat_sub.phpt ├── 014_mat_mult.phpt ├── 015_is_valid_utf8.phpt ├── 016_is_interned_string.phpt ├── 017_refcount.phpt ├── 018_is_reference.phpt └── 019_is_equal_zval.phpt /README.md: -------------------------------------------------------------------------------- 1 | Quality of Life Improvement Functions for PHP Core 2 | ================================================== 3 | 4 | A set of quality of life improvement functions designed for integration into PHP core. 5 | 6 | This is a custom, compile-time PHP extension that is NOT intended for production use as-is. Instead, it is intended as a conversation starter on the PHP internals mailing list for making notable improvements to the core product. If approved through the PHP internals gauntlet, some future version of PHP itself could feature these enhancements. You could be looking at the future! (Or it could go nowhere fast, bogged down in politics. Sigh.) Looking for assistance/support from other folks to help champion the functions found in this extension. 7 | 8 | The extension won't compile as-is against PHP 8.x with `gd` enabled because PHP 8.x doesn't appear to have a way to export `gd_image_ce` from `ext/gd/gd.c`, which is required to use the `Z_PARAM_OBJECT_OF_CLASS()` macro. See the Compiling/Installing section below for options. 9 | 10 | The code in this repository may be licensed by the PHP core development team under The PHP License 3.01 when integrating the extension into core. Users who want to experiment with this extension may license this extension under The PHP License 3.01, MIT, or LGPL - your choice. 11 | 12 | Compiling/Installing 13 | -------------------- 14 | 15 | Fair warning: This extension is NOT designed to be installed on production systems. The Compiling/Installing section of this document (i.e. what you are reading) has NOT been tested since the extension was developed inline with a specially prepared PHP 7.4.x for extension development using some flavor of gcc on Ubuntu Linux. This extension might or might not compile for you but that's not really the point of this code repository. 16 | 17 | With that warning/caveat out of the way, running `phpize` from within the directory with the `config.m4`/`config.w32` file should get the party started. 18 | 19 | Useful documentation: 20 | 21 | https://www.php.net/manual/en/install.pecl.phpize.php 22 | 23 | Probably useful SO post: 24 | 25 | https://stackoverflow.com/questions/3108937/how-to-install-and-run-phpize 26 | 27 | If you run into problems with compiling `imagexportpixels()` and `imageimportpixels()` on PHP 8.x, you can: 28 | 29 | * Nuke all code and references to those two functions if you don't want to use them. 30 | * Compile PHP without `gd` support and then compile the extension as-is. That should disable compilation of those functions. 31 | * Compile against PHP 7.4.x instead. 32 | * Or copy the functions into `ext/gd/gd.c` in a mainline PHP 8.x branch and compile them into PHP directly if you do actually want to try them out in PHP 8.x. 33 | 34 | Background 35 | ---------- 36 | 37 | This extension was originally intended to be a series of inline memory manipulation functions for high performance string manipulation. Zend engine has the policy of performing copy-on-write for every string operation. This means that memory is regularly allocated and deallocated. In general, this is fine because freed memory goes back into the allocation pool and therefore reduces the number of overall actual allocation requests to the system. 38 | 39 | However, very serious performance issues arise when attempting to process and manipulate strings one byte at a time. This is because PHP lacks specific facilities for inline string manipulation as well as not efficiently manipulating really large strings (e.g. 1MB or more). Specifically, when appending one byte at a time under certain circumstances, I can get up to 9MB/sec on an Intel Core i7 with [system memory capable of performing transfers up to 23GB/sec](https://ram.userbenchmark.com/SpeedTest/91386/GSKILL-F4-2133C15-8GVR-4x8GB). Modifying existing strings inline only peaks at 35MB/sec on the same hardware. Those are not typos. In short, PHP's 0.2% of maximum system memory performance while churning 100% of one CPU thread to modify strings inline is _very_ unimpressive. Of course, this won't come as any surprise to the PHP core dev team nor is it a design flaw of the language itself. 40 | 41 | The development of the [Incredibly Flexible Data Storage file format](https://github.com/cubiclesoft/ifds) lead to the idea to create an extension to improve upon the situation in certain areas where performance is crucial. As development of this extension progressed, various ideas for a variety of functions ended up being merged into the `str_splice()` super function and eventually the original concept for the extension went by the wayside. As such, some functions are performance-oriented while others are simply useful for userland development and/or extension development. So the extension was renamed to be the "qolfuncs" extension because these functions/ideas/concepts improve overall quality of life in various important ways when writing PHP code. All of the functions are highly desirable for integration into PHP core. 42 | 43 | What follows is a breakdown of each function by prototype and description, who the target audience is, why it should be incorporated into PHP core as soon as possible, and how it should be incorporated into PHP core. Any showstopper bugs/issues will, of course, be addressed and fixed. 44 | 45 | str_splice() 46 | ------------ 47 | 48 | `int str_splice(string &$dst, int $dst_offset, [ ?int $dst_length = null, string $src = '', int $src_offset = 0, ?int $src_length = null, int $src_repeat = 1, bool $shrink = true, ?int $dst_lastsize = null ])` 49 | 50 | Removes the string designated by offset and length in the destination and replaces it with the optional source substring. 51 | 52 | * $dst - A string passed by reference to splice. 53 | * $dst_offset - The offset into the destination string to start the splice. Supports negative offsets from the end of the string. 54 | * $dst_length - The length of the destination string to replace from the offset. Supports negative lengths from the end of the string and is nullable (Default is null). 55 | * $src - A string to insert starting at $dst_offset (Default is ''). 56 | * $src_offset - The offset into the source string to begin copying. Supports negative offsets from the end of the string (Default is 0). 57 | * $src_length - The length of the source string to copy. Supports negative lengths from the end of the string and is nullable (Default is null). 58 | * $src_repeat - The number of times to repeat the source string (Default is 1). 59 | * $shrink - Whether or not to shrink the destination string (Default is true). When false and the destination is smaller than the input buffer, then this affects the return value. 60 | * $dst_lastsize - The last size returned from str_splice(). Nullable (Default is null). 61 | 62 | Returns: The size of the destination string. 63 | 64 | Unlike `substr_replace()`, `str_splice()` can result in zero memory allocations under carefully controlled conditions. The function, when possible, performs inline memory manipulation for up to 10x potential performance gains. Testing `str_splice()` against IFDS shows a 2x improvement to random write performance and notably simpler code (sequential write and both read types remained roughly the same). Artificial benchmarks show an increase of 160 to 9,200 times faster over built-in options for manipulating large, fixed size 20MB buffers. Real-world application performance will vary but probably reside more in the "2x to 3x faster" range. Also, the bigger the buffer being worked with, the greater the gain due to fewer copy operations. 65 | 66 | The `$src_repeat` option allows for filling the destination with a repeated substring. For example, writing 4KB of 0x00 to some part of the string. 67 | 68 | This function also supports virtual buffers. When $shrink is false, the buffer size is not reduced and the return value reflects the size of the virtual buffer. 69 | 70 | Target audience: Users working with large data. Roughly similar in name/design to `array_splice()` only it doesn't return the removed string. 71 | 72 | Why it should be added to PHP core: Better performance and more features than `substr_replace()`. Notably better performance for equal sized strings where `$dst_length == $src_length * $src_repeat`. Fundamentally different design from other string functions in PHP. 73 | 74 | How it should be incorporated into PHP core: As-is into `ext/standard/string.c`. Maybe remove the commented out `php_printf()` calls to clean up the code though. 75 | 76 | str_realloc() 77 | ------------- 78 | 79 | `int str_realloc(string &$str, int $size, [ bool $fast = false ])` 80 | 81 | Reallocates the buffer associated with a string and returns the previous size. 82 | 83 | * $str - A string to resize. 84 | * $size - The new size of the string. 85 | * $fast - Whether or not to reallocate the string for the new size when shrinking (Default is false). 86 | 87 | Returns: The previous size of the string. 88 | 89 | This function can preallocate a buffer or truncate an existing string. Inline modifying one byte at a time is approximately 1.7 to 3.8 times faster than appending one byte at a time to the end of a string. 90 | 91 | Goes hand-in-hand with str_splice() virtual buffers. For example, preallocating an estimated 1MB buffer, filling the buffer using str_splice(), and then calling str_realloc() to finalize the string. 92 | 93 | Target audience: Users working with large data. 94 | 95 | Why it should be added to PHP core: Better performance. Works in concert with str_splice(). Gives indirect access to zend_string_realloc(). 96 | 97 | How it should be incorporated into PHP core: As-is into `ext/standard/string.c`. 98 | 99 | explode_substr() 100 | ---------------- 101 | 102 | `array explode_substr(string separator, string str [, ?int limit = null, ?int str_offset = 0, ?int str_length = null ])` 103 | 104 | Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned. 105 | 106 | * $separator - A string containing a separator to split on. 107 | * $str - The string to split. 108 | * $limit - The number of components to return. 109 | * $str_offset - The offset into the string to begin splitting. Supports negative offsets from the end of the string (Default is 0). 110 | * $str_length - The length of the string being split. Supports negative lengths from the end of the string and is nullable (Default is null). 111 | 112 | Returns: An array containing the split string components. 113 | 114 | This proof-of-concept function is nearly identical to explode() but adds string offset and length parameters for the $str. Useful for extracting substrings that need to be split. 115 | 116 | Target audience: Users that call `explode("...", substr($str))`. 117 | 118 | Why it should be added to PHP core: Saves a call to substr(). 119 | 120 | How it should be incorporated into PHP core: Just extend `explode()` with offset and length parameters. Don't add a new global function. `explode_substr()` exists solely to provide working code. 121 | 122 | str_split_substr() 123 | ------------------ 124 | 125 | `array str_split_substr(string str [, ?int split_length = 1, ?int str_offset = 0, ?int str_length = null ])` 126 | 127 | Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. 128 | 129 | * $str - A string to split. 130 | * $split_length - The chunk length of each entry in the array (Default is 1). 131 | * $str_offset - The offset into the string to begin splitting. Supports negative offsets from the end of the string (Default is 0). 132 | * $str_length - The length of the string being split. Supports negative lengths from the end of the string and is nullable (Default is null). 133 | 134 | Returns: An array containing the split string components. 135 | 136 | This proof-of-concept function is nearly identical to str_split() but adds string offset and length parameters for the $str. Useful for extracting substrings that need to be split. 137 | 138 | Target audience: Users that call `str_split(substr($str))`. 139 | 140 | Why it should be added to PHP core: Saves a call to substr() and keeps the str_split() prototype in line with explode(). 141 | 142 | How it should be incorporated into PHP core: Just extend `str_split()` with offset and length parameters. Don't add a new global function. `str_split_substr()` exists solely to provide working code. 143 | 144 | fread_mem() 145 | ----------- 146 | 147 | `int|false fread_mem(resource fp, string &$str, [ int str_offset = 0, ?int length = null ])` 148 | 149 | Binary-safe inline file read. 150 | 151 | * $fp - A resource to an open file. 152 | * $str - A string to store the read data in. 153 | * $str_offset - The offset into the string to begin reading into. Supports negative offsets from the end of the string (Default is 0). 154 | * $length - The maximum number of bytes to read. Nullable (Default is null). 155 | 156 | Returns: An integer containing the number of bytes read on success, false otherwise. 157 | 158 | This function reads data from a stream into the destination string starting at the specified offset. 159 | 160 | Target audience: All users. 161 | 162 | Why it should be added to PHP core: It is extremely common to call `fread()`, check for failure, and then append the returned data to another string in a loop. This eliminates two memory allocations per loop. 163 | 164 | How it should be incorporated into PHP core: It could be incorporated as-is OR extend `fread()` with inline support. 165 | 166 | fwrite_substr() 167 | --------------- 168 | 169 | `int|false fwrite_substr(resource fp, string str [, ?int str_offset = 0, ?int str_length = null ])` 170 | 171 | Binary-safe file write. 172 | 173 | * $fp - A resource to an open file. 174 | * $str - A string to store the read data in. 175 | * $str_offset - The offset into the string to begin writing from. Supports negative offsets from the end of the string (Default is 0). 176 | * $str_length - The length of the string being written. Supports negative lengths from the end of the string and is nullable (Default is null). 177 | 178 | Returns: An integer containing the number of bytes read on success, false otherwise. 179 | 180 | This proof-of-concept function is nearly identical to `fwrite()` but adds an offset parameter for the $str. Useful for efficiently writing partial buffers to non-blocking network streams. 181 | 182 | Target audience: All users. 183 | 184 | Why it should be added to PHP core: It is extremely common to call `fwrite()`, get back a "write succeeded" response BUT only part of the data was written, and then have to chop up the buffer to be able to call fwrite() again to send the rest of the data. This is extremely inefficient for large buffers. 185 | 186 | How it should be incorporated into PHP core: Just extend `fwrite()` with an offset parameter. Don't add a new global function. Unfortunately, there is already a `length` parameter in `fwrite()`, which means `offset` will have to go after `length`. `fwrite_substr()` just showcases how nice the `fwrite()` prototype could have looked if it had been designed this way from the beginning. 187 | 188 | hash_substr() 189 | ------------- 190 | 191 | `string hash_substr(string algo, string data[, bool raw_output = false, ?int data_offset = 0, ?int data_length = null])` 192 | 193 | Generate a hash of a given input string. Returns lowercase hexits by default. 194 | 195 | * $algo - A string containing a hash algorithm. 196 | * $data - The data to hash. 197 | * $raw_output - Output raw data when true, lowercase hexits when false (Default is false). 198 | * $data_offset - The offset into the string to begin hashing from. Supports negative offsets from the end of the string (Default is 0). 199 | * $data_length - The length of the string to hash. Supports negative lengths from the end of the string and is nullable (Default is null). 200 | 201 | Returns: A string containing the result of the hash. 202 | 203 | This proof-of-concept function is nearly identical to `hash()` but adds data offset and length parameters. Very useful for handling binary data within a substring such as: 204 | 205 | ``` 206 | 4 bytes size 207 | Data 208 | 4 byte CRC-32 209 | ``` 210 | 211 | Target audience: Users who work with binary data containing hashes/CRCs. 212 | 213 | Why it should be added to PHP core: Copying data out of a string just to hash it is fairly expensive when a few minor pointer adjustments have the same effect. 214 | 215 | How it should be incorporated into PHP core: Just extend `hash()` with offset and length parameters. Don't add a new global function. hash_substr()` exists solely to provide working code. 216 | 217 | hash_hmac_substr() 218 | ------------------ 219 | 220 | `string hash_hmac_substr(string algo, string data, string key[, bool raw_output = false, ?int data_offset = 0, ?int data_length = null])` 221 | 222 | Generate a hash of a given input string with a key using HMAC. Returns lowercase hexits by default. 223 | 224 | * $algo - A string containing a hash algorithm. 225 | * $data - The data to hash. 226 | * $key - A string containing the HMAC key. 227 | * $raw_output - Output raw data when true, lowercase hexits when false (Default is false). 228 | * $data_offset - The offset into the string to begin hashing from. Supports negative offsets from the end of the string (Default is 0). 229 | * $data_length - The length of the string to hash. Supports negative lengths from the end of the string and is nullable (Default is null). 230 | 231 | Returns: A string containing the result of the hash. 232 | 233 | This proof-of-concept function is nearly identical to `hash_hmac()` but adds data offset and length parameters. Very useful for handling binary data within a substring. 234 | 235 | Target audience: Some users who work with binary data. This function is less useful than `hash_substr()` but exists for completeness and consistency. 236 | 237 | Why it should be added to PHP core: Copying data out of a string just to hash it is fairly expensive when a few minor pointer adjustments have the same effect. 238 | 239 | How it should be incorporated into PHP core: Just extend `hash_hmac()` with offset and length parameters. Don't add a new global function. hash_hmac_substr()` exists solely to provide working code. 240 | 241 | imagexportpixels() 242 | ------------------ 243 | 244 | `array imageexportpixels(resource im, int x, int y, int width, int height)` 245 | 246 | Export the colors/color indexes of a range of pixels as an array. 247 | 248 | * $im - A resource/instance of GdImage (depending on PHP version). 249 | * $x - The upper left corner x-coordinate to start from. 250 | * $y - The upper left corner y-coordinate to start from. 251 | * $width - A positive integer width. 252 | * $height - A positive integer height. 253 | 254 | Returns: A 2D array containing the exported pixel colors on success, emits a notice and returns a boolean of false on error. 255 | 256 | This function improves performance when working with gd images at the pixel level. Calling `imagecolorat()` in a loop is slow. For example, when processing a 3000x5000 pixel image, that's 15 million function calls to `imagecolorat()` to access every pixel of the image. This function can rapidly export any portion of the image as a userland 2D array of integers. Instead of 15 million PHP function calls, even one call per row would be a mere 5,000 function calls. 257 | 258 | Target audience: All users who use gd to do image processing. 259 | 260 | Why it should be added to PHP core: Improved performance. Also, PECL ImageMagick has a similar but more advanced function. 261 | 262 | How it should be incorporated into PHP core: As-is into `ext/gd/gc.c`. Although PHP 8.x changed things up for gd, so it will need some minor code cleanup. 263 | 264 | imageimportpixels() 265 | ------------------- 266 | 267 | `bool imageimportpixels(resource im, int x, int y, array colors)` 268 | 269 | Sets pixels to the specified colors in the 2D array. 270 | 271 | * $im - A resource/instance of GdImage (depending on PHP version). 272 | * $x - The upper left corner x-coordinate to start from. 273 | * $y - The upper left corner y-coordinate to start from. 274 | * $colors - A 2D array of integers representing pixel colors. 275 | 276 | Returns: A boolean indicating whether or not the operation was successful. 277 | 278 | This function improves performance when working with gd images at the pixel level. Calling `imagesetpixel()` in a loop is slow. For example, when processing a 3000x5000 pixel image, that's 15 million function calls to `imagesetpixel()` to modify every pixel of the image. This function can rapidly import any portion of the image from a userland 2D array of integers. Instead of 15 million PHP function calls, even one call per row would be a mere 5,000 function calls. 279 | 280 | Target audience: All users who use gd to do image manipulation. 281 | 282 | Why it should be added to PHP core: Improved performance. Also, PECL ImageMagick has a similar but more advanced function. 283 | 284 | How it should be incorporated into PHP core: As-is into `ext/gd/gc.c`. Although PHP 8.x changed things up for gd, so it will need some minor code cleanup. 285 | 286 | mat_add() 287 | --------- 288 | 289 | `array mat_add(array $a, array $b)` 290 | 291 | Adds the values of two 2D matrices. 292 | 293 | * $a - A 2D array. 294 | * $b - A 2D array. 295 | 296 | Returns: A 2D array with the values of each corresponding cell added together. 297 | 298 | This function performs a matrix addition operation on two matrices and returns the result. It also exercises a number of new macros designed to iterate over HashTable structures. 299 | 300 | Target audience: Users working with matrices. 301 | 302 | Why it should be added to PHP core: Performant matrix addition from linear algebra. See: https://en.wikipedia.org/wiki/Matrix_(mathematics) 303 | 304 | How it should be incorporated into PHP core: As-is into `ext/standard/math.c`. The macros (e.g. `HT_NEXT_ELEMENT_VAL_IND()`) and relevant inline functions (e.g. `zend_hash_get_next_zval()`) should be placed as-is into `Zend/zend_hash.h`. 305 | 306 | mat_sub() 307 | --------- 308 | 309 | `array mat_sub(array $a, array $b)` 310 | 311 | Subtracts the values of two 2D matrices. 312 | 313 | * $a - A 2D array. 314 | * $b - A 2D array. 315 | 316 | Returns: A 2D array with the values of each corresponding cell subtracted together. 317 | 318 | This function performs a matrix subtraction operation on two matrices and returns the result. Basically a copy of mat_add() but for subtraction. 319 | 320 | Target audience: Users working with matrices. 321 | 322 | Why it should be added to PHP core: Performant matrix subtraction from linear algebra. 323 | 324 | How it should be incorporated into PHP core: As-is into `ext/standard/math.c`. 325 | 326 | mat_mult() 327 | ---------- 328 | 329 | `mat_mult(array $a, array|float|int $b, [int $row = null])` 330 | 331 | Multiplies the values of two 2D matrices or the values of a 2D matrix or row of the matrix with a scalar value. 332 | 333 | * $a - A 2D array. 334 | * $b - A 2D array or a numeric scalar value. 335 | * $row - An integer specifying the number of the row to multiply the scalar value on (Default is null). 336 | 337 | Returns: A 2D array with the resulting values of the multiplication operation. 338 | 339 | This function performs a matrix multiplication operation on one or two matrices and returns the result. When using a scalar value, it can affect just one row with the $row parameter. 340 | 341 | Matrix multiplication of two matrices from linear algebra is a traditionally O(N^3) algorithm. This function is `max_execution_time`-aware to avoid consuming CPU resources beyond the execution time limit on all supported platforms. 342 | 343 | Target audience: Users working with matrices. 344 | 345 | Why it should be added to PHP core: Performant matrix multiplication operations from linear algebra. Preliminary testing shows this function to be 46 times faster than the best userland implementation. 346 | 347 | How it should be incorporated into PHP core: As-is into `ext/standard/math.c`. 348 | 349 | is_valid_utf8() 350 | --------------- 351 | 352 | `bool is_valid_utf8(string $value, [ bool $standard = false, int $combinelimit = 16 ])` 353 | 354 | Finds whether the given value is a valid UTF-8 string. 355 | 356 | * $value - Any input type to validate as a UTF-8 string. 357 | * $standard - Whether or not to strictly adhere to the Unicode standard (Default is false - see Notes). 358 | * $combinelimit - The maximum number of sequential combination code points to allow (Default is 16). 359 | 360 | Returns: A boolean of true if the string is a valid UTF-8 string, false otherwise. 361 | 362 | This function determines whether or not a zval is a string and, if it is, whether or not it is valid UTF-8. It also sets the `IS_STR_VALID_UTF8` garbage collection flag on success for non-interned strings so that future calls against the same string take less time. 363 | 364 | The `$standard` option decides whether or not to allow the full 0x0000 through 0x007F character range. There is significant risk with allowing certain control characters to reach databases, consoles, command-lines, etc. This function, by default, limits the range to 0x0020 through 0x007E plus `\r`, `\n`, and `\t`. 365 | 366 | The `$combinelimit` limits [Zalgo text](https://en.wikipedia.org/wiki/Zalgo_text) to reasonable lengths. Zalgo text abuses combination code points, which can make a webpage unreadable. The maximum number of legitimate combination code points to date is 8 code points in Tibetan. So this function doubles that by default. 367 | 368 | Target audience: All users. 369 | 370 | Why it should be added to PHP core: Validating UTF-8 in userland requires processing one byte at a time, which is slow and better done in PHP core. `mb_check_encoding($string, "UTF-8")` requires mbstring, which is not compiled in by default and doesn't have options for important protections that PHP should have out of the box. Recursive regex checking is an even worse option and can crash PCRE. Unicode is a complex Standard and most people get it wrong. In addition, passing malformed UTF-8 to databases, processes, or various libraries can trigger security vulnerabilities. UTF-8 is the most popular character set globally for the web. PHP accepts UTF-8 user input but does not have a built-in UTF-8 validation function. 371 | 372 | How it should be incorporated into PHP core: As a global function, it would perhaps fit into `ext/standard/type.c` or `ext/standard/string.c`? But maybe it would work equally well as a filter. Or both? 373 | 374 | is_interned_string() 375 | -------------------- 376 | 377 | `bool is_interned_string(mixed $value)` 378 | 379 | Finds whether the given variable is an interned (immutable) string. 380 | 381 | $value - Any input type to validate as an interned string. 382 | 383 | Returns: A boolean of true if the string is interned (immutable), false otherwise. 384 | 385 | This function differentiates between the two internal types of strings in PHP core. Interned (immutable) strings have a refcount of 1 and are never garbage collected while other strings are refcounted and garbage collected. 386 | 387 | Target audience: Extension developers. Maybe some userland library developers too. 388 | 389 | Why it should be added to PHP core: Language completeness. Could also be useful for some userland devs for performance optimizations. 390 | 391 | How it should be incorporated into PHP core: As-is into `ext/standard/type.c`. 392 | 393 | refcount() 394 | ---------- 395 | 396 | `int refcount(mixed &$value)` 397 | 398 | Returns the userland internal reference count of a zval. 399 | 400 | * $value - Any input variable to retrieve its refcount. 401 | 402 | Returns: An accurate userland perspective reference count of a zval. Interned strings always return a refcount of 1. 403 | 404 | This function returns the accurate userland-oriented reference count of a variable. Unlike `debug_zval_dump()`, it accomplishes this by passing the variable by reference instead of by value, correctly calculates the true reference count (reference refcount minus the Zend engine increment minus one + value refcount), and returns a straight integer value to the caller instead of a string. 405 | 406 | Weak references do not solve all refcounting problems. When there is a balancing act to maintain, especially in flexible caching scenarios, weak references cannot be used. 407 | 408 | Target audience: All users who need to implement flexible caching mechanisms such as "When 10,000 rows have been loaded and cached, prune the cache down to about 5,000 rows." Also useful for extension developers. 409 | 410 | Why it should be added to PHP core: No reasonable built-in function exists to get just the refcount. `debug_zval_dump()` is insufficient and also a tad incorrect. 411 | 412 | How it should be incorporated into PHP core: As-is into `ext/standard/var.c`. 413 | 414 | is_reference() 415 | -------------- 416 | 417 | `bool is_reference(mixed &$value)` 418 | 419 | Finds whether the type of a variable is a reference. 420 | 421 | * $value - Any input variable to determine if it is a reference. 422 | 423 | Returns: A boolean of true if the variable is a reference, false otherwise. 424 | 425 | This function determines whether or not a variable is a reference. 426 | 427 | As a side note: I didn't know if this function was going to even be possible but I've needed it on many occasions for debugging purposes. The Zend engine VM only has two options for its opcodes for parameters: Coerce a variable to a reference OR coerce a variable to a value. Fortunately, coercion to a reference is temporary during the function call and causes the refcount to be a minimum of two and is only higher if it is an actual reference variable. 428 | 429 | Target audience: All users. Lots of weird application bugs can crop up with references. 430 | 431 | Why it should be added to PHP core: A useful debugging tool. Also, language completeness. However, it does rely on a side effect of how the engine functions. 432 | 433 | How it should be incorporated into PHP core: As-is into `ext/standard/type.c`. 434 | 435 | is_equal_zval() 436 | --------------- 437 | 438 | `bool is_equal_zval(mixed &$value, mixed &$value2, [ bool $deref = true ])` 439 | 440 | Compares two raw zvals for equality but does not compare data for equality. 441 | 442 | * $value - Any input variable to determine if it has equality to $value2. 443 | * $value2 - Any input variable to determine if it has equality to $value. 444 | * $deref - Whether or not to compare data pointers (Default is true). 445 | 446 | Returns: A boolean of true if the zvals are pointing at the same reference variable (for references) or data pointer. 447 | 448 | This function performs a low-level pointer zval equality comparison operation between `$value` and `$value2`. Handy for looking at complex zval mechanisms behind the scenes of PHP but not much else. Very different from the equality operator (`===`). 449 | 450 | Target audience: Extension developers. Maybe someone looking to optimize some code? 451 | 452 | Why it should be added to PHP core: Language completeness. Exposes some inner workings of zvals that are otherwise difficult to surface. 453 | 454 | How it should be incorporated into PHP core: As-is into `ext/standard/var.c`. 455 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension qolfuncs 3 | 4 | PHP_ARG_ENABLE(qolfuncs, [whether to enable quality of life improvement functions support], 5 | [AS_HELP_STRING([--enable-qolfuncs], [Enable quality of life improvement functions support])], [no]) 6 | 7 | if test "$PHP_QOLFUNCS" != "no"; then 8 | dnl # Finish defining basic extension support. 9 | AC_DEFINE(HAVE_QOLFUNCS, 1, [Whether you have quality of life improvement functions support]) 10 | PHP_NEW_EXTENSION(qolfuncs, qolfuncs.c, $ext_shared) 11 | fi 12 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | ARG_ENABLE('qolfuncs', 'enable quality of life improvement functions support', 'no'); 2 | 3 | if (PHP_QOLFUNCS != 'no') { 4 | AC_DEFINE('HAVE_QOLFUNCS', 1, 'quality of life improvement functions support enabled'); 5 | 6 | EXTENSION('qolfuncs', 'qolfuncs.c', null, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); 7 | } 8 | -------------------------------------------------------------------------------- /php_qolfuncs.h: -------------------------------------------------------------------------------- 1 | /* qolfuncs extension for PHP */ 2 | 3 | #ifndef PHP_QOLFUNCS_H 4 | # define PHP_QOLFUNCS_H 5 | 6 | extern zend_module_entry qolfuncs_module_entry; 7 | # define phpext_qolfuncs_ptr &qolfuncs_module_entry 8 | 9 | # define PHP_QOLFUNCS_VERSION "0.1.0" 10 | 11 | PHP_FUNCTION(str_splice); 12 | PHP_FUNCTION(str_realloc); 13 | PHP_FUNCTION(explode_substr); 14 | PHP_FUNCTION(str_split_substr); 15 | PHPAPI PHP_FUNCTION(fread_mem); 16 | PHPAPI PHP_FUNCTION(fwrite_substr); 17 | PHP_FUNCTION(hash_substr); 18 | PHP_FUNCTION(hash_hmac_substr); 19 | PHP_FUNCTION(imageexportpixels); 20 | PHP_FUNCTION(imageimportpixels); 21 | PHP_FUNCTION(mat_add); 22 | PHP_FUNCTION(mat_sub); 23 | PHP_FUNCTION(mat_mult); 24 | PHP_FUNCTION(is_valid_utf8); 25 | PHP_FUNCTION(is_interned_string); 26 | PHP_FUNCTION(refcount); 27 | PHP_FUNCTION(is_reference); 28 | PHP_FUNCTION(is_equal_zval); 29 | 30 | # if defined(ZTS) && defined(COMPILE_DL_QOLFUNCS) 31 | ZEND_TSRMLS_CACHE_EXTERN() 32 | # endif 33 | 34 | #endif /* PHP_QOLFUNCS_H */ 35 | -------------------------------------------------------------------------------- /qolfuncs.c: -------------------------------------------------------------------------------- 1 | /* qolfuncs extension for PHP */ 2 | 3 | #ifdef HAVE_CONFIG_H 4 | # include "config.h" 5 | #endif 6 | 7 | #include "php.h" 8 | #include "ext/standard/info.h" 9 | #include "php_qolfuncs.h" 10 | 11 | /* For compatibility with older PHP versions */ 12 | #ifndef ZEND_PARSE_PARAMETERS_NONE 13 | #define ZEND_PARSE_PARAMETERS_NONE() \ 14 | ZEND_PARSE_PARAMETERS_START(0, 0) \ 15 | ZEND_PARSE_PARAMETERS_END() 16 | #endif 17 | 18 | /* {{{ Removes the string designated by offset and length and replaces it with the optional substring. 19 | int str_splice(string &$dst, int $dst_offset, [ ?int $dst_length = null, string $src = '', int $src_offset = 0, ?int $src_length = null, int $src_repeat = 1, bool $shrink = true, ?int $dst_lastsize = null ]) 20 | */ 21 | PHP_FUNCTION(str_splice) 22 | { 23 | int numargs = ZEND_NUM_ARGS(); 24 | zval *zdst; 25 | zend_string *zsdst; 26 | char *dst, *dst2, *dst_ptr, *dst_ptr2; 27 | size_t dst_size, dst_size2, copy_limit, dst_diff; 28 | zend_long dst_offset, dst_length, dst_lastsize; 29 | zval *zdst_length = NULL, *zdst_lastsize = NULL; 30 | 31 | zval *zsrc; 32 | char *src = ""; 33 | size_t src_size = 0; 34 | zend_long src_offset = 0, src_repeat = 1, src_length, src_finallength; 35 | zval *zsrc_length = NULL; 36 | zend_bool shrink = 1; 37 | zend_bool samestr = 0; 38 | 39 | ZEND_PARSE_PARAMETERS_START(2, 9) 40 | Z_PARAM_ZVAL(zdst) 41 | Z_PARAM_LONG(dst_offset) 42 | Z_PARAM_OPTIONAL 43 | Z_PARAM_ZVAL(zdst_length) 44 | Z_PARAM_ZVAL(zsrc) 45 | Z_PARAM_LONG(src_offset) 46 | Z_PARAM_ZVAL(zsrc_length) 47 | Z_PARAM_LONG(src_repeat) 48 | Z_PARAM_BOOL(shrink) 49 | Z_PARAM_ZVAL(zdst_lastsize) 50 | ZEND_PARSE_PARAMETERS_END(); 51 | 52 | /* Dereference the destination zval as needed. */ 53 | //if (Z_TYPE_P(zdst) == IS_REFERENCE) php_printf("Destination is a reference!\n"); 54 | //php_printf("Destination zval! Before: %p", zdst); 55 | ZVAL_DEREF(zdst); 56 | 57 | convert_to_string_ex(zdst); 58 | 59 | //php_printf(", After: %p\n", zdst); 60 | //if (ZSTR_IS_INTERNED(Z_STR_P(zdst))) php_printf("Destination is interned!\n"); 61 | 62 | /* Convert the source to a string as needed. */ 63 | if (numargs >= 4) 64 | { 65 | convert_to_string_ex(zsrc); 66 | //if (ZSTR_IS_INTERNED(Z_STR_P(zsrc))) php_printf("Source is interned!\n"); 67 | 68 | if (Z_STR_P(zsrc) == Z_STR_P(zdst)) samestr = 1; 69 | } 70 | 71 | if (EG(exception)) 72 | { 73 | return; 74 | } 75 | 76 | /* Confirm that the destination is indeed a string. */ 77 | if (Z_TYPE_P(zdst) != IS_STRING) 78 | { 79 | php_error_docref(NULL, E_ERROR, "Destination must be a string"); 80 | 81 | return; 82 | } 83 | 84 | /* Normalize offsets and lengths. */ 85 | if (numargs < 8) dst_lastsize = (zend_long)Z_STRLEN_P(zdst); 86 | else 87 | { 88 | //if (Z_TYPE_P(zdst_lastsize) == IS_REFERENCE) php_printf("Last size is a reference!\n"); 89 | ZVAL_DEREF(zdst_lastsize); 90 | 91 | if (Z_TYPE_P(zdst_lastsize) == IS_NULL) dst_lastsize = (zend_long)Z_STRLEN_P(zdst); 92 | else 93 | { 94 | dst_lastsize = Z_LVAL_P(zdst_lastsize); 95 | 96 | if (dst_lastsize < 0) dst_lastsize = 0; 97 | 98 | if (dst_lastsize > Z_STRLEN_P(zdst)) dst_lastsize = Z_STRLEN_P(zdst); 99 | } 100 | } 101 | 102 | if (dst_offset < 0) 103 | { 104 | if (-dst_offset > dst_lastsize) dst_offset = 0; 105 | else dst_offset += dst_lastsize; 106 | } 107 | else if (dst_offset > dst_lastsize) 108 | { 109 | dst_offset = dst_lastsize; 110 | } 111 | 112 | if (numargs < 3) dst_length = dst_lastsize; 113 | else 114 | { 115 | //if (Z_TYPE_P(zdst_length) == IS_REFERENCE) php_printf("Destination length is a reference!\n"); 116 | ZVAL_DEREF(zdst_length); 117 | 118 | if (Z_TYPE_P(zdst_length) == IS_NULL) dst_length = dst_lastsize; 119 | else dst_length = Z_LVAL_P(zdst_length); 120 | } 121 | 122 | if (dst_length < 0) 123 | { 124 | if (-dst_length > dst_lastsize) dst_length = 0; 125 | else dst_length += dst_lastsize - dst_offset; 126 | } 127 | 128 | if (dst_length > dst_lastsize - dst_offset) dst_length = dst_lastsize - dst_offset; 129 | 130 | 131 | if (numargs >= 4) src_size = Z_STRLEN_P(zsrc); 132 | 133 | if (src_offset < 0) 134 | { 135 | if (-src_offset > (zend_long)src_size) src_offset = 0; 136 | else src_offset += (zend_long)src_size; 137 | } 138 | else if (src_offset > (zend_long)src_size) 139 | { 140 | src_offset = (zend_long)src_size; 141 | } 142 | 143 | if (numargs < 6) src_length = (zend_long)src_size; 144 | else 145 | { 146 | //if (Z_TYPE_P(zsrc_length) == IS_REFERENCE) php_printf("Source length is a reference!\n"); 147 | ZVAL_DEREF(zsrc_length); 148 | 149 | if (Z_TYPE_P(zsrc_length) == IS_NULL) src_length = (zend_long)src_size; 150 | else src_length = Z_LVAL_P(zsrc_length); 151 | } 152 | 153 | if (src_length < 0) 154 | { 155 | if (-src_length > (zend_long)src_size) src_length = 0; 156 | else src_length += (zend_long)src_size - src_offset; 157 | } 158 | 159 | if (src_length > src_size - src_offset) src_length = (zend_long)src_size - src_offset; 160 | 161 | if (src_repeat < 1) src_repeat = 1; 162 | 163 | src_finallength = src_length * src_repeat; 164 | 165 | 166 | /* Calculate final buffer size. */ 167 | dst_size = dst_lastsize - dst_length + src_finallength; 168 | dst_size2 = dst_size; 169 | if (!shrink && dst_size < Z_STRLEN_P(zdst)) dst_size = Z_STRLEN_P(zdst); 170 | 171 | //php_printf("Refcount: %d\n", zend_string_refcount(Z_STR_P(zdst))); 172 | //php_printf("dst_offset: %ld, dst_length: %ld, src_offset: %ld, src_length: %ld, src_finallength: %ld, dst_size2: %ld, dst_size: %ld\n", dst_offset, dst_length, src_offset, src_length, src_finallength, dst_size2, dst_size); 173 | //if (samestr) php_printf("Source and destination strings are the same!\n"); 174 | 175 | /* Allocate a new destination string if: */ 176 | /* The destination is interned/immutable. */ 177 | /* Has more than one (src and dst zend_string are different) or two (src and dst zend_string are the same) references. */ 178 | /* Or source and destination are the same string but are different initial sizes and the initial substring overlaps. */ 179 | if (ZSTR_IS_INTERNED(Z_STR_P(zdst)) || zend_string_refcount(Z_STR_P(zdst)) > samestr + 1 || (samestr && src_length != dst_length && ((src_offset < dst_offset && src_offset + src_finallength > dst_offset) || (src_offset >= dst_offset && src_offset < dst_offset + dst_length)))) 180 | { 181 | //php_printf("Allocating new string.\n"); 182 | zsdst = zend_string_alloc(dst_size, 0); 183 | dst = ZSTR_VAL(zsdst); 184 | 185 | memcpy(dst, Z_STRVAL_P(zdst), dst_size + 1); 186 | 187 | ZEND_TRY_ASSIGN_STR(zdst, zsdst); 188 | } 189 | else 190 | { 191 | /* The existing string just needs to be larger. */ 192 | if (dst_size > Z_STRLEN_P(zdst)) 193 | { 194 | if (samestr) ZEND_TRY_ASSIGN_NULL(zsrc); 195 | 196 | //php_printf("Resizing existing string! Before: %p", Z_STR_P(zdst)); 197 | ZVAL_STR(zdst, zend_string_realloc(Z_STR_P(zdst), dst_size, 0)); 198 | //php_printf(", After: %p\n", Z_STR_P(zdst)); 199 | 200 | if (samestr) zsrc = zdst; 201 | } 202 | 203 | //php_printf("Using existing string!\n"); 204 | /* Notable optimization for all other scenarios: Directly alter the string. */ 205 | zsdst = Z_STR_P(zdst); 206 | dst = ZSTR_VAL(zsdst); 207 | } 208 | 209 | /* If the source length is different than the destination length, move the data for the final space. */ 210 | if (src_finallength != dst_length) 211 | { 212 | memmove(dst + dst_offset + src_finallength, dst + dst_offset + dst_length, dst_lastsize - dst_length - dst_offset); 213 | dst[dst_size] = '\0'; 214 | 215 | /* If the source and destination strings are the same, adjust the source offset now that the data has moved. */ 216 | if (numargs >= 4 && Z_STR_P(zsrc) == zsdst && src_offset > dst_offset) src_offset += (src_finallength - dst_length); 217 | 218 | /* Update the string size as needed. */ 219 | if (shrink && src_finallength < dst_length) ZSTR_LEN(zsdst) = dst_size; 220 | } 221 | 222 | /* Copy the source. */ 223 | if (src_finallength > 0) 224 | { 225 | if (numargs >= 4) src = Z_STRVAL_P(zsrc); 226 | //if (src == dst) php_printf("Source and destination string pointers are identical!\n"); 227 | 228 | if (src_length == 1) 229 | { 230 | /* Handle the one byte repeating string fill via memset(). */ 231 | if (src_finallength > 1) memset(dst + dst_offset, *(src + src_offset), src_finallength); 232 | else dst[dst_offset] = *(src + src_offset); 233 | } 234 | else 235 | { 236 | /* Move/copy the source. */ 237 | if (src == dst) memmove(dst + dst_offset, src + src_offset, src_length); 238 | else memcpy(dst + dst_offset, src + src_offset, src_length); 239 | 240 | if (src_repeat > 1) 241 | { 242 | /* Attempt to keep the maximum amount of data duplicated per memcpy() call to a size that hopefully keeps the source string within the onboard CPU cache. */ 243 | copy_limit = 32768 / src_length; 244 | if (!copy_limit) copy_limit++; 245 | copy_limit *= src_length * 2; 246 | 247 | dst2 = dst + dst_offset; 248 | 249 | dst_ptr = dst2 + src_length; 250 | dst_ptr2 = dst2 + src_finallength; 251 | 252 | while (dst_ptr < dst_ptr2) 253 | { 254 | dst_diff = ((dst_ptr - dst2) < (dst_ptr2 - dst_ptr) ? (dst_ptr - dst2) : (dst_ptr2 - dst_ptr)); 255 | if (dst_diff > copy_limit) dst_diff = copy_limit; 256 | 257 | memcpy(dst_ptr, dst2, dst_diff); 258 | 259 | dst_ptr += dst_diff; 260 | } 261 | } 262 | } 263 | } 264 | 265 | //php_printf("Source: %s\r\nDestination/Final: %s\r\n", (numargs >= 4 ? Z_STRVAL_P(zsrc) : "[NONE]"), Z_STRVAL_P(zdst)); 266 | 267 | /* Reset zend_string internals. */ 268 | zend_string_forget_hash_val(zsdst); 269 | 270 | RETURN_LONG(dst_size2); 271 | } 272 | /* }}} */ 273 | 274 | /* {{{ Reallocates the buffer associated with a string and returns the previous size. 275 | int str_realloc(string &$str, int $size, [ bool $fast = false ]) 276 | */ 277 | PHP_FUNCTION(str_realloc) 278 | { 279 | zval *zstr; 280 | zend_string *zsstr; 281 | char *str; 282 | size_t str_size; 283 | zend_long newsize; 284 | zend_bool fast = 0; 285 | 286 | ZEND_PARSE_PARAMETERS_START(2, 3) 287 | Z_PARAM_ZVAL(zstr) 288 | Z_PARAM_LONG(newsize) 289 | Z_PARAM_OPTIONAL 290 | Z_PARAM_BOOL(fast) 291 | ZEND_PARSE_PARAMETERS_END(); 292 | 293 | /* Dereference the zval as needed. */ 294 | //if (Z_TYPE_P(zstr) == IS_REFERENCE) php_printf("zstr is a reference!\n"); 295 | //php_printf("Destination zval! Before: %p", zstr); 296 | ZVAL_DEREF(zstr); 297 | 298 | convert_to_string_ex(zstr); 299 | 300 | //php_printf(", After: %p\n", zstr); 301 | //if (ZSTR_IS_INTERNED(Z_STR_P(zstr))) php_printf("zstr is interned!\n"); 302 | 303 | if (EG(exception)) 304 | { 305 | return; 306 | } 307 | 308 | str_size = Z_STRLEN_P(zstr); 309 | 310 | if (str_size != newsize) 311 | { 312 | /* Always allocate a new string if: */ 313 | /* The destination is interned/immutable. */ 314 | /* Or has more than one reference. */ 315 | if (ZSTR_IS_INTERNED(Z_STR_P(zstr)) || zend_string_refcount(Z_STR_P(zstr)) > 1) 316 | { 317 | //php_printf("Allocating new string.\n"); 318 | zsstr = zend_string_alloc(newsize, 0); 319 | str = ZSTR_VAL(zsstr); 320 | 321 | if (str_size < newsize) 322 | { 323 | memcpy(str, Z_STRVAL_P(zstr), str_size); 324 | memset(str + str_size, 0, newsize - str_size); 325 | } 326 | else 327 | { 328 | memcpy(str, Z_STRVAL_P(zstr), newsize); 329 | } 330 | 331 | ZEND_TRY_ASSIGN_STR(zstr, zsstr); 332 | } 333 | else if (fast && str_size > newsize) 334 | { 335 | /* Don't reallocate when shrinking the buffer in fast mode. */ 336 | //php_printf("Fast shrink.\n"); 337 | Z_STRLEN_P(zstr) = newsize; 338 | 339 | /* Reset zend_string internals. */ 340 | zend_string_forget_hash_val(Z_STR_P(zstr)); 341 | } 342 | else 343 | { 344 | //php_printf("Resizing string! Before: %p", Z_STR_P(zstr)); 345 | ZVAL_STR(zstr, zend_string_realloc(Z_STR_P(zstr), newsize, 0)); 346 | //php_printf(", After: %p\n", Z_STR_P(zstr)); 347 | 348 | if (str_size < newsize) memset(Z_STRVAL_P(zstr) + str_size, 0, newsize - str_size); 349 | } 350 | 351 | Z_STRVAL_P(zstr)[newsize] = '\0'; 352 | } 353 | 354 | RETURN_LONG(str_size); 355 | } 356 | /* }}} */ 357 | 358 | /* {{{ php_adjust_substr_offset_length 359 | */ 360 | PHPAPI void php_adjust_substr_offset_length(zend_string *str, zend_long *str_offset, zend_long *str_length) 361 | { 362 | zend_long str_size = ZSTR_LEN(str); 363 | 364 | if ((*str_offset) < 0) 365 | { 366 | if (-(*str_offset) > str_size) (*str_offset) = 0; 367 | else (*str_offset) += str_size; 368 | } 369 | else if ((*str_offset) > str_size) 370 | { 371 | (*str_offset) = str_size; 372 | } 373 | 374 | if ((*str_length) < 0) 375 | { 376 | if (-(*str_length) > str_size) (*str_length) = 0; 377 | else (*str_length) += str_size - (*str_offset); 378 | } 379 | 380 | if ((*str_length) > str_size - (*str_offset)) (*str_length) = str_size - (*str_offset); 381 | } 382 | /* }}} */ 383 | 384 | 385 | /* Start of code lifted directly from ext/standard/string.c and modified slightly to support substrings. */ 386 | 387 | /* {{{ php_explode_substr 388 | */ 389 | PHPAPI void php_explode_substr(const zend_string *delim, zend_string *str, zval *return_value, zend_long str_offset, zend_long str_length, zend_long limit) 390 | { 391 | const char *p1 = ZSTR_VAL(str) + str_offset; 392 | const char *endp = p1 + str_length; 393 | const char *p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp); 394 | zval tmp; 395 | 396 | if (p2 == NULL) { 397 | ZVAL_STR_COPY(&tmp, str); 398 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); 399 | } else { 400 | do { 401 | size_t l = p2 - p1; 402 | 403 | if (l == 0) { 404 | ZVAL_EMPTY_STRING(&tmp); 405 | } else if (l == 1) { 406 | ZVAL_INTERNED_STR(&tmp, ZSTR_CHAR((zend_uchar)(*p1))); 407 | } else { 408 | ZVAL_STRINGL(&tmp, p1, p2 - p1); 409 | } 410 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); 411 | p1 = p2 + ZSTR_LEN(delim); 412 | p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp); 413 | } while (p2 != NULL && --limit > 1); 414 | 415 | if (p1 <= endp) { 416 | ZVAL_STRINGL(&tmp, p1, endp - p1); 417 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); 418 | } 419 | } 420 | } 421 | /* }}} */ 422 | 423 | /* {{{ php_explode_substr_negative_limit 424 | */ 425 | PHPAPI void php_explode_substr_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long str_offset, zend_long str_length, zend_long limit) 426 | { 427 | #define EXPLODE_ALLOC_STEP 64 428 | const char *p1 = ZSTR_VAL(str) + str_offset; 429 | const char *endp = p1 + str_length; 430 | const char *p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp); 431 | zval tmp; 432 | 433 | if (p2 == NULL) { 434 | /* 435 | do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0 436 | by doing nothing we return empty array 437 | */ 438 | } else { 439 | size_t allocated = EXPLODE_ALLOC_STEP, found = 0; 440 | zend_long i, to_return; 441 | const char **positions = emalloc(allocated * sizeof(char *)); 442 | 443 | positions[found++] = p1; 444 | do { 445 | if (found >= allocated) { 446 | allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */ 447 | #if PHP_MAJOR_VERSION >= 8 448 | positions = erealloc(ZEND_VOIDP(positions), allocated*sizeof(char *)); 449 | #else 450 | positions = erealloc(positions, allocated*sizeof(char *)); 451 | #endif 452 | } 453 | positions[found++] = p1 = p2 + ZSTR_LEN(delim); 454 | p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp); 455 | } while (p2 != NULL); 456 | 457 | to_return = limit + found; 458 | /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */ 459 | for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */ 460 | ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]); 461 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); 462 | } 463 | efree((void *)positions); 464 | } 465 | #undef EXPLODE_ALLOC_STEP 466 | } 467 | /* }}} */ 468 | 469 | /* {{{ proto array explode_substr(string separator, string str [, ?int limit = null, ?int str_offset = 0, ?int str_length = null ]) 470 | Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned. */ 471 | PHP_FUNCTION(explode_substr) 472 | { 473 | int numargs = ZEND_NUM_ARGS(); 474 | zend_string *str, *delim; 475 | zend_long limit = ZEND_LONG_MAX; /* No limit */ 476 | zend_long str_offset = 0, str_length; 477 | zval tmp, *zlimit = NULL, *zstr_length = NULL; 478 | 479 | ZEND_PARSE_PARAMETERS_START(2, 5) 480 | Z_PARAM_STR(delim) 481 | Z_PARAM_STR(str) 482 | Z_PARAM_OPTIONAL 483 | Z_PARAM_ZVAL(zlimit) 484 | Z_PARAM_LONG(str_offset) 485 | Z_PARAM_ZVAL(zstr_length) 486 | ZEND_PARSE_PARAMETERS_END(); 487 | 488 | if (ZSTR_LEN(delim) == 0) { 489 | php_error_docref(NULL, E_WARNING, "Empty delimiter"); 490 | RETURN_FALSE; 491 | } 492 | 493 | array_init(return_value); 494 | 495 | if (ZSTR_LEN(str) == 0) { 496 | if (limit >= 0) { 497 | ZVAL_EMPTY_STRING(&tmp); 498 | zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp); 499 | } 500 | return; 501 | } 502 | 503 | if (numargs >= 3 && Z_TYPE_P(zlimit) != IS_NULL) { 504 | limit = Z_LVAL_P(zlimit); 505 | } 506 | 507 | if (numargs >= 5 && Z_TYPE_P(zstr_length) != IS_NULL) { 508 | str_length = Z_LVAL_P(zstr_length); 509 | } else { 510 | str_length = ZSTR_LEN(str); 511 | } 512 | 513 | php_adjust_substr_offset_length(str, &str_offset, &str_length); 514 | 515 | if (limit > 1) { 516 | php_explode_substr(delim, str, return_value, str_offset, str_length, limit); 517 | } else if (limit < 0) { 518 | php_explode_substr_negative_limit(delim, str, return_value, str_offset, str_length, limit); 519 | } else { 520 | ZVAL_STR_COPY(&tmp, str); 521 | zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp); 522 | } 523 | } 524 | /* }}} */ 525 | 526 | /* {{{ proto array str_split_substr(string str [, ?int split_length = 1, ?int str_offset = 0, ?int str_length = null ]) 527 | Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long. */ 528 | PHP_FUNCTION(str_split_substr) 529 | { 530 | int numargs = ZEND_NUM_ARGS(); 531 | zend_string *str; 532 | zend_long split_length = 1; 533 | const char *p; 534 | size_t n_reg_segments; 535 | zend_long str_offset = 0, str_length; 536 | zval *zsplit_length = NULL, *zstr_length = NULL; 537 | 538 | ZEND_PARSE_PARAMETERS_START(1, 4) 539 | Z_PARAM_STR(str) 540 | Z_PARAM_OPTIONAL 541 | Z_PARAM_ZVAL(zsplit_length) 542 | Z_PARAM_LONG(str_offset) 543 | Z_PARAM_ZVAL(zstr_length) 544 | ZEND_PARSE_PARAMETERS_END(); 545 | 546 | if (numargs >= 2 && Z_TYPE_P(zsplit_length) != IS_NULL) { 547 | split_length = Z_LVAL_P(zsplit_length); 548 | } 549 | 550 | if (split_length <= 0) { 551 | php_error_docref(NULL, E_WARNING, "The length of each segment must be greater than zero"); 552 | RETURN_FALSE; 553 | } 554 | 555 | if (numargs >= 4 && Z_TYPE_P(zstr_length) != IS_NULL) { 556 | str_length = Z_LVAL_P(zstr_length); 557 | } else { 558 | str_length = ZSTR_LEN(str); 559 | } 560 | 561 | php_adjust_substr_offset_length(str, &str_offset, &str_length); 562 | 563 | 564 | p = ZSTR_VAL(str) + str_offset; 565 | 566 | if (0 == str_length || (size_t)split_length >= str_length) { 567 | array_init_size(return_value, 1); 568 | add_next_index_stringl(return_value, p, str_length); 569 | return; 570 | } 571 | 572 | array_init_size(return_value, (uint32_t)(((str_length - 1) / split_length) + 1)); 573 | 574 | n_reg_segments = str_length / split_length; 575 | 576 | while (n_reg_segments-- > 0) { 577 | add_next_index_stringl(return_value, p, split_length); 578 | p += split_length; 579 | } 580 | 581 | if (p < (ZSTR_VAL(str) + str_offset + str_length)) { 582 | add_next_index_stringl(return_value, p, (ZSTR_VAL(str) + str_offset + str_length - p)); 583 | } 584 | } 585 | /* }}} */ 586 | 587 | /* End of code lifted directly from ext/standard/string.c and modified slightly to support substrings. */ 588 | 589 | 590 | /* Start of code lifted directly from ext/standard/file.c and modified slightly to support inline reads and substring writes. */ 591 | 592 | #define PHP_STREAM_FROM_ZVAL(stream, arg) \ 593 | ZEND_ASSERT(Z_TYPE_P(arg) == IS_RESOURCE); \ 594 | php_stream_from_res(stream, Z_RES_P(arg)); 595 | 596 | /* {{{ proto int|false fread_mem(resource fp, string &$str, [ int str_offset = 0, ?int length = null ]) 597 | Binary-safe file read */ 598 | PHPAPI PHP_FUNCTION(fread_mem) 599 | { 600 | int numargs = ZEND_NUM_ARGS(); 601 | zval *res; 602 | php_stream *stream; 603 | zend_string *str; 604 | zend_long str_size, str_size2, str_offset = 0, length; 605 | zval *zstr, *zlength = NULL; 606 | ssize_t n; 607 | 608 | ZEND_PARSE_PARAMETERS_START(2, 4) 609 | Z_PARAM_RESOURCE(res) 610 | Z_PARAM_ZVAL(zstr) 611 | Z_PARAM_OPTIONAL 612 | Z_PARAM_LONG(str_offset) 613 | Z_PARAM_ZVAL(zlength) 614 | ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); 615 | 616 | PHP_STREAM_FROM_ZVAL(stream, res); 617 | 618 | /* Dereference the destination zval as needed. */ 619 | //if (Z_TYPE_P(zstr) == IS_REFERENCE) php_printf("Destination var is a reference!\n"); 620 | //php_printf("Destination zval! Before: %p", zstr); 621 | ZVAL_DEREF(zstr); 622 | 623 | convert_to_string_ex(zstr); 624 | 625 | //php_printf(", After: %p\n", zstr); 626 | //if (ZSTR_IS_INTERNED(Z_STR_P(zstr))) php_printf("Destination is interned!\n"); 627 | 628 | if (EG(exception)) 629 | { 630 | return; 631 | } 632 | 633 | /* Confirm that the destination is indeed a string. */ 634 | if (Z_TYPE_P(zstr) != IS_STRING) 635 | { 636 | php_error_docref(NULL, E_ERROR, "Destination variable must be a string"); 637 | 638 | return; 639 | } 640 | 641 | str = Z_STR_P(zstr); 642 | str_size = ZSTR_LEN(str); 643 | 644 | /* Normalize offsets and lengths. */ 645 | if (str_offset < 0) 646 | { 647 | if (-str_offset > str_size) str_offset = 0; 648 | else str_offset += str_size; 649 | } 650 | else if (str_offset > str_size) 651 | { 652 | str_offset = str_size; 653 | } 654 | 655 | if (numargs >= 4 && Z_TYPE_P(zlength) != IS_NULL) { 656 | length = Z_LVAL_P(zlength); 657 | } else { 658 | length = ZSTR_LEN(str) - str_offset; 659 | } 660 | 661 | if (length <= 0) { 662 | php_error_docref(NULL, E_WARNING, "Length parameter or destination variable buffer size must be greater than 0"); 663 | RETURN_FALSE; 664 | } 665 | 666 | str_size2 = str_offset + length; 667 | 668 | /* Allocate a new destination string if: */ 669 | /* The destination is interned/immutable. */ 670 | /* Or has more than one reference. */ 671 | if (ZSTR_IS_INTERNED(str) || zend_string_refcount(str) > 1) 672 | { 673 | //php_printf("Allocating new string.\n"); 674 | str = zend_string_alloc(str_size2, 0); 675 | 676 | memcpy(ZSTR_VAL(str), Z_STRVAL_P(zstr), str_size + 1); 677 | ZSTR_VAL(str)[str_size2] = '\0'; 678 | 679 | ZEND_TRY_ASSIGN_STR(zstr, str); 680 | } 681 | else if (str_size < str_size2) 682 | { 683 | /* The existing string just needs to be larger. */ 684 | //php_printf("Resizing existing string! Before: %p", Z_STR_P(zstr)); 685 | str = zend_string_realloc(str, str_size2, 0); 686 | ZSTR_VAL(str)[str_size2] = '\0'; 687 | 688 | ZVAL_STR(zstr, str); 689 | //php_printf(", After: %p\n", Z_STR_P(zstr)); 690 | } 691 | 692 | n = php_stream_read(stream, ZSTR_VAL(str) + str_offset, length); 693 | if (n < 0) 694 | { 695 | RETURN_FALSE; 696 | } 697 | 698 | /* Fill remaining space if the string was resized but the read operation didn't fulfill the original request. */ 699 | if (n < length && str_size != str_size2) 700 | { 701 | memset(ZSTR_VAL(str) + str_offset + n, 0, length - n); 702 | } 703 | 704 | RETURN_LONG(n); 705 | } 706 | /* }}} */ 707 | 708 | /* {{{ proto int|false fwrite_substr(resource fp, string str [, ?int str_offset = 0, ?int str_length = null ]) 709 | Binary-safe file write */ 710 | PHPAPI PHP_FUNCTION(fwrite_substr) 711 | { 712 | int numargs = ZEND_NUM_ARGS(); 713 | zval *res; 714 | zend_string *str; 715 | ssize_t ret; 716 | zend_long str_offset = 0, str_length; 717 | zval *zstr_length = NULL; 718 | php_stream *stream; 719 | 720 | ZEND_PARSE_PARAMETERS_START(2, 4) 721 | Z_PARAM_RESOURCE(res) 722 | Z_PARAM_STR(str) 723 | Z_PARAM_OPTIONAL 724 | Z_PARAM_LONG(str_offset) 725 | Z_PARAM_ZVAL(zstr_length) 726 | ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); 727 | 728 | if (numargs >= 4 && Z_TYPE_P(zstr_length) != IS_NULL) { 729 | str_length = Z_LVAL_P(zstr_length); 730 | } else { 731 | str_length = ZSTR_LEN(str); 732 | } 733 | 734 | php_adjust_substr_offset_length(str, &str_offset, &str_length); 735 | 736 | if (!str_length) { 737 | RETURN_LONG(0); 738 | } 739 | 740 | PHP_STREAM_FROM_ZVAL(stream, res); 741 | 742 | ret = php_stream_write(stream, ZSTR_VAL(str) + str_offset, str_length); 743 | if (ret < 0) { 744 | RETURN_FALSE; 745 | } 746 | 747 | RETURN_LONG(ret); 748 | } 749 | /* }}} */ 750 | 751 | /* End of code lifted directly from ext/standard/file.c and modified slightly to support inline reads and substring writes. */ 752 | 753 | 754 | /* Start of code lifted directly from ext/hash/hash.c and modified slightly to support substrings. */ 755 | 756 | #include "ext/hash/php_hash.h" 757 | #include "ext/standard/file.h" 758 | 759 | #if PHP_MAJOR_VERSION >= 8 760 | 761 | static void php_hash_do_hash( 762 | zval *return_value, zend_string *algo, char *data, size_t data_len, bool raw_output, bool isfilename, HashTable *args 763 | ) /* {{{ */ { 764 | zend_string *digest; 765 | const php_hash_ops *ops; 766 | void *context; 767 | php_stream *stream = NULL; 768 | 769 | ops = php_hash_fetch_ops(algo); 770 | if (!ops) { 771 | zend_argument_value_error(1, "must be a valid hashing algorithm"); 772 | RETURN_THROWS(); 773 | } 774 | if (isfilename) { 775 | if (CHECK_NULL_PATH(data, data_len)) { 776 | zend_argument_value_error(1, "must not contain any null bytes"); 777 | RETURN_THROWS(); 778 | } 779 | stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context)); 780 | if (!stream) { 781 | /* Stream will report errors opening file */ 782 | RETURN_FALSE; 783 | } 784 | } 785 | 786 | context = php_hash_alloc_context(ops); 787 | ops->hash_init(context, args); 788 | 789 | if (isfilename) { 790 | char buf[1024]; 791 | ssize_t n; 792 | 793 | while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) { 794 | ops->hash_update(context, (unsigned char *) buf, n); 795 | } 796 | php_stream_close(stream); 797 | if (n < 0) { 798 | efree(context); 799 | RETURN_FALSE; 800 | } 801 | } else { 802 | ops->hash_update(context, (unsigned char *) data, data_len); 803 | } 804 | 805 | digest = zend_string_alloc(ops->digest_size, 0); 806 | ops->hash_final((unsigned char *) ZSTR_VAL(digest), context); 807 | efree(context); 808 | 809 | if (raw_output) { 810 | ZSTR_VAL(digest)[ops->digest_size] = 0; 811 | RETURN_NEW_STR(digest); 812 | } else { 813 | zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); 814 | 815 | php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); 816 | ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; 817 | zend_string_release_ex(digest, 0); 818 | RETURN_NEW_STR(hex_digest); 819 | } 820 | } 821 | /* }}} */ 822 | 823 | /* {{{ Generate a hash of a given input string 824 | Returns lowercase hexits by default */ 825 | PHP_FUNCTION(hash_substr) 826 | { 827 | zend_string *algo; 828 | char *data; 829 | size_t data_len; 830 | zend_string *zdata; 831 | bool raw_output = 0; 832 | HashTable *args = NULL; 833 | zend_long data_offset = 0, data_len2; 834 | zval *zdata_len2; 835 | 836 | ZEND_PARSE_PARAMETERS_START(2, 6) 837 | Z_PARAM_STR(algo) 838 | Z_PARAM_STR(zdata) 839 | Z_PARAM_OPTIONAL 840 | Z_PARAM_BOOL(raw_output) 841 | Z_PARAM_ARRAY_HT(args) 842 | Z_PARAM_LONG(data_offset) 843 | Z_PARAM_ZVAL(zdata_len2) 844 | ZEND_PARSE_PARAMETERS_END(); 845 | 846 | if (ZEND_NUM_ARGS() >= 6 && Z_TYPE_P(zdata_len2) != IS_NULL) { 847 | data_len2 = Z_LVAL_P(zdata_len2); 848 | } else { 849 | data_len2 = ZSTR_LEN(zdata); 850 | } 851 | 852 | php_adjust_substr_offset_length(zdata, &data_offset, &data_len2); 853 | 854 | data = ZSTR_VAL(zdata) + data_offset; 855 | data_len = (size_t)data_len2; 856 | 857 | php_hash_do_hash(return_value, algo, data, data_len, raw_output, 0, args); 858 | } 859 | /* }}} */ 860 | 861 | #else 862 | 863 | static void php_hash_do_hash_substr(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */ 864 | { 865 | zend_string *digest; 866 | char *algo, *data; 867 | zend_string *zdata; 868 | size_t algo_len, data_len; 869 | zend_bool raw_output = raw_output_default; 870 | zend_long data_offset = 0, data_len2; 871 | zval *zdata_len2; 872 | const php_hash_ops *ops; 873 | void *context; 874 | php_stream *stream = NULL; 875 | 876 | if (isfilename) { 877 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &algo, &algo_len, &data, &data_len, &raw_output) == FAILURE) { 878 | return; 879 | } 880 | } else { 881 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS|blz", &algo, &algo_len, &zdata, &raw_output, &data_offset, &zdata_len2) == FAILURE) { 882 | return; 883 | } 884 | 885 | if (ZEND_NUM_ARGS() >= 5 && Z_TYPE_P(zdata_len2) != IS_NULL) { 886 | data_len2 = Z_LVAL_P(zdata_len2); 887 | } else { 888 | data_len2 = ZSTR_LEN(zdata); 889 | } 890 | 891 | php_adjust_substr_offset_length(zdata, &data_offset, &data_len2); 892 | 893 | data = ZSTR_VAL(zdata) + data_offset; 894 | data_len = (size_t)data_len2; 895 | } 896 | 897 | ops = php_hash_fetch_ops(algo, algo_len); 898 | if (!ops) { 899 | php_error_docref(NULL, E_WARNING, "Unknown hashing algorithm: %s", algo); 900 | RETURN_FALSE; 901 | } 902 | if (isfilename) { 903 | if (CHECK_NULL_PATH(data, data_len)) { 904 | php_error_docref(NULL, E_WARNING, "Invalid path"); 905 | RETURN_FALSE; 906 | } 907 | stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context)); 908 | if (!stream) { 909 | /* Stream will report errors opening file */ 910 | RETURN_FALSE; 911 | } 912 | } 913 | 914 | context = emalloc(ops->context_size); 915 | ops->hash_init(context); 916 | 917 | if (isfilename) { 918 | char buf[1024]; 919 | ssize_t n; 920 | 921 | while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) { 922 | ops->hash_update(context, (unsigned char *) buf, n); 923 | } 924 | php_stream_close(stream); 925 | if (n < 0) { 926 | efree(context); 927 | RETURN_FALSE; 928 | } 929 | } else { 930 | ops->hash_update(context, (unsigned char *) data, data_len); 931 | } 932 | 933 | digest = zend_string_alloc(ops->digest_size, 0); 934 | ops->hash_final((unsigned char *) ZSTR_VAL(digest), context); 935 | efree(context); 936 | 937 | if (raw_output) { 938 | ZSTR_VAL(digest)[ops->digest_size] = 0; 939 | RETURN_NEW_STR(digest); 940 | } else { 941 | zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); 942 | 943 | php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); 944 | ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; 945 | zend_string_release_ex(digest, 0); 946 | RETURN_NEW_STR(hex_digest); 947 | } 948 | } 949 | /* }}} */ 950 | 951 | /* {{{ proto string hash_substr(string algo, string data[, bool raw_output = false, ?int data_offset = 0, ?int data_length = null]) 952 | Generate a hash of a given input string 953 | Returns lowercase hexits by default */ 954 | PHP_FUNCTION(hash_substr) 955 | { 956 | php_hash_do_hash_substr(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); 957 | } 958 | /* }}} */ 959 | 960 | #endif 961 | 962 | /* Directly copied functions so that php_hash_do_hash_hmac_substr() will compile. */ 963 | #if PHP_MAJOR_VERSION >= 8 964 | 965 | static inline void php_hash_string_xor_char(unsigned char *out, const unsigned char *in, const unsigned char xor_with, const size_t length) { 966 | size_t i; 967 | for (i=0; i < length; i++) { 968 | out[i] = in[i] ^ xor_with; 969 | } 970 | } 971 | 972 | static inline void php_hash_string_xor(unsigned char *out, const unsigned char *in, const unsigned char *xor_with, const size_t length) { 973 | size_t i; 974 | for (i=0; i < length; i++) { 975 | out[i] = in[i] ^ xor_with[i]; 976 | } 977 | } 978 | 979 | static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *ops, void *context, const unsigned char *key, const size_t key_len) { 980 | memset(K, 0, ops->block_size); 981 | if (key_len > ops->block_size) { 982 | /* Reduce the key first */ 983 | ops->hash_init(context, NULL); 984 | ops->hash_update(context, key, key_len); 985 | ops->hash_final(K, context); 986 | } else { 987 | memcpy(K, key, key_len); 988 | } 989 | /* XOR the key with 0x36 to get the ipad) */ 990 | php_hash_string_xor_char(K, K, 0x36, ops->block_size); 991 | } 992 | 993 | static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops *ops, void *context, const unsigned char *key, const unsigned char *data, const zend_long data_size) { 994 | ops->hash_init(context, NULL); 995 | ops->hash_update(context, key, ops->block_size); 996 | ops->hash_update(context, data, data_size); 997 | ops->hash_final(final, context); 998 | } 999 | 1000 | static void php_hash_do_hash_hmac( 1001 | zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, bool raw_output, bool isfilename 1002 | ) /* {{{ */ { 1003 | zend_string *digest; 1004 | unsigned char *K; 1005 | const php_hash_ops *ops; 1006 | void *context; 1007 | php_stream *stream = NULL; 1008 | 1009 | ops = php_hash_fetch_ops(algo); 1010 | if (!ops || !ops->is_crypto) { 1011 | zend_argument_value_error(1, "must be a valid cryptographic hashing algorithm"); 1012 | RETURN_THROWS(); 1013 | } 1014 | 1015 | if (isfilename) { 1016 | if (CHECK_NULL_PATH(data, data_len)) { 1017 | zend_argument_value_error(2, "must not contain any null bytes"); 1018 | RETURN_THROWS(); 1019 | } 1020 | stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context)); 1021 | if (!stream) { 1022 | /* Stream will report errors opening file */ 1023 | RETURN_FALSE; 1024 | } 1025 | } 1026 | 1027 | context = php_hash_alloc_context(ops); 1028 | 1029 | K = emalloc(ops->block_size); 1030 | digest = zend_string_alloc(ops->digest_size, 0); 1031 | 1032 | php_hash_hmac_prep_key(K, ops, context, (unsigned char *) key, key_len); 1033 | 1034 | if (isfilename) { 1035 | char buf[1024]; 1036 | ssize_t n; 1037 | ops->hash_init(context, NULL); 1038 | ops->hash_update(context, K, ops->block_size); 1039 | while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) { 1040 | ops->hash_update(context, (unsigned char *) buf, n); 1041 | } 1042 | php_stream_close(stream); 1043 | if (n < 0) { 1044 | efree(context); 1045 | efree(K); 1046 | zend_string_release(digest); 1047 | RETURN_FALSE; 1048 | } 1049 | 1050 | ops->hash_final((unsigned char *) ZSTR_VAL(digest), context); 1051 | } else { 1052 | php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) data, data_len); 1053 | } 1054 | 1055 | php_hash_string_xor_char(K, K, 0x6A, ops->block_size); 1056 | 1057 | php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) ZSTR_VAL(digest), ops->digest_size); 1058 | 1059 | /* Zero the key */ 1060 | ZEND_SECURE_ZERO(K, ops->block_size); 1061 | efree(K); 1062 | efree(context); 1063 | 1064 | if (raw_output) { 1065 | ZSTR_VAL(digest)[ops->digest_size] = 0; 1066 | RETURN_NEW_STR(digest); 1067 | } else { 1068 | zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); 1069 | 1070 | php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); 1071 | ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; 1072 | zend_string_release_ex(digest, 0); 1073 | RETURN_NEW_STR(hex_digest); 1074 | } 1075 | } 1076 | /* }}} */ 1077 | 1078 | /* {{{ Generate a hash of a given input string with a key using HMAC 1079 | Returns lowercase hexits by default */ 1080 | PHP_FUNCTION(hash_hmac_substr) 1081 | { 1082 | zend_string *algo; 1083 | char *data, *key; 1084 | size_t data_len, key_len; 1085 | zend_string *zdata; 1086 | bool raw_output = 0; 1087 | zend_long data_offset = 0, data_len2; 1088 | zval *zdata_len2; 1089 | 1090 | /* Hmm...should hash_hmac() reserve an $options array parameter for future use and/or consistency with hash()? */ 1091 | /* Should this be using zend_parse_parameters() or should this be using the macro expansion mechanism? */ 1092 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSs|blz", &algo, &zdata, &key, &key_len, &raw_output, &data_offset, &zdata_len2) == FAILURE) { 1093 | RETURN_THROWS(); 1094 | } 1095 | 1096 | if (ZEND_NUM_ARGS() >= 6 && Z_TYPE_P(zdata_len2) != IS_NULL) { 1097 | data_len2 = Z_LVAL_P(zdata_len2); 1098 | } else { 1099 | data_len2 = ZSTR_LEN(zdata); 1100 | } 1101 | 1102 | php_adjust_substr_offset_length(zdata, &data_offset, &data_len2); 1103 | 1104 | data = ZSTR_VAL(zdata) + data_offset; 1105 | data_len = (size_t)data_len2; 1106 | 1107 | php_hash_do_hash_hmac(return_value, algo, data, data_len, key, key_len, raw_output, 0); 1108 | } 1109 | /* }}} */ 1110 | 1111 | #else 1112 | 1113 | static inline void php_hash_string_xor_char(unsigned char *out, const unsigned char *in, const unsigned char xor_with, const size_t length) { 1114 | size_t i; 1115 | for (i=0; i < length; i++) { 1116 | out[i] = in[i] ^ xor_with; 1117 | } 1118 | } 1119 | 1120 | static inline void php_hash_string_xor(unsigned char *out, const unsigned char *in, const unsigned char *xor_with, const size_t length) { 1121 | size_t i; 1122 | for (i=0; i < length; i++) { 1123 | out[i] = in[i] ^ xor_with[i]; 1124 | } 1125 | } 1126 | 1127 | static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *ops, void *context, const unsigned char *key, const size_t key_len) { 1128 | memset(K, 0, ops->block_size); 1129 | if (key_len > ops->block_size) { 1130 | /* Reduce the key first */ 1131 | ops->hash_init(context); 1132 | ops->hash_update(context, key, key_len); 1133 | ops->hash_final(K, context); 1134 | } else { 1135 | memcpy(K, key, key_len); 1136 | } 1137 | /* XOR the key with 0x36 to get the ipad) */ 1138 | php_hash_string_xor_char(K, K, 0x36, ops->block_size); 1139 | } 1140 | 1141 | static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops *ops, void *context, const unsigned char *key, const unsigned char *data, const zend_long data_size) { 1142 | ops->hash_init(context); 1143 | ops->hash_update(context, key, ops->block_size); 1144 | ops->hash_update(context, data, data_size); 1145 | ops->hash_final(final, context); 1146 | } 1147 | 1148 | static void php_hash_do_hash_hmac_substr(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */ 1149 | { 1150 | zend_string *digest; 1151 | char *algo, *data, *key; 1152 | zend_string *zdata; 1153 | unsigned char *K; 1154 | size_t algo_len, data_len, key_len; 1155 | zend_bool raw_output = raw_output_default; 1156 | zend_long data_offset = 0, data_len2; 1157 | zval *zdata_len2; 1158 | const php_hash_ops *ops; 1159 | void *context; 1160 | php_stream *stream = NULL; 1161 | 1162 | if (isfilename) { 1163 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|b", &algo, &algo_len, &data, &data_len, 1164 | &key, &key_len, &raw_output) == FAILURE) { 1165 | return; 1166 | } 1167 | } else { 1168 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "sSs|blz", &algo, &algo_len, &zdata, &key, &key_len, &raw_output, &data_offset, &zdata_len2) == FAILURE) { 1169 | return; 1170 | } 1171 | 1172 | if (ZEND_NUM_ARGS() >= 6 && Z_TYPE_P(zdata_len2) != IS_NULL) { 1173 | data_len2 = Z_LVAL_P(zdata_len2); 1174 | } else { 1175 | data_len2 = ZSTR_LEN(zdata); 1176 | } 1177 | 1178 | php_adjust_substr_offset_length(zdata, &data_offset, &data_len2); 1179 | 1180 | data = ZSTR_VAL(zdata) + data_offset; 1181 | data_len = (size_t)data_len2; 1182 | } 1183 | 1184 | ops = php_hash_fetch_ops(algo, algo_len); 1185 | if (!ops) { 1186 | php_error_docref(NULL, E_WARNING, "Unknown hashing algorithm: %s", algo); 1187 | RETURN_FALSE; 1188 | } 1189 | else if (!ops->is_crypto) { 1190 | php_error_docref(NULL, E_WARNING, "Non-cryptographic hashing algorithm: %s", algo); 1191 | RETURN_FALSE; 1192 | } 1193 | 1194 | if (isfilename) { 1195 | if (CHECK_NULL_PATH(data, data_len)) { 1196 | php_error_docref(NULL, E_WARNING, "Invalid path"); 1197 | RETURN_FALSE; 1198 | } 1199 | stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context)); 1200 | if (!stream) { 1201 | /* Stream will report errors opening file */ 1202 | RETURN_FALSE; 1203 | } 1204 | } 1205 | 1206 | context = emalloc(ops->context_size); 1207 | 1208 | K = emalloc(ops->block_size); 1209 | digest = zend_string_alloc(ops->digest_size, 0); 1210 | 1211 | php_hash_hmac_prep_key(K, ops, context, (unsigned char *) key, key_len); 1212 | 1213 | if (isfilename) { 1214 | char buf[1024]; 1215 | ssize_t n; 1216 | ops->hash_init(context); 1217 | ops->hash_update(context, K, ops->block_size); 1218 | while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) { 1219 | ops->hash_update(context, (unsigned char *) buf, n); 1220 | } 1221 | php_stream_close(stream); 1222 | if (n < 0) { 1223 | efree(context); 1224 | efree(K); 1225 | zend_string_release(digest); 1226 | RETURN_FALSE; 1227 | } 1228 | 1229 | ops->hash_final((unsigned char *) ZSTR_VAL(digest), context); 1230 | } else { 1231 | php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) data, data_len); 1232 | } 1233 | 1234 | php_hash_string_xor_char(K, K, 0x6A, ops->block_size); 1235 | 1236 | php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) ZSTR_VAL(digest), ops->digest_size); 1237 | 1238 | /* Zero the key */ 1239 | ZEND_SECURE_ZERO(K, ops->block_size); 1240 | efree(K); 1241 | efree(context); 1242 | 1243 | if (raw_output) { 1244 | ZSTR_VAL(digest)[ops->digest_size] = 0; 1245 | RETURN_NEW_STR(digest); 1246 | } else { 1247 | zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); 1248 | 1249 | php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); 1250 | ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; 1251 | zend_string_release_ex(digest, 0); 1252 | RETURN_NEW_STR(hex_digest); 1253 | } 1254 | } 1255 | /* }}} */ 1256 | 1257 | /* {{{ proto string hash_hmac_substr(string algo, string data, string key[, bool raw_output = false, ?int data_offset = 0, ?int data_length = null]) 1258 | Generate a hash of a given input string with a key using HMAC 1259 | Returns lowercase hexits by default */ 1260 | PHP_FUNCTION(hash_hmac_substr) 1261 | { 1262 | php_hash_do_hash_hmac_substr(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); 1263 | } 1264 | /* }}} */ 1265 | 1266 | #endif 1267 | 1268 | /* End of code lifted directly from ext/hash/hash.c and modified slightly to support substrings. */ 1269 | 1270 | 1271 | #if defined(HAVE_LIBGD) || defined(HAVE_GD_BUNDLED) 1272 | 1273 | #include "ext/gd/php_gd.h" 1274 | #ifdef HAVE_GD_BUNDLED 1275 | # include "ext/gd/libgd/gd.h" 1276 | # include "ext/gd/libgd/gd_errors.h" 1277 | #else 1278 | # include 1279 | # include 1280 | #endif 1281 | 1282 | /* 1283 | // If you want to test the gd functions, modify gd.c: 1284 | 1285 | zend_class_entry *phpi_gd_get_gd_image_ce(void) 1286 | { 1287 | return gd_image_ce; 1288 | } 1289 | 1290 | 1291 | // And add to php_gd.h: 1292 | 1293 | PHP_GD_API zend_class_entry *phpi_gd_get_gd_image_ce(void); 1294 | */ 1295 | 1296 | /* {{{ proto array imageexportpixels(resource im, int x, int y, int width, int height) 1297 | Export the colors/color indexes of a range of pixels as an array. */ 1298 | PHP_FUNCTION(imageexportpixels) 1299 | { 1300 | zval *IM, tmparr, tmpval; 1301 | zend_long x, x2, x3, y, y2, y3, width, height; 1302 | gdImagePtr im; 1303 | 1304 | ZEND_PARSE_PARAMETERS_START(5, 5) 1305 | #if PHP_MAJOR_VERSION >= 8 1306 | /* Z_PARAM_OBJECT_OF_CLASS(IM, gd_image_ce) */ 1307 | Z_PARAM_OBJECT_OF_CLASS(IM, phpi_gd_get_gd_image_ce()) 1308 | #else 1309 | Z_PARAM_RESOURCE(IM) 1310 | #endif 1311 | Z_PARAM_LONG(x) 1312 | Z_PARAM_LONG(y) 1313 | Z_PARAM_LONG(width) 1314 | Z_PARAM_LONG(height) 1315 | ZEND_PARSE_PARAMETERS_END(); 1316 | 1317 | #if PHP_MAJOR_VERSION >= 8 1318 | im = php_gd_libgdimageptr_from_zval_p(IM); 1319 | #else 1320 | if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", phpi_get_le_gd())) == NULL) { 1321 | RETURN_FALSE; 1322 | } 1323 | #endif 1324 | 1325 | if (width < 1) { 1326 | php_error_docref(NULL, E_NOTICE, "Expected width to be a positive integer"); 1327 | RETURN_FALSE; 1328 | } 1329 | 1330 | if (height < 1) { 1331 | php_error_docref(NULL, E_NOTICE, "Expected height to be a positive integer"); 1332 | RETURN_FALSE; 1333 | } 1334 | 1335 | x3 = x + width; 1336 | y3 = y + height; 1337 | 1338 | ZVAL_LONG(&tmpval, 0); 1339 | 1340 | if (gdImageTrueColor(im)) { 1341 | if (im->tpixels && gdImageBoundsSafe(im, x, y) && gdImageBoundsSafe(im, x3 - 1, y3 - 1)) { 1342 | array_init_size(return_value, height); 1343 | 1344 | for (y2 = y; y2 < y3; y2++) 1345 | { 1346 | array_init_size(&tmparr, width); 1347 | 1348 | for (x2 = x; x2 < x3; x2++) 1349 | { 1350 | Z_LVAL(tmpval) = (zend_long)gdImageTrueColorPixel(im, x2, y2); 1351 | 1352 | zend_hash_next_index_insert_new(Z_ARRVAL(tmparr), &tmpval); 1353 | } 1354 | 1355 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmparr); 1356 | } 1357 | } else { 1358 | php_error_docref(NULL, E_NOTICE, "[" ZEND_LONG_FMT "," ZEND_LONG_FMT " ... " ZEND_LONG_FMT "," ZEND_LONG_FMT "] is out of bounds", x, y, x3 - 1, y3 - 1); 1359 | RETURN_FALSE; 1360 | } 1361 | } else { 1362 | if (im->pixels && gdImageBoundsSafe(im, x, y) && gdImageBoundsSafe(im, x3 - 1, y3 - 1)) { 1363 | array_init_size(return_value, height); 1364 | 1365 | for (y2 = y; y2 < y3; y2++) 1366 | { 1367 | array_init_size(&tmparr, width); 1368 | 1369 | for (x2 = x; x2 < x3; x2++) 1370 | { 1371 | Z_LVAL(tmpval) = (zend_long)im->pixels[y2][x2]; 1372 | 1373 | zend_hash_next_index_insert_new(Z_ARRVAL(tmparr), &tmpval); 1374 | } 1375 | 1376 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmparr); 1377 | } 1378 | } else { 1379 | php_error_docref(NULL, E_NOTICE, "[" ZEND_LONG_FMT "," ZEND_LONG_FMT " ... " ZEND_LONG_FMT "," ZEND_LONG_FMT "] is out of bounds", x, y, x3 - 1, y3 - 1); 1380 | RETURN_FALSE; 1381 | } 1382 | } 1383 | } 1384 | /* }}} */ 1385 | 1386 | /* {{{ proto bool imageimportpixels(resource im, int x, int y, array colors) 1387 | Sets pixels to the specified colors in the 2D array. */ 1388 | PHP_FUNCTION(imageimportpixels) 1389 | { 1390 | zval *IM, *tmparr, *tmpval; 1391 | zend_long x, x2, y; 1392 | HashTable *colors; 1393 | gdImagePtr im; 1394 | 1395 | ZEND_PARSE_PARAMETERS_START(4, 4) 1396 | #if PHP_MAJOR_VERSION >= 8 1397 | /* Z_PARAM_OBJECT_OF_CLASS(IM, gd_image_ce) */ 1398 | Z_PARAM_OBJECT_OF_CLASS(IM, phpi_gd_get_gd_image_ce()) 1399 | #else 1400 | Z_PARAM_RESOURCE(IM) 1401 | #endif 1402 | Z_PARAM_LONG(x) 1403 | Z_PARAM_LONG(y) 1404 | Z_PARAM_ARRAY_HT(colors) 1405 | ZEND_PARSE_PARAMETERS_END(); 1406 | 1407 | #if PHP_MAJOR_VERSION >= 8 1408 | im = php_gd_libgdimageptr_from_zval_p(IM); 1409 | #else 1410 | if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", phpi_get_le_gd())) == NULL) { 1411 | RETURN_FALSE; 1412 | } 1413 | #endif 1414 | 1415 | ZEND_HASH_FOREACH_VAL_IND(colors, tmparr) { 1416 | if (EXPECTED(Z_TYPE_P(tmparr) == IS_ARRAY)) { 1417 | x2 = x; 1418 | 1419 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(tmparr), tmpval) { 1420 | gdImageSetPixel(im, x2, y, zval_get_long(tmpval)); 1421 | 1422 | x2++; 1423 | } ZEND_HASH_FOREACH_END(); 1424 | } 1425 | 1426 | y++; 1427 | } ZEND_HASH_FOREACH_END(); 1428 | 1429 | RETURN_TRUE; 1430 | } 1431 | /* }}} */ 1432 | 1433 | #endif 1434 | 1435 | 1436 | /* Useful functions and macros that should exist in zend_hash.h or zend_types.h for iterating over a hash like ZEND_HASH_FOREACH()/ZEND_HASH_REVERSE_FOREACH(). */ 1437 | #define ZEND_HASH_NUM_USED(__ht) \ 1438 | (__ht)->nNumUsed 1439 | 1440 | #define ZEND_HASH_BUCKET_VAL(__bucket) \ 1441 | &((__bucket)->val) 1442 | 1443 | /* PHP 8.2 and later muddy the waters with packed zvals vs. Buckets. */ 1444 | #if PHP_MAJOR_VERSION > 8 || PHP_MINOR_VERSION >= 2 1445 | 1446 | #define ZEND_HASH_FIRST_ELEMENT(__ht) \ 1447 | ((void *)(__ht)->arPacked) 1448 | 1449 | #define ZEND_HASH_LAST_ELEMENT(__ht) \ 1450 | ((void *)ZEND_HASH_ELEMENT((__ht), ZEND_HASH_NUM_USED(__ht) - 1)) 1451 | 1452 | #define ZEND_HASH_END_ELEMENT(__ht) \ 1453 | ((void *)ZEND_HASH_ELEMENT((__ht), ZEND_HASH_NUM_USED(__ht))) 1454 | 1455 | #else 1456 | 1457 | #define ZEND_HASH_ELEMENT_SIZE(__ht) \ 1458 | sizeof(Bucket) 1459 | 1460 | #define ZEND_HASH_ELEMENT_EX(__ht, _idx, _size) \ 1461 | ((zval*)(((char*)(__ht)->arPacked) + ((_idx) * (_size)))) 1462 | 1463 | #define ZEND_HASH_ELEMENT(__ht, _idx) \ 1464 | ZEND_HASH_ELEMENT_EX(__ht, _idx, ZEND_HASH_ELEMENT_SIZE(__ht)) 1465 | 1466 | #define ZEND_HASH_NEXT_ELEMENT(_el, _size) \ 1467 | ((void *)(((char *)(_el)) + (_size))) 1468 | 1469 | #define ZEND_HASH_PREV_ELEMENT(_el, _size) \ 1470 | ((void *)(((char *)(_el)) - (_size))) 1471 | 1472 | #define ZEND_HASH_FIRST_ELEMENT(__ht) \ 1473 | (__ht)->arData 1474 | 1475 | #define ZEND_HASH_LAST_ELEMENT(__ht) \ 1476 | ((__ht)->arData + (__ht)->nNumUsed - 1) 1477 | 1478 | #define ZEND_HASH_END_ELEMENT(__ht) \ 1479 | ((__ht)->arData + (__ht)->nNumUsed) 1480 | 1481 | #endif 1482 | 1483 | static zend_always_inline zval *zend_hash_get_next_zval(HashTable *ht, void **iterator, zend_bool indirect) 1484 | { 1485 | size_t elemsize = ZEND_HASH_ELEMENT_SIZE(ht); 1486 | zval *zv; 1487 | void *end; 1488 | 1489 | if (EXPECTED(*iterator)) { 1490 | *iterator = ZEND_HASH_NEXT_ELEMENT(*iterator, elemsize); 1491 | } else { 1492 | *iterator = ZEND_HASH_FIRST_ELEMENT(ht); 1493 | } 1494 | 1495 | end = ZEND_HASH_END_ELEMENT(ht); 1496 | 1497 | for (; *iterator < end; *iterator = ZEND_HASH_NEXT_ELEMENT(*iterator, elemsize)) { 1498 | if (HT_IS_PACKED(ht)) { 1499 | zv = (zval *)(*iterator); 1500 | } else { 1501 | zv = ZEND_HASH_BUCKET_VAL(((Bucket *)(*iterator))); 1502 | 1503 | if (indirect && Z_TYPE_P(zv) == IS_INDIRECT) { 1504 | zv = Z_INDIRECT_P(zv); 1505 | } 1506 | } 1507 | 1508 | if (EXPECTED(Z_TYPE_P(zv) != IS_UNDEF)) break; 1509 | } 1510 | 1511 | if (UNEXPECTED(*iterator >= end)) { 1512 | *iterator = NULL; 1513 | 1514 | zv = NULL; 1515 | } 1516 | 1517 | return zv; 1518 | } 1519 | 1520 | static zend_always_inline zval *zend_hash_get_prev_zval(HashTable *ht, void **iterator, zend_bool indirect) 1521 | { 1522 | size_t elemsize = ZEND_HASH_ELEMENT_SIZE(ht); 1523 | zval *zv; 1524 | Bucket *first; 1525 | 1526 | if (EXPECTED(*iterator)) { 1527 | *iterator = ZEND_HASH_PREV_ELEMENT(*iterator, elemsize); 1528 | } else { 1529 | *iterator = ZEND_HASH_LAST_ELEMENT(ht); 1530 | } 1531 | 1532 | first = ZEND_HASH_FIRST_ELEMENT(ht); 1533 | 1534 | for (; *iterator >= first; *iterator = ZEND_HASH_PREV_ELEMENT(*iterator, elemsize)) { 1535 | if (HT_IS_PACKED(ht)) { 1536 | zv = (zval *)(*iterator); 1537 | } else { 1538 | zv = ZEND_HASH_BUCKET_VAL(((Bucket *)(*iterator))); 1539 | 1540 | if (indirect && Z_TYPE_P(zv) == IS_INDIRECT) { 1541 | zv = Z_INDIRECT_P(zv); 1542 | } 1543 | } 1544 | 1545 | if (EXPECTED(Z_TYPE_P(zv) != IS_UNDEF)) break; 1546 | } 1547 | 1548 | if (UNEXPECTED(*iterator < first)) { 1549 | *iterator = NULL; 1550 | 1551 | zv = NULL; 1552 | } 1553 | 1554 | return zv; 1555 | } 1556 | 1557 | #define HT_NEXT_ELEMENT_EX(ht, iterator, indirect) \ 1558 | zend_hash_get_next_zval((ht), &(iterator), (indirect)) 1559 | 1560 | #define HT_NEXT_ELEMENT_VAL(ht, iterator) \ 1561 | HT_NEXT_ELEMENT_EX(ht, iterator, 0) 1562 | 1563 | #define HT_NEXT_ELEMENT_VAL_IND(ht, iterator) \ 1564 | HT_NEXT_ELEMENT_EX(ht, iterator, 1) 1565 | 1566 | #define HT_PREV_ELEMENT_EX(ht, iterator, indirect) \ 1567 | zend_hash_get_prev_zval((ht), &(iterator), (indirect)) 1568 | 1569 | #define HT_PREV_ELEMENT_VAL(ht, iterator) \ 1570 | HT_PREV_ELEMENT_EX(ht, iterator, 0) 1571 | 1572 | #define HT_PREV_ELEMENT_VAL_IND(ht, iterator) \ 1573 | HT_PREV_ELEMENT_EX(ht, iterator, 1) 1574 | 1575 | 1576 | /* {{{ proto array mat_add(array $a, array $b) 1577 | Adds the values of two 2D matrices. */ 1578 | PHP_FUNCTION(mat_add) 1579 | { 1580 | HashTable *a, *a2, *b, *b2; 1581 | void *arow = NULL, *acol, *brow = NULL, *bcol; 1582 | zval *aval, *bval, tmprow, tmplval, tmpdval; 1583 | size_t rows, cols, x; 1584 | 1585 | ZEND_PARSE_PARAMETERS_START(2, 2) 1586 | Z_PARAM_ARRAY_HT(a) 1587 | Z_PARAM_ARRAY_HT(b) 1588 | ZEND_PARSE_PARAMETERS_END(); 1589 | 1590 | rows = zend_array_count(a); 1591 | x = zend_array_count(b); 1592 | if (rows < x) rows = x; 1593 | 1594 | array_init_size(return_value, rows); 1595 | 1596 | ZVAL_LONG(&tmplval, 0); 1597 | ZVAL_DOUBLE(&tmpdval, 0.0); 1598 | 1599 | aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1600 | bval = HT_NEXT_ELEMENT_VAL_IND(b, brow); 1601 | while (arow || brow) { 1602 | acol = NULL; 1603 | bcol = NULL; 1604 | 1605 | if (arow && Z_TYPE_P(aval) == IS_ARRAY) { 1606 | cols = zend_array_count(Z_ARRVAL_P(aval)); 1607 | 1608 | a2 = Z_ARRVAL_P(aval); 1609 | aval = HT_NEXT_ELEMENT_VAL_IND(a2, acol); 1610 | } else { 1611 | cols = 0; 1612 | 1613 | a2 = NULL; 1614 | } 1615 | 1616 | if (brow && Z_TYPE_P(bval) == IS_ARRAY) { 1617 | x = zend_array_count(Z_ARRVAL_P(bval)); 1618 | if (cols < x) cols = x; 1619 | 1620 | b2 = Z_ARRVAL_P(bval); 1621 | bval = HT_NEXT_ELEMENT_VAL_IND(b2, bcol); 1622 | } else { 1623 | b2 = NULL; 1624 | } 1625 | 1626 | array_init_size(&tmprow, cols); 1627 | 1628 | while (acol || bcol) { 1629 | if ((acol && Z_TYPE_P(aval) == IS_DOUBLE) || (bcol && Z_TYPE_P(bval) == IS_DOUBLE)) { 1630 | if (acol) { 1631 | Z_DVAL(tmpdval) = zval_get_double(aval); 1632 | } else { 1633 | Z_DVAL(tmpdval) = 0.0; 1634 | } 1635 | 1636 | if (bcol) { 1637 | Z_DVAL(tmpdval) += zval_get_double(bval); 1638 | } 1639 | 1640 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmpdval); 1641 | } else { 1642 | if (acol) { 1643 | Z_LVAL(tmplval) = zval_get_long(aval); 1644 | } else { 1645 | Z_LVAL(tmplval) = 0; 1646 | } 1647 | 1648 | if (bcol) { 1649 | Z_LVAL(tmplval) += zval_get_long(bval); 1650 | } 1651 | 1652 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmplval); 1653 | } 1654 | 1655 | if (acol) aval = HT_NEXT_ELEMENT_VAL_IND(a2, acol); 1656 | if (bcol) bval = HT_NEXT_ELEMENT_VAL_IND(b2, bcol); 1657 | } 1658 | 1659 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmprow); 1660 | 1661 | if (arow) aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1662 | if (brow) bval = HT_NEXT_ELEMENT_VAL_IND(b, brow); 1663 | } 1664 | } 1665 | /* }}} */ 1666 | 1667 | 1668 | /* {{{ proto array mat_sub(array $a, array $b) 1669 | Subtracts the values of two 2D matrices. */ 1670 | PHP_FUNCTION(mat_sub) 1671 | { 1672 | HashTable *a, *a2, *b, *b2; 1673 | void *arow = NULL, *acol, *brow = NULL, *bcol; 1674 | zval *aval, *bval, tmprow, tmplval, tmpdval; 1675 | size_t rows, cols, x; 1676 | 1677 | ZEND_PARSE_PARAMETERS_START(2, 2) 1678 | Z_PARAM_ARRAY_HT(a) 1679 | Z_PARAM_ARRAY_HT(b) 1680 | ZEND_PARSE_PARAMETERS_END(); 1681 | 1682 | rows = zend_array_count(a); 1683 | x = zend_array_count(b); 1684 | if (rows < x) rows = x; 1685 | 1686 | array_init_size(return_value, rows); 1687 | 1688 | ZVAL_LONG(&tmplval, 0); 1689 | ZVAL_DOUBLE(&tmpdval, 0.0); 1690 | 1691 | aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1692 | bval = HT_NEXT_ELEMENT_VAL_IND(b, brow); 1693 | while (arow || brow) { 1694 | acol = NULL; 1695 | bcol = NULL; 1696 | 1697 | if (arow && Z_TYPE_P(aval) == IS_ARRAY) { 1698 | cols = zend_array_count(Z_ARRVAL_P(aval)); 1699 | 1700 | a2 = Z_ARRVAL_P(aval); 1701 | aval = HT_NEXT_ELEMENT_VAL_IND(a2, acol); 1702 | } else { 1703 | cols = 0; 1704 | 1705 | a2 = NULL; 1706 | } 1707 | 1708 | if (brow && Z_TYPE_P(bval) == IS_ARRAY) { 1709 | x = zend_array_count(Z_ARRVAL_P(bval)); 1710 | if (cols < x) cols = x; 1711 | 1712 | b2 = Z_ARRVAL_P(bval); 1713 | bval = HT_NEXT_ELEMENT_VAL_IND(b2, bcol); 1714 | } else { 1715 | b2 = NULL; 1716 | } 1717 | 1718 | array_init_size(&tmprow, cols); 1719 | 1720 | while (acol || bcol) { 1721 | if ((acol && Z_TYPE_P(aval) == IS_DOUBLE) || (bcol && Z_TYPE_P(bval) == IS_DOUBLE)) { 1722 | if (acol) { 1723 | Z_DVAL(tmpdval) = zval_get_double(aval); 1724 | } else { 1725 | Z_DVAL(tmpdval) = 0.0; 1726 | } 1727 | 1728 | if (bcol) { 1729 | Z_DVAL(tmpdval) -= zval_get_double(bval); 1730 | } 1731 | 1732 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmpdval); 1733 | } else { 1734 | if (acol) { 1735 | Z_LVAL(tmplval) = zval_get_long(aval); 1736 | } else { 1737 | Z_LVAL(tmplval) = 0; 1738 | } 1739 | 1740 | if (bcol) { 1741 | Z_LVAL(tmplval) -= zval_get_long(bval); 1742 | } 1743 | 1744 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmplval); 1745 | } 1746 | 1747 | if (acol) aval = HT_NEXT_ELEMENT_VAL_IND(a2, acol); 1748 | if (bcol) bval = HT_NEXT_ELEMENT_VAL_IND(b2, bcol); 1749 | } 1750 | 1751 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmprow); 1752 | 1753 | if (arow) aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1754 | if (brow) bval = HT_NEXT_ELEMENT_VAL_IND(b, brow); 1755 | } 1756 | } 1757 | /* }}} */ 1758 | 1759 | 1760 | /* {{{ proto array mat_mult(array $a, array|float|int $b, [int $row = null]) 1761 | Multiplies the values of two 2D matrices or the values of a 2D matrix or row of the matrix with a scalar value. */ 1762 | PHP_FUNCTION(mat_mult) 1763 | { 1764 | int numargs = ZEND_NUM_ARGS(); 1765 | HashTable *a, *b; 1766 | void *arow = NULL, *acol; 1767 | zval *zb, *zrownum, *aval, *aval2, *bval, *bval2, tmprow, tmplval, tmpdval, tmpval; 1768 | size_t Arows, Acols = 0, Bcols, i, j, k, x; 1769 | zend_bool usedoubles = 0; 1770 | double tmpda, *dB, *dBptr, *dC; 1771 | zend_long tmpzla, *zlB, *zlBptr, *zlC, rownum; 1772 | 1773 | ZEND_PARSE_PARAMETERS_START(2, 3) 1774 | Z_PARAM_ARRAY_HT(a) 1775 | Z_PARAM_ZVAL(zb) 1776 | Z_PARAM_OPTIONAL 1777 | Z_PARAM_ZVAL(zrownum) 1778 | ZEND_PARSE_PARAMETERS_END(); 1779 | 1780 | Arows = zend_array_count(a); 1781 | 1782 | array_init_size(return_value, Arows); 1783 | 1784 | if (Z_TYPE_P(zb) == IS_ARRAY) { 1785 | /* Determine the number of columns in A and whether or not to use doubles. */ 1786 | ZEND_HASH_FOREACH_VAL_IND(a, aval) { 1787 | if (EXPECTED(Z_TYPE_P(aval) == IS_ARRAY)) { 1788 | x = 0; 1789 | 1790 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(aval), aval2) { 1791 | if (Z_TYPE_P(aval2) != IS_LONG) usedoubles = 1; 1792 | 1793 | x++; 1794 | } ZEND_HASH_FOREACH_END(); 1795 | 1796 | if (Acols < x) Acols = x; 1797 | } 1798 | } ZEND_HASH_FOREACH_END(); 1799 | 1800 | b = Z_ARRVAL_P(zb); 1801 | 1802 | x = zend_array_count(b); 1803 | if (Acols < x) Acols = x; 1804 | 1805 | /* Determine the number of columns in B and whether or not to use doubles. */ 1806 | Bcols = 0; 1807 | ZEND_HASH_FOREACH_VAL_IND(b, bval) { 1808 | if (EXPECTED(Z_TYPE_P(bval) == IS_ARRAY)) { 1809 | x = 0; 1810 | 1811 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(bval), bval2) { 1812 | if (Z_TYPE_P(bval2) != IS_LONG) usedoubles = 1; 1813 | 1814 | x++; 1815 | } ZEND_HASH_FOREACH_END(); 1816 | 1817 | if (Bcols < x) Bcols = x; 1818 | } 1819 | } ZEND_HASH_FOREACH_END(); 1820 | 1821 | /* If a double value is in either A or B, then multiply/add using doubles. Otherwise, multiply using zend_long. */ 1822 | if (usedoubles) { 1823 | /* Allocate temporary buffers. */ 1824 | dB = (double *)emalloc(Acols * Bcols * sizeof(double)); 1825 | dC = (double *)emalloc(Bcols * sizeof(double)); 1826 | 1827 | /* Clone matrix B into dB. */ 1828 | dBptr = dB; 1829 | ZEND_HASH_FOREACH_VAL_IND(b, bval) { 1830 | x = 0; 1831 | 1832 | if (EXPECTED(Z_TYPE_P(bval) == IS_ARRAY)) { 1833 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(bval), bval2) { 1834 | *dBptr = zval_get_double(bval2); 1835 | 1836 | dBptr++; 1837 | 1838 | x++; 1839 | } ZEND_HASH_FOREACH_END(); 1840 | } 1841 | 1842 | for (; x < Bcols; x++) { 1843 | *dBptr = 0.0; 1844 | 1845 | dBptr++; 1846 | } 1847 | } ZEND_HASH_FOREACH_END(); 1848 | 1849 | /* Multiply two virtually normalized matrices (A and B) together. O(N^3). */ 1850 | aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1851 | ZVAL_DOUBLE(&tmpdval, 0.0); 1852 | for (i = 0; i < Arows; i++) { 1853 | dBptr = dB; 1854 | 1855 | acol = NULL; 1856 | if (arow && EXPECTED(Z_TYPE_P(aval) == IS_ARRAY)) { 1857 | aval2 = HT_NEXT_ELEMENT_VAL_IND(Z_ARRVAL_P(aval), acol); 1858 | } else { 1859 | aval2 = NULL; 1860 | } 1861 | 1862 | if (EXPECTED(acol)) { 1863 | tmpda = zval_get_double(aval2); 1864 | } else { 1865 | tmpda = 0.0; 1866 | } 1867 | 1868 | for (j = 0; j < Bcols; j++) { 1869 | dC[j] = tmpda * *dBptr; 1870 | 1871 | dBptr++; 1872 | } 1873 | 1874 | for (k = 1; k < Acols; k++) { 1875 | if (EXPECTED(acol)) { 1876 | aval2 = HT_NEXT_ELEMENT_VAL_IND(Z_ARRVAL_P(aval), acol); 1877 | } 1878 | 1879 | if (EXPECTED(acol)) { 1880 | tmpda = zval_get_double(aval2); 1881 | } else { 1882 | /* Skip adding 0.0's if current row is short. */ 1883 | break; 1884 | } 1885 | 1886 | for (j = 0; j < Bcols; j++) { 1887 | dC[j] += tmpda * *dBptr; 1888 | 1889 | dBptr++; 1890 | } 1891 | } 1892 | 1893 | /* Write next C row. */ 1894 | array_init_size(&tmprow, Bcols); 1895 | 1896 | for (j = 0; j < Bcols; j++) { 1897 | Z_DVAL(tmpdval) = dC[j]; 1898 | 1899 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmpdval); 1900 | } 1901 | 1902 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmprow); 1903 | 1904 | /* Bail out reasonably quickly if the global PHP runtime timeout has been exceeded. */ 1905 | #if PHP_MAJOR_VERSION >= 8 1906 | if (zend_atomic_bool_load_ex(&EG(timed_out))) { 1907 | #else 1908 | if (EG(timed_out)) { 1909 | #endif 1910 | arow = NULL; 1911 | } 1912 | 1913 | if (arow) aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1914 | } 1915 | 1916 | efree(dB); 1917 | efree(dC); 1918 | } else { 1919 | /* zend_long version. */ 1920 | 1921 | /* Allocate temporary buffers. */ 1922 | zlB = (zend_long *)emalloc(Acols * Bcols * sizeof(zend_long)); 1923 | zlC = (zend_long *)emalloc(Bcols * sizeof(zend_long)); 1924 | 1925 | /* Clone matrix B into zlB. */ 1926 | zlBptr = zlB; 1927 | ZEND_HASH_FOREACH_VAL_IND(b, bval) { 1928 | x = 0; 1929 | 1930 | if (EXPECTED(Z_TYPE_P(bval) == IS_ARRAY)) { 1931 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(bval), bval2) { 1932 | *zlBptr = zval_get_long(bval2); 1933 | 1934 | zlBptr++; 1935 | 1936 | x++; 1937 | } ZEND_HASH_FOREACH_END(); 1938 | } 1939 | 1940 | for (; x < Bcols; x++) { 1941 | *zlBptr = 0; 1942 | 1943 | zlBptr++; 1944 | } 1945 | } ZEND_HASH_FOREACH_END(); 1946 | 1947 | /* Multiply two virtually normalized matrices (A and B) together. O(N^3). */ 1948 | aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 1949 | ZVAL_LONG(&tmplval, 0); 1950 | for (i = 0; i < Arows; i++) { 1951 | zlBptr = zlB; 1952 | 1953 | acol = NULL; 1954 | if (arow && EXPECTED(Z_TYPE_P(aval) == IS_ARRAY)) { 1955 | aval2 = HT_NEXT_ELEMENT_VAL_IND(Z_ARRVAL_P(aval), acol); 1956 | } else { 1957 | aval2 = NULL; 1958 | } 1959 | 1960 | if (EXPECTED(acol)) { 1961 | tmpzla = zval_get_long(aval2); 1962 | } else { 1963 | tmpzla = 0; 1964 | } 1965 | 1966 | for (j = 0; j < Bcols; j++) { 1967 | zlC[j] = tmpzla * *zlBptr; 1968 | 1969 | zlBptr++; 1970 | } 1971 | 1972 | for (k = 1; k < Acols; k++) { 1973 | if (EXPECTED(acol)) { 1974 | aval2 = HT_NEXT_ELEMENT_VAL_IND(Z_ARRVAL_P(aval), acol); 1975 | } 1976 | 1977 | if (EXPECTED(acol)) { 1978 | tmpzla = zval_get_long(aval2); 1979 | } else { 1980 | /* Skip adding 0's if current row is short. */ 1981 | break; 1982 | } 1983 | 1984 | for (j = 0; j < Bcols; j++) { 1985 | zlC[j] += tmpzla * *zlBptr; 1986 | 1987 | zlBptr++; 1988 | } 1989 | } 1990 | 1991 | /* Write next C row. */ 1992 | array_init_size(&tmprow, Bcols); 1993 | 1994 | for (j = 0; j < Bcols; j++) { 1995 | Z_LVAL(tmplval) = zlC[j]; 1996 | 1997 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmplval); 1998 | } 1999 | 2000 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmprow); 2001 | 2002 | /* Bail out reasonably quickly if the global PHP runtime timeout has been exceeded. */ 2003 | #if PHP_MAJOR_VERSION >= 8 2004 | if (zend_atomic_bool_load_ex(&EG(timed_out))) { 2005 | #else 2006 | if (EG(timed_out)) { 2007 | #endif 2008 | arow = NULL; 2009 | } 2010 | 2011 | if (arow) aval = HT_NEXT_ELEMENT_VAL_IND(a, arow); 2012 | } 2013 | 2014 | efree(zlB); 2015 | efree(zlC); 2016 | } 2017 | } else { 2018 | /* Scalar multiply. */ 2019 | if (Z_TYPE_P(zb) != IS_DOUBLE && Z_TYPE_P(zb) != IS_LONG) { 2020 | convert_to_double(zb); 2021 | } 2022 | 2023 | if (Z_TYPE_P(zb) == IS_DOUBLE) { 2024 | ZVAL_DOUBLE(&tmpdval, 0.0); 2025 | 2026 | if (numargs >= 3 && Z_TYPE_P(zrownum) != IS_NULL) { 2027 | rownum = Z_LVAL_P(zrownum); 2028 | } else { 2029 | zrownum = NULL; 2030 | 2031 | rownum = -1; 2032 | } 2033 | 2034 | ZEND_HASH_FOREACH_VAL_IND(a, aval) { 2035 | if (zrownum && rownum) { 2036 | /* Copy rows except for the specified row. Keys are ignored. */ 2037 | ZVAL_COPY(&tmpval, aval); 2038 | 2039 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmpval); 2040 | } else if (Z_TYPE_P(aval) == IS_ARRAY) { 2041 | /* Scalar multiply the array values. */ 2042 | array_init_size(&tmprow, zend_array_count(Z_ARRVAL_P(aval))); 2043 | 2044 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(aval), aval2) { 2045 | Z_DVAL(tmpdval) = zval_get_double(aval2); 2046 | Z_DVAL(tmpdval) *= Z_DVAL_P(zb); 2047 | 2048 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmpdval); 2049 | } ZEND_HASH_FOREACH_END(); 2050 | 2051 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmprow); 2052 | } else { 2053 | /* Row is not an array but attempt to multiply anyway. */ 2054 | Z_DVAL(tmpdval) = zval_get_double(aval); 2055 | Z_DVAL(tmpdval) *= Z_DVAL_P(zb); 2056 | 2057 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmpdval); 2058 | } 2059 | 2060 | rownum--; 2061 | } ZEND_HASH_FOREACH_END(); 2062 | } else { 2063 | /* zend_long version. */ 2064 | ZVAL_LONG(&tmplval, 0); 2065 | 2066 | if (numargs >= 3 && Z_TYPE_P(zrownum) != IS_NULL) { 2067 | rownum = Z_LVAL_P(zrownum); 2068 | } else { 2069 | zrownum = NULL; 2070 | 2071 | rownum = -1; 2072 | } 2073 | 2074 | ZEND_HASH_FOREACH_VAL_IND(a, aval) { 2075 | if (zrownum && rownum) { 2076 | /* Copy rows except for the specified row. Keys are ignored. */ 2077 | ZVAL_COPY(&tmpval, aval); 2078 | 2079 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmpval); 2080 | } else if (Z_TYPE_P(aval) == IS_ARRAY) { 2081 | /* Scalar multiply the array values. */ 2082 | array_init_size(&tmprow, zend_array_count(Z_ARRVAL_P(aval))); 2083 | 2084 | ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(aval), aval2) { 2085 | Z_LVAL(tmplval) = zval_get_long(aval2); 2086 | Z_LVAL(tmplval) *= Z_LVAL_P(zb); 2087 | 2088 | zend_hash_next_index_insert_new(Z_ARRVAL(tmprow), &tmplval); 2089 | } ZEND_HASH_FOREACH_END(); 2090 | 2091 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmprow); 2092 | } else { 2093 | /* Row is not an array but attempt to multiply anyway. */ 2094 | Z_LVAL(tmplval) = zval_get_long(aval); 2095 | Z_LVAL(tmplval) *= Z_LVAL_P(zb); 2096 | 2097 | zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmplval); 2098 | } 2099 | 2100 | rownum--; 2101 | } ZEND_HASH_FOREACH_END(); 2102 | } 2103 | } 2104 | } 2105 | /* }}} */ 2106 | 2107 | /* {{{ proto bool is_valid_utf8(string $value, [ bool $standard = false, int $combinelimit = 16 ]) 2108 | Finds whether the given value is a valid UTF-8 string. */ 2109 | PHP_FUNCTION(is_valid_utf8) 2110 | { 2111 | zval *value; 2112 | unsigned char *strp; 2113 | size_t y; 2114 | uint32_t cp; 2115 | zend_bool standard = 0, first = 1; 2116 | zend_long numcombine = 0, combinelimit = 16; 2117 | 2118 | ZEND_PARSE_PARAMETERS_START(1, 3) 2119 | Z_PARAM_ZVAL(value) 2120 | Z_PARAM_OPTIONAL 2121 | Z_PARAM_BOOL(standard) 2122 | Z_PARAM_LONG(combinelimit) 2123 | ZEND_PARSE_PARAMETERS_END(); 2124 | 2125 | if (Z_TYPE_P(value) != IS_STRING) { 2126 | RETURN_FALSE; 2127 | } 2128 | 2129 | if (GC_FLAGS(Z_STR_P(value)) & IS_STR_VALID_UTF8) { 2130 | RETURN_TRUE; 2131 | } 2132 | 2133 | strp = (unsigned char *)Z_STRVAL_P(value); 2134 | y = Z_STRLEN_P(value); 2135 | 2136 | while (y) { 2137 | /* The official UTF-8 specification says 0x0000-0x007F are valid UTF-8. */ 2138 | /* However, given the nature of PHP being web-oriented, commonly displayed, non-control characters should not be allowed to be declared to be valid UTF-8 by default. */ 2139 | /* Too much is at stake to take on the risk. Also only maliciously malformed strings or binary data are likely to contain control characters in the first place. */ 2140 | if ((!standard && ((*strp >= 0x20 && *strp <= 0x7E) || *strp == 0x09 || *strp == 0x0A || *strp == 0x0D)) || (standard && *strp <= 0x7F)) { 2141 | strp++; 2142 | y--; 2143 | 2144 | numcombine = 0; 2145 | } else if (*strp < 0xC2 || *strp > 0xF4) { 2146 | RETURN_FALSE; 2147 | } else { 2148 | if (y < 2 || (strp[1] & 0xC0) != 0x80) { 2149 | RETURN_FALSE; 2150 | } 2151 | 2152 | if (((*strp) & 0xE0) == 0xC0) { 2153 | cp = ((((uint32_t)(*strp) & 0x1F) << 6) | ((uint32_t)strp[1] & 0x3F)); 2154 | 2155 | /* 0x*FFFE and 0x*FFFF are reserved. */ 2156 | if ((cp & 0xFFFE) == 0xFFFE) { 2157 | RETURN_FALSE; 2158 | } 2159 | 2160 | /* First code point can't be a combining code point. */ 2161 | if (cp >= 0x0300 && cp <= 0x036F) { 2162 | numcombine++; 2163 | 2164 | if (first || numcombine >= combinelimit) { 2165 | RETURN_FALSE; 2166 | } 2167 | } else { 2168 | numcombine = 0; 2169 | } 2170 | 2171 | strp += 2; 2172 | y -= 2; 2173 | } else { 2174 | if (y < 3 || (strp[2] & 0xC0) != 0x80) { 2175 | RETURN_FALSE; 2176 | } 2177 | 2178 | if (((*strp) & 0xF0) == 0xE0) { 2179 | cp = ((((uint32_t)(*strp) & 0x0F) << 12) | (((uint32_t)strp[1] & 0x3F) << 6) | ((uint32_t)strp[2] & 0x3F)); 2180 | 2181 | /* Handle overlong. */ 2182 | /* 0xD800-0xDFFF are for UTF-16 surrogate pairs. Invalid code points. */ 2183 | /* 0xFDD0-0xFDEF are non-characters. */ 2184 | /* 0x*FFFE and 0x*FFFF are reserved. */ 2185 | if (cp <= 0x07FF || (cp >= 0xD800 && cp <= 0xDFFF) || (cp >= 0xFDD0 && cp <= 0xFDEF) || (cp & 0xFFFE) == 0xFFFE) { 2186 | RETURN_FALSE; 2187 | } 2188 | 2189 | /* First code point can't be a combining code point. */ 2190 | if ((cp >= 0x1DC0 && cp <= 0x1DFF) || (cp >= 0x20D0 && cp <= 0x20FF) || (cp >= 0xFE20 && cp <= 0xFE2F)) { 2191 | numcombine++; 2192 | 2193 | if (first || numcombine >= combinelimit) { 2194 | RETURN_FALSE; 2195 | } 2196 | } else { 2197 | numcombine = 0; 2198 | } 2199 | 2200 | strp += 3; 2201 | y -= 3; 2202 | } else { 2203 | if (y < 4 || (strp[3] & 0xC0) != 0x80 || ((*strp) & 0xF8) != 0xF0) { 2204 | RETURN_FALSE; 2205 | } 2206 | 2207 | cp = ((((uint32_t)(*strp) & 0x07) << 18) | (((uint32_t)strp[1] & 0x3F) << 12) | (((uint32_t)strp[2] & 0x3F) << 6) | ((uint32_t)strp[3] & 0x3F)); 2208 | 2209 | /* Handle overlong and max value. */ 2210 | /* 0x*FFFE and 0x*FFFF are reserved. */ 2211 | if (cp <= 0xFFFF || cp > 0x10FFFF || (cp & 0xFFFE) == 0xFFFE) { 2212 | RETURN_FALSE; 2213 | } 2214 | 2215 | strp += 4; 2216 | y -= 4; 2217 | 2218 | numcombine = 0; 2219 | } 2220 | } 2221 | } 2222 | 2223 | first = 0; 2224 | } 2225 | 2226 | /* Cache the result using the awkward garbage collection flag that exists for PCRE. */ 2227 | if (!ZSTR_IS_INTERNED(Z_STR_P(value))) { 2228 | GC_ADD_FLAGS(Z_STR_P(value), IS_STR_VALID_UTF8); 2229 | } 2230 | 2231 | RETURN_TRUE; 2232 | } 2233 | /* }}} */ 2234 | 2235 | /* {{{ proto bool is_interned_string(mixed $value) 2236 | Finds whether the given variable is an interned (immutable) string. */ 2237 | PHP_FUNCTION(is_interned_string) 2238 | { 2239 | zval *value; 2240 | 2241 | ZEND_PARSE_PARAMETERS_START(1, 1) 2242 | Z_PARAM_ZVAL(value) 2243 | ZEND_PARSE_PARAMETERS_END(); 2244 | 2245 | if (Z_TYPE_P(value) == IS_STRING && ZSTR_IS_INTERNED(Z_STR_P(value))) { 2246 | RETURN_TRUE; 2247 | } 2248 | 2249 | RETURN_FALSE; 2250 | } 2251 | /* }}} */ 2252 | 2253 | /* {{{ proto int refcount(mixed &$value) 2254 | Returns the userland internal reference count of a zval. */ 2255 | PHP_FUNCTION(refcount) 2256 | { 2257 | zval *value; 2258 | zend_long num = 0; 2259 | 2260 | ZEND_PARSE_PARAMETERS_START(1, 1) 2261 | Z_PARAM_ZVAL(value) 2262 | ZEND_PARSE_PARAMETERS_END(); 2263 | 2264 | /* Value is passed in by reference via reflection. */ 2265 | if (Z_ISREF_P(value)) 2266 | { 2267 | /* Ignore both Zend engine's increment of the refcount and the temporary conversion to a reference. */ 2268 | num = GC_REFCOUNT(Z_REF_P(value)) - 2; 2269 | //php_printf("Ref: %ld\n", num); 2270 | 2271 | value = Z_REFVAL_P(value); 2272 | } 2273 | 2274 | /* Interned strings are a special case. See zend_string_refcount(). */ 2275 | if (Z_TYPE_P(value) == IS_STRING && ZSTR_IS_INTERNED(Z_STR_P(value))) { 2276 | RETURN_LONG(1); 2277 | } else if (Z_REFCOUNTED_P(value) || Z_TYPE_P(value) == IS_ARRAY) { 2278 | /* Other refcounted values. */ 2279 | //php_printf("Value: %ld\n", (zend_long)GC_REFCOUNT(Z_COUNTED_P(value))); 2280 | num += GC_REFCOUNT(Z_COUNTED_P(value)); 2281 | } else { 2282 | /* Non-refcounted values. */ 2283 | num++; 2284 | } 2285 | 2286 | RETURN_LONG(num); 2287 | } 2288 | /* }}} */ 2289 | 2290 | /* {{{ proto bool is_reference(mixed &$value) 2291 | Finds whether the type of a variable is a reference. */ 2292 | PHP_FUNCTION(is_reference) 2293 | { 2294 | zval *value; 2295 | 2296 | ZEND_PARSE_PARAMETERS_START(1, 1) 2297 | Z_PARAM_ZVAL(value) 2298 | ZEND_PARSE_PARAMETERS_END(); 2299 | 2300 | /* Value is passed in by reference via reflection. */ 2301 | if (Z_ISREF_P(value) && GC_REFCOUNT(Z_REF_P(value)) > 2) { 2302 | RETURN_TRUE; 2303 | } 2304 | 2305 | RETURN_FALSE; 2306 | } 2307 | /* }}} */ 2308 | 2309 | /* {{{ proto bool is_equal_zval(mixed &$value, mixed &$value2, [ bool $deref = true ]) 2310 | Compares two raw zvals for equality but does not compare data for equality. */ 2311 | PHP_FUNCTION(is_equal_zval) 2312 | { 2313 | zval *value, *value2; 2314 | zend_bool deref = 1; 2315 | 2316 | ZEND_PARSE_PARAMETERS_START(2, 3) 2317 | Z_PARAM_ZVAL(value) 2318 | Z_PARAM_ZVAL(value2) 2319 | Z_PARAM_OPTIONAL 2320 | Z_PARAM_BOOL(deref) 2321 | ZEND_PARSE_PARAMETERS_END(); 2322 | 2323 | /* Values are passed in by reference via reflection. */ 2324 | ZVAL_DEREF(value); 2325 | ZVAL_DEREF(value2); 2326 | 2327 | if (value == value2) { 2328 | RETURN_TRUE; 2329 | } 2330 | 2331 | /* Dereferences and compares the data pointer. */ 2332 | if (deref) { 2333 | if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && Z_TYPE_P(value2) != IS_LONG && Z_TYPE_P(value2) != IS_DOUBLE && Z_PTR_P(value) == Z_PTR_P(value2)) { 2334 | RETURN_TRUE; 2335 | } 2336 | } 2337 | 2338 | RETURN_FALSE; 2339 | } 2340 | /* }}} */ 2341 | 2342 | 2343 | /* {{{ PHP_RINIT_FUNCTION 2344 | */ 2345 | PHP_RINIT_FUNCTION(qolfuncs) 2346 | { 2347 | #if defined(ZTS) && defined(COMPILE_DL_QOLFUNCS) 2348 | ZEND_TSRMLS_CACHE_UPDATE(); 2349 | #endif 2350 | 2351 | return SUCCESS; 2352 | } 2353 | /* }}} */ 2354 | 2355 | /* {{{ PHP_MINFO_FUNCTION 2356 | */ 2357 | PHP_MINFO_FUNCTION(qolfuncs) 2358 | { 2359 | php_info_print_table_start(); 2360 | php_info_print_table_header(2, "qolfuncs (quality of life improvement functions) support", "enabled"); 2361 | php_info_print_table_end(); 2362 | } 2363 | /* }}} */ 2364 | 2365 | /* {{{ arginfo 2366 | */ 2367 | ZEND_BEGIN_ARG_INFO_EX(arginfo_str_splice, 0, 0, 2) 2368 | ZEND_ARG_TYPE_INFO(1, dst, IS_STRING, 0) 2369 | ZEND_ARG_INFO(0, dst_offset) 2370 | ZEND_ARG_TYPE_INFO(0, dst_length, IS_LONG, 1) 2371 | ZEND_ARG_INFO(0, src) 2372 | ZEND_ARG_INFO(0, src_offset) 2373 | ZEND_ARG_TYPE_INFO(0, src_length, IS_LONG, 1) 2374 | ZEND_ARG_INFO(0, src_repeat) 2375 | ZEND_ARG_INFO(0, shrink) 2376 | ZEND_ARG_TYPE_INFO(0, dst_lastsize, IS_LONG, 1) 2377 | ZEND_END_ARG_INFO() 2378 | 2379 | ZEND_BEGIN_ARG_INFO_EX(arginfo_str_realloc, 0, 0, 2) 2380 | ZEND_ARG_TYPE_INFO(1, str, IS_STRING, 0) 2381 | ZEND_ARG_INFO(0, size) 2382 | ZEND_ARG_INFO(0, fast) 2383 | ZEND_END_ARG_INFO() 2384 | 2385 | ZEND_BEGIN_ARG_INFO_EX(arginfo_explode_substr, 0, 0, 2) 2386 | ZEND_ARG_INFO(0, separator) 2387 | ZEND_ARG_INFO(0, str) 2388 | ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 1) 2389 | ZEND_ARG_INFO(0, str_offset) 2390 | ZEND_ARG_TYPE_INFO(0, str_length, IS_LONG, 1) 2391 | ZEND_END_ARG_INFO() 2392 | 2393 | ZEND_BEGIN_ARG_INFO_EX(arginfo_str_split_substr, 0, 0, 1) 2394 | ZEND_ARG_INFO(0, str) 2395 | ZEND_ARG_TYPE_INFO(0, split_length, IS_LONG, 1) 2396 | ZEND_ARG_INFO(0, str_offset) 2397 | ZEND_ARG_TYPE_INFO(0, str_length, IS_LONG, 1) 2398 | ZEND_END_ARG_INFO() 2399 | 2400 | ZEND_BEGIN_ARG_INFO_EX(arginfo_fread_mem, 0, 0, 2) 2401 | ZEND_ARG_INFO(0, fp) 2402 | ZEND_ARG_TYPE_INFO(1, str, IS_STRING, 0) 2403 | ZEND_ARG_INFO(0, str_offset) 2404 | ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 1) 2405 | ZEND_END_ARG_INFO() 2406 | 2407 | ZEND_BEGIN_ARG_INFO_EX(arginfo_fwrite_substr, 0, 0, 2) 2408 | ZEND_ARG_INFO(0, fp) 2409 | ZEND_ARG_INFO(0, str) 2410 | ZEND_ARG_INFO(0, str_offset) 2411 | ZEND_ARG_TYPE_INFO(0, str_length, IS_LONG, 1) 2412 | ZEND_END_ARG_INFO() 2413 | 2414 | ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_substr, 0, 0, 2) 2415 | ZEND_ARG_INFO(0, algo) 2416 | ZEND_ARG_INFO(0, data) 2417 | ZEND_ARG_INFO(0, raw_output) 2418 | #if PHP_MAJOR_VERSION >= 8 2419 | ZEND_ARG_INFO(0, options) 2420 | #endif 2421 | ZEND_ARG_INFO(0, data_offset) 2422 | ZEND_ARG_TYPE_INFO(0, data_length, IS_LONG, 1) 2423 | ZEND_END_ARG_INFO() 2424 | 2425 | ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hmac_substr, 0, 0, 3) 2426 | ZEND_ARG_INFO(0, algo) 2427 | ZEND_ARG_INFO(0, data) 2428 | ZEND_ARG_INFO(0, key) 2429 | ZEND_ARG_INFO(0, raw_output) 2430 | #if PHP_MAJOR_VERSION >= 8 2431 | /* Hmm...see notes in hash_hmac_substr(). There seems to be some inconsistency between the definitions of hash() and hash_hmac(). */ 2432 | /* ZEND_ARG_INFO(0, options) */ 2433 | #endif 2434 | ZEND_ARG_INFO(0, data_offset) 2435 | ZEND_ARG_TYPE_INFO(0, data_length, IS_LONG, 1) 2436 | ZEND_END_ARG_INFO() 2437 | 2438 | ZEND_BEGIN_ARG_INFO(arginfo_imageexportpixels, 0) 2439 | ZEND_ARG_INFO(0, im) 2440 | ZEND_ARG_INFO(0, x) 2441 | ZEND_ARG_INFO(0, y) 2442 | ZEND_ARG_INFO(0, width) 2443 | ZEND_ARG_INFO(0, height) 2444 | ZEND_END_ARG_INFO() 2445 | 2446 | ZEND_BEGIN_ARG_INFO(arginfo_imageimportpixels, 0) 2447 | ZEND_ARG_INFO(0, im) 2448 | ZEND_ARG_INFO(0, x) 2449 | ZEND_ARG_INFO(0, y) 2450 | ZEND_ARG_INFO(0, colors) 2451 | ZEND_END_ARG_INFO() 2452 | 2453 | ZEND_BEGIN_ARG_INFO(arginfo_mat_add, 0) 2454 | ZEND_ARG_INFO(0, a) 2455 | ZEND_ARG_INFO(0, b) 2456 | ZEND_END_ARG_INFO() 2457 | 2458 | ZEND_BEGIN_ARG_INFO(arginfo_mat_sub, 0) 2459 | ZEND_ARG_INFO(0, a) 2460 | ZEND_ARG_INFO(0, b) 2461 | ZEND_END_ARG_INFO() 2462 | 2463 | ZEND_BEGIN_ARG_INFO_EX(arginfo_mat_mult, 0, 0, 2) 2464 | ZEND_ARG_INFO(0, a) 2465 | ZEND_ARG_INFO(0, b) 2466 | ZEND_ARG_TYPE_INFO(0, row, IS_LONG, 1) 2467 | ZEND_END_ARG_INFO() 2468 | 2469 | ZEND_BEGIN_ARG_INFO_EX(arginfo_is_valid_utf8, 0, 0, 1) 2470 | ZEND_ARG_INFO(0, value) 2471 | ZEND_ARG_INFO(0, standard) 2472 | ZEND_ARG_INFO(0, combinelimit) 2473 | ZEND_END_ARG_INFO() 2474 | 2475 | ZEND_BEGIN_ARG_INFO(arginfo_is_interned_string, 0) 2476 | ZEND_ARG_INFO(0, value) 2477 | ZEND_END_ARG_INFO() 2478 | 2479 | ZEND_BEGIN_ARG_INFO(arginfo_refcount, 0) 2480 | ZEND_ARG_INFO(1, value) 2481 | ZEND_END_ARG_INFO() 2482 | 2483 | ZEND_BEGIN_ARG_INFO(arginfo_is_reference, 0) 2484 | ZEND_ARG_INFO(1, value) 2485 | ZEND_END_ARG_INFO() 2486 | 2487 | ZEND_BEGIN_ARG_INFO_EX(arginfo_is_equal_zval, 0, 0, 2) 2488 | ZEND_ARG_INFO(1, value) 2489 | ZEND_ARG_INFO(1, value2) 2490 | ZEND_ARG_INFO(0, deref) 2491 | ZEND_END_ARG_INFO() 2492 | /* }}} */ 2493 | 2494 | /* {{{ qolfuncs_functions[] 2495 | */ 2496 | static const zend_function_entry qolfuncs_functions[] = { 2497 | PHP_FE(str_splice, arginfo_str_splice) 2498 | PHP_FE(str_realloc, arginfo_str_realloc) 2499 | PHP_FE(explode_substr, arginfo_explode_substr) 2500 | PHP_FE(str_split_substr, arginfo_str_split_substr) 2501 | PHP_FE(fread_mem, arginfo_fread_mem) 2502 | PHP_FE(fwrite_substr, arginfo_fwrite_substr) 2503 | PHP_FE(hash_substr, arginfo_hash_substr) 2504 | PHP_FE(hash_hmac_substr, arginfo_hash_hmac_substr) 2505 | 2506 | #if defined(HAVE_LIBGD) || defined(HAVE_GD_BUNDLED) 2507 | PHP_FE(imageexportpixels, arginfo_imageexportpixels) 2508 | PHP_FE(imageimportpixels, arginfo_imageimportpixels) 2509 | #endif 2510 | 2511 | PHP_FE(mat_add, arginfo_mat_add) 2512 | PHP_FE(mat_sub, arginfo_mat_sub) 2513 | PHP_FE(mat_mult, arginfo_mat_mult) 2514 | PHP_FE(is_valid_utf8, arginfo_is_valid_utf8) 2515 | PHP_FE(is_interned_string, arginfo_is_interned_string) 2516 | PHP_FE(refcount, arginfo_refcount) 2517 | PHP_FE(is_reference, arginfo_is_reference) 2518 | PHP_FE(is_equal_zval, arginfo_is_equal_zval) 2519 | PHP_FE_END 2520 | }; 2521 | /* }}} */ 2522 | 2523 | /* {{{ qolfuncs_module_entry 2524 | */ 2525 | zend_module_entry qolfuncs_module_entry = { 2526 | STANDARD_MODULE_HEADER, 2527 | "qolfuncs", /* Extension name */ 2528 | qolfuncs_functions, /* zend_function_entry */ 2529 | NULL, /* PHP_MINIT - Module initialization */ 2530 | NULL, /* PHP_MSHUTDOWN - Module shutdown */ 2531 | PHP_RINIT(qolfuncs), /* PHP_RINIT - Request initialization */ 2532 | NULL, /* PHP_RSHUTDOWN - Request shutdown */ 2533 | PHP_MINFO(qolfuncs), /* PHP_MINFO - Module info */ 2534 | PHP_QOLFUNCS_VERSION, /* Version */ 2535 | STANDARD_MODULE_PROPERTIES 2536 | }; 2537 | /* }}} */ 2538 | 2539 | #ifdef COMPILE_DL_QOLFUNCS 2540 | # ifdef ZTS 2541 | ZEND_TSRMLS_CACHE_DEFINE() 2542 | # endif 2543 | ZEND_GET_MODULE(qolfuncs) 2544 | #endif 2545 | -------------------------------------------------------------------------------- /tests/001_ext_enabled.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check if qolfuncs is loaded 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECT-- 10 | The qolfuncs extension is available 11 | -------------------------------------------------------------------------------- /tests/002_str_splice.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | str_splice() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 189 | --EXPECT-- 190 | Interned string test: 191 | string(16) "Interned string." 192 | int(15) 193 | string(15) "Dynamic string." 194 | string(16) "Interned string." 195 | int(15) 196 | string(15) "Dynamic string." 197 | 198 | Referenced parameter test: 199 | int(16) 200 | string(16) "Modified string." 201 | string(16) "Modified string." 202 | 203 | Bounds test 1 (str_splice("0123456789", -12, -12, "ABCDE")): 204 | int(15) 205 | string(15) "ABCDE0123456789" 206 | 207 | Bounds test 2 (str_splice("0123456789", 12, 12, "ABCDE")): 208 | int(15) 209 | string(15) "0123456789ABCDE" 210 | 211 | Bounds test 3 (str_splice("0123456789", 5, 12, "ABCDE")): 212 | int(10) 213 | string(10) "01234ABCDE" 214 | 215 | Bounds test 4 (str_splice("0123456789", 5, 12, "ABCDE", -12, -12)): 216 | int(5) 217 | string(5) "01234" 218 | 219 | Bounds test 5 (str_splice("0123456789", 5, 12, "ABCDE", 12, 12)): 220 | int(5) 221 | string(5) "01234" 222 | 223 | Bounds test 6 (str_splice("0123456789", 5, 12, "ABCDE", 2, 12)): 224 | int(8) 225 | string(8) "01234CDE" 226 | 227 | Bounds test 7 (str_splice("0123456789", 5, 12, "ABCDE", 2, 12, 3)): 228 | int(14) 229 | string(14) "01234CDECDECDE" 230 | 231 | Bounds test 8 (str_splice("0123456789", -2, 12, "ABCDE", 0, 12, 1, false, 7)): 232 | int(10) 233 | string(10) "01234ABCDE" 234 | 235 | Bounds test 9 (str_splice("0123456789", -2, 12, "ABCDE", 0, 12, 1, false, 17)): 236 | int(13) 237 | string(13) "01234567ABCDE" 238 | 239 | Bounds test 10 (str_splice("0123456789", -3, 12, "ABCDE", 0, 12, 1, false, 7)): 240 | int(9) 241 | string(10) "0123ABCDE9" 242 | 243 | Same string test: 244 | int(15) 245 | string(15) "BCCCCBCCCCCCCCC" 246 | string(15) "AAAAABBBBBCCCCC" 247 | int(15) 248 | string(15) "AAAAAAAAABAAAAB" 249 | int(14) 250 | string(14) "AAAAAAABBCCCCC" 251 | int(15) 252 | string(15) "AAAAAAABBBCCCCC" 253 | int(15) 254 | string(15) "AAAAAAABBBCCCCC" 255 | int(15) 256 | string(15) "AAAAABBBCCCCCCC" 257 | 258 | Simple truncation test: 259 | int(10) 260 | string(10) "AAAAABBBBB" 261 | string(15) "AAAAABBBBBCCCCC" 262 | 263 | Simple insertion test: 264 | int(20) 265 | string(20) "AAAAABBBBBDDDDDCCCCC" 266 | string(15) "AAAAABBBBBCCCCC" 267 | 268 | Buffer control test: 269 | int(13) 270 | string(15) "AAAAADDDCCCCCCC" 271 | int(15) 272 | string(15) "AAAAAEEEEECCCCC" 273 | int(25) 274 | string(25) "AAAAAEEEEECCCCCEEEEECCCCC" 275 | string(15) "AAAAABBBBBCCCCC" 276 | 277 | Silent buffer control test: 278 | string(25) "AAAAAEEEEECCCCCEEEEECCCCC" 279 | string(15) "AAAAABBBBBCCCCC" 280 | 281 | Repeating bytes test: 282 | int(20) 283 | string(20) "AAAAABBBBBDDDDDCCCCC" 284 | string(15) "AAAAABBBBBCCCCC" 285 | int(40) 286 | string(40) "AAAAABBBBBDDDDDDDDDDDDDDDDDDDDDDDDDCCCCC" 287 | int(1500015) 288 | bool(true) 289 | -------------------------------------------------------------------------------- /tests/003_str_realloc.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | str_realloc() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 54 | --EXPECT-- 55 | Interned string test: 56 | string(16) "Interned string." 57 | int(16) 58 | string(8) "Interned" 59 | string(16) "Interned string." 60 | int(16) 61 | string(8) "Interned" 62 | 63 | Same size test: 64 | int(10) 65 | string(10) "AAAAABBBBB" 66 | 67 | Expansion test: 68 | int(10) 69 | bool(true) 70 | 71 | Shrink test: 72 | int(10) 73 | bool(true) 74 | 75 | Fast shrink test: 76 | int(10) 77 | bool(true) 78 | -------------------------------------------------------------------------------- /tests/004_explode_substr.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | explode_substr() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 13 | --EXPECT-- 14 | array(5) { 15 | [0]=> 16 | string(1) "1" 17 | [1]=> 18 | string(1) "2" 19 | [2]=> 20 | string(1) "3" 21 | [3]=> 22 | string(1) "4" 23 | [4]=> 24 | string(1) "5" 25 | } 26 | array(5) { 27 | [0]=> 28 | string(1) "1" 29 | [1]=> 30 | string(1) "2" 31 | [2]=> 32 | string(1) "3" 33 | [3]=> 34 | string(1) "4" 35 | [4]=> 36 | string(1) "5" 37 | } 38 | -------------------------------------------------------------------------------- /tests/005_str_split_substr.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | str_split_substr() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 13 | --EXPECT-- 14 | array(3) { 15 | [0]=> 16 | string(3) "123" 17 | [1]=> 18 | string(3) "456" 19 | [2]=> 20 | string(3) "789" 21 | } 22 | array(3) { 23 | [0]=> 24 | string(3) "123" 25 | [1]=> 26 | string(3) "456" 27 | [2]=> 28 | string(3) "789" 29 | } 30 | -------------------------------------------------------------------------------- /tests/006_fread_mem.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | fread_mem() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 24 | --EXPECT-- 25 | int(256) 26 | string(256) "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" 27 | int(256) 28 | bool(true) 29 | -------------------------------------------------------------------------------- /tests/007_fwrite_substr.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | fwrite_substr() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 28 | --EXPECT-- 29 | int(9) 30 | string(9) "1|2|3|4|5" 31 | int(9) 32 | string(9) "1|2|3|4|5" 33 | -------------------------------------------------------------------------------- /tests/008_hash_substr.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | hash_substr() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | = 8) 11 | { 12 | var_dump(hash_substr("sha256", "ABCDE123456789VWXYZ", false, array(), 5, -5) === hash("sha256", "123456789")); 13 | var_dump(hash_substr("sha256", "ABCDE123456789VWXYZ", false, array(), -14, -5) === hash("sha256", "123456789")); 14 | } 15 | else 16 | { 17 | var_dump(hash_substr("sha256", "ABCDE123456789VWXYZ", false, 5, -5) === hash("sha256", "123456789")); 18 | var_dump(hash_substr("sha256", "ABCDE123456789VWXYZ", false, -14, -5) === hash("sha256", "123456789")); 19 | } 20 | ?> 21 | --EXPECT-- 22 | bool(true) 23 | bool(true) 24 | -------------------------------------------------------------------------------- /tests/009_hash_hmac_substr.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | hash_hmac_substr() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 13 | --EXPECT-- 14 | bool(true) 15 | bool(true) 16 | -------------------------------------------------------------------------------- /tests/010_imagexportpixels.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | imageexportpixels() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 34 | --EXPECT-- 35 | bool(true) 36 | -------------------------------------------------------------------------------- /tests/011_imageimportpixels.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | imageimportpixels() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 33 | --EXPECT-- 34 | bool(true) 35 | bool(true) 36 | -------------------------------------------------------------------------------- /tests/012_mat_add.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mat_add() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 44 | --EXPECT-- 45 | array(3) { 46 | [0]=> 47 | array(4) { 48 | [0]=> 49 | int(2) 50 | [1]=> 51 | int(4) 52 | [2]=> 53 | int(4) 54 | [3]=> 55 | int(6) 56 | } 57 | [1]=> 58 | array(4) { 59 | [0]=> 60 | int(6) 61 | [1]=> 62 | int(8) 63 | [2]=> 64 | int(8) 65 | [3]=> 66 | int(10) 67 | } 68 | [2]=> 69 | array(4) { 70 | [0]=> 71 | int(10) 72 | [1]=> 73 | int(12) 74 | [2]=> 75 | int(12) 76 | [3]=> 77 | int(14) 78 | } 79 | } 80 | array(3) { 81 | [0]=> 82 | array(4) { 83 | [0]=> 84 | int(1) 85 | [1]=> 86 | int(2) 87 | [2]=> 88 | int(3) 89 | [3]=> 90 | int(4) 91 | } 92 | [1]=> 93 | array(4) { 94 | [0]=> 95 | int(5) 96 | [1]=> 97 | int(6) 98 | [2]=> 99 | int(7) 100 | [3]=> 101 | int(8) 102 | } 103 | [2]=> 104 | array(4) { 105 | [0]=> 106 | int(9) 107 | [1]=> 108 | int(10) 109 | [2]=> 110 | int(11) 111 | [3]=> 112 | int(12) 113 | } 114 | } 115 | array(3) { 116 | [0]=> 117 | array(4) { 118 | [0]=> 119 | int(2) 120 | [1]=> 121 | int(3) 122 | [2]=> 123 | int(4) 124 | [3]=> 125 | int(5) 126 | } 127 | [1]=> 128 | array(4) { 129 | [0]=> 130 | int(6) 131 | [1]=> 132 | int(7) 133 | [2]=> 134 | int(8) 135 | [3]=> 136 | int(8) 137 | } 138 | [2]=> 139 | array(4) { 140 | [0]=> 141 | int(9) 142 | [1]=> 143 | int(10) 144 | [2]=> 145 | int(11) 146 | [3]=> 147 | int(12) 148 | } 149 | } 150 | array(3) { 151 | [0]=> 152 | array(4) { 153 | [0]=> 154 | float(1.1) 155 | [1]=> 156 | float(2.1) 157 | [2]=> 158 | float(3.1) 159 | [3]=> 160 | float(4.1) 161 | } 162 | [1]=> 163 | array(4) { 164 | [0]=> 165 | float(5.1) 166 | [1]=> 167 | float(6.1) 168 | [2]=> 169 | float(7.1) 170 | [3]=> 171 | float(8.1) 172 | } 173 | [2]=> 174 | array(4) { 175 | [0]=> 176 | float(9.1) 177 | [1]=> 178 | float(10.1) 179 | [2]=> 180 | float(11.1) 181 | [3]=> 182 | float(12.1) 183 | } 184 | } 185 | array(3) { 186 | [0]=> 187 | array(4) { 188 | [0]=> 189 | int(1) 190 | [1]=> 191 | int(2) 192 | [2]=> 193 | int(3) 194 | [3]=> 195 | int(4) 196 | } 197 | [1]=> 198 | array(4) { 199 | [0]=> 200 | int(5) 201 | [1]=> 202 | int(6) 203 | [2]=> 204 | int(7) 205 | [3]=> 206 | int(8) 207 | } 208 | [2]=> 209 | array(4) { 210 | [0]=> 211 | int(9) 212 | [1]=> 213 | int(10) 214 | [2]=> 215 | int(11) 216 | [3]=> 217 | int(12) 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /tests/013_mat_sub.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mat_sub() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 44 | --EXPECT-- 45 | array(3) { 46 | [0]=> 47 | array(4) { 48 | [0]=> 49 | int(0) 50 | [1]=> 51 | int(0) 52 | [2]=> 53 | int(2) 54 | [3]=> 55 | int(2) 56 | } 57 | [1]=> 58 | array(4) { 59 | [0]=> 60 | int(4) 61 | [1]=> 62 | int(4) 63 | [2]=> 64 | int(6) 65 | [3]=> 66 | int(6) 67 | } 68 | [2]=> 69 | array(4) { 70 | [0]=> 71 | int(8) 72 | [1]=> 73 | int(8) 74 | [2]=> 75 | int(10) 76 | [3]=> 77 | int(10) 78 | } 79 | } 80 | array(3) { 81 | [0]=> 82 | array(4) { 83 | [0]=> 84 | int(1) 85 | [1]=> 86 | int(2) 87 | [2]=> 88 | int(3) 89 | [3]=> 90 | int(4) 91 | } 92 | [1]=> 93 | array(4) { 94 | [0]=> 95 | int(5) 96 | [1]=> 97 | int(6) 98 | [2]=> 99 | int(7) 100 | [3]=> 101 | int(8) 102 | } 103 | [2]=> 104 | array(4) { 105 | [0]=> 106 | int(9) 107 | [1]=> 108 | int(10) 109 | [2]=> 110 | int(11) 111 | [3]=> 112 | int(12) 113 | } 114 | } 115 | array(3) { 116 | [0]=> 117 | array(4) { 118 | [0]=> 119 | int(0) 120 | [1]=> 121 | int(1) 122 | [2]=> 123 | int(2) 124 | [3]=> 125 | int(3) 126 | } 127 | [1]=> 128 | array(4) { 129 | [0]=> 130 | int(4) 131 | [1]=> 132 | int(5) 133 | [2]=> 134 | int(6) 135 | [3]=> 136 | int(8) 137 | } 138 | [2]=> 139 | array(4) { 140 | [0]=> 141 | int(9) 142 | [1]=> 143 | int(10) 144 | [2]=> 145 | int(11) 146 | [3]=> 147 | int(12) 148 | } 149 | } 150 | array(3) { 151 | [0]=> 152 | array(4) { 153 | [0]=> 154 | float(0.9) 155 | [1]=> 156 | float(1.9) 157 | [2]=> 158 | float(2.9) 159 | [3]=> 160 | float(3.9) 161 | } 162 | [1]=> 163 | array(4) { 164 | [0]=> 165 | float(4.9) 166 | [1]=> 167 | float(5.9) 168 | [2]=> 169 | float(6.9) 170 | [3]=> 171 | float(7.9) 172 | } 173 | [2]=> 174 | array(4) { 175 | [0]=> 176 | float(8.9) 177 | [1]=> 178 | float(9.9) 179 | [2]=> 180 | float(10.9) 181 | [3]=> 182 | float(11.9) 183 | } 184 | } 185 | array(3) { 186 | [0]=> 187 | array(4) { 188 | [0]=> 189 | int(1) 190 | [1]=> 191 | int(2) 192 | [2]=> 193 | int(3) 194 | [3]=> 195 | int(4) 196 | } 197 | [1]=> 198 | array(4) { 199 | [0]=> 200 | int(5) 201 | [1]=> 202 | int(6) 203 | [2]=> 204 | int(7) 205 | [3]=> 206 | int(8) 207 | } 208 | [2]=> 209 | array(4) { 210 | [0]=> 211 | int(9) 212 | [1]=> 213 | int(10) 214 | [2]=> 215 | int(11) 216 | [3]=> 217 | int(12) 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /tests/014_mat_mult.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mat_mult() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 199 | --EXPECT-- 200 | Matrix multiply (integer): 201 | bool(true) 202 | bool(true) 203 | bool(true) 204 | 205 | Matrix multiply (double): 206 | bool(true) 207 | bool(true) 208 | bool(true) 209 | 210 | Scalar multiply (integer): 211 | bool(true) 212 | bool(true) 213 | bool(true) 214 | bool(true) 215 | bool(true) 216 | bool(true) 217 | 218 | Scalar multiply (double): 219 | bool(true) 220 | bool(true) 221 | bool(true) 222 | bool(true) 223 | bool(true) 224 | bool(true) 225 | 226 | NxM multiply (integer): 227 | bool(true) 228 | bool(true) 229 | bool(true) 230 | 231 | NxM multiply (double): 232 | bool(true) 233 | bool(true) 234 | bool(true) 235 | -------------------------------------------------------------------------------- /tests/015_is_valid_utf8.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | is_valid_utf8() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 581 | --EXPECT-- 582 | Basic test: 583 | bool(true) 584 | bool(true) 585 | bool(true) 586 | 587 | ASCII range test: 588 | bool(false) 589 | bool(true) 590 | bool(true) 591 | bool(true) 592 | bool(false) 593 | bool(true) 594 | bool(true) 595 | bool(false) 596 | 597 | bool(true) 598 | bool(true) 599 | bool(true) 600 | bool(true) 601 | bool(true) 602 | bool(true) 603 | bool(true) 604 | bool(true) 605 | 606 | Invalid first byte test: 607 | bool(false) 608 | bool(false) 609 | bool(false) 610 | 611 | Two byte range test: 612 | bool(false) 613 | bool(true) 614 | bool(true) 615 | bool(false) 616 | 617 | bool(false) 618 | bool(true) 619 | bool(true) 620 | bool(false) 621 | 622 | bool(false) 623 | 624 | Three byte E0 range test: 625 | bool(false) 626 | bool(false) 627 | bool(false) 628 | bool(true) 629 | bool(true) 630 | bool(false) 631 | 632 | bool(false) 633 | bool(false) 634 | bool(false) 635 | bool(true) 636 | bool(true) 637 | bool(false) 638 | 639 | bool(false) 640 | bool(true) 641 | bool(true) 642 | bool(false) 643 | 644 | bool(false) 645 | bool(true) 646 | bool(true) 647 | bool(false) 648 | 649 | bool(false) 650 | bool(false) 651 | 652 | Three byte E1-EC range test: 653 | bool(false) 654 | bool(true) 655 | bool(true) 656 | bool(false) 657 | 658 | bool(false) 659 | bool(true) 660 | bool(true) 661 | bool(false) 662 | 663 | bool(false) 664 | bool(true) 665 | bool(true) 666 | bool(false) 667 | 668 | bool(false) 669 | bool(true) 670 | bool(true) 671 | bool(false) 672 | 673 | bool(false) 674 | bool(true) 675 | bool(true) 676 | bool(false) 677 | 678 | bool(false) 679 | bool(true) 680 | bool(true) 681 | bool(false) 682 | 683 | bool(false) 684 | bool(true) 685 | bool(true) 686 | bool(false) 687 | 688 | bool(false) 689 | bool(true) 690 | bool(true) 691 | bool(false) 692 | 693 | Three byte ED range test: 694 | bool(false) 695 | bool(true) 696 | bool(true) 697 | bool(false) 698 | bool(false) 699 | bool(false) 700 | 701 | bool(false) 702 | bool(true) 703 | bool(true) 704 | bool(false) 705 | bool(false) 706 | bool(false) 707 | 708 | bool(false) 709 | bool(true) 710 | bool(true) 711 | bool(false) 712 | 713 | bool(false) 714 | bool(true) 715 | bool(true) 716 | bool(false) 717 | 718 | Three byte EE-EF range test: 719 | bool(false) 720 | bool(true) 721 | bool(true) 722 | bool(false) 723 | 724 | bool(false) 725 | bool(true) 726 | bool(true) 727 | bool(false) 728 | 729 | bool(false) 730 | bool(true) 731 | bool(true) 732 | bool(false) 733 | 734 | bool(false) 735 | bool(true) 736 | bool(true) 737 | bool(false) 738 | 739 | bool(false) 740 | bool(true) 741 | bool(true) 742 | bool(false) 743 | 744 | bool(false) 745 | bool(true) 746 | bool(false) 747 | bool(false) 748 | 749 | bool(false) 750 | bool(true) 751 | bool(true) 752 | bool(false) 753 | 754 | bool(false) 755 | bool(true) 756 | bool(false) 757 | bool(false) 758 | 759 | bool(false) 760 | bool(false) 761 | 762 | Four byte F0 range test: 763 | bool(false) 764 | bool(false) 765 | bool(false) 766 | bool(true) 767 | bool(true) 768 | bool(false) 769 | 770 | bool(false) 771 | bool(false) 772 | bool(false) 773 | bool(true) 774 | bool(true) 775 | bool(false) 776 | 777 | bool(false) 778 | bool(false) 779 | bool(false) 780 | bool(true) 781 | bool(true) 782 | bool(false) 783 | 784 | bool(false) 785 | bool(false) 786 | bool(false) 787 | bool(true) 788 | bool(false) 789 | bool(false) 790 | 791 | bool(false) 792 | bool(true) 793 | bool(true) 794 | bool(false) 795 | 796 | bool(false) 797 | bool(true) 798 | bool(true) 799 | bool(false) 800 | 801 | bool(false) 802 | bool(true) 803 | bool(true) 804 | bool(false) 805 | 806 | bool(false) 807 | bool(true) 808 | bool(false) 809 | bool(false) 810 | 811 | bool(false) 812 | bool(true) 813 | bool(true) 814 | bool(false) 815 | 816 | bool(false) 817 | bool(true) 818 | bool(true) 819 | bool(false) 820 | 821 | bool(false) 822 | bool(true) 823 | bool(true) 824 | bool(false) 825 | 826 | bool(false) 827 | bool(true) 828 | bool(false) 829 | bool(false) 830 | 831 | bool(false) 832 | bool(false) 833 | bool(false) 834 | bool(false) 835 | bool(false) 836 | bool(false) 837 | bool(false) 838 | bool(false) 839 | bool(false) 840 | 841 | Four byte F1-F3 range test: 842 | bool(false) 843 | bool(true) 844 | bool(true) 845 | bool(false) 846 | 847 | bool(false) 848 | bool(true) 849 | bool(true) 850 | bool(false) 851 | 852 | bool(false) 853 | bool(true) 854 | bool(true) 855 | bool(false) 856 | 857 | bool(false) 858 | bool(true) 859 | bool(false) 860 | bool(false) 861 | 862 | bool(false) 863 | bool(true) 864 | bool(true) 865 | bool(false) 866 | 867 | bool(false) 868 | bool(true) 869 | bool(true) 870 | bool(false) 871 | 872 | bool(false) 873 | bool(true) 874 | bool(true) 875 | bool(false) 876 | 877 | bool(false) 878 | bool(true) 879 | bool(false) 880 | bool(false) 881 | 882 | bool(false) 883 | bool(true) 884 | bool(true) 885 | bool(false) 886 | 887 | bool(false) 888 | bool(true) 889 | bool(true) 890 | bool(false) 891 | 892 | bool(false) 893 | bool(true) 894 | bool(true) 895 | bool(false) 896 | 897 | bool(false) 898 | bool(true) 899 | bool(false) 900 | bool(false) 901 | 902 | bool(false) 903 | bool(true) 904 | bool(true) 905 | bool(false) 906 | 907 | bool(false) 908 | bool(true) 909 | bool(true) 910 | bool(false) 911 | 912 | bool(false) 913 | bool(true) 914 | bool(true) 915 | bool(false) 916 | 917 | bool(false) 918 | bool(true) 919 | bool(false) 920 | bool(false) 921 | 922 | bool(false) 923 | bool(true) 924 | bool(true) 925 | bool(false) 926 | 927 | bool(false) 928 | bool(true) 929 | bool(true) 930 | bool(false) 931 | 932 | bool(false) 933 | bool(true) 934 | bool(true) 935 | bool(false) 936 | 937 | bool(false) 938 | bool(true) 939 | bool(false) 940 | bool(false) 941 | 942 | bool(false) 943 | bool(true) 944 | bool(true) 945 | bool(false) 946 | 947 | bool(false) 948 | bool(true) 949 | bool(true) 950 | bool(false) 951 | 952 | bool(false) 953 | bool(true) 954 | bool(true) 955 | bool(false) 956 | 957 | bool(false) 958 | bool(true) 959 | bool(false) 960 | bool(false) 961 | 962 | bool(false) 963 | bool(false) 964 | bool(false) 965 | bool(false) 966 | bool(false) 967 | bool(false) 968 | bool(false) 969 | bool(false) 970 | bool(false) 971 | bool(false) 972 | bool(false) 973 | bool(false) 974 | bool(false) 975 | bool(false) 976 | bool(false) 977 | bool(false) 978 | bool(false) 979 | bool(false) 980 | bool(false) 981 | bool(false) 982 | bool(false) 983 | bool(false) 984 | bool(false) 985 | bool(false) 986 | 987 | Four byte F4 range test: 988 | bool(false) 989 | bool(true) 990 | bool(true) 991 | bool(false) 992 | bool(false) 993 | bool(false) 994 | 995 | bool(false) 996 | bool(true) 997 | bool(true) 998 | bool(false) 999 | bool(false) 1000 | bool(false) 1001 | 1002 | bool(false) 1003 | bool(true) 1004 | bool(true) 1005 | bool(false) 1006 | bool(false) 1007 | bool(false) 1008 | 1009 | bool(false) 1010 | bool(true) 1011 | bool(false) 1012 | bool(false) 1013 | bool(false) 1014 | bool(false) 1015 | 1016 | bool(false) 1017 | bool(true) 1018 | bool(true) 1019 | bool(false) 1020 | 1021 | bool(false) 1022 | bool(true) 1023 | bool(true) 1024 | bool(false) 1025 | 1026 | bool(false) 1027 | bool(true) 1028 | bool(true) 1029 | bool(false) 1030 | 1031 | bool(false) 1032 | bool(true) 1033 | bool(false) 1034 | bool(false) 1035 | 1036 | bool(false) 1037 | bool(true) 1038 | bool(true) 1039 | bool(false) 1040 | 1041 | bool(false) 1042 | bool(true) 1043 | bool(true) 1044 | bool(false) 1045 | 1046 | bool(false) 1047 | bool(true) 1048 | bool(true) 1049 | bool(false) 1050 | 1051 | bool(false) 1052 | bool(true) 1053 | bool(false) 1054 | bool(false) 1055 | 1056 | bool(false) 1057 | bool(false) 1058 | 1059 | Surrogates 0xD800-0xDFFF test: 1060 | bool(false) 1061 | bool(false) 1062 | 1063 | Non-characters 0xFDD0-0xFDEF test: 1064 | bool(false) 1065 | bool(false) 1066 | 1067 | Combining code points test: 1068 | bool(false) 1069 | bool(false) 1070 | bool(false) 1071 | bool(false) 1072 | bool(false) 1073 | bool(false) 1074 | bool(false) 1075 | bool(false) 1076 | 1077 | bool(true) 1078 | bool(true) 1079 | bool(true) 1080 | bool(true) 1081 | bool(true) 1082 | bool(true) 1083 | bool(true) 1084 | bool(true) 1085 | 1086 | bool(true) 1087 | bool(true) 1088 | bool(true) 1089 | bool(true) 1090 | bool(true) 1091 | bool(true) 1092 | bool(true) 1093 | bool(true) 1094 | 1095 | bool(true) 1096 | bool(false) 1097 | bool(false) 1098 | -------------------------------------------------------------------------------- /tests/016_is_interned_string.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | is_interned_string() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 18 | --EXPECT-- 19 | bool(true) 20 | bool(true) 21 | bool(false) 22 | -------------------------------------------------------------------------------- /tests/017_refcount.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | refcount() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 88 | --EXPECT-- 89 | Interned string test: 90 | int(1) 91 | int(1) 92 | int(1) 93 | int(1) 94 | int(1) 95 | int(1) 96 | 97 | Dynamic string test: 98 | int(1) 99 | int(2) 100 | int(2) 101 | int(3) 102 | int(4) 103 | int(3) 104 | int(4) 105 | int(3) 106 | int(3) 107 | int(1) 108 | int(3) 109 | int(2) 110 | 111 | Non-refcounted type test: 112 | int(1) 113 | int(2) 114 | -------------------------------------------------------------------------------- /tests/018_is_reference.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | is_reference() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 59 | --EXPECT-- 60 | Interned string test: 61 | bool(false) 62 | bool(false) 63 | bool(false) 64 | bool(true) 65 | bool(true) 66 | bool(false) 67 | bool(true) 68 | bool(false) 69 | bool(true) 70 | bool(false) 71 | 72 | Dynamic string test: 73 | bool(false) 74 | bool(false) 75 | bool(false) 76 | bool(true) 77 | bool(true) 78 | bool(false) 79 | bool(true) 80 | bool(false) 81 | bool(true) 82 | bool(false) 83 | -------------------------------------------------------------------------------- /tests/019_is_equal_zval.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | is_equal_zval() test 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 24 | --EXPECT-- 25 | bool(true) 26 | bool(true) 27 | bool(true) 28 | bool(true) 29 | bool(true) 30 | bool(false) 31 | bool(false) 32 | bool(true) 33 | --------------------------------------------------------------------------------