parent
0c77bc53e4
commit
6309346c9a
28 changed files with 2049 additions and 16 deletions
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
$array = [1, 3, 4]; |
||||||
|
$array->push(5); |
||||||
|
|
||||||
|
$array->slice(1, 2)->find(3); |
||||||
|
|
||||||
|
$a = 100; |
||||||
|
$a->add(99)->mul(2)->sub(100); |
||||||
|
|
||||||
|
$s = "hello"; |
||||||
|
$s->upper(); |
||||||
|
|
||||||
@ -0,0 +1,5 @@ |
|||||||
|
<?php |
||||||
|
$fp = fopen("php://stdin", "r"); |
||||||
|
|
||||||
|
$stream = |
||||||
|
|
||||||
@ -0,0 +1,7 @@ |
|||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
function main() |
||||||
|
{ |
||||||
|
$a = 100; |
||||||
|
$a->unknownMethod(); |
||||||
|
} |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
function main() |
||||||
|
{ |
||||||
|
$a = 100; |
||||||
|
$a->add(); |
||||||
|
} |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
function main() |
||||||
|
{ |
||||||
|
(100)->add(99); |
||||||
|
} |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
<?php |
||||||
|
function main() |
||||||
|
{ |
||||||
|
$a = 100; |
||||||
|
$a->add(1); |
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
class UniversalMethodCallTest extends \BaseTest |
||||||
|
{ |
||||||
|
public function testUnknownMethodOnInt() |
||||||
|
{ |
||||||
|
$this->exec("Cannot call method `unknownMethod()` on variable of type php::Int", 'universal-method-int-undefined.php'); |
||||||
|
} |
||||||
|
|
||||||
|
public function testAddWithWrongArgCount() |
||||||
|
{ |
||||||
|
$this->exec('Method `add()` expects exactly 1 argument(s), 0 given', 'universal-method-int-wrong-args.php'); |
||||||
|
} |
||||||
|
|
||||||
|
public function testMutatingMethodOnNonVarExpr() |
||||||
|
{ |
||||||
|
$this->exec('Cannot call mutating method `push()` on a non-variable expression', 'universal-method-mutating-expr.php'); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,418 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace PhpAot\Php; |
||||||
|
|
||||||
|
use PhpParser\Node; |
||||||
|
use PhpParser\Node\Expr; |
||||||
|
use PhpParser\NodeAbstract; |
||||||
|
|
||||||
|
trait UniversalMethodCall |
||||||
|
{ |
||||||
|
protected const array UNIVERSAL_METHODS = [ |
||||||
|
self::TYPE_INT => [ |
||||||
|
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'mod' => ['handler' => 'calc_op', 'op' => '%', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'inc' => ['handler' => 'calc_inc', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'dec' => ['handler' => 'calc_dec', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => self::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
], |
||||||
|
self::TYPE_FLOAT => [ |
||||||
|
'add' => ['handler' => 'calc_op', 'op' => '+', 'return_type' => self::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'sub' => ['handler' => 'calc_op', 'op' => '-', 'return_type' => self::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'mul' => ['handler' => 'calc_op', 'op' => '*', 'return_type' => self::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'div' => ['handler' => 'calc_op', 'op' => '/', 'return_type' => self::TYPE_FLOAT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'inc' => ['handler' => 'calc_inc', 'return_type' => self::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'dec' => ['handler' => 'calc_dec', 'return_type' => self::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
], |
||||||
|
self::TYPE_BOOL => [ |
||||||
|
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
], |
||||||
|
self::TYPE_STR => [ |
||||||
|
// --- stdext string_methods (all use PHP standard functions) --- |
||||||
|
'length' => ['handler' => 'php_fn', 'fn' => 'strlen', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'isEmpty' => ['handler' => 'str_is_empty', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'lower' => ['handler' => 'php_fn', 'fn' => 'strtolower', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'upper' => ['handler' => 'php_fn', 'fn' => 'strtoupper', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'lowerFirst' => ['handler' => 'php_fn', 'fn' => 'lcfirst', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'upperFirst' => ['handler' => 'php_fn', 'fn' => 'ucfirst', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'upperWords' => ['handler' => 'php_fn', 'fn' => 'ucwords', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'addCSlashes' => ['handler' => 'php_fn', 'fn' => 'addcslashes', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'addSlashes' => ['handler' => 'php_fn', 'fn' => 'addslashes', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'chunkSplit' => ['handler' => 'php_fn', 'fn' => 'chunk_split', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'countChars' => ['handler' => 'php_fn', 'fn' => 'count_chars', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'htmlEntityDecode' => ['handler' => 'php_fn', 'fn' => 'html_entity_decode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'htmlEntityEncode' => ['handler' => 'php_fn', 'fn' => 'htmlentities', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 3], |
||||||
|
'htmlSpecialCharsEncode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 3], |
||||||
|
'htmlSpecialCharsDecode' => ['handler' => 'php_fn', 'fn' => 'htmlspecialchars_decode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'trim' => ['handler' => 'php_fn', 'fn' => 'trim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'lTrim' => ['handler' => 'php_fn', 'fn' => 'ltrim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'rTrim' => ['handler' => 'php_fn', 'fn' => 'rtrim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'parseStr' => ['handler' => 'php_fn', 'fn' => 'swoole_parse_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'parseUrl' => ['handler' => 'php_fn', 'fn' => 'parse_url', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'contains' => ['handler' => 'php_fn', 'fn' => 'str_contains', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'incr' => ['handler' => 'php_fn', 'fn' => 'str_increment', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'decr' => ['handler' => 'php_fn', 'fn' => 'str_decrement', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'pad' => ['handler' => 'php_fn', 'fn' => 'str_pad', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'repeat' => ['handler' => 'php_fn', 'fn' => 'str_repeat', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'replace' => ['handler' => 'php_fn', 'fn' => 'str_replace', 'receiver_pos' => 3, 'return_type' => self::TYPE_STR, 'min_args' => 2, 'max_args' => 3], |
||||||
|
'iReplace' => ['handler' => 'php_fn', 'fn' => 'str_ireplace', 'receiver_pos' => 3, 'return_type' => self::TYPE_STR, 'min_args' => 2, 'max_args' => 3], |
||||||
|
'shuffle' => ['handler' => 'php_fn', 'fn' => 'str_shuffle', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'split' => ['handler' => 'php_fn', 'fn' => 'explode', 'receiver_pos' => 2, 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'startsWith' => ['handler' => 'php_fn', 'fn' => 'str_starts_with', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'endsWith' => ['handler' => 'php_fn', 'fn' => 'str_ends_with', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'wordCount' => ['handler' => 'php_fn', 'fn' => 'str_word_count', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'iCompare' => ['handler' => 'php_fn', 'fn' => 'strcasecmp', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'compare' => ['handler' => 'php_fn', 'fn' => 'strcmp', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'find' => ['handler' => 'php_fn', 'fn' => 'strstr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'iFind' => ['handler' => 'php_fn', 'fn' => 'stristr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'stripTags' => ['handler' => 'php_fn', 'fn' => 'strip_tags', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'stripCSlashes' => ['handler' => 'php_fn', 'fn' => 'stripcslashes', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'stripSlashes' => ['handler' => 'php_fn', 'fn' => 'stripslashes', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'iIndexOf' => ['handler' => 'php_fn', 'fn' => 'stripos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'indexOf' => ['handler' => 'php_fn', 'fn' => 'strpos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'lastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strrpos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'iLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'strripos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'lastCharIndexOf' => ['handler' => 'php_fn', 'fn' => 'strrchr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'substr' => ['handler' => 'php_fn', 'fn' => 'substr', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'substrCompare' => ['handler' => 'php_fn', 'fn' => 'substr_compare', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 4], |
||||||
|
'substrCount' => ['handler' => 'php_fn', 'fn' => 'substr_count', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'substrReplace' => ['handler' => 'php_fn', 'fn' => 'substr_replace', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'reverse' => ['handler' => 'php_fn', 'fn' => 'strrev', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'md5' => ['handler' => 'php_fn', 'fn' => 'md5', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'sha1' => ['handler' => 'php_fn', 'fn' => 'sha1', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'crc32' => ['handler' => 'php_fn', 'fn' => 'crc32', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'hash' => ['handler' => 'php_fn', 'fn' => 'swoole_hash', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'hashCode' => ['handler' => 'php_fn', 'fn' => 'crc32', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'base64Decode' => ['handler' => 'php_fn', 'fn' => 'base64_decode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'base64Encode' => ['handler' => 'php_fn', 'fn' => 'base64_encode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'urlDecode' => ['handler' => 'php_fn', 'fn' => 'urldecode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'urlEncode' => ['handler' => 'php_fn', 'fn' => 'urlencode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'rawUrlEncode' => ['handler' => 'php_fn', 'fn' => 'rawurlencode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'rawUrlDecode' => ['handler' => 'php_fn', 'fn' => 'rawurldecode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'match' => ['handler' => 'php_fn', 'fn' => 'swoole_str_match', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'matchAll' => ['handler' => 'php_fn', 'fn' => 'swoole_str_match_all', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'isNumeric' => ['handler' => 'php_fn', 'fn' => 'is_numeric', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
// mbstring |
||||||
|
'mbUpperFirst' => ['handler' => 'php_fn', 'fn' => 'mb_ucfirst', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbLowerFirst' => ['handler' => 'php_fn', 'fn' => 'mb_lcfirst', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbTrim' => ['handler' => 'php_fn', 'fn' => 'mb_trim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbSubstrCount' => ['handler' => 'php_fn', 'fn' => 'mb_substr_count', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'mbSubstr' => ['handler' => 'php_fn', 'fn' => 'mb_substr', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbUpper' => ['handler' => 'php_fn', 'fn' => 'mb_strtoupper', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbLower' => ['handler' => 'php_fn', 'fn' => 'mb_strtolower', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbFind' => ['handler' => 'php_fn', 'fn' => 'mb_strstr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strpos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbLastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strrpos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbILastIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strripos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbLastCharIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_strrchr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbILastCharIndex' => ['handler' => 'php_fn', 'fn' => 'mb_strrichr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbLength' => ['handler' => 'php_fn', 'fn' => 'mb_strlen', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbIFind' => ['handler' => 'php_fn', 'fn' => 'mb_stristr', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbIIndexOf' => ['handler' => 'php_fn', 'fn' => 'mb_stripos', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbCut' => ['handler' => 'php_fn', 'fn' => 'mb_strcut', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'mbRTrim' => ['handler' => 'php_fn', 'fn' => 'mb_rtrim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbLTrim' => ['handler' => 'php_fn', 'fn' => 'mb_ltrim', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'mbDetectEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_detect_encoding', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'mbConvertEncoding' => ['handler' => 'php_fn', 'fn' => 'mb_convert_encoding', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'mbConvertCase' => ['handler' => 'php_fn', 'fn' => 'mb_convert_case', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
// serialize |
||||||
|
'unserialize' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'unmarshal' => ['handler' => 'php_fn', 'fn' => 'unserialize', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'jsonDecode' => ['handler' => 'php_fn', 'fn' => 'swoole_str_json_decode', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'jsonDecodeToObject' => ['handler' => 'php_fn', 'fn' => 'swoole_str_json_decode_to_object', 'return_type' => self::TYPE_OBJECT, 'min_args' => 0, 'max_args' => 2], |
||||||
|
// phpx C++ methods (no PHP function equivalent) |
||||||
|
'equals' => ['handler' => 'direct_method', 'method' => 'equals', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'append' => ['handler' => 'direct_method_mutate', 'method' => 'append', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
// conversions |
||||||
|
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => self::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toString' => ['handler' => 'identity', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
], |
||||||
|
self::TYPE_ARRAY => [ |
||||||
|
// --- stdext array_methods (all use PHP standard functions) --- |
||||||
|
'all' => ['handler' => 'php_fn', 'fn' => 'array_all', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'any' => ['handler' => 'php_fn', 'fn' => 'array_any', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'changeKeyCase' => ['handler' => 'php_fn', 'fn' => 'array_change_key_case', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'chunk' => ['handler' => 'php_fn', 'fn' => 'array_chunk', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'column' => ['handler' => 'php_fn', 'fn' => 'array_column', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'countValues' => ['handler' => 'php_fn', 'fn' => 'array_count_values', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'diff' => ['handler' => 'php_fn', 'fn' => 'array_diff', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'diffAssoc' => ['handler' => 'php_fn', 'fn' => 'array_diff_assoc', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'diffKey' => ['handler' => 'php_fn', 'fn' => 'array_diff_key', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'filter' => ['handler' => 'php_fn', 'fn' => 'array_filter', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'find' => ['handler' => 'php_fn', 'fn' => 'array_find', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'flip' => ['handler' => 'php_fn', 'fn' => 'array_flip', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'intersect' => ['handler' => 'php_fn', 'fn' => 'array_intersect', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'intersectAssoc' => ['handler' => 'php_fn', 'fn' => 'array_intersect_assoc', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'isList' => ['handler' => 'php_fn', 'fn' => 'array_is_list', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'keyExists' => ['handler' => 'php_fn', 'fn' => 'array_key_exists', 'receiver_pos' => 2, 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'keyFirst' => ['handler' => 'php_fn', 'fn' => 'array_key_first', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'keyLast' => ['handler' => 'php_fn', 'fn' => 'array_key_last', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'keys' => ['handler' => 'php_fn', 'fn' => 'array_keys', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 2], |
||||||
|
'map' => ['handler' => 'php_fn', 'fn' => 'array_map', 'receiver_pos' => 2, 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'pad' => ['handler' => 'php_fn', 'fn' => 'array_pad', 'return_type' => self::TYPE_ARRAY, 'min_args' => 2, 'max_args' => 2], |
||||||
|
'product' => ['handler' => 'php_fn', 'fn' => 'array_product', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'rand' => ['handler' => 'php_fn', 'fn' => 'array_rand', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'reduce' => ['handler' => 'php_fn', 'fn' => 'array_reduce', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'replace' => ['handler' => 'php_fn', 'fn' => 'array_replace', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'reverse' => ['handler' => 'php_fn', 'fn' => 'array_reverse', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'search' => ['handler' => 'php_fn', 'fn' => 'array_search', 'receiver_pos' => 2, 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'slice' => ['handler' => 'php_fn', 'fn' => 'array_slice', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'sum' => ['handler' => 'php_fn', 'fn' => 'array_sum', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'unique' => ['handler' => 'php_fn', 'fn' => 'array_unique', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'values' => ['handler' => 'php_fn', 'fn' => 'array_values', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'count' => ['handler' => 'php_fn', 'fn' => 'count', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'merge' => ['handler' => 'php_fn', 'fn' => 'array_merge', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'contains' => ['handler' => 'php_fn', 'fn' => 'in_array', 'receiver_pos' => 2, 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'join' => ['handler' => 'php_fn', 'fn' => 'implode', 'receiver_pos' => 2, 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'isTyped' => ['handler' => 'php_fn', 'fn' => 'swoole_array_is_typed', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'isEmpty' => ['handler' => 'direct_method', 'method' => 'empty', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
// mutating via PHP reference functions |
||||||
|
'sort' => ['handler' => 'php_fn_ref', 'fn' => 'sort', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'pop' => ['handler' => 'php_fn_ref', 'fn' => 'array_pop', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'push' => ['handler' => 'php_fn_ref', 'fn' => 'array_push', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'shift' => ['handler' => 'php_fn_ref', 'fn' => 'array_shift', 'return_type' => self::TYPE_VAR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'unshift' => ['handler' => 'php_fn_ref', 'fn' => 'array_unshift', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => -1], |
||||||
|
'splice' => ['handler' => 'php_fn_ref', 'fn' => 'array_splice', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 3], |
||||||
|
'walk' => ['handler' => 'php_fn_ref', 'fn' => 'array_walk', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'replaceStr' => ['handler' => 'php_fn', 'fn' => 'swoole_array_replace_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'iReplaceStr' => ['handler' => 'php_fn', 'fn' => 'swoole_array_ireplace_str', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 2], |
||||||
|
// serialize |
||||||
|
'serialize' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'marshal' => ['handler' => 'php_fn', 'fn' => 'serialize', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'jsonEncode' => ['handler' => 'php_fn', 'fn' => 'json_encode', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 2], |
||||||
|
// phpx C++ methods (no PHP function equivalent) |
||||||
|
'set' => ['handler' => 'direct_method_mutate', 'method' => 'set', 'return_type' => self::TYPE_ARRAY, 'min_args' => 2, 'max_args' => 2], |
||||||
|
'get' => ['handler' => 'direct_method', 'method' => 'get', 'return_type' => self::TYPE_VAR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'del' => ['handler' => 'direct_method_mutate', 'method' => 'del', 'return_type' => self::TYPE_ARRAY, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'clean' => ['handler' => 'direct_method_mutate', 'method' => 'clean', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], |
||||||
|
// conversions |
||||||
|
'toInt' => ['handler' => 'convert_fn', 'fn' => 'toInt', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toFloat' => ['handler' => 'convert_fn', 'fn' => 'toFloat', 'return_type' => self::TYPE_FLOAT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toBool' => ['handler' => 'convert_fn', 'fn' => 'toBool', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'toString' => ['handler' => 'convert_fn', 'fn' => 'toString', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
], |
||||||
|
self::TYPE_STREAM => [ |
||||||
|
// --- stdext stream_methods --- |
||||||
|
'write' => ['handler' => 'php_fn', 'fn' => 'fwrite', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'read' => ['handler' => 'php_fn', 'fn' => 'fread', 'return_type' => self::TYPE_STR, 'min_args' => 1, 'max_args' => 1], |
||||||
|
'close' => ['handler' => 'php_fn', 'fn' => 'fclose', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'dataSync' => ['handler' => 'php_fn', 'fn' => 'fdatasync', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'sync' => ['handler' => 'php_fn', 'fn' => 'fsync', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'truncate' => ['handler' => 'php_fn', 'fn' => 'ftruncate', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 1], |
||||||
|
'stat' => ['handler' => 'php_fn', 'fn' => 'fstat', 'return_type' => self::TYPE_ARRAY, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'seek' => ['handler' => 'php_fn', 'fn' => 'fseek', 'return_type' => self::TYPE_INT, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'tell' => ['handler' => 'php_fn', 'fn' => 'ftell', 'return_type' => self::TYPE_INT, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'lock' => ['handler' => 'php_fn', 'fn' => 'flock', 'return_type' => self::TYPE_BOOL, 'min_args' => 1, 'max_args' => 2], |
||||||
|
'eof' => ['handler' => 'php_fn', 'fn' => 'feof', 'return_type' => self::TYPE_BOOL, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'getChar' => ['handler' => 'php_fn', 'fn' => 'fgetc', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 0], |
||||||
|
'getLine' => ['handler' => 'php_fn', 'fn' => 'fgets', 'return_type' => self::TYPE_STR, 'min_args' => 0, 'max_args' => 1], |
||||||
|
], |
||||||
|
]; |
||||||
|
|
||||||
|
private const array MUTATING_HANDLERS = ['direct_method_mutate', 'php_fn_ref']; |
||||||
|
|
||||||
|
private const array TYPE_SEARCH_ORDER = [self::TYPE_STR, self::TYPE_ARRAY, self::TYPE_INT, self::TYPE_FLOAT, self::TYPE_BOOL, self::TYPE_STREAM]; |
||||||
|
|
||||||
|
protected function detectUniversalMethodReturnType(string $type, string $method): ?string |
||||||
|
{ |
||||||
|
if ($type === self::TYPE_VAR) { |
||||||
|
foreach (self::TYPE_SEARCH_ORDER as $searchType) { |
||||||
|
$def = static::UNIVERSAL_METHODS[$searchType][$method] ?? null; |
||||||
|
if ($def !== null) { |
||||||
|
return $def['return_type']; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
return static::UNIVERSAL_METHODS[$type][$method]['return_type'] ?? null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Search all type tables for a method. Returns [type, def] or null. |
||||||
|
*/ |
||||||
|
protected function findUniversalMethodAnyType(string $type, string $method): ?array |
||||||
|
{ |
||||||
|
if ($type === self::TYPE_VAR) { |
||||||
|
foreach (self::TYPE_SEARCH_ORDER as $searchType) { |
||||||
|
$def = static::UNIVERSAL_METHODS[$searchType][$method] ?? null; |
||||||
|
if ($def !== null) { |
||||||
|
return $def; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
return static::UNIVERSAL_METHODS[$type][$method] ?? null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $receiver C++ expression for the receiver |
||||||
|
* @param bool $isVar Whether the receiver is a variable (allows mutating methods) |
||||||
|
*/ |
||||||
|
protected function parseUniversalMethodCall(Node\Expr\MethodCall $expr, string $receiver, string $method, array $def, bool $isVar = true): ?string |
||||||
|
{ |
||||||
|
$this->validateUniversalMethodArgs($expr, $method, $def, $isVar); |
||||||
|
|
||||||
|
return match ($def['handler']) { |
||||||
|
'calc_op' => $this->genUniversalCalcOp($receiver, $def['op'], $expr->args), |
||||||
|
'calc_inc' => '(' . $receiver . ' + 1)', |
||||||
|
'calc_dec' => '(' . $receiver . ' - 1)', |
||||||
|
'direct_method' => $this->genUniversalDirectMethod($receiver, $def['method'], $expr->args), |
||||||
|
'direct_method_mutate' => $this->genUniversalMutatingMethod($receiver, $def['method'], $expr->args), |
||||||
|
'convert_fn' => $this->genUniversalConvertFn($receiver, $def['fn'], $def['return_type']), |
||||||
|
'str_is_empty' => '(' . $receiver . '.length() == 0)', |
||||||
|
'identity' => $receiver, |
||||||
|
'php_fn' => $this->genUniversalPhpFn($receiver, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0), |
||||||
|
'php_fn_ref' => $this->genUniversalPhpFnRef($receiver, $def['fn'], $expr->args, $def['return_type']), |
||||||
|
default => null, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generate a stream method call with null guard. |
||||||
|
* Stream resources are nullable — throws Error if the receiver is null. |
||||||
|
*/ |
||||||
|
protected function genStreamNullGuard(Node\Expr\MethodCall $expr, string $receiver, string $method, array $def): string |
||||||
|
{ |
||||||
|
$this->validateUniversalMethodArgs($expr, $method, $def, false); |
||||||
|
|
||||||
|
// Evaluate receiver into temp var to avoid double evaluation |
||||||
|
$streamVar = $this->addTmpVar(self::TYPE_VAR); |
||||||
|
$this->context->beforeStmtLines[] = $streamVar . ' = ' . $receiver . ';'; |
||||||
|
|
||||||
|
$methodCall = match ($def['handler']) { |
||||||
|
'php_fn' => $this->genUniversalPhpFn($streamVar, $def['fn'], $expr->args, $def['receiver_pos'] ?? 0), |
||||||
|
'direct_method' => $this->genUniversalDirectMethod($streamVar, $def['method'], $expr->args), |
||||||
|
default => null, |
||||||
|
}; |
||||||
|
|
||||||
|
$errorMsg = "Cannot call method '{$method}' on a closed or invalid stream resource"; |
||||||
|
$lambda = "[&]() -> php::Variant {\n"; |
||||||
|
$lambda .= "if (!{$streamVar}.isResource()) {\n"; |
||||||
|
$lambda .= "throwError(\"{$errorMsg}\");\n"; |
||||||
|
$lambda .= "return php::null;\n"; |
||||||
|
$lambda .= "}\n"; |
||||||
|
$lambda .= "return {$methodCall};\n"; |
||||||
|
$lambda .= "}()"; |
||||||
|
|
||||||
|
$tmpVar = $this->addTmpVar(self::TYPE_VAR); |
||||||
|
$this->context->beforeStmtLines[] = "{$tmpVar} = {$lambda};"; |
||||||
|
return $tmpVar; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Wrap a non-variable receiver expression in the appropriate type conversion |
||||||
|
* so that direct C++ method calls (e.g. .get(), .set()) work on the correct type. |
||||||
|
*/ |
||||||
|
private function wrapUniversalReceiver(string $type, string $expr): string |
||||||
|
{ |
||||||
|
$convFns = [ |
||||||
|
self::TYPE_ARRAY => 'toArray', |
||||||
|
self::TYPE_STR => 'toString', |
||||||
|
self::TYPE_INT => 'toInt', |
||||||
|
self::TYPE_FLOAT => 'toFloat', |
||||||
|
self::TYPE_BOOL => 'toBool', |
||||||
|
]; |
||||||
|
if (isset($convFns[$type])) { |
||||||
|
return 'php::' . $convFns[$type] . '(' . $expr . ')'; |
||||||
|
} |
||||||
|
return $expr; |
||||||
|
} |
||||||
|
|
||||||
|
private function validateUniversalMethodArgs(Node\Expr\MethodCall $expr, string $method, array $def, bool $isVar): void |
||||||
|
{ |
||||||
|
$argCount = count($expr->args); |
||||||
|
$maxArgs = $def['max_args']; |
||||||
|
if ($maxArgs === -1) { |
||||||
|
$maxArgs = PHP_INT_MAX; |
||||||
|
} |
||||||
|
if ($argCount < $def['min_args'] || $argCount > $maxArgs) { |
||||||
|
$expected = $def['min_args'] === $def['max_args'] ? "exactly {$def['min_args']}" : "{$def['min_args']} to {$def['max_args']}"; |
||||||
|
$this->fatalError($expr, "Method `{$method}()` expects {$expected} argument(s), {$argCount} given"); |
||||||
|
} |
||||||
|
|
||||||
|
if (!$isVar && in_array($def['handler'], self::MUTATING_HANDLERS, true)) { |
||||||
|
$this->fatalError($expr, 'Cannot call mutating method `' . $method . '()` on a non-variable expression'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function genUniversalCalcOp(string $object, string $op, array $args): string |
||||||
|
{ |
||||||
|
$argExpr = $this->parseExpr($args[0]->value); |
||||||
|
return '(' . $object . ' ' . $op . ' ' . $argExpr . ')'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function genUniversalDirectMethod(string $object, string $cppMethod, array $args): string |
||||||
|
{ |
||||||
|
if (empty($args)) { |
||||||
|
return $object . '.' . $cppMethod . '()'; |
||||||
|
} |
||||||
|
$argExprs = []; |
||||||
|
foreach ($args as $arg) { |
||||||
|
$argExprs[] = $this->parseExpr($arg->value); |
||||||
|
} |
||||||
|
return $object . '.' . $cppMethod . '(' . implode(', ', $argExprs) . ')'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function genUniversalMutatingMethod(string $object, string $cppMethod, array $args): string |
||||||
|
{ |
||||||
|
$call = $this->genUniversalDirectMethod($object, $cppMethod, $args); |
||||||
|
return '(' . $call . ', ' . $object . ')'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function genUniversalConvertFn(string $receiver, string $fn, string $type): string |
||||||
|
{ |
||||||
|
return 'php::' . $fn . '(' . $receiver . ')'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function genUniversalPhpFn(string $receiver, string $phpFunc, array $args, int $receiverPos = 0): string |
||||||
|
{ |
||||||
|
$argExprs = []; |
||||||
|
$userArgs = []; |
||||||
|
foreach ($args as $arg) { |
||||||
|
$userArgs[] = $this->parseExpr($arg->value); |
||||||
|
} |
||||||
|
|
||||||
|
if ($receiverPos === 0) { |
||||||
|
$argExprs = [$receiver]; |
||||||
|
foreach ($userArgs as $ua) { |
||||||
|
$argExprs[] = $ua; |
||||||
|
} |
||||||
|
} else { |
||||||
|
foreach ($userArgs as $i => $ua) { |
||||||
|
if ($i === $receiverPos - 1) { |
||||||
|
$argExprs[] = $receiver; |
||||||
|
} |
||||||
|
$argExprs[] = $ua; |
||||||
|
} |
||||||
|
if ($receiverPos > count($userArgs)) { |
||||||
|
$argExprs[] = $receiver; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 'php::call(' . $this->getFuncPtr($phpFunc) . ', php::ArgList{' . implode(', ', $argExprs) . '})'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function genUniversalPhpFnRef(string $receiver, string $phpFunc, array $args, string $returnType): string |
||||||
|
{ |
||||||
|
$tmpRef = $this->addTmpVar(self::TYPE_REF); |
||||||
|
$tmpVar = $this->addTmpVar(self::TYPE_VAR); |
||||||
|
$argExprs = ['&' . $tmpRef]; |
||||||
|
foreach ($args as $arg) { |
||||||
|
$argExprs[] = $this->parseExpr($arg->value); |
||||||
|
} |
||||||
|
$this->context->beforeStmtLines[] = $tmpRef . ' = ' . $receiver . '.toReference();'; |
||||||
|
$this->context->beforeStmtLines[] = $tmpVar . ' = php::call(' . $this->getFuncPtr($phpFunc) . ', php::ArgList{' . implode(', ', $argExprs) . '});'; |
||||||
|
return $tmpVar; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
--TEST-- |
||||||
|
array_method: 0 |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
$array = []; |
||||||
|
$array[0] = 1; |
||||||
|
$array[1] = 2; |
||||||
|
$array[2] = 3; |
||||||
|
$array[3] = 999; |
||||||
|
Assert::false($array->isEmpty()); |
||||||
|
Assert::eq($array->count(), count($array)); |
||||||
|
Assert::eq($array->slice(0, 2), [1, 2]); |
||||||
|
Assert::true($array->contains(999)); |
||||||
|
Assert::eq($array->search(999), 3); |
||||||
|
|
||||||
|
$array2 = []; |
||||||
|
Assert::true($array2->isEmpty()); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
--TEST-- |
||||||
|
array_method: 1 |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$array = ["lemon", "orange", "banana", "apple"]; |
||||||
|
$sorted_array = array("apple", "banana", "lemon", "orange",); |
||||||
|
$array->sort(SORT_NATURAL | SORT_FLAG_CASE); |
||||||
|
Assert::same($array, $sorted_array); |
||||||
|
echo "done\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
done |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
--TEST-- |
||||||
|
Universal method: array_method/2 (shift/unshift on Ref) |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
$array = array("orange", "banana", "apple", "raspberry"); |
||||||
|
|
||||||
|
$stack = $array; |
||||||
|
Assert::eq($stack->count(), 4); |
||||||
|
$fruit = $stack->shift(); |
||||||
|
Assert::eq($fruit, "orange"); |
||||||
|
Assert::eq($stack->count(), 3); |
||||||
|
Assert::eq($array->count(), 4); |
||||||
|
|
||||||
|
$stack->unshift("mango"); |
||||||
|
Assert::eq($stack->count(), 4); |
||||||
|
|
||||||
|
$stack2 = array("orange", "banana", "apple", "raspberry"); |
||||||
|
Assert::eq($stack2->count(), 4); |
||||||
|
$stack2->shift(); |
||||||
|
Assert::eq($stack2->count(), 3); |
||||||
|
|
||||||
|
echo "done\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
done |
||||||
@ -0,0 +1,300 @@ |
|||||||
|
--TEST-- |
||||||
|
Universal method call: Array type methods |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function _even_filter($v) { return $v % 2 == 0; } |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
// push / count / isEmpty |
||||||
|
$arr = []; |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
$arr->push(10); |
||||||
|
$arr->push(20); |
||||||
|
$arr->push(30); |
||||||
|
var_dump($arr->count()); |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
|
||||||
|
// pop |
||||||
|
$arr = []; |
||||||
|
$arr->push(100); |
||||||
|
$arr->push(200); |
||||||
|
$arr->push(300); |
||||||
|
var_dump($arr->pop()); |
||||||
|
var_dump($arr->count()); |
||||||
|
var_dump($arr->pop()); |
||||||
|
var_dump($arr->pop()); |
||||||
|
|
||||||
|
// get / set / del / keyExists |
||||||
|
$arr = []; |
||||||
|
$arr->set("name", "John"); |
||||||
|
$arr->set("age", 30); |
||||||
|
var_dump($arr->get("name")); |
||||||
|
var_dump($arr->get("age")); |
||||||
|
var_dump($arr->keyExists("age")); |
||||||
|
$arr->del("age"); |
||||||
|
var_dump($arr->keyExists("age")); |
||||||
|
var_dump($arr->keyExists("name")); |
||||||
|
|
||||||
|
// clean |
||||||
|
$arr = []; |
||||||
|
$arr->push(1); |
||||||
|
$arr->push(2); |
||||||
|
$arr->push(3); |
||||||
|
$arr->clean(); |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
var_dump($arr->count()); |
||||||
|
|
||||||
|
// keyExists / contains |
||||||
|
$arr = []; |
||||||
|
$arr->push("apple"); |
||||||
|
$arr->push("banana"); |
||||||
|
$arr->push("cherry"); |
||||||
|
var_dump($arr->keyExists(0)); |
||||||
|
var_dump($arr->keyExists(5)); |
||||||
|
var_dump($arr->contains("banana")); |
||||||
|
var_dump($arr->contains("grape")); |
||||||
|
|
||||||
|
// search |
||||||
|
$arr = []; |
||||||
|
$arr->push("red"); |
||||||
|
$arr->push("green"); |
||||||
|
$arr->push("blue"); |
||||||
|
$arr->push("green"); |
||||||
|
var_dump($arr->search("green")); |
||||||
|
var_dump($arr->search("yellow")); |
||||||
|
|
||||||
|
// isList |
||||||
|
$arr = []; |
||||||
|
$arr->push("a"); |
||||||
|
$arr->push("b"); |
||||||
|
$arr->push("c"); |
||||||
|
var_dump($arr->isList()); |
||||||
|
$hash = []; |
||||||
|
$hash->set("k", "v"); |
||||||
|
var_dump($hash->isList()); |
||||||
|
|
||||||
|
// keys / values |
||||||
|
$arr = []; |
||||||
|
$arr->set("x", 1); |
||||||
|
$arr->set("y", 2); |
||||||
|
$arr->set("z", 3); |
||||||
|
$keys = $arr->keys(); |
||||||
|
var_dump($keys->count()); |
||||||
|
var_dump($keys->contains("x")); |
||||||
|
var_dump($keys->contains("y")); |
||||||
|
$vals = $arr->values(); |
||||||
|
var_dump($vals->contains(1)); |
||||||
|
var_dump($vals->contains(3)); |
||||||
|
|
||||||
|
// keyFirst / keyLast |
||||||
|
var_dump($arr->keyFirst()); |
||||||
|
var_dump($arr->keyLast()); |
||||||
|
|
||||||
|
// join |
||||||
|
$arr = []; |
||||||
|
$arr->push("one"); |
||||||
|
$arr->push("two"); |
||||||
|
$arr->push("three"); |
||||||
|
var_dump($arr->join(", ")); |
||||||
|
|
||||||
|
// merge (mutating) |
||||||
|
$arr1 = []; |
||||||
|
$arr1->push("a"); |
||||||
|
$arr1->push("b"); |
||||||
|
$arr2 = []; |
||||||
|
$arr2->push("c"); |
||||||
|
$arr2->push("d"); |
||||||
|
$arr1->merge($arr2); |
||||||
|
var_dump($arr1->count()); |
||||||
|
var_dump($arr1->get(2)); |
||||||
|
|
||||||
|
// slice |
||||||
|
$arr = []; |
||||||
|
$arr->push("a"); |
||||||
|
$arr->push("b"); |
||||||
|
$arr->push("c"); |
||||||
|
$arr->push("d"); |
||||||
|
$arr->push("e"); |
||||||
|
$sliced = $arr->slice(1, 3); |
||||||
|
var_dump($sliced->count()); |
||||||
|
var_dump($sliced->get(0)); |
||||||
|
$sliced2 = $arr->slice(-2); |
||||||
|
var_dump($sliced2->count()); |
||||||
|
var_dump($sliced2->get(0)); |
||||||
|
|
||||||
|
// sort (mutating) |
||||||
|
$arr = []; |
||||||
|
$arr->push(3); |
||||||
|
$arr->push(1); |
||||||
|
$arr->push(4); |
||||||
|
$arr->push(1); |
||||||
|
$arr->push(5); |
||||||
|
$arr->sort(); |
||||||
|
var_dump($arr->get(0)); |
||||||
|
var_dump($arr->get(1)); |
||||||
|
var_dump($arr->get(4)); |
||||||
|
|
||||||
|
// reverse |
||||||
|
$arr = []; |
||||||
|
$arr->push(1); |
||||||
|
$arr->push(2); |
||||||
|
$arr->push(3); |
||||||
|
$reversed = $arr->reverse(); |
||||||
|
var_dump($reversed->get(0)); |
||||||
|
var_dump($reversed->get(2)); |
||||||
|
|
||||||
|
// unique |
||||||
|
$arr = []; |
||||||
|
$arr->push("a"); |
||||||
|
$arr->push("b"); |
||||||
|
$arr->push("a"); |
||||||
|
$arr->push("c"); |
||||||
|
$unique = $arr->unique(); |
||||||
|
var_dump($unique->count()); |
||||||
|
|
||||||
|
// flip |
||||||
|
$arr = []; |
||||||
|
$arr->set("a", 1); |
||||||
|
$arr->set("b", 2); |
||||||
|
$flipped = $arr->flip(); |
||||||
|
var_dump($flipped->keyExists(1)); |
||||||
|
|
||||||
|
// sum / product |
||||||
|
$arr = []; |
||||||
|
$arr->push(2); |
||||||
|
$arr->push(4); |
||||||
|
$arr->push(6); |
||||||
|
var_dump($arr->sum()); |
||||||
|
var_dump($arr->product()); |
||||||
|
|
||||||
|
// chunk |
||||||
|
$arr = []; |
||||||
|
$arr->push(1); |
||||||
|
$arr->push(2); |
||||||
|
$arr->push(3); |
||||||
|
$arr->push(4); |
||||||
|
$arr->push(5); |
||||||
|
$chunks = $arr->chunk(2); |
||||||
|
var_dump($chunks->count()); |
||||||
|
|
||||||
|
// diff |
||||||
|
$a = []; |
||||||
|
$a->push("green"); |
||||||
|
$a->push("red"); |
||||||
|
$a->push("blue"); |
||||||
|
$b = []; |
||||||
|
$b->push("green"); |
||||||
|
$b->push("yellow"); |
||||||
|
$diff = $a->diff($b); |
||||||
|
var_dump($diff->count()); |
||||||
|
var_dump($diff->contains("red")); |
||||||
|
var_dump($diff->contains("blue")); |
||||||
|
|
||||||
|
// filter |
||||||
|
$arr = []; |
||||||
|
$arr->push(1); |
||||||
|
$arr->push(2); |
||||||
|
$arr->push(3); |
||||||
|
$arr->push(4); |
||||||
|
$evens = $arr->filter('_even_filter'); |
||||||
|
var_dump($evens->count()); |
||||||
|
var_dump($evens->get(1)); |
||||||
|
|
||||||
|
// values |
||||||
|
$arr = []; |
||||||
|
$arr->set("x", 10); |
||||||
|
$arr->set("y", 20); |
||||||
|
$vals2 = $arr->values(); |
||||||
|
var_dump($vals2->count()); |
||||||
|
var_dump($vals2->get(0)); |
||||||
|
var_dump($vals2->get(1)); |
||||||
|
|
||||||
|
// replace |
||||||
|
$base = []; |
||||||
|
$base->push("orange"); |
||||||
|
$base->push("banana"); |
||||||
|
$base->push("apple"); |
||||||
|
$repl = []; |
||||||
|
$repl->push("pineapple"); |
||||||
|
$result = $base->replace($repl); |
||||||
|
var_dump($result->get(0)); |
||||||
|
|
||||||
|
// jsonEncode |
||||||
|
$arr = []; |
||||||
|
$arr->set("name", "test"); |
||||||
|
$arr->set("value", 123); |
||||||
|
var_dump($arr->jsonEncode()); |
||||||
|
|
||||||
|
// toInt / toFloat / toBool / toString |
||||||
|
$arr = []; |
||||||
|
$arr->push(1); |
||||||
|
var_dump($arr->toInt()); |
||||||
|
var_dump($arr->toFloat()); |
||||||
|
var_dump($arr->toBool()); |
||||||
|
|
||||||
|
echo "done\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(true) |
||||||
|
int(3) |
||||||
|
bool(false) |
||||||
|
int(300) |
||||||
|
int(2) |
||||||
|
int(200) |
||||||
|
int(100) |
||||||
|
string(4) "John" |
||||||
|
int(30) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
int(0) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
int(1) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
int(3) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
string(1) "x" |
||||||
|
string(1) "z" |
||||||
|
string(15) "one, two, three" |
||||||
|
int(4) |
||||||
|
string(1) "c" |
||||||
|
int(3) |
||||||
|
string(1) "b" |
||||||
|
int(1) |
||||||
|
string(1) "d" |
||||||
|
int(1) |
||||||
|
int(1) |
||||||
|
int(5) |
||||||
|
int(3) |
||||||
|
int(1) |
||||||
|
int(3) |
||||||
|
bool(true) |
||||||
|
int(12) |
||||||
|
int(48) |
||||||
|
int(3) |
||||||
|
int(2) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
int(2) |
||||||
|
int(2) |
||||||
|
int(2) |
||||||
|
int(10) |
||||||
|
int(20) |
||||||
|
string(9) "pineapple" |
||||||
|
string(27) "{"name":"test","value":123}" |
||||||
|
int(1) |
||||||
|
float(1) |
||||||
|
bool(true) |
||||||
|
done |
||||||
@ -0,0 +1,175 @@ |
|||||||
|
--TEST-- |
||||||
|
array_method: all array methods test |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function _odd($var) |
||||||
|
{ |
||||||
|
return $var & 1; |
||||||
|
} |
||||||
|
|
||||||
|
function _cube($n) |
||||||
|
{ |
||||||
|
return ($n * $n * $n); |
||||||
|
} |
||||||
|
|
||||||
|
function _sum($carry, $item) |
||||||
|
{ |
||||||
|
$carry += $item; |
||||||
|
return $carry; |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$array = array("FirSt" => 1, "SecOnd" => 4); |
||||||
|
Assert::eq($array->changeKeyCase(CASE_UPPER), array_change_key_case($array, CASE_UPPER)); |
||||||
|
|
||||||
|
$array = array('a', 'b', 'c', 'd', 'e'); |
||||||
|
Assert::eq($array->chunk(2), array_chunk($array, 2)); |
||||||
|
|
||||||
|
$records = [ |
||||||
|
[ |
||||||
|
'id' => 2135, |
||||||
|
'first_name' => 'John', |
||||||
|
'last_name' => 'Doe', |
||||||
|
], |
||||||
|
[ |
||||||
|
'id' => 3245, |
||||||
|
'first_name' => 'Sally', |
||||||
|
'last_name' => 'Smith', |
||||||
|
], |
||||||
|
[ |
||||||
|
'id' => 5342, |
||||||
|
'first_name' => 'Jane', |
||||||
|
'last_name' => 'Jones', |
||||||
|
], |
||||||
|
[ |
||||||
|
'id' => 5623, |
||||||
|
'first_name' => 'Peter', |
||||||
|
'last_name' => 'Doe', |
||||||
|
] |
||||||
|
]; |
||||||
|
Assert::eq($records->column('id'), array_column($records, 'id')); |
||||||
|
|
||||||
|
$array = array(1, "hello", 1, "world", "hello"); |
||||||
|
Assert::eq($array->countValues(), array_count_values($array)); |
||||||
|
|
||||||
|
$array1 = array("a" => "green", "red", "blue", "red"); |
||||||
|
$array2 = array("b" => "green", "yellow", "red"); |
||||||
|
Assert::eq($array1->diff($array2), array_diff($array1, $array2)); |
||||||
|
|
||||||
|
$array1 = array("a" => "green", "b" => "brown", "c" => "blue", "red"); |
||||||
|
$array2 = array("a" => "green", "yellow", "red"); |
||||||
|
Assert::eq($array1->diffAssoc($array2), array_diff_assoc($array1, $array2)); |
||||||
|
|
||||||
|
$array1 = array('blue' => 1, 'red' => 2, 'green' => 3, 'purple' => 4); |
||||||
|
$array2 = array('green' => 5, 'yellow' => 7, 'cyan' => 8); |
||||||
|
Assert::eq($array1->diffKey($array2), array_diff_key($array1, $array2)); |
||||||
|
|
||||||
|
$array = [6, 7, 8, 9, 10, 11, 12]; |
||||||
|
Assert::eq($array->filter('_odd'), array_filter($array, '_odd')); |
||||||
|
|
||||||
|
$input = array("oranges", "apples", "pears"); |
||||||
|
Assert::eq($input->flip(), array_flip($input)); |
||||||
|
|
||||||
|
$array1 = array("a" => "green", "red", "blue"); |
||||||
|
$array2 = array("b" => "green", "yellow", "red"); |
||||||
|
Assert::eq($array1->intersect($array2), array_intersect($array1, $array2)); |
||||||
|
Assert::eq($array1->intersectAssoc($array2), array_intersect_assoc($array1, $array2)); |
||||||
|
|
||||||
|
$array = ['apple', 2, 3]; |
||||||
|
Assert::eq($array->isList(), array_is_list($array)); |
||||||
|
|
||||||
|
$array = ['first' => 1, 'second' => 4]; |
||||||
|
Assert::eq($array->keyExists('first'), array_key_exists('first', $array)); |
||||||
|
|
||||||
|
$array = ['a' => 1, 'b' => 2, 'c' => 3]; |
||||||
|
Assert::eq($array->keyFirst(), array_key_first($array)); |
||||||
|
Assert::eq($array->keyLast(), array_key_last($array)); |
||||||
|
Assert::eq($array->keys(), array_keys($array)); |
||||||
|
Assert::eq($array->values(), array_values($array)); |
||||||
|
|
||||||
|
$a = [1, 2, 3, 4, 5]; |
||||||
|
Assert::eq($a->map('_cube'), array_map('_cube', $a)); |
||||||
|
|
||||||
|
$array = array(12, 10, 9); |
||||||
|
Assert::eq($array->pad(5, 0), array_pad($array, 5, 0)); |
||||||
|
|
||||||
|
$a = array(2, 4, 6, 8); |
||||||
|
Assert::eq($a->product(), array_product($a)); |
||||||
|
|
||||||
|
$array = array("Neo", "Morpheus", "Trinity", "Cypher", "Tank"); |
||||||
|
Assert::notEq($array->rand(2), array_rand($array, 2)); |
||||||
|
|
||||||
|
$array = array(1, 2, 3, 4, 5); |
||||||
|
Assert::eq($array->reduce('_sum'), array_reduce($array, '_sum')); |
||||||
|
|
||||||
|
$base = array("orange", "banana", "apple", "raspberry"); |
||||||
|
$replacements = array(0 => "pineapple", 4 => "cherry"); |
||||||
|
$replacements2 = array(0 => "grape"); |
||||||
|
Assert::eq($base->replace($replacements, $replacements2), array_replace($base, $replacements, $replacements2)); |
||||||
|
|
||||||
|
$array = array("php", 4.0, array("green", "red")); |
||||||
|
$rev = $array->reverse(); |
||||||
|
Assert::eq($rev->reverse(), array_reverse(array_reverse($array))); |
||||||
|
|
||||||
|
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red'); |
||||||
|
Assert::eq($array->search('green'), array_search('green', $array)); |
||||||
|
|
||||||
|
$array = array("a", "b", "c", "d", "e"); |
||||||
|
Assert::eq($array->slice(-2, 1), array_slice($array, -2, 1)); |
||||||
|
|
||||||
|
$a = array(2, 4, 6, 8); |
||||||
|
Assert::eq($a->sum(), array_sum($a)); |
||||||
|
|
||||||
|
$array = ["a" => "green", "red", "b" => "green", "blue", "red"]; |
||||||
|
Assert::eq($array->unique(), array_unique($array)); |
||||||
|
Assert::eq($array->count(), count($array)); |
||||||
|
|
||||||
|
$array = array("Mac", "NT", "Irix", "Linux"); |
||||||
|
Assert::eq($array->contains("Mac"), in_array("Mac", $array)); |
||||||
|
Assert::eq($array->join(","), implode(",", $array)); |
||||||
|
|
||||||
|
$array = []; |
||||||
|
Assert::true($array->isEmpty()); |
||||||
|
|
||||||
|
$fruits1 = array("lemon", "orange", "banana", "apple"); |
||||||
|
$fruits2 = array("lemon", "orange", "banana", "apple"); |
||||||
|
sort($fruits2); |
||||||
|
Assert::eq($fruits1->sort(), $fruits2); |
||||||
|
Assert::eq($fruits1, $fruits2); |
||||||
|
|
||||||
|
$stack = array("orange", "banana", "apple", "raspberry"); |
||||||
|
Assert::eq($stack->pop(), 'raspberry'); |
||||||
|
Assert::eq(array_pop($stack), 'apple'); |
||||||
|
|
||||||
|
$array = array("red","green"); |
||||||
|
$array->push("blue"); |
||||||
|
Assert::eq($array, ["red","green", "blue"]); |
||||||
|
array_push($array, "yellow"); |
||||||
|
Assert::eq($array, ["red","green", "blue", "yellow"]); |
||||||
|
|
||||||
|
$stack = array("orange", "banana", "apple", "raspberry"); |
||||||
|
Assert::eq($stack->shift(), 'orange'); |
||||||
|
Assert::eq(array_shift($stack), 'banana'); |
||||||
|
|
||||||
|
$queue = ["orange", "banana"]; |
||||||
|
$queue->unshift("orange"); |
||||||
|
Assert::eq($queue, ["orange", "orange", "banana"]); |
||||||
|
array_unshift($queue, "orange"); |
||||||
|
Assert::eq($queue, ["orange", "orange", "orange", "banana"]); |
||||||
|
|
||||||
|
$array1 = array("red", "green", "blue", "yellow"); |
||||||
|
$array2 = array("red", "green", "blue", "yellow"); |
||||||
|
Assert::eq($array1->splice(2), array_splice($array2, 2)); |
||||||
|
Assert::eq($array1, $array2); |
||||||
|
|
||||||
|
$find = array("Hello","world"); |
||||||
|
$replace = array("B"); |
||||||
|
$arr = array("Hello","world","!"); |
||||||
|
Assert::eq($arr->replaceStr($find, $replace), str_replace($find, $replace, $arr)); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
--TEST-- |
||||||
|
Universal method call on native typed variables |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
// Int methods |
||||||
|
$a = 100; |
||||||
|
$a->add(50); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->sub(30); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->mul(2); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->div(4); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$b = $a->add(10); |
||||||
|
var_dump($b); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
// Int conversion |
||||||
|
var_dump($a->toString()); |
||||||
|
var_dump($a->toFloat()); |
||||||
|
|
||||||
|
// String methods |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->length()); |
||||||
|
var_dump($s->upper()); |
||||||
|
var_dump($s->substr(0, 5)); |
||||||
|
$words = $s->split(" "); |
||||||
|
var_dump($words->count()); |
||||||
|
|
||||||
|
// Array methods |
||||||
|
$arr = []; |
||||||
|
$arr->push(100); |
||||||
|
$arr->push(200); |
||||||
|
$arr->push(300); |
||||||
|
var_dump($arr->count()); |
||||||
|
var_dump($arr->get(0)); |
||||||
|
var_dump($arr->get(1)); |
||||||
|
var_dump($arr->keyExists(0)); |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
var_dump($arr->isList()); |
||||||
|
|
||||||
|
// Array pop |
||||||
|
$last = $arr->pop(); |
||||||
|
var_dump($last); |
||||||
|
var_dump($arr->count()); |
||||||
|
|
||||||
|
// Array clean |
||||||
|
$arr->clean(); |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
|
||||||
|
// Float methods |
||||||
|
$f = 3.14; |
||||||
|
$f->add(1.0); |
||||||
|
var_dump($f); |
||||||
|
var_dump($f->toInt()); |
||||||
|
|
||||||
|
echo "done\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(100) |
||||||
|
int(100) |
||||||
|
int(100) |
||||||
|
int(100) |
||||||
|
int(110) |
||||||
|
int(100) |
||||||
|
string(3) "100" |
||||||
|
float(100) |
||||||
|
int(11) |
||||||
|
string(11) "HELLO WORLD" |
||||||
|
string(5) "hello" |
||||||
|
int(2) |
||||||
|
int(3) |
||||||
|
int(100) |
||||||
|
int(200) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
int(300) |
||||||
|
int(2) |
||||||
|
bool(true) |
||||||
|
float(3.14) |
||||||
|
int(3) |
||||||
|
done |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
--TEST-- |
||||||
|
stream_method: 0 |
||||||
|
--SKIPIF-- |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$filepath = "/tmp/test.txt"; |
||||||
|
$fp = fopen($filepath, "w+"); |
||||||
|
$rdata = random_bytes(1024); |
||||||
|
Assert::greaterThan($fp->write($rdata->base64Encode()), $rdata->length()); |
||||||
|
$fp->seek(0); |
||||||
|
Assert::eq($rdata, $fp->read(8192)->base64Decode()); |
||||||
|
Assert::eq($fp->stat(), fstat($fp)); |
||||||
|
Assert::true($fp->sync()); |
||||||
|
Assert::true($fp->dataSync()); |
||||||
|
$fp->seek(100); |
||||||
|
Assert::true($fp->tell() == 100); |
||||||
|
Assert::true($fp->lock(LOCK_SH) == true); |
||||||
|
Assert::true($fp->lock(LOCK_UN) == true); |
||||||
|
Assert::true($fp->eof() == feof($fp)); |
||||||
|
|
||||||
|
$fp->seek(0); |
||||||
|
$char = $fp->getChar(); |
||||||
|
$fp->seek(0); |
||||||
|
Assert::eq($char, fgetc($fp)); |
||||||
|
$fp->seek(0); |
||||||
|
$line = $fp->getLine(); |
||||||
|
$fp->seek(0); |
||||||
|
Assert::eq($line, fgets($fp)); |
||||||
|
Assert::true($fp->truncate(1000)); |
||||||
|
|
||||||
|
Assert::true($fp->close()); |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,60 @@ |
|||||||
|
--TEST-- |
||||||
|
Universal method call on stream resources with null guard |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
// Test 1: fopen()->write() returns bytes written (php_fn: fwrite) |
||||||
|
$tmpfile = tempnam(sys_get_temp_dir(), 'aot'); |
||||||
|
var_dump(fopen($tmpfile, 'w')->write("hello") !== false); |
||||||
|
|
||||||
|
// Test 2: fopen()->read() returns content (php_fn: fread) |
||||||
|
// (re-open for reading — new stream) |
||||||
|
var_dump(fopen($tmpfile, 'r')->read(5)); |
||||||
|
|
||||||
|
// Test 3: fopen()->close() returns bool (php_fn: fclose) |
||||||
|
var_dump(fopen($tmpfile, 'r')->close()); |
||||||
|
|
||||||
|
// Test 4: fopen()->eof() returns bool (php_fn: feof) |
||||||
|
$f = fopen($tmpfile, 'w'); |
||||||
|
$f->write("test"); |
||||||
|
$f->close(); |
||||||
|
var_dump(fopen($tmpfile, 'r')->eof()); |
||||||
|
|
||||||
|
// Test 5: fopen()->tell() returns position (php_fn: ftell) |
||||||
|
$r = fopen($tmpfile, 'r'); |
||||||
|
var_dump($r->tell() === 0); |
||||||
|
$r->read(2); |
||||||
|
var_dump($r->tell() === 2); |
||||||
|
$r->close(); |
||||||
|
|
||||||
|
// Test 6: fopen()->getChar() returns single char (php_fn: fgetc) |
||||||
|
$f2 = fopen($tmpfile, 'r'); |
||||||
|
var_dump($f2->getChar()); |
||||||
|
$f2->close(); |
||||||
|
|
||||||
|
// Test 7: Null guard — fopen() with invalid mode causes null, |
||||||
|
// calling write() should throw Error |
||||||
|
try { |
||||||
|
@fopen($tmpfile, 'INVALID_MODE')->write('data'); |
||||||
|
var_dump(false); // should not reach here |
||||||
|
} catch (\Error $e) { |
||||||
|
var_dump(str_contains($e->getMessage(), 'closed or invalid stream resource')); |
||||||
|
} |
||||||
|
|
||||||
|
unlink($tmpfile); |
||||||
|
echo "OK\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(true) |
||||||
|
string(5) "hello" |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
string(1) "t" |
||||||
|
bool(true) |
||||||
|
OK |
||||||
|
|
||||||
@ -0,0 +1,22 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: 0 |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$string = 'hello world, this is a test string'; |
||||||
|
Assert::false($string->isEmpty()); |
||||||
|
Assert::eq($string->length(), strlen($string)); |
||||||
|
Assert::eq($string->substr(0, 5), 'hello'); |
||||||
|
Assert::eq($string->contains('world'), true); |
||||||
|
Assert::eq($string->indexOf('test'), strpos($string, 'test')); |
||||||
|
Assert::eq($string->split(' '), explode(' ', $string)); |
||||||
|
|
||||||
|
$empty = ''; |
||||||
|
Assert::true($empty->isEmpty()); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: 1 |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$str = "first=value&arr[]=foo+bar&arr[]=baz"; |
||||||
|
|
||||||
|
$output = $str->parseStr(); |
||||||
|
Assert::eq($output['first'], 'value'); |
||||||
|
Assert::eq($output['arr'][0], 'foo bar'); |
||||||
|
Assert::eq($output['arr'][1], 'baz'); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: json |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$array = ['a' => random_bytes(128)->base64Encode(), 'b' => random_int(1, PHP_INT_MAX), 'c' => php_uname()]; |
||||||
|
|
||||||
|
$str = $array->jsonEncode(); |
||||||
|
Assert::notEmpty($str); |
||||||
|
Assert::eq($str->jsonDecode(), $array); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: marshal |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$array = []; |
||||||
|
$array->set('a', 'marshal_test_value'); |
||||||
|
$array->set('b', 100); |
||||||
|
$array->set('c', 'system_info'); |
||||||
|
|
||||||
|
$str = $array->marshal(); |
||||||
|
Assert::notEmpty($str); |
||||||
|
Assert::eq($str->unmarshal(), $array); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,25 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: match |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$str = 'foobarbaz'; |
||||||
|
$regex1 = '/(foo)(bar)(baz)/'; |
||||||
|
$matches = $str->match($regex1, PREG_OFFSET_CAPTURE); |
||||||
|
|
||||||
|
preg_match($regex1, $str, $matches2, PREG_OFFSET_CAPTURE); |
||||||
|
Assert::eq($matches, $matches2); |
||||||
|
|
||||||
|
$html = "<b>bold text</b><a href=howdy.html>click me</a>"; |
||||||
|
$regex2 = "/(<([\w]+)[^>]*>)(.*?)(<\/\\2>)/"; |
||||||
|
preg_match_all($regex2, $html, $matches2, PREG_SET_ORDER); |
||||||
|
|
||||||
|
$matches = $html->matchAll($regex2, PREG_SET_ORDER); |
||||||
|
Assert::eq($matches, $matches2); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: mbstring |
||||||
|
--SKIPIF-- |
||||||
|
<?php |
||||||
|
if (!extension_loaded("mbstring")) exit("skip mbstring extension not loaded"); |
||||||
|
?> |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$text = 'bbbb大声向宇宙呐喊bbbb'; |
||||||
|
Assert::eq($text->mbUpperFirst(), mb_ucfirst($text)); |
||||||
|
|
||||||
|
$text = 'Bbbb大声向宇宙呐喊bbbb'; |
||||||
|
Assert::eq($text->mbLowerFirst(), mb_lcfirst($text)); |
||||||
|
|
||||||
|
$text = "\t\t大声向宇宙呐喊 :) ... "; |
||||||
|
Assert::eq($text->mbTrim(), mb_trim($text)); |
||||||
|
Assert::eq($text->mbLTrim(), mb_ltrim($text)); |
||||||
|
Assert::eq($text->mbRTrim(), mb_rtrim($text)); |
||||||
|
|
||||||
|
$text = '大声大声大声向宇宙呐喊'; |
||||||
|
Assert::eq($text->mbSubstrCount('大声'), mb_substr_count($text, '大声')); |
||||||
|
|
||||||
|
$text = "大声大声大声向宇宙呐喊"; |
||||||
|
Assert::eq($text->mbSubstr(0, -1), mb_substr($text, 0, -1)); |
||||||
|
|
||||||
|
$text = 'b大声大声大声向宇宙呐喊'; |
||||||
|
Assert::eq($text->mbUpper(), mb_strtoupper($text)); |
||||||
|
|
||||||
|
$text = 'BBBBBB大声大声大声向宇宙呐喊'; |
||||||
|
Assert::eq($text->mbLower(), mb_strtolower($text)); |
||||||
|
|
||||||
|
$email = 'name我哦知道@example.com'; |
||||||
|
Assert::eq($email->mbFind('哦'), mb_strstr($email, '哦')); |
||||||
|
Assert::eq($email->mbIFind('道'), mb_stristr($email, '道')); |
||||||
|
|
||||||
|
$mystring1 = '当地特产'; |
||||||
|
$mystring2 = '现在只是谈谈'; |
||||||
|
Assert::true($mystring1->mbIndexOf('地') === mb_strpos($mystring1, '地')); |
||||||
|
Assert::true($mystring2->mbIIndexOf('A') === mb_stripos($mystring2, 'a')); |
||||||
|
|
||||||
|
$mystring = '啦啦啦啦啦啦啦'; |
||||||
|
Assert::true($mystring->mbLastIndexOf('好') === false); |
||||||
|
Assert::true(mb_strrpos($mystring, '好') === false); |
||||||
|
Assert::eq($mystring->mbILastIndexOf('啦'), mb_strripos($mystring, '啦')); |
||||||
|
Assert::eq($mystring->mbLastCharIndexOf('啦'), mb_strrchr($mystring, '啦')); |
||||||
|
Assert::eq($mystring->mbILastCharIndex('啦'), mb_strrichr($mystring, '啦')); |
||||||
|
|
||||||
|
$text = '啦啦啦啦啦啦啦'; |
||||||
|
Assert::eq($text->mbLength(), mb_strlen($text)); |
||||||
|
Assert::eq($text->mbCut(0, 2), mb_strcut($text, 0, 2)); |
||||||
|
Assert::eq($text->mbDetectEncoding(), mb_detect_encoding($text)); |
||||||
|
Assert::eq($text->mbConvertEncoding('GBK'), mb_convert_encoding($text, 'GBK')); |
||||||
|
|
||||||
|
$text = 'bbbbba啦啦啦啦啦啦啦'; |
||||||
|
Assert::eq( |
||||||
|
$text->mbConvertCase(MB_CASE_UPPER)->mbConvertCase(MB_CASE_LOWER), |
||||||
|
mb_convert_case(mb_convert_case($text, MB_CASE_UPPER), MB_CASE_LOWER) |
||||||
|
); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,167 @@ |
|||||||
|
--TEST-- |
||||||
|
string_method: all string methods test |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
require __DIR__ . '/../../../src/Assert.php'; |
||||||
|
|
||||||
|
$text = 'aaaaaa'; |
||||||
|
Assert::eq($text->length(), strlen($text)); |
||||||
|
|
||||||
|
$text = ''; |
||||||
|
Assert::eq($text->isEmpty(), true); |
||||||
|
|
||||||
|
$text = 'A'; |
||||||
|
Assert::eq($text->lower(), strtolower($text)); |
||||||
|
Assert::eq($text->lowerFirst(), lcfirst($text)); |
||||||
|
|
||||||
|
$text = 'b'; |
||||||
|
Assert::eq($text->upper(), strtoupper($text)); |
||||||
|
Assert::eq($text->upperFirst(), ucfirst($text)); |
||||||
|
|
||||||
|
$text = 'hello world!'; |
||||||
|
Assert::eq($text->upperWords(), ucwords($text)); |
||||||
|
|
||||||
|
$text = "PHP isThirty\nYears Old!\tYay to the Elephant!\n"; |
||||||
|
$characters = "\0..\37!@\177..\377"; |
||||||
|
Assert::eq($text->addCSlashes($characters), addCSlashes($text, $characters)); |
||||||
|
|
||||||
|
$text = "O'Reilly?"; |
||||||
|
Assert::eq($text->addSlashes(), addslashes($text)); |
||||||
|
|
||||||
|
$text = 'This is quite a long string, which will get broken up because the line is going to be too long after base64 encoding it.'; |
||||||
|
$encoded = $text->base64Encode(); |
||||||
|
Assert::eq($encoded->chunkSplit(), chunk_split(base64_encode($text))); |
||||||
|
|
||||||
|
$text = "Two Ts and one F."; |
||||||
|
Assert::eq($text->countChars(1), count_chars($text, 1)); |
||||||
|
|
||||||
|
$text = "I'll \"walk\" the <b>dog</b> now"; |
||||||
|
$encoded = $text->htmlEntityEncode(); |
||||||
|
Assert::eq($encoded->htmlEntityDecode(), html_entity_decode(htmlentities($text))); |
||||||
|
|
||||||
|
$text = "<a href='test'>Test</a>"; |
||||||
|
$encoded = $text->htmlSpecialCharsEncode(ENT_QUOTES); |
||||||
|
Assert::eq($encoded->htmlSpecialCharsDecode(), htmlspecialchars_decode(htmlspecialchars($text, ENT_QUOTES))); |
||||||
|
|
||||||
|
$text = "\t\tThese are a few words :) ... "; |
||||||
|
Assert::eq($text->trim(), trim($text)); |
||||||
|
Assert::eq($text->lTrim(), ltrim($text)); |
||||||
|
Assert::eq($text->rTrim(), rtrim($text)); |
||||||
|
|
||||||
|
$text = "first=value&arr[]=foo+bar&arr[]=baz"; |
||||||
|
parse_str($text, $result); |
||||||
|
Assert::eq($text->parseStr(), $result); |
||||||
|
|
||||||
|
$url = 'http://username:password@hostname:9090/path?arg=value#anchor'; |
||||||
|
Assert::eq($url->parseUrl(), parse_url($url)); |
||||||
|
|
||||||
|
$text = 'The lazy fox jumped over the fence'; |
||||||
|
Assert::eq($text->contains('lazy'), str_contains($text, 'lazy')); |
||||||
|
|
||||||
|
$text = 'ABC'; |
||||||
|
Assert::eq($text->incr(), str_increment($text)); |
||||||
|
Assert::eq($text->decr(), str_decrement($text)); |
||||||
|
|
||||||
|
$text = "<body text=%BODY%>"; |
||||||
|
Assert::eq($text->replace("%BODY%", "black"), str_replace("%BODY%", "black", $text)); |
||||||
|
Assert::eq($text->iReplace("%body%", "black"), str_ireplace("%body%", "black", $text)); |
||||||
|
|
||||||
|
$text = "Alien"; |
||||||
|
Assert::eq($text->pad(10), str_pad($text, 10)); |
||||||
|
|
||||||
|
$text = "-="; |
||||||
|
Assert::eq($text->repeat(10), str_repeat($text, 10)); |
||||||
|
|
||||||
|
$text = 'abcdef'; |
||||||
|
Assert::notEq($text->shuffle(), str_shuffle($text)); |
||||||
|
|
||||||
|
$text = "piece1 piece2 piece3 piece4 piece5 piece6"; |
||||||
|
Assert::eq($text->split(' '), explode(' ', $text)); |
||||||
|
|
||||||
|
$text = 'The lazy fox jumped over the fence'; |
||||||
|
Assert::eq($text->startsWith('The'), str_starts_with($text, 'The')); |
||||||
|
Assert::eq($text->endsWith('fence'), str_ends_with($text, 'fence')); |
||||||
|
|
||||||
|
$text = "Hello fri3nd, you're\n looking good today!"; |
||||||
|
Assert::eq($text->wordCount(0), str_word_count($text, 0)); |
||||||
|
Assert::eq($text->wordCount(1), str_word_count($text, 1)); |
||||||
|
Assert::eq($text->wordCount(2), str_word_count($text, 2)); |
||||||
|
|
||||||
|
$var1 = "Hello"; |
||||||
|
$var2 = "hello"; |
||||||
|
Assert::eq($var1->iCompare($var2), strcasecmp($var1, $var2)); |
||||||
|
Assert::eq($var1->compare($var2), strcmp($var1, $var2)); |
||||||
|
|
||||||
|
$email = 'name@example.com'; |
||||||
|
Assert::eq($email->find('@'), strstr($email, '@')); |
||||||
|
Assert::eq($email->iFind('N'), stristr($email, 'N')); |
||||||
|
|
||||||
|
$text = '<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>'; |
||||||
|
Assert::eq($text->stripTags(), strip_tags($text)); |
||||||
|
|
||||||
|
$text = 'I\'d have a coffee.\nNot a problem.'; |
||||||
|
Assert::eq($text->stripCSlashes(), stripcslashes($text)); |
||||||
|
|
||||||
|
$str = "Is your name O\'reilly?"; |
||||||
|
Assert::eq($str->stripSlashes(), stripslashes($str)); |
||||||
|
|
||||||
|
$mystring1 = 'xyz'; |
||||||
|
$mystring2 = 'ABC'; |
||||||
|
Assert::true($mystring1->iIndexOf('A') === stripos($mystring1, 'a')); |
||||||
|
Assert::true($mystring2->iIndexOf('A') === stripos($mystring2, 'a')); |
||||||
|
Assert::eq($mystring2->indexOf('a'), strpos($mystring2, 'a')); |
||||||
|
|
||||||
|
$mystring = 'Elephpant'; |
||||||
|
Assert::true($mystring->lastIndexOf('b') === false); |
||||||
|
Assert::true(strrpos($mystring, 'b') === false); |
||||||
|
Assert::eq($mystring->iLastIndexOf('E'), strripos($mystring, 'E')); |
||||||
|
Assert::eq($mystring->lastCharIndexOf('E'), strrchr($mystring, 'E')); |
||||||
|
|
||||||
|
$text = "abcdef"; |
||||||
|
Assert::eq($text->substr(0, -1), substr($text, 0, -1)); |
||||||
|
Assert::eq($text->substrCompare("bc", 1, 2), substr_compare($text, "bc", 1, 2)); |
||||||
|
|
||||||
|
$text = 'This is a test'; |
||||||
|
Assert::eq($text->substrCount('is'), substr_count($text, 'is')); |
||||||
|
|
||||||
|
$var = 'ABCDEFGH:/MNRPQR/'; |
||||||
|
Assert::eq($var->substrReplace('bob', 0), substr_replace($var, 'bob', 0)); |
||||||
|
|
||||||
|
$var = 'abcdefghijklmn'; |
||||||
|
Assert::eq($var->reverse(), strrev($var)); |
||||||
|
Assert::eq($var->md5(), md5($var)); |
||||||
|
Assert::eq($var->sha1(), sha1($var)); |
||||||
|
Assert::eq($var->crc32(), crc32($var)); |
||||||
|
Assert::eq($var->hash('sha256'), hash('sha256', $var)); |
||||||
|
|
||||||
|
$str = 'This is an encoded string'; |
||||||
|
$encoded = $str->base64Encode(); |
||||||
|
Assert::eq($encoded->base64Decode(), base64_decode(base64_encode($str))); |
||||||
|
|
||||||
|
$text = 'Data123!@-_ +'; |
||||||
|
$encoded = $text->urlEncode(); |
||||||
|
Assert::eq($encoded->urlDecode(), urldecode(urlencode($text))); |
||||||
|
|
||||||
|
$text = 'foo @+%/'; |
||||||
|
$encoded = $text->rawUrlEncode(); |
||||||
|
Assert::eq($encoded->rawUrlDecode(), rawurldecode(rawurlencode($text))); |
||||||
|
|
||||||
|
$text = "PHP is the web scripting language of choice."; |
||||||
|
$result1 = $text->match("/php/i"); |
||||||
|
preg_match("/php/i", $text, $result2); |
||||||
|
Assert::eq($result1, $result2); |
||||||
|
|
||||||
|
$pattern = "/\(? (\d{3})? \)? (?(1) [\-\s] ) \d{3}-\d{4}/x"; |
||||||
|
$subject = "Call 555-1212 or 1-800-555-1212"; |
||||||
|
$result1 = $subject->matchAll($pattern); |
||||||
|
preg_match_all($pattern, $subject, $result2); |
||||||
|
Assert::eq($result1, $result2); |
||||||
|
|
||||||
|
$text = '112'; |
||||||
|
Assert::eq($text->isNumeric(), is_numeric($text)); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
@ -0,0 +1,191 @@ |
|||||||
|
--TEST-- |
||||||
|
Universal method call: String type methods |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
// length / isEmpty |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->length()); |
||||||
|
var_dump($s->isEmpty()); |
||||||
|
$empty = ""; |
||||||
|
var_dump($empty->isEmpty()); |
||||||
|
|
||||||
|
// upper / lower |
||||||
|
$s = "Hello World"; |
||||||
|
var_dump($s->upper()); |
||||||
|
var_dump($s->lower()); |
||||||
|
|
||||||
|
// lowerFirst / upperFirst |
||||||
|
$s = "Hello World"; |
||||||
|
var_dump($s->lowerFirst()); |
||||||
|
var_dump($s->upperFirst()); |
||||||
|
|
||||||
|
// upperWords |
||||||
|
$s = "hello world!"; |
||||||
|
var_dump($s->upperWords()); |
||||||
|
|
||||||
|
// trim / lTrim / rTrim |
||||||
|
$s = " hello world "; |
||||||
|
var_dump($s->trim()); |
||||||
|
var_dump($s->lTrim()); |
||||||
|
var_dump($s->rTrim()); |
||||||
|
|
||||||
|
// startsWith / endsWith / contains |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->startsWith("hello")); |
||||||
|
var_dump($s->startsWith("world")); |
||||||
|
var_dump($s->endsWith("world")); |
||||||
|
var_dump($s->endsWith("hello")); |
||||||
|
var_dump($s->contains("lo wo")); |
||||||
|
var_dump($s->contains("xxx")); |
||||||
|
|
||||||
|
// indexOf / lastIndexOf |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->indexOf("world")); |
||||||
|
var_dump($s->indexOf("xxx")); |
||||||
|
var_dump($s->lastIndexOf("o")); |
||||||
|
|
||||||
|
// iCompare / compare |
||||||
|
$s = "abc"; |
||||||
|
var_dump($s->compare("abc")); |
||||||
|
var_dump($s->compare("abd")); |
||||||
|
var_dump($s->iCompare("ABC")); |
||||||
|
|
||||||
|
// reverse / md5 / sha1 / crc32 |
||||||
|
$s = "hello"; |
||||||
|
var_dump($s->reverse()); |
||||||
|
var_dump($s->md5()); |
||||||
|
var_dump($s->sha1()); |
||||||
|
var_dump($s->crc32()); |
||||||
|
|
||||||
|
// base64Encode / base64Decode |
||||||
|
$s = "hello"; |
||||||
|
$encoded = $s->base64Encode(); |
||||||
|
var_dump($encoded); |
||||||
|
var_dump($encoded->base64Decode()); |
||||||
|
|
||||||
|
// urlEncode / urlDecode |
||||||
|
$s = "hello world"; |
||||||
|
$encoded = $s->urlEncode(); |
||||||
|
var_dump($encoded); |
||||||
|
var_dump($encoded->urlDecode()); |
||||||
|
|
||||||
|
// equals |
||||||
|
$s = "hello"; |
||||||
|
var_dump($s->equals("hello")); |
||||||
|
var_dump($s->equals("HELLO")); |
||||||
|
var_dump($s->equals("HELLO", true)); |
||||||
|
|
||||||
|
// isNumeric |
||||||
|
$n1 = "123"; |
||||||
|
$n2 = "12.3"; |
||||||
|
$n3 = "abc"; |
||||||
|
var_dump($n1->isNumeric()); |
||||||
|
var_dump($n2->isNumeric()); |
||||||
|
var_dump($n3->isNumeric()); |
||||||
|
|
||||||
|
// append (mutating) |
||||||
|
$s = "hello"; |
||||||
|
$s->append(" world"); |
||||||
|
var_dump($s); |
||||||
|
|
||||||
|
// substr |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->substr(6)); |
||||||
|
var_dump($s->substr(0, 5)); |
||||||
|
var_dump($s->substr(-5)); |
||||||
|
|
||||||
|
// split |
||||||
|
$s = "a,b,c,d"; |
||||||
|
$parts = $s->split(","); |
||||||
|
var_dump($parts->count()); |
||||||
|
var_dump($parts->get(0)); |
||||||
|
var_dump($parts->get(2)); |
||||||
|
|
||||||
|
// stripTags |
||||||
|
$s = "<p>Hello <b>World</b></p>"; |
||||||
|
var_dump($s->stripTags("")); |
||||||
|
|
||||||
|
// addSlashes |
||||||
|
$s = "O'Reilly"; |
||||||
|
var_dump($s->addSlashes()); |
||||||
|
|
||||||
|
// toString (identity) |
||||||
|
$s = "hello"; |
||||||
|
var_dump($s->toString()); |
||||||
|
|
||||||
|
// toInt / toFloat / toBool |
||||||
|
$s = "123"; |
||||||
|
var_dump($s->toInt()); |
||||||
|
$s = "3.14"; |
||||||
|
var_dump($s->toFloat()); |
||||||
|
$s = "1"; |
||||||
|
var_dump($s->toBool()); |
||||||
|
|
||||||
|
// substrCount |
||||||
|
$s = "hello hello world"; |
||||||
|
var_dump($s->substrCount("hello")); |
||||||
|
|
||||||
|
// wordCount |
||||||
|
$s = "hello world test"; |
||||||
|
var_dump($s->wordCount()); |
||||||
|
|
||||||
|
echo "done\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(11) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
string(11) "HELLO WORLD" |
||||||
|
string(11) "hello world" |
||||||
|
string(11) "hello World" |
||||||
|
string(11) "Hello World" |
||||||
|
string(12) "Hello World!" |
||||||
|
string(11) "hello world" |
||||||
|
string(13) "hello world " |
||||||
|
string(13) " hello world" |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
int(6) |
||||||
|
bool(false) |
||||||
|
int(7) |
||||||
|
int(0) |
||||||
|
int(-1) |
||||||
|
int(0) |
||||||
|
string(5) "olleh" |
||||||
|
string(32) "5d41402abc4b2a76b9719d911017c592" |
||||||
|
string(40) "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" |
||||||
|
int(907060870) |
||||||
|
string(8) "aGVsbG8=" |
||||||
|
string(5) "hello" |
||||||
|
string(11) "hello+world" |
||||||
|
string(11) "hello world" |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
string(11) "hello world" |
||||||
|
string(5) "world" |
||||||
|
string(5) "hello" |
||||||
|
string(5) "world" |
||||||
|
int(4) |
||||||
|
string(1) "a" |
||||||
|
string(1) "c" |
||||||
|
string(11) "Hello World" |
||||||
|
string(9) "O\'Reilly" |
||||||
|
string(5) "hello" |
||||||
|
int(123) |
||||||
|
float(3.14) |
||||||
|
bool(true) |
||||||
|
int(2) |
||||||
|
int(3) |
||||||
|
done |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
function main() { |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Int methods |
||||||
|
$a = 100; |
||||||
|
$a->add(50); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->sub(30); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->mul(2); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->div(4); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$b = $a->add(10); |
||||||
|
var_dump($b); |
||||||
|
var_dump($a); // $a also mutated |
||||||
|
|
||||||
|
// Int conversion |
||||||
|
var_dump($a->toString()); |
||||||
|
var_dump($a->toFloat()); |
||||||
|
|
||||||
|
// String methods |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->length()); |
||||||
|
var_dump($s->upper()); |
||||||
|
var_dump($s->substr(0, 5)); |
||||||
|
var_dump($s->split(" ")->count()); |
||||||
|
|
||||||
|
// Array methods |
||||||
|
$arr = []; |
||||||
|
$arr->append(100); |
||||||
|
$arr->append(200); |
||||||
|
$arr->append(300); |
||||||
|
var_dump($arr->count()); |
||||||
|
var_dump($arr->get(0)); |
||||||
|
var_dump($arr->get(1)); |
||||||
|
var_dump($arr->exists(0)); |
||||||
|
var_dump($arr->empty()); |
||||||
|
var_dump($arr->isList()); |
||||||
|
|
||||||
|
// Array pop |
||||||
|
$last = $arr->pop(); |
||||||
|
var_dump($last); |
||||||
|
var_dump($arr->count()); |
||||||
|
|
||||||
|
// Array clean |
||||||
|
$arr->clean(); |
||||||
|
var_dump($arr->empty()); |
||||||
|
|
||||||
|
// Float methods |
||||||
|
$f = 3.14; |
||||||
|
$f->add(1.0); |
||||||
|
var_dump($f); |
||||||
|
var_dump($f->toInt()); |
||||||
|
|
||||||
|
echo "done\n"; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
--TEST-- |
||||||
|
Universal method call on native typed variables |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
// Int methods |
||||||
|
$a = 100; |
||||||
|
$a->add(50); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->sub(30); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->mul(2); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$a->div(4); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
$b = $a->add(10); |
||||||
|
var_dump($b); |
||||||
|
var_dump($a); |
||||||
|
|
||||||
|
// Int conversion |
||||||
|
var_dump($a->toString()); |
||||||
|
var_dump($a->toFloat()); |
||||||
|
|
||||||
|
// String methods |
||||||
|
$s = "hello world"; |
||||||
|
var_dump($s->length()); |
||||||
|
var_dump($s->upper()); |
||||||
|
var_dump($s->substr(0, 5)); |
||||||
|
$words = $s->split(" "); |
||||||
|
var_dump($words->count()); |
||||||
|
|
||||||
|
// Array methods |
||||||
|
$arr = []; |
||||||
|
$arr->push(100); |
||||||
|
$arr->push(200); |
||||||
|
$arr->push(300); |
||||||
|
var_dump($arr->count()); |
||||||
|
var_dump($arr->get(0)); |
||||||
|
var_dump($arr->get(1)); |
||||||
|
var_dump($arr->keyExists(0)); |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
var_dump($arr->isList()); |
||||||
|
|
||||||
|
// Array pop |
||||||
|
$last = $arr->pop(); |
||||||
|
var_dump($last); |
||||||
|
var_dump($arr->count()); |
||||||
|
|
||||||
|
// Array clean |
||||||
|
$arr->clean(); |
||||||
|
var_dump($arr->isEmpty()); |
||||||
|
|
||||||
|
// Float methods |
||||||
|
$f = 3.14; |
||||||
|
$f->add(1.0); |
||||||
|
var_dump($f); |
||||||
|
var_dump($f->toInt()); |
||||||
|
|
||||||
|
echo "done\n"; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(100) |
||||||
|
int(100) |
||||||
|
int(100) |
||||||
|
int(100) |
||||||
|
int(110) |
||||||
|
int(100) |
||||||
|
string(3) "100" |
||||||
|
float(100) |
||||||
|
int(11) |
||||||
|
string(11) "HELLO WORLD" |
||||||
|
string(5) "hello" |
||||||
|
int(2) |
||||||
|
int(3) |
||||||
|
int(100) |
||||||
|
int(200) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
|
int(300) |
||||||
|
int(2) |
||||||
|
bool(true) |
||||||
|
float(3.14) |
||||||
|
int(3) |
||||||
|
done |
||||||
Loading…
Reference in new issue