Skip to content

refactor: Replace magic methods with explicit methods split into feature traits - #1

Merged
PresentKim merged 3 commits into
mainfrom
ccr-9f9fde41-ba23ar
Oct 7, 2026
Merged

PresentKim merged 3 commits into
mainfrom
ccr-9f9fde41-ba23ar

Conversation

@PresentKim

@PresentKim PresentKim commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

Replaces the __call / __callStatic magic dispatch with explicitly declared methods, split into feature traits, and fixes a few correctness issues found along the way.

Changes

  • No more magic methods. Every documented method (name, nameAs, nameFrom, nameFromAs) is a real method. Traits are flattened into the class at compile time, so they cost nothing at runtime.
  • Feature traits (src/kim/present/lib/arrayutils/traits/): ConcatTrait, SetTrait, IterationTrait, SearchTrait, SortTrait, StackTrait, ShapeTrait.
  • Cheaper implementations: first/last/keyFirst/keyLast no longer build array_keys(), includes/indexOf use in_array/array_search, slice uses array_slice, and instance methods call exchangeArray() directly.
  • PHP 7.1 compatible: no 7.3+ functions, and empty variadic calls (push(), diff(), ...) are guarded.
  • Generators / iterators are now unpacked (ArrayUtils::toArray()); previously (array) turned them into [].
  • Tests: added a PHPUnit suite (composer test); ext-libxml / ext-simplexml moved to require-dev (benchmarks only).

Behavior changes

  • keyExists uses array_key_exists (a null value now counts as existing).
  • reduceRight passes the original keys to the callback (previously integer keys were renumbered).
  • first/last/random return null on an empty array instead of raising a warning; fill/slice accept $end = null as documented (was a TypeError); flatMap works with string-keyed arrays.
  • Undocumented call styles that only worked through __call (e.g. everyAs(), __map()) no longer exist, and unknown methods raise PHP's native "undefined method" error instead of BadMethodCallException.
  • popFrom / shiftFrom still do not modify the given array (unchanged).

Benchmark: main vs this branch

The repo's own PHPBench suite (phpbench run, 1000-element arrays; arrayutils variant, mean per call), run sequentially on the same machine with PHP 8.3. Not run on PHP 7.x.

56 benchmarks: 51 faster by 1.1x or more, 5 unchanged, none slower. Geometric mean speedup is 2.8x.

method native (μs) main (μs) branch (μs) change
chunk 8.301 13.575 9.513 1.4× faster
column 0.059 0.862 0.272 3.2× faster
combine 0.056 0.848 0.279 3.0× faster
concat 2.384 8.983 4.892 1.8× faster
concatSoft 2.438 11.904 1.186 10.0× faster
countValues 0.057 0.895 0.275 3.3× faster
diff 52.180 59.509 50.520 1.2× faster
diffAssoc 35.306 41.301 34.927 1.2× faster
diffKey 2.457 7.502 2.862 2.6× faster
every 20.302 35.950 30.095 1.2× faster
fill 0.055 0.937 0.377 2.5× faster
fillKeys 0.059 0.898 0.265 3.4× faster
filter 31.839 47.713 42.798 1.1× faster
find 10.681 21.616 15.647 1.4× faster
findIndex 11.468 23.048 16.086 1.4× faster
first 0.035 6.099 0.047 130.7× faster
flat 0.067 1.873 0.453 4.1× faster
flatMap 0.339 1.894 0.669 2.8× faster
flip 3.846 8.867 5.049 1.8× faster
forEach 21.625 35.398 31.194 1.1× faster
includes 0.573 10.559 0.559 18.9× faster
indexOf 0.529 10.805 0.561 19.3× faster
intersect 982.319 1042.341 962.270 ≈ same
intersectAssoc 37.620 44.641 38.114 1.2× faster
intersectKey 7.469 12.927 8.142 1.6× faster
join 6.951 12.421 6.994 1.8× faster
keyExists 0.036 5.462 0.673 8.1× faster
keyFirst 0.048 5.972 0.684 8.7× faster
keyLast 0.049 6.120 1.458 4.2× faster
keyRandom 0.043 6.298 1.106 5.7× faster
keys 0.769 6.201 1.768 3.5× faster
last 0.049 6.171 1.489 4.1× faster
map 22.990 43.084 39.687 ≈ same
mapAssoc 65.477 76.166 72.828 ≈ same
mapKey 33.207 43.515 39.325 1.1× faster
merge 2.571 9.003 4.601 2.0× faster
mergeSoft 2.372 11.701 1.227 9.5× faster
pad 1.059 6.111 2.360 2.6× faster
pop 0.887 5.234 0.877 6.0× faster
push 0.892 5.230 2.069 2.5× faster
random 0.048 6.299 0.393 16.0× faster
reduce 22.987 39.998 34.430 1.2× faster
reduceRight 25.111 41.362 40.997 ≈ same
replace 0.828 8.124 7.062 1.2× faster
reverse 1.287 6.830 2.329 2.9× faster
search 0.531 10.600 0.588 18.0× faster
shift 1.927 6.332 1.877 3.4× faster
slice 0.143 6.373 0.818 7.8× faster
some 10.741 21.992 16.155 1.4× faster
sort 90.820 94.669 95.604 ≈ same
sortKey 24.904 30.630 26.993 1.1× faster
splice 3.841 8.192 3.996 2.1× faster
sum 1.904 7.031 1.995 3.5× faster
unique 25.296 31.109 28.103 1.1× faster
unshift 3.617 8.249 4.863 1.7× faster
values 0.034 5.059 1.028 4.9× faster

The native column is the plain PHP function from the same benchmark, measured in the main run, for reference. Sub-microsecond native values are close to the measurement floor, so compare the main and branch columns with each other.

Verification

  • Compared every method (instance, As, From, FromAs) of the old and new classes on 8 inputs and about 1,100 calls. Results and instance state matched, except for the intentional changes listed above.
  • vendor/bin/phpunit: 8 tests, 38 assertions pass (PHP 8.3).

…ure traits

Remove __call/__callStatic dispatch and declare every method (name, nameAs,
nameFrom, nameFromAs) directly, grouped into traits by feature. Code stays
compatible with PHP 7.1 (guards for empty variadic calls to array_* functions).
- Add ArrayUtils::toArray() so generators and other iterators are unpacked
  instead of being cast to an empty array
- keyExists now uses array_key_exists (null values count as existing)
- reduceRight passes the original keys to the callback
- Call exchangeArray() directly in instance methods
- Move ext-libxml/ext-simplexml to require-dev (only used by benchmarks)
- Add PHPUnit test suite
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant