├── provision ├── php │ └── ref.ini ├── apache │ ├── ports.conf │ └── 000-default.conf ├── nginx │ └── default ├── provision.sh └── .bashrc ├── config.w32 ├── test-report.sh ├── scripts ├── subsplit.sh ├── replace_expect.php └── refresh-package-xml.php ├── tests ├── 001-extension_info.phpt ├── 002-WeakReference-notifier_callable_string.phpt ├── 004-SoftReference-notifier_callable_string.phpt ├── 002-WeakReference-orig_dtor_called_after_wr_dies_first.phpt ├── 004-SoftReference-orig_dtor_called_after_wr_dies_first.phpt ├── 002-WeakReference-orig_dtor_and_notifier_when_wr_dies_first.phpt ├── 004-SoftReference-orig_dtor_and_notifier_when_wr_dies_first.phpt ├── 002-WeakReference-orig_dtor_called_once.phpt ├── 004-SoftReference-orig_dtor_called_once.phpt ├── 001-extension-loaded.phpt ├── 002-WeakReference-notifier_invalid_callback.phpt ├── 004-SoftReference-notifier_invalid_callback.phpt ├── 002-WeakReference-orig_dtor_called.phpt ├── 004-SoftReference-orig_dtor_called.phpt ├── 002-WeakReference-extended_dtor_called.phpt ├── 004-SoftReference-extended_dtor_called.phpt ├── 002-WeakReference-notifier_callable_array.phpt ├── 004-SoftReference-notifier_callable_array.phpt ├── 002-WeakReference-exception-before-notifier.phpt ├── 004-SoftReference-exception-before-notifier.phpt ├── 002-WeakReference-multiple_obj.phpt ├── 004-SoftReference-multiple_obj.phpt ├── 002-NotifierException-basic.phpt ├── 004-SoftReference-notified_with_weak.phpt ├── 002-WeakReference-notifier.phpt ├── 004-SoftReference-notifier.phpt ├── 002-WeakReference-exception_in_callback.phpt ├── 004-SoftReference-exception_in_callback.phpt ├── 002-WeakReference-multiple_with_notify_and_orig_dtor.phpt ├── 002-WeakReference-serialize_extended_not_allowed.phpt ├── 004-SoftReference-multiple_with_notify_and_orig_dtor.phpt ├── 004-SoftReference-serialize_extended_not_allowed.phpt ├── 002-WeakReference-die_in_dtor.phpt ├── 005-Soft-and-Weak-Reference-exception-before-notifier.phpt ├── 002-WeakReference-exception-before-and-from-notifier.phpt ├── 004-SoftReference-exception-before-and-from-notifier.phpt ├── 004-SoftReference-die_in_dtor.phpt ├── 002-WeakReference-exception_in_orig_dtor.phpt ├── 004-SoftReference-exception_in_orig_dtor.phpt ├── 002-WeakReference-orig_dtor_and_notify.phpt ├── 004-SoftReference-orig_dtor_and_notify.phpt ├── 002-WeakReference-notified.phpt ├── 004-SoftReference-notified_prevent_destoying_forever_with_weak.phpt ├── 002-WeakReference-serialize_not_allowed.phpt ├── 004-SoftReference-notified_prevent_destoying_with_weak.phpt ├── 004-SoftReference-serialize_not_allowed.phpt ├── 002-WeakReference-exception_in_orig_dtor_and_callback.phpt ├── 004-SoftReference-exception_in_orig_dtor_and_callback.phpt ├── 002-WeakReference-notifier_clone_change.phpt ├── 004-SoftReference-notifier_clone_change.phpt ├── 002-WeakReference-multiple_weak.phpt ├── 004-SoftReference-multiple_weak.phpt ├── 005-Soft-and-Weak-Reference-exception-before-and-from-notifier.phpt ├── 004-SoftReference-notified.phpt ├── 004-SoftReference-notified_prevent_destoying_multiple.phpt ├── 002-WeakReference-notifier_not_called_after_wr_dies_first.phpt ├── 004-SoftReference-notifier_not_called_after_wr_dies_first.phpt ├── 001-verify-method-case.phpt ├── 002-WeakReference-reference-deleted-during-notifier.phpt ├── 002-WeakReference-basic.phpt ├── 004-SofReference-basic.phpt ├── 002-WeakReference-dump_extended.phpt ├── 004-SoftReference-dump_extended.phpt ├── 002-WeakReference-spl_object_storage_debug_hash_consistent.phpt ├── 004-SoftReference-spl_object_storage_debug_hash_consistent.phpt ├── 002-WeakReference-spl_hash_consistent.phpt ├── 004-SoftReference-spl_hash_consistent.phpt ├── 002-WeakReference-clone_extended.phpt ├── 002-WeakReference-exception_in_multiple_callbacks.phpt ├── 004-SoftReference-exception_in_multiple_callbacks.phpt ├── 004-SoftReference-clone_extended.phpt ├── 002-AbstractReference-basic.phpt ├── 002-WeakReference-object-handle-reuse.phpt ├── 002-WeakReference-closure.phpt ├── 004-SoftReference-closure.phpt ├── 003-functions-weakrefcounted_after_all_refs_died.phpt ├── 002-AbstractReference-clone.phpt ├── 002-WeakReference-notifier_change.phpt ├── 004-SoftReference-notifier_change.phpt ├── 002-WeakReference-spl_object_storage_hash_consistent.phpt ├── 004-SoftReference-spl_object_storage_hash_consistent.phpt ├── .stubs.php ├── 001-verify-methods-signature.phpt ├── 003-functions-soft.phpt ├── 002-WeakReference-clone.phpt ├── 004-SoftReference-clone.phpt ├── 004-SoftReference-notified_prevent_destoying.phpt ├── 004-SoftReference-notified_prevent_destoying_forever.phpt ├── 003-functions-soft-and-weak.phpt ├── 003-functions.phpt ├── .testsuite.php └── 001-verify_extension_entities.phpt ├── stubs ├── src │ ├── SoftReference.php │ ├── WeakReference.php │ ├── NotifierException.php │ ├── functions.php │ └── AbstractReference.php ├── composer.json ├── README.md └── LICENSE ├── .travis.yml ├── php_ref_functions.h ├── .gitignore ├── CMakeLists.txt ├── php_ref_notifier_exception.h ├── LICENSE ├── php_ref.h ├── Vagrantfile ├── config.m4 ├── php_ref_reference.h ├── ref.c ├── php_ref_notifier_exception.c └── php_ref_functions.c /provision/php/ref.ini: -------------------------------------------------------------------------------- 1 | ; configuration for php weak module 2 | ; priority=20 3 | extension=weak.so 4 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | #ARG_ENABLE("ref", "enable ref support", "no"); 2 | 3 | if (PHP_REF != "no") { 4 | EXTENSION("ref", "ref.c", PHP_REF_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); 5 | } 6 | `` 7 | -------------------------------------------------------------------------------- /test-report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | result=`find tests -type f -and -name "*.diff" -or -name "*.out" -or -name "*.mem" | sort | while read file; do 3 | echo "FILE: ${file}" 4 | cat "$file" 5 | echo "\n" 6 | done` 7 | 8 | if [ -n "$result" ]; then 9 | echo "$result" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /scripts/subsplit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" 4 | 5 | cd $DIR 6 | 7 | if [ -d ".subsplit" ]; then 8 | git subsplit update 9 | else 10 | git subsplit init . 11 | fi 12 | 13 | git subsplit publish --heads="master" stubs:git@github.com:pinepain/php-ref-stubs.git 14 | -------------------------------------------------------------------------------- /tests/001-extension_info.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ref extension info 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | info(); 10 | 11 | ?> 12 | --EXPECTF-- 13 | ref 14 | 15 | Ref support => enabled 16 | Version => %s 17 | Revision => %s 18 | Compiled => %s @ %s 19 | -------------------------------------------------------------------------------- /provision/apache/ports.conf: -------------------------------------------------------------------------------- 1 | # If you just change the port or add more ports here, you will likely also 2 | # have to change the VirtualHost statement in 3 | # /etc/apache2/sites-enabled/000-default.conf 4 | 5 | Listen 8080 6 | 7 | 8 | Listen 443 9 | 10 | 11 | 12 | Listen 443 13 | 14 | 15 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 16 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier_callable_string.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - callable notifier passed as string 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 22 | ?> 23 | EOF 24 | --EXPECT-- 25 | Notified 26 | 27 | EOF 28 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier_callable_string.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - callable notifier passed as string 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 22 | ?> 23 | EOF 24 | --EXPECT-- 25 | Notified 26 | 27 | EOF 28 | -------------------------------------------------------------------------------- /stubs/src/SoftReference.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 9 | * 10 | * For the full copyright and license information, please view the 11 | * LICENSE file that was distributed with this source or visit 12 | * http://opensource.org/licenses/MIT 13 | */ 14 | 15 | namespace Ref; 16 | 17 | class SoftReference extends AbstractReference 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /stubs/src/WeakReference.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 9 | * 10 | * For the full copyright and license information, please view the 11 | * LICENSE file that was distributed with this source or visit 12 | * http://opensource.org/licenses/MIT 13 | */ 14 | 15 | namespace Ref; 16 | 17 | class WeakReference extends AbstractReference 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /tests/002-WeakReference-orig_dtor_called_after_wr_dies_first.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - original object destructor called after weak reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 21 | EOF 22 | --EXPECT-- 23 | WeakTests\TrackingDtor's destructor called 24 | EOF 25 | -------------------------------------------------------------------------------- /tests/004-SoftReference-orig_dtor_called_after_wr_dies_first.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - original object destructor called after soft reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 21 | EOF 22 | --EXPECT-- 23 | WeakTests\TrackingDtor's destructor called 24 | EOF 25 | -------------------------------------------------------------------------------- /tests/002-WeakReference-orig_dtor_and_notifier_when_wr_dies_first.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - original object destructor called after weak reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 21 | EOF 22 | --EXPECT-- 23 | WeakTests\TrackingDtor's destructor called 24 | EOF 25 | -------------------------------------------------------------------------------- /tests/004-SoftReference-orig_dtor_and_notifier_when_wr_dies_first.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - original object destructor called after soft reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 21 | EOF 22 | --EXPECT-- 23 | WeakTests\TrackingDtor's destructor called 24 | EOF 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | language: php 5 | 6 | env: 7 | global: 8 | - NO_INTERACTION=1 9 | - TEST_TIMEOUT=120 10 | 11 | matrix: 12 | allow_failures: 13 | - php: nightly 14 | 15 | include: 16 | - php: 7.2 17 | - php: 7.2 18 | env: TEST_PHP_ARGS=-m 19 | 20 | - php: nightly 21 | # - php: nightly 22 | # env: TEST_PHP_ARGS=-m 23 | 24 | before_install: 25 | - phpize && ./configure && make 26 | 27 | script: 28 | - sh -c "make test | tee result.txt" 29 | - sh test-report.sh 30 | 31 | addons: 32 | apt: 33 | packages: 34 | - valgrind 35 | -------------------------------------------------------------------------------- /tests/002-WeakReference-orig_dtor_called_once.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - multiple weak references to the same object, original object destructor called once 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 22 | EOF 23 | --EXPECT-- 24 | WeakTests\TrackingDtor's destructor called 25 | EOF 26 | -------------------------------------------------------------------------------- /tests/004-SoftReference-orig_dtor_called_once.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - multiple soft references to the same object, original object destructor called once 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 22 | EOF 23 | --EXPECT-- 24 | WeakTests\TrackingDtor's destructor called 25 | EOF 26 | -------------------------------------------------------------------------------- /tests/001-extension-loaded.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for ref extension presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | ref extension is available 22 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier_invalid_callback.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - invalid notifier callback passed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 17 | } 18 | 19 | $helper->line(); 20 | ?> 21 | EOF 22 | --EXPECT-- 23 | TypeError: Argument 2 passed to Ref\AbstractReference::__construct() must be callable or null, string given 24 | 25 | EOF 26 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier_invalid_callback.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - invalid notifier callback passed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 17 | } 18 | 19 | $helper->line(); 20 | ?> 21 | EOF 22 | --EXPECT-- 23 | TypeError: Argument 2 passed to Ref\AbstractReference::__construct() must be callable or null, string given 24 | 25 | EOF 26 | -------------------------------------------------------------------------------- /php_ref_functions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #ifndef PHP_REF_FUNCTIONS_H 14 | #define PHP_REF_FUNCTIONS_H 15 | 16 | #include "php.h" 17 | 18 | #ifdef ZTS 19 | #include "TSRM.h" 20 | #endif 21 | 22 | extern const zend_function_entry php_ref_functions[]; 23 | 24 | #endif /* PHP_REF_FUNCTIONS_H */ 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.loT 4 | *.la 5 | .libs 6 | acinclude.m4 7 | aclocal.m4 8 | autom4te.cache 9 | build 10 | config.guess 11 | config.h 12 | config.h.in 13 | config.log 14 | config.nice 15 | config.status 16 | config.sub 17 | configure 18 | configure.ac 19 | configure.in 20 | include 21 | install-sh 22 | libtool 23 | ltmain.sh 24 | Makefile 25 | Makefile.fragments 26 | Makefile.global 27 | Makefile.objects 28 | missing 29 | mkinstalldirs 30 | modules 31 | run-tests.php 32 | 33 | confdefs.h 34 | conftest 35 | conftest.c 36 | conftest.err 37 | tmp-php.ini 38 | 39 | tests/* 40 | !tests/*.phpt 41 | !tests/.testsuite.php 42 | !tests/.stubs.php 43 | 44 | /.subsplit 45 | /.vagrant 46 | 47 | ref-*.tgz 48 | -------------------------------------------------------------------------------- /tests/002-WeakReference-orig_dtor_called.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - original object destructor called but notifier not when weak reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 24 | EOF 25 | --EXPECT-- 26 | WeakTests\TrackingDtor's destructor called 27 | EOF 28 | -------------------------------------------------------------------------------- /tests/004-SoftReference-orig_dtor_called.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - original object destructor called but notifier not when soft reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 24 | EOF 25 | --EXPECT-- 26 | WeakTests\TrackingDtor's destructor called 27 | EOF 28 | -------------------------------------------------------------------------------- /stubs/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pinepain/php-ref-stubs", 3 | "type": "library", 4 | "description": "Stub files for pinepain/php-ref php extension", 5 | "keywords": [ 6 | "dev", 7 | "stub", 8 | "php-ref", 9 | "soft", 10 | "weak", 11 | "ref", 12 | "reference", 13 | "softref", 14 | "weakref", 15 | "weakreference", 16 | "softreference" 17 | ], 18 | "homepage": "https://github.com/pinepain/php-ref-stubs", 19 | "license": "MIT", 20 | "authors": [ 21 | { 22 | "name": "Bogdan Padalko", 23 | "email": "pinepain@gmail.com", 24 | "homepage": "https://github.com/pinepain" 25 | } 26 | ], 27 | "require": { 28 | "php": "~7.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/002-WeakReference-extended_dtor_called.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - dump representation of extended reference class 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 27 | ?> 28 | EOF 29 | --EXPECT-- 30 | Dtoring ExtendedWeakReferenceTrackingDtor 31 | 32 | EOF 33 | -------------------------------------------------------------------------------- /tests/004-SoftReference-extended_dtor_called.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - dump representation of extended reference class 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 27 | ?> 28 | EOF 29 | --EXPECT-- 30 | Dtoring ExtendedWeakReferenceTrackingDtor 31 | 32 | EOF 33 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier_callable_array.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - callable notifier passed as array 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | wr = new Ref\WeakReference($obj, [$this, 'notifier']); 19 | } 20 | 21 | public function notifier() 22 | { 23 | echo 'Notified', PHP_EOL; 24 | } 25 | } 26 | 27 | $obj = new stdClass(); 28 | $t = new Test($obj); 29 | $obj = null; 30 | 31 | $helper->line(); 32 | ?> 33 | EOF 34 | --EXPECT-- 35 | Notified 36 | 37 | EOF 38 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier_callable_array.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - callable notifier passed as array 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | wr = new Ref\SoftReference($obj, [$this, 'notifier']); 19 | } 20 | 21 | public function notifier() 22 | { 23 | echo 'Notified', PHP_EOL; 24 | } 25 | } 26 | 27 | $obj = new stdClass(); 28 | $t = new Test($obj); 29 | $obj = null; 30 | 31 | $helper->line(); 32 | ?> 33 | EOF 34 | --EXPECT-- 35 | Notified 36 | 37 | EOF 38 | -------------------------------------------------------------------------------- /tests/002-WeakReference-exception-before-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - exception thrown outside notifier before destructing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 28 | } 29 | 30 | ?> 31 | EOF 32 | --EXPECT-- 33 | Weak notifier called 34 | RuntimeException: Test exception 35 | EOF 36 | -------------------------------------------------------------------------------- /tests/004-SoftReference-exception-before-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - exception thrown outside notifier before destructing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 28 | } 29 | 30 | ?> 31 | EOF 32 | --EXPECT-- 33 | Soft notifier called 34 | RuntimeException: Test exception 35 | EOF 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(php-ref) 3 | 4 | # NOTE: This CMake file is just for syntax highlighting in CLion 5 | 6 | include_directories(/usr/local/include/php) 7 | include_directories(/usr/local/include/php/TSRM) 8 | include_directories(/usr/local/include/php/Zend) 9 | include_directories(/usr/local/include/php/ext) 10 | include_directories(/usr/local/include/php/main) 11 | include_directories(/usr/local/include/php/sapi) 12 | 13 | add_definitions(-DCOMPILE_DL_REF) 14 | 15 | set(SOURCE_FILES 16 | php_ref.h 17 | ref.c 18 | 19 | php_ref_notifier_exception.c 20 | php_ref_notifier_exception.h 21 | 22 | php_ref_reference.c 23 | php_ref_reference.h 24 | 25 | php_ref_functions.c 26 | php_ref_functions.h 27 | ) 28 | 29 | add_executable(php_ref ${SOURCE_FILES}) 30 | -------------------------------------------------------------------------------- /tests/002-WeakReference-multiple_obj.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - track only single specific object 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 26 | $obj0 = null; 27 | 28 | 29 | ?> 30 | EOF 31 | --EXPECT-- 32 | WeakTests\TrackingDtor's destructor called 33 | 34 | WeakTests\TrackingDtor's destructor called 35 | Weak notifier called 36 | EOF 37 | -------------------------------------------------------------------------------- /tests/004-SoftReference-multiple_obj.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - track only single specific object 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 26 | $obj0 = null; 27 | 28 | 29 | ?> 30 | EOF 31 | --EXPECT-- 32 | WeakTests\TrackingDtor's destructor called 33 | 34 | Weak notifier called 35 | WeakTests\TrackingDtor's destructor called 36 | EOF 37 | -------------------------------------------------------------------------------- /tests/002-NotifierException-basic.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\NotifierException - basic 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 15 | EOF 16 | --EXPECTF-- 17 | object(Ref\NotifierException)#1 (8) { 18 | ["message":protected]=> 19 | string(4) "Test" 20 | ["string":"Exception":private]=> 21 | string(0) "" 22 | ["code":protected]=> 23 | int(0) 24 | ["file":protected]=> 25 | string(%d) "%s" 26 | ["line":protected]=> 27 | int(5) 28 | ["trace":"Exception":private]=> 29 | array(0) { 30 | } 31 | ["previous":"Exception":private]=> 32 | NULL 33 | ["exceptions":"Ref\NotifierException":private]=> 34 | array(0) { 35 | } 36 | } 37 | EOF 38 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified_with_weak.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - soft reference notifier then original object dtor and then soft notifiers called 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 27 | EOF 28 | --EXPECT-- 29 | Soft notifier called 30 | WeakTests\TrackingDtor's destructor called 31 | Weak notifier called 32 | EOF 33 | -------------------------------------------------------------------------------- /php_ref_notifier_exception.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #ifndef PHP_REF_NOTIFIER_EXCEPTION_H 14 | #define PHP_REF_NOTIFIER_EXCEPTION_H 15 | 16 | #include "php.h" 17 | 18 | #ifdef ZTS 19 | #include "TSRM.h" 20 | #endif 21 | 22 | extern zend_class_entry *php_ref_notifier_exception_class_entry; 23 | 24 | void php_ref_create_notifier_exception(zval *exception, const char *message, zval *thrown); 25 | 26 | PHP_MINIT_FUNCTION(php_ref_notifier_exception); 27 | 28 | 29 | #endif /* PHP_REF_NOTIFIER_EXCEPTION_H */ 30 | -------------------------------------------------------------------------------- /stubs/src/NotifierException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 9 | * 10 | * For the full copyright and license information, please view the 11 | * LICENSE file that was distributed with this source or visit 12 | * http://opensource.org/licenses/MIT 13 | */ 14 | 15 | 16 | namespace Ref; 17 | 18 | 19 | use Exception; 20 | use Throwable; 21 | 22 | 23 | class NotifierException extends Exception 24 | { 25 | private $exceptions = []; 26 | 27 | /** 28 | * Get exceptions that were thrown from notifiers 29 | * 30 | * @return Throwable[] 31 | */ 32 | public function getExceptions(): array 33 | { 34 | return $this->exceptions; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - notifier accessor 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier is null by default', $wr0->notifier(), null); 20 | $helper->assert('Null notifier acceptable', $wr1->notifier(), null); 21 | $helper->assert('Callback notifier acceptable', $wr2->notifier(), $callback_notifier); 22 | 23 | $helper->line(); 24 | ?> 25 | EOF 26 | --EXPECT-- 27 | Notifier is null by default: ok 28 | Null notifier acceptable: ok 29 | Callback notifier acceptable: ok 30 | 31 | EOF 32 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - notifier accessor 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier is null by default', $sr0->notifier(), null); 20 | $helper->assert('Null notifier acceptable', $sr1->notifier(), null); 21 | $helper->assert('Callback notifier acceptable', $sr2->notifier(), $callback_notifier); 22 | 23 | $helper->line(); 24 | ?> 25 | EOF 26 | --EXPECT-- 27 | Notifier is null by default: ok 28 | Null notifier acceptable: ok 29 | Callback notifier acceptable: ok 30 | 31 | EOF 32 | -------------------------------------------------------------------------------- /tests/002-WeakReference-exception_in_callback.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - exception thrown in callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 26 | } 27 | 28 | ?> 29 | EOF 30 | --EXPECT-- 31 | WeakTests\TrackingDtor's destructor called 32 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 33 | thrown: 34 | #0: Exception: Test exception from callback 35 | EOF 36 | -------------------------------------------------------------------------------- /tests/004-SoftReference-exception_in_callback.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - exception thrown in callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 26 | } 27 | 28 | ?> 29 | EOF 30 | --EXPECT-- 31 | WeakTests\TrackingDtor's destructor called 32 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 33 | thrown: 34 | #0: Exception: Test exception from callback 35 | EOF 36 | -------------------------------------------------------------------------------- /tests/002-WeakReference-multiple_with_notify_and_orig_dtor.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - multiple weak references with notifiers, original object destructor called once and after all notifiers 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 29 | EOF 30 | --EXPECT-- 31 | WeakTests\TrackingDtor's destructor called 32 | Weak notifier 2 called 33 | Weak notifier 1 called 34 | EOF 35 | -------------------------------------------------------------------------------- /tests/002-WeakReference-serialize_extended_not_allowed.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - serialize extended reference that tries to implemet Serializable interface is not allowed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 32 | EOF 33 | --EXPECT-- 34 | Fatal error: Class SerializableWeakReference could not implement interface Serializable in Unknown on line 0 35 | -------------------------------------------------------------------------------- /tests/004-SoftReference-multiple_with_notify_and_orig_dtor.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - multiple soft references with notifiers, original object destructor called once and after all notifiers 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 29 | EOF 30 | --EXPECT-- 31 | Weak notifier 2 called 32 | Weak notifier 1 called 33 | WeakTests\TrackingDtor's destructor called 34 | EOF 35 | -------------------------------------------------------------------------------- /tests/004-SoftReference-serialize_extended_not_allowed.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - serialize extended reference that tries to implemet Serializable interface is not allowed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 32 | EOF 33 | --EXPECT-- 34 | Fatal error: Class SerializableWeakReference could not implement interface Serializable in Unknown on line 0 35 | -------------------------------------------------------------------------------- /tests/002-WeakReference-die_in_dtor.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - destructor calls die() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 32 | $helper->line(); 33 | } 34 | 35 | register_shutdown_function(function () use (&$wr, &$helper) { 36 | echo 'We did not die properly', PHP_EOL; 37 | }); 38 | 39 | 40 | ?> 41 | EOF 42 | --EXPECT-- 43 | Destructor dies 44 | -------------------------------------------------------------------------------- /tests/005-Soft-and-Weak-Reference-exception-before-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference + Ref\WeakReference - exception thrown outside notifier before destructing 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 33 | } 34 | 35 | ?> 36 | EOF 37 | --EXPECT-- 38 | Soft notifier called 39 | Weak notifier called 40 | RuntimeException: Test exception 41 | EOF 42 | -------------------------------------------------------------------------------- /tests/002-WeakReference-exception-before-and-from-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - exception thrown outside notifier before destructing and in notifier 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 29 | } 30 | 31 | ?> 32 | EOF 33 | --EXPECT-- 34 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 35 | previous: RuntimeException: Test exception 36 | thrown: 37 | #0: RuntimeException: From weak notifier 38 | EOF 39 | -------------------------------------------------------------------------------- /tests/004-SoftReference-exception-before-and-from-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - exception thrown outside notifier before destructing and in notifier 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 29 | } 30 | 31 | ?> 32 | EOF 33 | --EXPECT-- 34 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 35 | previous: RuntimeException: Test exception 36 | thrown: 37 | #0: RuntimeException: From soft notifier 38 | EOF 39 | -------------------------------------------------------------------------------- /tests/004-SoftReference-die_in_dtor.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - destructor calls die() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 32 | $helper->line(); 33 | } 34 | 35 | register_shutdown_function(function () use (&$sr, &$helper) { 36 | echo 'We did not die properly', PHP_EOL; 37 | }); 38 | 39 | 40 | ?> 41 | EOF 42 | --EXPECT-- 43 | Soft notifier called 44 | Destructor dies 45 | -------------------------------------------------------------------------------- /tests/002-WeakReference-exception_in_orig_dtor.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - exception thrown in orig dtor 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 33 | } 34 | 35 | ?> 36 | EOF 37 | --EXPECT-- 38 | Dtor called 39 | Weak callback called 40 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 41 | thrown: 42 | #0: Exception: Test exception from dtor 43 | EOF 44 | -------------------------------------------------------------------------------- /tests/004-SoftReference-exception_in_orig_dtor.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - exception thrown in orig dtor 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 33 | } 34 | 35 | ?> 36 | EOF 37 | --EXPECT-- 38 | Weak callback called 39 | Dtor called 40 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 41 | thrown: 42 | #0: Exception: Test exception from dtor 43 | EOF 44 | -------------------------------------------------------------------------------- /tests/002-WeakReference-orig_dtor_and_notify.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - original object destructor called with notify callback before it 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert("Weak references points to original object", $wr->get() === $obj); 23 | 24 | $helper->line(); 25 | $obj = null; 26 | $helper->line(); 27 | 28 | $helper->assert("Weak references points to null", $wr->get() === null); 29 | 30 | $obj = null; 31 | 32 | ?> 33 | EOF 34 | --EXPECT-- 35 | Weak references points to original object: ok 36 | 37 | WeakTests\TrackingDtor's destructor called 38 | Weak notifier called 39 | 40 | Weak references points to null: ok 41 | EOF 42 | -------------------------------------------------------------------------------- /tests/004-SoftReference-orig_dtor_and_notify.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - original object destructor called with notify callback after it 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert("Weak references points to original object", $sr->get() === $obj); 23 | 24 | $helper->line(); 25 | $obj = null; 26 | $helper->line(); 27 | 28 | $helper->assert("Weak references points to null", $sr->get() === null); 29 | 30 | $obj = null; 31 | 32 | ?> 33 | EOF 34 | --EXPECT-- 35 | Weak references points to original object: ok 36 | 37 | Weak notifier called 38 | WeakTests\TrackingDtor's destructor called 39 | 40 | Weak references points to null: ok 41 | EOF 42 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notified.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - weak reference notified when object destroyed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier called', true); 15 | $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); 16 | $helper->assert('Notifier get weak reference as it argument', $reference instanceof Ref\WeakReference); 17 | $helper->assert('Original object is null', null === $obj); 18 | $helper->assert('Weak reference in notifier is null', null === $reference->get()); 19 | }); 20 | 21 | $obj = null; 22 | 23 | ?> 24 | EOF 25 | --EXPECT-- 26 | Notifier called: ok 27 | Notifier get 1 argument: ok 28 | Notifier get weak reference as it argument: ok 29 | Original object is null: ok 30 | Weak reference in notifier is null: ok 31 | EOF 32 | -------------------------------------------------------------------------------- /stubs/README.md: -------------------------------------------------------------------------------- 1 | # Stub files for [php-ref](https://github.com/pinepain/php-ref) PHP extension 2 | 3 | This is a git subtree split of [php-ref](https://github.com/pinepain/php-ref) extension stub files. 4 | 5 | **All issues and pull-requests should be submitted to the original [php-ref](https://github.com/pinepain/php-ref) repository.** 6 | 7 | ### PLEASE READ: 8 | 9 | Maintaining this project takes significant amount of time and efforts. 10 | If you like my work and want to show your appreciation, please consider supporting me at https://www.patreon.com/pinepain. 11 | 12 | ## Installation 13 | 14 | If you are also using Composer, it is recommended that you add the [php-ref-stub](https://github.com/pinepain/php-ref-stubs) 15 | package as a dev-mode requirement. It provides skeleton definitions and annotations to enable support for auto-completion 16 | in your IDE and other code-analysis tools. 17 | 18 | composer require --dev pinepain/php-ref-stubs 19 | 20 | 21 | ## License 22 | 23 | [php-ref](https://github.com/pinepain/php-ref) PHP extension is licensed under the [MIT license](http://opensource.org/licenses/MIT). 24 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified_prevent_destoying_forever_with_weak.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - prevent original object from being destroyed, soft notifiers called only when soft ref released 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | get(); 21 | }); 22 | 23 | $wr = new Ref\WeakReference($obj, function (Ref\WeakReference $reference){ 24 | echo 'Weak notifier called', PHP_EOL; 25 | }); 26 | 27 | $obj = null; 28 | $sr = null; 29 | 30 | echo 'Here original dtor will be called', PHP_EOL; 31 | $obj_copy = null; 32 | 33 | ?> 34 | EOF 35 | --EXPECT-- 36 | Soft notifier called 37 | Here original dtor will be called 38 | WeakTests\TrackingDtor's destructor called 39 | Weak notifier called 40 | EOF 41 | -------------------------------------------------------------------------------- /tests/002-WeakReference-serialize_not_allowed.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - serialize reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | dump($wr); 15 | 16 | try { 17 | $serialized = serialize($wr); 18 | $helper->dump($serialized); 19 | } catch (\Throwable $e) { 20 | $helper->exception_export($e); 21 | } 22 | 23 | $helper->line(); 24 | 25 | ?> 26 | EOF 27 | --EXPECT-- 28 | object(Ref\WeakReference)#3 (2) refcount(3){ 29 | ["referent":"Ref\AbstractReference":private]=> 30 | object(stdClass)#2 (0) refcount(2){ 31 | } 32 | ["notifier":"Ref\AbstractReference":private]=> 33 | object(Closure)#4 (1) refcount(2){ 34 | ["parameter"]=> 35 | array(1) refcount(1){ 36 | ["$reference"]=> 37 | string(10) "" refcount(1) 38 | } 39 | } 40 | } 41 | Exception: Serialization of 'Ref\WeakReference' is not allowed 42 | 43 | EOF 44 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified_prevent_destoying_with_weak.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - prevent original object from being destroyed forever, soft notifiers will not be called 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | get(); 21 | }); 22 | 23 | $wr = new Ref\WeakReference($obj, function (Ref\WeakReference $reference){ 24 | echo 'Weak notifier called', PHP_EOL; 25 | }); 26 | 27 | $obj = null; 28 | //$sr = null; 29 | 30 | echo 'Here soft reference notifier will be called', PHP_EOL; 31 | $obj_copy = null; 32 | 33 | ?> 34 | EOF 35 | --EXPECT-- 36 | Soft notifier called 37 | Here soft reference notifier will be called 38 | Soft notifier called 39 | EOF 40 | WeakTests\TrackingDtor's destructor called 41 | -------------------------------------------------------------------------------- /tests/004-SoftReference-serialize_not_allowed.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - serialize reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | dump($sr); 15 | 16 | try { 17 | $serialized = serialize($sr); 18 | $helper->dump($serialized); 19 | } catch (\Throwable $e) { 20 | $helper->exception_export($e); 21 | } 22 | 23 | $helper->line(); 24 | 25 | ?> 26 | EOF 27 | --EXPECT-- 28 | object(Ref\SoftReference)#3 (2) refcount(3){ 29 | ["referent":"Ref\AbstractReference":private]=> 30 | object(stdClass)#2 (0) refcount(2){ 31 | } 32 | ["notifier":"Ref\AbstractReference":private]=> 33 | object(Closure)#4 (1) refcount(2){ 34 | ["parameter"]=> 35 | array(1) refcount(1){ 36 | ["$reference"]=> 37 | string(10) "" refcount(1) 38 | } 39 | } 40 | } 41 | Exception: Serialization of 'Ref\SoftReference' is not allowed 42 | 43 | EOF 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016-2018 Bogdan Padalko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stubs/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016-2018 Bogdan Padalko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/002-WeakReference-exception_in_orig_dtor_and_callback.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - exception thrown in orig dtor and callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 36 | } 37 | 38 | ?> 39 | EOF 40 | --EXPECT-- 41 | Dtor called 42 | Callback called 43 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 44 | thrown: 45 | #0: Exception: Test exception from dtor 46 | #1: Exception: Test exception from callback 47 | EOF 48 | -------------------------------------------------------------------------------- /tests/004-SoftReference-exception_in_orig_dtor_and_callback.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - exception thrown in orig dtor and callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 36 | } 37 | 38 | ?> 39 | EOF 40 | --EXPECT-- 41 | Callback called 42 | Dtor called 43 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 44 | thrown: 45 | #0: Exception: Test exception from callback 46 | #1: Exception: Test exception from dtor 47 | EOF 48 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier_clone_change.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - change notifier on cloned object 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier is callback', $wr->notifier(), $callback_notifier_1); 23 | 24 | $wr1 = clone $wr; 25 | $helper->assert('Cloned notifier is array', $wr1->notifier(), $callback_notifier_1); 26 | $wr1->notifier($callback_notifier_2); 27 | $helper->assert('Cloned notifier changed to it own callback', $wr1->notifier(), $callback_notifier_2); 28 | 29 | $helper->line(); 30 | $obj = null; 31 | $helper->line(); 32 | ?> 33 | EOF 34 | --EXPECT-- 35 | Notifier is callback: ok 36 | Cloned notifier is array: ok 37 | Cloned notifier changed to it own callback: ok 38 | 39 | Callback notifier 2 40 | Callback notifier 1 41 | 42 | EOF 43 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier_clone_change.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - change notifier on cloned object 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier is callback', $sr->notifier(), $callback_notifier_1); 23 | 24 | $sr1 = clone $sr; 25 | $helper->assert('Cloned notifier is array', $sr1->notifier(), $callback_notifier_1); 26 | $sr1->notifier($callback_notifier_2); 27 | $helper->assert('Cloned notifier changed to it own callback', $sr1->notifier(), $callback_notifier_2); 28 | 29 | $helper->line(); 30 | $obj = null; 31 | $helper->line(); 32 | ?> 33 | EOF 34 | --EXPECT-- 35 | Notifier is callback: ok 36 | Cloned notifier is array: ok 37 | Cloned notifier changed to it own callback: ok 38 | 39 | Callback notifier 2 40 | Callback notifier 1 41 | 42 | EOF 43 | -------------------------------------------------------------------------------- /tests/002-WeakReference-multiple_weak.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - multiple weak references to the same object 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert("First weak references points to original object", $wr1->get() === $obj); 21 | $helper->assert("Second weak references points to original object", $wr2->get() === $obj); 22 | 23 | $helper->line(); 24 | $obj = null; 25 | $helper->line(); 26 | 27 | $helper->assert("First weak references points to null", $wr1->get() === null); 28 | $helper->assert("Second weak references points to null", $wr2->get() === null); 29 | 30 | $wr1 = null; 31 | $wr2 = null; 32 | 33 | ?> 34 | EOF 35 | --EXPECT-- 36 | First weak references points to original object: ok 37 | Second weak references points to original object: ok 38 | 39 | WeakTests\TrackingDtor's destructor called 40 | 41 | First weak references points to null: ok 42 | Second weak references points to null: ok 43 | EOF 44 | -------------------------------------------------------------------------------- /tests/004-SoftReference-multiple_weak.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - multiple soft references to the same object 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert("First soft references points to original object", $sr1->get() === $obj); 21 | $helper->assert("Second soft references points to original object", $sr2->get() === $obj); 22 | 23 | $helper->line(); 24 | $obj = null; 25 | $helper->line(); 26 | 27 | $helper->assert("First soft references points to null", $sr1->get() === null); 28 | $helper->assert("Second soft references points to null", $sr2->get() === null); 29 | 30 | $sr1 = null; 31 | $sr2 = null; 32 | 33 | ?> 34 | EOF 35 | --EXPECT-- 36 | First soft references points to original object: ok 37 | Second soft references points to original object: ok 38 | 39 | WeakTests\TrackingDtor's destructor called 40 | 41 | First soft references points to null: ok 42 | Second soft references points to null: ok 43 | EOF 44 | -------------------------------------------------------------------------------- /tests/005-Soft-and-Weak-Reference-exception-before-and-from-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference + Ref\WeakReference - exception thrown outside notifier before destructing and in notifier 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 34 | } 35 | 36 | ?> 37 | EOF 38 | --EXPECT-- 39 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 40 | previous: RuntimeException: Test exception 41 | thrown: 42 | #0: RuntimeException: From soft notifier 43 | #1: RuntimeException: From weak notifier 44 | EOF 45 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - soft reference notified when object destroyed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier called', true); 15 | $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); 16 | $helper->assert('Notifier get soft reference as it argument', $reference instanceof Ref\SoftReference); 17 | $helper->assert('Original object is null', null === $obj); 18 | $helper->assert('Soft reference in notifier is not null', null !== $reference->get()); 19 | $helper->assert('Soft reference in notifier points to original object', $reference->get() instanceof stdClass); 20 | }); 21 | 22 | $obj = null; 23 | 24 | ?> 25 | EOF 26 | --EXPECT-- 27 | Notifier called: ok 28 | Notifier get 1 argument: ok 29 | Notifier get soft reference as it argument: ok 30 | Original object is null: ok 31 | Soft reference in notifier is not null: ok 32 | Soft reference in notifier points to original object: ok 33 | EOF 34 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified_prevent_destoying_multiple.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - do not call later soft notifiers when object was prevented from being destroyed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | get(); 31 | } 32 | }); 33 | 34 | 35 | $obj = null; 36 | $obj_copy = null; 37 | 38 | ?> 39 | EOF 40 | --EXPECT-- 41 | Backup soft notifier called 42 | Original object was prevented from being destroyed 43 | Backup soft notifier called 44 | Soft notifier called 45 | WeakTests\TrackingDtor's destructor called 46 | EOF 47 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier_not_called_after_wr_dies_first.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - notifier not called after weak reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 30 | $helper->line(); 31 | } 32 | 33 | $helper->assert('Referent object dead', $wr->get() === null); 34 | $helper->assert('Referent object invalid', $wr->valid(), false); 35 | $helper->line(); 36 | 37 | ?> 38 | EOF 39 | --EXPECT-- 40 | Weak notifier called 41 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 42 | thrown: 43 | #0: Exception: Destructor throws exception 44 | 45 | Referent object dead: ok 46 | Referent object invalid: ok 47 | 48 | EOF 49 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier_not_called_after_wr_dies_first.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - notifier not called after weak reference dies first 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 30 | $helper->line(); 31 | } 32 | 33 | $helper->assert('Referent object dead', $sr->get() === null); 34 | $helper->assert('Referent object invalid', $sr->valid(), false); 35 | $helper->line(); 36 | 37 | ?> 38 | EOF 39 | --EXPECT-- 40 | Weak notifier called 41 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 42 | thrown: 43 | #0: Exception: Destructor throws exception 44 | 45 | Referent object dead: ok 46 | Referent object invalid: ok 47 | 48 | EOF 49 | -------------------------------------------------------------------------------- /tests/001-verify-method-case.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check whether all methods follows naming convention 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getClasses(); 10 | 11 | 12 | class MethodsCaseVerifier 13 | { 14 | private $invalid = []; 15 | 16 | public function verifyClass(ReflectionClass $class) 17 | { 18 | foreach ($class->getMethods() as $m) { 19 | $this->verifyMethod($m); 20 | } 21 | } 22 | 23 | public function verifyMethod(ReflectionMethod $method) 24 | { 25 | if (strtolower($method->getName()[0]) == $method->getName()[0]) { 26 | return; 27 | } 28 | 29 | $method_name = $method->getDeclaringClass()->getName() . '::' . $method->getName(); 30 | 31 | if (isset($this->invalid[$method_name])) { 32 | return; 33 | } 34 | 35 | $this->invalid[$method_name] = true; 36 | 37 | echo "{$method_name}() - invalid method name", PHP_EOL; 38 | } 39 | 40 | public function isValid() 41 | { 42 | return empty($this->invalid); 43 | } 44 | } 45 | 46 | $v = new MethodsCaseVerifier(); 47 | 48 | 49 | foreach ($classes as $c) { 50 | $v->verifyClass($c); 51 | } 52 | 53 | if ($v->isValid()) { 54 | echo 'All methods are valid', PHP_EOL; 55 | } 56 | 57 | ?> 58 | --EXPECT-- 59 | All methods are valid 60 | -------------------------------------------------------------------------------- /tests/002-WeakReference-reference-deleted-during-notifier.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - reference deleted during notifier call 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | storage[$hash]); 20 | var_dump($this); 21 | }); 22 | 23 | $this->storage[$hash] = $key; 24 | } 25 | } 26 | 27 | $map = new Test(); 28 | 29 | $key_1 = new stdClass(); 30 | $value_1 = new stdClass(); 31 | 32 | $map->put($key_1, $value_1, 'test'); 33 | 34 | $key_1 = null; 35 | ?> 36 | --EXPECT-- 37 | {closure} 38 | object(Test)#1 (1) { 39 | ["storage"]=> 40 | array(1) { 41 | ["test"]=> 42 | object(Ref\WeakReference)#4 (2) { 43 | ["referent":"Ref\AbstractReference":private]=> 44 | NULL 45 | ["notifier":"Ref\AbstractReference":private]=> 46 | object(Closure)#5 (2) { 47 | ["static"]=> 48 | array(1) { 49 | ["hash"]=> 50 | string(4) "test" 51 | } 52 | ["this"]=> 53 | *RECURSION* 54 | } 55 | } 56 | } 57 | } 58 | object(Test)#1 (1) { 59 | ["storage"]=> 60 | array(0) { 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /provision/apache/000-default.conf: -------------------------------------------------------------------------------- 1 | 2 | # The ServerName directive sets the request scheme, hostname and port that 3 | # the server uses to identify itself. This is used when creating 4 | # redirection URLs. In the context of virtual hosts, the ServerName 5 | # specifies what hostname must appear in the request's Host: header to 6 | # match this virtual host. For the default virtual host (this file) this 7 | # value is not decisive as it is used as a last resort host regardless. 8 | # However, you must set it for any further virtual host explicitly. 9 | #ServerName www.example.com 10 | 11 | ServerAdmin webmaster@localhost 12 | DocumentRoot /var/www/html 13 | 14 | DirectoryIndex index.php index-apache.html 15 | 16 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 17 | # error, crit, alert, emerg. 18 | # It is also possible to configure the loglevel for particular 19 | # modules, e.g. 20 | #LogLevel info ssl:warn 21 | 22 | ErrorLog ${APACHE_LOG_DIR}/error.log 23 | CustomLog ${APACHE_LOG_DIR}/access.log combined 24 | 25 | # For most configuration files from conf-available/, which are 26 | # enabled or disabled at a global level, it is possible to 27 | # include a line for only one particular virtual host. For example the 28 | # following line enables the CGI configuration for this host only 29 | # after it has been globally disabled with "a2disconf". 30 | #Include conf-available/serve-cgi-bin.conf 31 | 32 | 33 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 34 | -------------------------------------------------------------------------------- /php_ref.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #ifndef PHP_REF_H 14 | #define PHP_REF_H 15 | 16 | #include "php.h" 17 | 18 | extern zend_module_entry php_ref_module_entry; 19 | #define phpext_ref_ptr &php_ref_module_entry 20 | 21 | #ifndef PHP_REF_VERSION 22 | #define PHP_REF_VERSION "0.7.0" 23 | #endif 24 | 25 | #ifndef PHP_REF_REVISION 26 | #define PHP_REF_REVISION "dev" 27 | #endif 28 | 29 | #if PHP_VERSION_ID < 70200 30 | // should never get her, but just in case 31 | #error PHP >= 7.2 required 32 | #endif 33 | 34 | #define PHP_REF_NS "Ref" 35 | 36 | #ifdef PHP_WIN32 37 | # define PHP_REF_API __declspec(dllexport) 38 | #elif defined(__GNUC__) && __GNUC__ >= 4 39 | # define PHP_REF_API __attribute__ ((visibility("default"))) 40 | #else 41 | # define PHP_REF_API 42 | #endif 43 | 44 | 45 | #ifdef ZTS 46 | #include "TSRM.h" 47 | #endif 48 | 49 | ZEND_BEGIN_MODULE_GLOBALS(ref) 50 | HashTable *referents; 51 | ZEND_END_MODULE_GLOBALS(ref) 52 | 53 | ZEND_EXTERN_MODULE_GLOBALS(ref); 54 | #define PHP_REF_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(ref, v) 55 | 56 | #if defined(ZTS) && defined(COMPILE_DL_REF) 57 | ZEND_TSRMLS_CACHE_EXTERN(); 58 | #endif 59 | 60 | #endif /* PHP_REF_H */ 61 | -------------------------------------------------------------------------------- /tests/002-WeakReference-basic.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - dump representation, get() and valid() methods 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('When referent object alive'); 16 | $helper->assert('Referent object alive', $wr->get() === $obj); 17 | $helper->assert('Referent object valid', $wr->valid()); 18 | $helper->dump($wr); 19 | $helper->space(); 20 | 21 | $obj = null; 22 | 23 | 24 | $helper->header('When referent object dead'); 25 | $helper->assert('Referent object dead', $wr->get() === null); 26 | $helper->assert('Referent object invalid', $wr->valid(), false); 27 | $helper->dump($wr); 28 | $helper->line(); 29 | ?> 30 | EOF 31 | --EXPECT-- 32 | When referent object alive: 33 | --------------------------- 34 | Referent object alive: ok 35 | Referent object valid: ok 36 | object(Ref\WeakReference)#3 (2) refcount(3){ 37 | ["referent":"Ref\AbstractReference":private]=> 38 | object(stdClass)#2 (0) refcount(2){ 39 | } 40 | ["notifier":"Ref\AbstractReference":private]=> 41 | NULL 42 | } 43 | 44 | 45 | When referent object dead: 46 | -------------------------- 47 | Referent object dead: ok 48 | Referent object invalid: ok 49 | object(Ref\WeakReference)#3 (2) refcount(3){ 50 | ["referent":"Ref\AbstractReference":private]=> 51 | NULL 52 | ["notifier":"Ref\AbstractReference":private]=> 53 | NULL 54 | } 55 | 56 | EOF 57 | -------------------------------------------------------------------------------- /tests/004-SofReference-basic.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - dump representation, get() and valid() methods 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('When referent object alive'); 16 | $helper->assert('Referent object alive', $ref->get() === $obj); 17 | $helper->assert('Referent object valid', $ref->valid()); 18 | $helper->dump($ref); 19 | $helper->space(); 20 | 21 | $obj = null; 22 | 23 | 24 | $helper->header('When referent object dead'); 25 | $helper->assert('Referent object dead', $ref->get() === null); 26 | $helper->assert('Referent object invalid', $ref->valid(), false); 27 | $helper->dump($ref); 28 | $helper->line(); 29 | ?> 30 | EOF 31 | --EXPECT-- 32 | When referent object alive: 33 | --------------------------- 34 | Referent object alive: ok 35 | Referent object valid: ok 36 | object(Ref\SoftReference)#3 (2) refcount(3){ 37 | ["referent":"Ref\AbstractReference":private]=> 38 | object(stdClass)#2 (0) refcount(2){ 39 | } 40 | ["notifier":"Ref\AbstractReference":private]=> 41 | NULL 42 | } 43 | 44 | 45 | When referent object dead: 46 | -------------------------- 47 | Referent object dead: ok 48 | Referent object invalid: ok 49 | object(Ref\SoftReference)#3 (2) refcount(3){ 50 | ["referent":"Ref\AbstractReference":private]=> 51 | NULL 52 | ["notifier":"Ref\AbstractReference":private]=> 53 | NULL 54 | } 55 | 56 | EOF 57 | -------------------------------------------------------------------------------- /tests/002-WeakReference-dump_extended.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - dump representation of extended reference class 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 21 | 22 | $obj = null; 23 | 24 | var_dump($wr); 25 | $helper->line(); 26 | ?> 27 | EOF 28 | --EXPECT-- 29 | object(WeakTests\ExtendedWeakReference)#3 (3) { 30 | ["test":"WeakTests\ExtendedWeakReference":private]=> 31 | array(1) { 32 | [0]=> 33 | int(42) 34 | } 35 | ["referent":"Ref\AbstractReference":private]=> 36 | object(stdClass)#2 (0) { 37 | } 38 | ["notifier":"Ref\AbstractReference":private]=> 39 | object(Closure)#4 (1) { 40 | ["parameter"]=> 41 | array(1) { 42 | ["$reference"]=> 43 | string(10) "" 44 | } 45 | } 46 | } 47 | 48 | object(WeakTests\ExtendedWeakReference)#3 (3) { 49 | ["test":"WeakTests\ExtendedWeakReference":private]=> 50 | array(1) { 51 | [0]=> 52 | int(42) 53 | } 54 | ["referent":"Ref\AbstractReference":private]=> 55 | NULL 56 | ["notifier":"Ref\AbstractReference":private]=> 57 | object(Closure)#4 (1) { 58 | ["parameter"]=> 59 | array(1) { 60 | ["$reference"]=> 61 | string(10) "" 62 | } 63 | } 64 | } 65 | 66 | EOF 67 | -------------------------------------------------------------------------------- /tests/004-SoftReference-dump_extended.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - dump representation of extended reference class 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 21 | 22 | $obj = null; 23 | 24 | var_dump($sr); 25 | $helper->line(); 26 | ?> 27 | EOF 28 | --EXPECT-- 29 | object(WeakTests\ExtendedSoftReference)#3 (3) { 30 | ["test":"WeakTests\ExtendedSoftReference":private]=> 31 | array(1) { 32 | [0]=> 33 | int(42) 34 | } 35 | ["referent":"Ref\AbstractReference":private]=> 36 | object(stdClass)#2 (0) { 37 | } 38 | ["notifier":"Ref\AbstractReference":private]=> 39 | object(Closure)#4 (1) { 40 | ["parameter"]=> 41 | array(1) { 42 | ["$reference"]=> 43 | string(10) "" 44 | } 45 | } 46 | } 47 | 48 | object(WeakTests\ExtendedSoftReference)#3 (3) { 49 | ["test":"WeakTests\ExtendedSoftReference":private]=> 50 | array(1) { 51 | [0]=> 52 | int(42) 53 | } 54 | ["referent":"Ref\AbstractReference":private]=> 55 | NULL 56 | ["notifier":"Ref\AbstractReference":private]=> 57 | object(Closure)#4 (1) { 58 | ["parameter"]=> 59 | array(1) { 60 | ["$reference"]=> 61 | string(10) "" 62 | } 63 | } 64 | } 65 | 66 | EOF 67 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # All Vagrant configuration is done here. The most common configuration 9 | # options are documented and commented below. For a complete reference, 10 | # please see the online documentation at vagrantup.com. 11 | 12 | # Every Vagrant virtual environment requires a box to build off of. 13 | config.vm.box = "bento/ubuntu-16.04" 14 | 15 | # Disable automatic box update checking. If you disable this, then 16 | # boxes will only be checked for updates when the user runs 17 | # `vagrant box outdated`. This is not recommended. 18 | # config.vm.box_check_update = false 19 | 20 | # Create a private network, which allows host-only access to the machine 21 | # using a specific IP. 22 | config.vm.network "private_network", ip: "192.168.33.102" 23 | 24 | # NOTE: temporary workaround 25 | #config.vm.hostname = "php-ref.vagrant" 26 | config.vm.provision :shell, inline: "hostnamectl set-hostname php-ref.vagrant" 27 | 28 | config.ssh.insert_key = false 29 | 30 | config.vm.synced_folder ".", "/home/vagrant/php-ref" 31 | 32 | config.vm.provider "virtualbox" do |vb| 33 | # Don't boot with headless mode 34 | #vb.gui = true 35 | 36 | #vb.customize ["modifyvm", :id, "--memory", 2048] 37 | #vb.customize ["modifyvm", :id, "--cpus", 4] 38 | end 39 | 40 | config.vm.provision "shell", path: './provision/provision.sh', privileged: false 41 | end 42 | -------------------------------------------------------------------------------- /tests/002-WeakReference-spl_object_storage_debug_hash_consistent.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - SplObjectStorage hashes in debug output still consistent before and after wrapping 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | attach($obj); 15 | $wr = new Ref\WeakReference($obj); 16 | $current_hash = spl_object_hash($obj); 17 | $helper->assert('Stored in SplObjectStorage weak-referenced object hash matches origin one', $original_hash == $current_hash); 18 | 19 | ob_start(); 20 | debug_zval_dump($s); 21 | $res = ob_get_contents(); 22 | ob_end_clean(); 23 | 24 | $helper->assert('Object hash in SplObjectStorage debug output not changed', false !== strpos($res, $original_hash)); 25 | $helper->line(); 26 | 27 | debug_zval_dump($original_hash); 28 | debug_zval_dump($current_hash); 29 | echo $res; 30 | 31 | $helper->line(); 32 | 33 | ?> 34 | EOF 35 | --EXPECTF-- 36 | Stored in SplObjectStorage weak-referenced object hash matches origin one: ok 37 | Object hash in SplObjectStorage debug output not changed: ok 38 | 39 | string(32) "%s" refcount(2) 40 | string(32) "%s" refcount(2) 41 | object(SplObjectStorage)#2 (1) refcount(2){ 42 | ["storage":"SplObjectStorage":private]=> 43 | array(1) refcount(1){ 44 | ["%s"]=> 45 | array(2) refcount(1){ 46 | ["obj"]=> 47 | object(stdClass)#3 (0) refcount(2){ 48 | } 49 | ["inf"]=> 50 | NULL 51 | } 52 | } 53 | } 54 | 55 | EOF 56 | -------------------------------------------------------------------------------- /tests/004-SoftReference-spl_object_storage_debug_hash_consistent.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - SplObjectStorage hashes in debug output still consistent before and after wrapping 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | attach($obj); 15 | $sr = new Ref\SoftReference($obj); 16 | $current_hash = spl_object_hash($obj); 17 | $helper->assert('Stored in SplObjectStorage soft-referenced object hash matches origin one', $original_hash == $current_hash); 18 | 19 | ob_start(); 20 | debug_zval_dump($s); 21 | $res = ob_get_contents(); 22 | ob_end_clean(); 23 | 24 | $helper->assert('Object hash in SplObjectStorage debug output not changed', false !== strpos($res, $original_hash)); 25 | $helper->line(); 26 | 27 | debug_zval_dump($original_hash); 28 | debug_zval_dump($current_hash); 29 | echo $res; 30 | 31 | $helper->line(); 32 | 33 | ?> 34 | EOF 35 | --EXPECTF-- 36 | Stored in SplObjectStorage soft-referenced object hash matches origin one: ok 37 | Object hash in SplObjectStorage debug output not changed: ok 38 | 39 | string(32) "%s" refcount(2) 40 | string(32) "%s" refcount(2) 41 | object(SplObjectStorage)#2 (1) refcount(2){ 42 | ["storage":"SplObjectStorage":private]=> 43 | array(1) refcount(1){ 44 | ["%s"]=> 45 | array(2) refcount(1){ 46 | ["obj"]=> 47 | object(stdClass)#3 (0) refcount(2){ 48 | } 49 | ["inf"]=> 50 | NULL 51 | } 52 | } 53 | } 54 | 55 | EOF 56 | -------------------------------------------------------------------------------- /tests/002-WeakReference-spl_hash_consistent.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - spl_object_hash still consistent before and after wrapping 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Wrapped object hash matches origin one', $original_hash == $current_hash); 21 | 22 | 23 | $wr2 = new Ref\WeakReference($obj); 24 | 25 | $double_hash = spl_object_hash($obj); 26 | $helper->assert('Repeatedly wrapped object hash does not changes', $current_hash == $double_hash); 27 | 28 | $wr = null; 29 | 30 | $again_hash = spl_object_hash($obj); 31 | $helper->assert('Repeatedly wrapped object hash does not changes after some reference death', $current_hash == $again_hash); 32 | 33 | $wr2 = null; 34 | 35 | $nullified_hash = spl_object_hash($obj); 36 | $helper->assert('Wrapped object hash still not changed even after all references died', $current_hash == $original_hash); 37 | $helper->assert('Wrapped object hash still the same even after all references died', $current_hash == $nullified_hash); 38 | 39 | $helper->line(); 40 | ?> 41 | EOF 42 | --EXPECT-- 43 | Wrapped object hash matches origin one: ok 44 | Repeatedly wrapped object hash does not changes: ok 45 | Repeatedly wrapped object hash does not changes after some reference death: ok 46 | Wrapped object hash still not changed even after all references died: ok 47 | Wrapped object hash still the same even after all references died: ok 48 | 49 | EOF 50 | -------------------------------------------------------------------------------- /tests/004-SoftReference-spl_hash_consistent.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - spl_object_hash still consistent before and after wrapping 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Wrapped object hash matches origin one', $original_hash == $current_hash); 21 | 22 | 23 | $sr2 = new Ref\SoftReference($obj); 24 | 25 | $double_hash = spl_object_hash($obj); 26 | $helper->assert('Repeatedly wrapped object hash does not changes', $current_hash == $double_hash); 27 | 28 | $sr = null; 29 | 30 | $again_hash = spl_object_hash($obj); 31 | $helper->assert('Repeatedly wrapped object hash does not changes after some reference death', $current_hash == $again_hash); 32 | 33 | $sr2 = null; 34 | 35 | $nullified_hash = spl_object_hash($obj); 36 | $helper->assert('Wrapped object hash still not changed even after all references died', $current_hash == $original_hash); 37 | $helper->assert('Wrapped object hash still the same even after all references died', $current_hash == $nullified_hash); 38 | 39 | $helper->line(); 40 | ?> 41 | EOF 42 | --EXPECT-- 43 | Wrapped object hash matches origin one: ok 44 | Repeatedly wrapped object hash does not changes: ok 45 | Repeatedly wrapped object hash does not changes after some reference death: ok 46 | Wrapped object hash still not changed even after all references died: ok 47 | Wrapped object hash still the same even after all references died: ok 48 | 49 | EOF 50 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension ref 3 | 4 | PHP_ARG_ENABLE(ref, whether to enable ref support, 5 | dnl Make sure that the comment is aligned: 6 | [ --enable-ref Enable ref support]) 7 | 8 | if test "$PHP_REF" != "no"; then 9 | 10 | AC_MSG_CHECKING([Check for supported PHP versions]) 11 | PHP_REF_FOUND_VERSION=`${PHP_CONFIG} --version` 12 | PHP_REF_FOUND_VERNUM=`${PHP_CONFIG} --vernum` 13 | 14 | if test "$PHP_REF_FOUND_VERNUM" -lt "70200"; then 15 | AC_MSG_ERROR([not supported. PHP version >= 7.2 required (found $PHP_REF_FOUND_VERSION)]) 16 | else 17 | AC_MSG_RESULT([supported ($PHP_REF_FOUND_VERSION)]) 18 | fi 19 | 20 | echo "$PHP_VERSION_ID" 21 | 22 | if test -z "$TRAVIS" ; then 23 | type git &>/dev/null 24 | 25 | if test $? -eq 0 ; then 26 | git describe --abbrev=0 --tags &>/dev/null 27 | 28 | if test $? -eq 0 ; then 29 | AC_DEFINE_UNQUOTED([PHP_REF_VERSION], ["`git describe --abbrev=0 --tags`-`git rev-parse --abbrev-ref HEAD`-dev"], [git version]) 30 | fi 31 | 32 | git rev-parse --short HEAD &>/dev/null 33 | 34 | if test $? -eq 0 ; then 35 | AC_DEFINE_UNQUOTED([PHP_REF_REVISION], ["`git rev-parse --short HEAD`"], [git revision]) 36 | fi 37 | else 38 | AC_MSG_NOTICE([git not installed. Cannot obtain php-ref version tag. Install git.]) 39 | fi 40 | fi 41 | 42 | PHP_NEW_EXTENSION(ref, [ \ 43 | ref.c \ 44 | php_ref_notifier_exception.c \ 45 | php_ref_reference.c \ 46 | php_ref_functions.c \ 47 | ], $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) 48 | fi 49 | -------------------------------------------------------------------------------- /tests/002-WeakReference-clone_extended.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - clone reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 32 | 33 | $wr2 = clone $wr; 34 | 35 | var_dump($wr2); 36 | $helper->line(); 37 | 38 | 39 | ?> 40 | EOF 41 | --EXPECT-- 42 | object(WeakTests\ExtendedWeakReference)#4 (3) { 43 | ["test":"WeakTests\ExtendedWeakReference":private]=> 44 | array(1) { 45 | [0]=> 46 | int(42) 47 | } 48 | ["referent":"Ref\AbstractReference":private]=> 49 | object(stdClass)#2 (0) { 50 | } 51 | ["notifier":"Ref\AbstractReference":private]=> 52 | object(Closure)#3 (1) { 53 | ["parameter"]=> 54 | array(1) { 55 | ["$ref"]=> 56 | string(10) "" 57 | } 58 | } 59 | } 60 | 61 | object(WeakTests\ExtendedWeakReference)#5 (3) { 62 | ["test":"WeakTests\ExtendedWeakReference":private]=> 63 | array(1) { 64 | [0]=> 65 | int(42) 66 | } 67 | ["referent":"Ref\AbstractReference":private]=> 68 | object(stdClass)#2 (0) { 69 | } 70 | ["notifier":"Ref\AbstractReference":private]=> 71 | object(Closure)#3 (1) { 72 | ["parameter"]=> 73 | array(1) { 74 | ["$ref"]=> 75 | string(10) "" 76 | } 77 | } 78 | } 79 | 80 | EOF 81 | -------------------------------------------------------------------------------- /tests/002-WeakReference-exception_in_multiple_callbacks.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - exception thrown in callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 42 | } 43 | 44 | 45 | $helper->line(); 46 | ?> 47 | EOF 48 | --EXPECT-- 49 | WeakTests\TrackingDtor's destructor called 50 | Callback #0 called 51 | Callback #1 called 52 | Callback #2 called 53 | Callback #3 called 54 | Callback #4 called 55 | Callback #5 called 56 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 57 | thrown: 58 | #0: Exception: Test exception from callback #1 59 | #1: Exception: Test exception from callback #3 60 | 61 | EOF 62 | -------------------------------------------------------------------------------- /tests/004-SoftReference-exception_in_multiple_callbacks.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - exception thrown in callback 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | ref_exception_export($e); 42 | } 43 | 44 | 45 | $helper->line(); 46 | ?> 47 | EOF 48 | --EXPECT-- 49 | Callback #0 called 50 | Callback #1 called 51 | Callback #2 called 52 | Callback #3 called 53 | Callback #4 called 54 | Callback #5 called 55 | WeakTests\TrackingDtor's destructor called 56 | Ref\NotifierException: One or more exceptions thrown during notifiers calling 57 | thrown: 58 | #0: Exception: Test exception from callback #1 59 | #1: Exception: Test exception from callback #3 60 | 61 | EOF 62 | -------------------------------------------------------------------------------- /tests/004-SoftReference-clone_extended.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - clone reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | line(); 32 | 33 | $ref2 = clone $ref; 34 | 35 | var_dump($ref2); 36 | $helper->line(); 37 | 38 | 39 | ?> 40 | EOF 41 | --EXPECT-- 42 | object(WeakTests\ExtendedSoftReference)#4 (3) { 43 | ["test":"WeakTests\ExtendedSoftReference":private]=> 44 | array(1) { 45 | [0]=> 46 | int(42) 47 | } 48 | ["referent":"Ref\AbstractReference":private]=> 49 | object(stdClass)#2 (0) { 50 | } 51 | ["notifier":"Ref\AbstractReference":private]=> 52 | object(Closure)#3 (1) { 53 | ["parameter"]=> 54 | array(1) { 55 | ["$ref"]=> 56 | string(10) "" 57 | } 58 | } 59 | } 60 | 61 | object(WeakTests\ExtendedSoftReference)#5 (3) { 62 | ["test":"WeakTests\ExtendedSoftReference":private]=> 63 | array(1) { 64 | [0]=> 65 | int(42) 66 | } 67 | ["referent":"Ref\AbstractReference":private]=> 68 | object(stdClass)#2 (0) { 69 | } 70 | ["notifier":"Ref\AbstractReference":private]=> 71 | object(Closure)#3 (1) { 72 | ["parameter"]=> 73 | array(1) { 74 | ["$ref"]=> 75 | string(10) "" 76 | } 77 | } 78 | } 79 | 80 | EOF 81 | -------------------------------------------------------------------------------- /tests/002-AbstractReference-basic.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\AbstractReference - dump representation, get() and valid() methods 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | exception_export($e); 19 | $helper->space(); 20 | } 21 | 22 | 23 | $ref = new \WeakTests\TestAbstractReference($obj); 24 | 25 | $helper->header('When referent object alive'); 26 | $helper->assert('Referent object dead', $ref->get() === $obj); 27 | $helper->assert('Referent object invalid', $ref->valid(), false); 28 | $helper->dump($ref); 29 | $helper->space(); 30 | 31 | $obj = null; 32 | 33 | 34 | $helper->header('When referent object dead'); 35 | $helper->assert('Referent object dead', $ref->get() === $obj); 36 | $helper->assert('Referent object invalid', $ref->valid(), false); 37 | $helper->dump($ref); 38 | $helper->line(); 39 | 40 | 41 | 42 | ?> 43 | --EXPECT-- 44 | Error: Cannot instantiate abstract class Ref\AbstractReference 45 | 46 | 47 | When referent object alive: 48 | --------------------------- 49 | Referent object dead: failed 50 | Referent object invalid: ok 51 | object(WeakTests\TestAbstractReference)#4 (2) refcount(3){ 52 | ["referent":"Ref\AbstractReference":private]=> 53 | NULL 54 | ["notifier":"Ref\AbstractReference":private]=> 55 | NULL 56 | } 57 | 58 | 59 | When referent object dead: 60 | -------------------------- 61 | Referent object dead: ok 62 | Referent object invalid: ok 63 | object(WeakTests\TestAbstractReference)#4 (2) refcount(3){ 64 | ["referent":"Ref\AbstractReference":private]=> 65 | NULL 66 | ["notifier":"Ref\AbstractReference":private]=> 67 | NULL 68 | } 69 | -------------------------------------------------------------------------------- /tests/002-WeakReference-object-handle-reuse.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - reference should work for newly create tracked objects with same handles as previously tracked objects 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | storage[$hash]); 21 | }); 22 | 23 | $value = new WeakReference($value, function () use ($hash) { 24 | unset($this->storage[$hash]); 25 | }); 26 | 27 | $this->storage[$hash] = [$key, $value]; 28 | } 29 | } 30 | 31 | 32 | $map = new Test(); 33 | 34 | $key_1 = new stdClass(); 35 | $value_1 = new stdClass(); 36 | 37 | $key_2 = new stdClass(); 38 | $value_2 = new stdClass(); 39 | 40 | $map->put($key_1, $value_1, 'test_1'); 41 | $map->put($key_2, $value_2, 'test_2'); 42 | 43 | $helper->assert('count', 2, count($map->storage)); 44 | 45 | 46 | $key_1 = null; 47 | $helper->assert('count', 1, count($map->storage)); 48 | 49 | 50 | $value_2 = null; 51 | $helper->assert('count', 0, count($map->storage)); 52 | 53 | $key_1 = new stdClass(); 54 | $value_1 = new stdClass(); 55 | 56 | $key_2 = new stdClass(); 57 | $value_2 = new stdClass(); 58 | 59 | $map->put($key_1, $value_1, 'test_1'); 60 | $map->put($key_2, $value_2, 'test_2'); 61 | 62 | $helper->assert('count', 2, count($map->storage)); 63 | 64 | $key_1 = null; 65 | $helper->assert('count', 1, count($map->storage)); 66 | 67 | $value_2 = null; 68 | $helper->assert('count', 0, count($map->storage)); 69 | 70 | 71 | ?> 72 | --EXPECT-- 73 | count: ok 74 | count: ok 75 | count: ok 76 | count: ok 77 | count: ok 78 | count: ok 79 | -------------------------------------------------------------------------------- /tests/002-WeakReference-closure.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - referencing closure 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('When referent object alive'); 18 | $helper->assert('Referent object alive', $wr->get() === $obj); 19 | $helper->dump($wr); 20 | $helper->space(); 21 | 22 | $obj = null; 23 | 24 | $helper->header('When referent object dead'); 25 | $helper->assert('Referent object dead', $wr->get() === null); 26 | 27 | $helper->dump($wr); 28 | 29 | $helper->line(); 30 | ?> 31 | EOF 32 | --EXPECT-- 33 | When referent object alive: 34 | --------------------------- 35 | Referent object alive: ok 36 | object(Ref\WeakReference)#3 (2) refcount(3){ 37 | ["referent":"Ref\AbstractReference":private]=> 38 | object(Closure)#2 (1) refcount(2){ 39 | ["parameter"]=> 40 | array(1) refcount(1){ 41 | ["$greeting"]=> 42 | string(10) "" refcount(1) 43 | } 44 | } 45 | ["notifier":"Ref\AbstractReference":private]=> 46 | object(Closure)#4 (1) refcount(2){ 47 | ["parameter"]=> 48 | array(1) refcount(1){ 49 | ["$reference"]=> 50 | string(10) "" refcount(1) 51 | } 52 | } 53 | } 54 | 55 | 56 | When referent object dead: 57 | -------------------------- 58 | Referent object dead: ok 59 | object(Ref\WeakReference)#3 (2) refcount(3){ 60 | ["referent":"Ref\AbstractReference":private]=> 61 | NULL 62 | ["notifier":"Ref\AbstractReference":private]=> 63 | object(Closure)#4 (1) refcount(2){ 64 | ["parameter"]=> 65 | array(1) refcount(1){ 66 | ["$reference"]=> 67 | string(10) "" refcount(1) 68 | } 69 | } 70 | } 71 | 72 | EOF 73 | -------------------------------------------------------------------------------- /tests/004-SoftReference-closure.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - referencing closure 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('When referent object alive'); 18 | $helper->assert('Referent object alive', $sr->get() === $obj); 19 | $helper->dump($sr); 20 | $helper->space(); 21 | 22 | $obj = null; 23 | 24 | $helper->header('When referent object dead'); 25 | $helper->assert('Referent object dead', $sr->get() === null); 26 | 27 | $helper->dump($sr); 28 | 29 | $helper->line(); 30 | ?> 31 | EOF 32 | --EXPECT-- 33 | When referent object alive: 34 | --------------------------- 35 | Referent object alive: ok 36 | object(Ref\SoftReference)#3 (2) refcount(3){ 37 | ["referent":"Ref\AbstractReference":private]=> 38 | object(Closure)#2 (1) refcount(2){ 39 | ["parameter"]=> 40 | array(1) refcount(1){ 41 | ["$greeting"]=> 42 | string(10) "" refcount(1) 43 | } 44 | } 45 | ["notifier":"Ref\AbstractReference":private]=> 46 | object(Closure)#4 (1) refcount(2){ 47 | ["parameter"]=> 48 | array(1) refcount(1){ 49 | ["$reference"]=> 50 | string(10) "" refcount(1) 51 | } 52 | } 53 | } 54 | 55 | 56 | When referent object dead: 57 | -------------------------- 58 | Referent object dead: ok 59 | object(Ref\SoftReference)#3 (2) refcount(3){ 60 | ["referent":"Ref\AbstractReference":private]=> 61 | NULL 62 | ["notifier":"Ref\AbstractReference":private]=> 63 | object(Closure)#4 (1) refcount(2){ 64 | ["parameter"]=> 65 | array(1) refcount(1){ 66 | ["$reference"]=> 67 | string(10) "" refcount(1) 68 | } 69 | } 70 | } 71 | 72 | EOF 73 | -------------------------------------------------------------------------------- /provision/nginx/default: -------------------------------------------------------------------------------- 1 | # You may add here your 2 | # server { 3 | # ... 4 | # } 5 | # statements for each of your virtual hosts to this file 6 | 7 | ## 8 | # You should look at the following URL's in order to grasp a solid understanding 9 | # of Nginx configuration files in order to fully unleash the power of Nginx. 10 | # http://wiki.nginx.org/Pitfalls 11 | # http://wiki.nginx.org/QuickStart 12 | # http://wiki.nginx.org/Configuration 13 | # 14 | # Generally, you will want to move this file somewhere, and start with a clean 15 | # file but keep this around for reference. Or just disable in sites-enabled. 16 | # 17 | # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. 18 | ## 19 | 20 | server { 21 | listen 80 default_server; 22 | listen [::]:80 default_server ipv6only=on; 23 | 24 | root /var/www/html; 25 | index index.php index-nginx.html; 26 | 27 | # Make site accessible from http://localhost/ 28 | server_name localhost; 29 | 30 | location / { 31 | # First attempt to serve request as file, then 32 | # as directory, then fall back to displaying a 404. 33 | try_files $uri $uri/ =404; 34 | # Uncomment to enable naxsi on this location 35 | # include /etc/nginx/naxsi.rules 36 | } 37 | 38 | # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests 39 | #location /RequestDenied { 40 | # proxy_pass http://127.0.0.1:8080; 41 | #} 42 | 43 | #error_page 404 /404.html; 44 | 45 | # redirect server error pages to the static page /50x.html 46 | # 47 | error_page 500 502 503 504 /50x.html; 48 | location = /50x.html { 49 | root /usr/share/nginx/html; 50 | } 51 | 52 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 53 | # 54 | location ~ \.php$ { 55 | include fastcgi_params; 56 | #fastcgi_split_path_info ^(.+\.php)(/.+)$; 57 | # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini 58 | 59 | # With php5-cgi alone: 60 | fastcgi_pass 127.0.0.1:9000; 61 | # With php5-fpm: 62 | #fastcgi_pass unix:/var/run/php5-fpm.sock; 63 | fastcgi_index index.php; 64 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 65 | } 66 | } -------------------------------------------------------------------------------- /php_ref_reference.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #ifndef PHP_REF_REFERENCE_H 14 | #define PHP_REF_REFERENCE_H 15 | 16 | #include "php.h" 17 | 18 | #ifdef ZTS 19 | #include "TSRM.h" 20 | #endif 21 | 22 | extern zend_class_entry *php_ref_abstract_reference_class_entry; 23 | extern zend_class_entry *php_ref_soft_reference_class_entry; 24 | extern zend_class_entry *php_ref_weak_reference_class_entry; 25 | 26 | 27 | typedef struct _php_ref_referent_t php_ref_referent_t; 28 | typedef struct _php_ref_reference_t php_ref_reference_t; 29 | 30 | typedef void (*php_ref_register)(php_ref_reference_t *reference, php_ref_referent_t *referent); 31 | typedef void (*php_ref_unregister)(php_ref_reference_t *reference); 32 | 33 | extern php_ref_reference_t *php_ref_reference_fetch_object(zend_object *obj); 34 | extern php_ref_referent_t *php_ref_referent_find_ptr(zend_ulong h); 35 | extern void php_ref_globals_referents_ht_dtor(zval *zv); 36 | 37 | 38 | #define PHP_REF_REFERENCE_FETCH(zv) php_ref_reference_fetch_object(Z_OBJ_P(zv)) 39 | #define PHP_REF_REFERENCE_FETCH_INTO(pzval, into) php_ref_reference_t *(into) = PHP_REF_REFERENCE_FETCH((pzval)); 40 | 41 | struct _php_ref_referent_t { 42 | zval this_ptr; 43 | uint32_t handle; 44 | 45 | zend_object_handlers custom_handlers; 46 | const zend_object_handlers *original_handlers; 47 | 48 | HashTable soft_references; 49 | HashTable weak_references; 50 | 51 | uint32_t tracked; 52 | }; 53 | 54 | struct _php_ref_reference_t { 55 | php_ref_referent_t *referent; 56 | 57 | zval notifier; 58 | 59 | php_ref_register register_reference; 60 | php_ref_unregister unregister_reference; 61 | 62 | zval this_ptr; 63 | zend_object std; 64 | }; 65 | 66 | 67 | PHP_MINIT_FUNCTION(php_ref_reference); 68 | 69 | 70 | #endif /* PHP_REF_REFERENCE_H */ 71 | -------------------------------------------------------------------------------- /tests/003-functions-weakrefcounted_after_all_refs_died.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\functions - test weakrefcount() functions after all references death 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('Test before any reference created'); 26 | $helper->dump_annotated('weakrefcounted($obj)', weakrefcounted($obj)); 27 | $helper->dump_annotated('weakrefcount($obj)', weakrefcount($obj)); 28 | $helper->dump_annotated('weakrefs($obj)', weakrefs($obj)); 29 | $helper->line(); 30 | 31 | 32 | $ref = new WeakReference($obj); 33 | 34 | $helper->header('Test when reference created'); 35 | $helper->dump_annotated('weakrefcounted($obj)', weakrefcounted($obj)); 36 | $helper->dump_annotated('weakrefcount($obj)', weakrefcount($obj)); 37 | $helper->dump_annotated('weakrefs($obj)', weakrefs($obj)); 38 | $helper->line(); 39 | 40 | $ref = null; 41 | 42 | $helper->header('Test when reference destroyed'); 43 | $helper->dump_annotated('weakrefcounted($obj)', weakrefcounted($obj)); 44 | $helper->dump_annotated('weakrefcount($obj)', weakrefcount($obj)); 45 | $helper->dump_annotated('weakrefs($obj)', weakrefs($obj)); 46 | $helper->line(); 47 | 48 | ?> 49 | --EXPECT-- 50 | Test before any reference created: 51 | ---------------------------------- 52 | weakrefcounted($obj): bool(false) 53 | weakrefcount($obj): int(0) 54 | weakrefs($obj): array(0) refcount(3){ 55 | } 56 | 57 | Test when reference created: 58 | ---------------------------- 59 | weakrefcounted($obj): bool(true) 60 | weakrefcount($obj): int(1) 61 | weakrefs($obj): array(1) refcount(3){ 62 | [0]=> 63 | object(Ref\WeakReference)#3 (2) refcount(2){ 64 | ["referent":"Ref\AbstractReference":private]=> 65 | object(stdClass)#2 (0) refcount(2){ 66 | } 67 | ["notifier":"Ref\AbstractReference":private]=> 68 | NULL 69 | } 70 | } 71 | 72 | Test when reference destroyed: 73 | ------------------------------ 74 | weakrefcounted($obj): bool(false) 75 | weakrefcount($obj): int(0) 76 | weakrefs($obj): array(0) refcount(3){ 77 | } 78 | -------------------------------------------------------------------------------- /tests/002-AbstractReference-clone.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\AbstractReference - clone reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | export_annotated('weakrefcount($obj)', weakrefcount($obj)); 26 | $helper->export_annotated('softrefcount($obj)', softrefcount($obj)); 27 | var_dump($ar); 28 | $helper->line(); 29 | 30 | $ar2 = clone $ar; 31 | 32 | $helper->assert('Cloned abstract reference matches original', $ar == $ar2); 33 | $helper->assert('Cloned abstract reference does not match original abstract reference strictly', $ar !== $ar2); 34 | $helper->line(); 35 | 36 | $helper->export_annotated('weakrefcount($obj)', weakrefcount($obj)); 37 | $helper->export_annotated('softrefcount($obj)', softrefcount($obj)); 38 | var_dump($ar2); 39 | $helper->line(); 40 | 41 | $helper->assert('Abstract references reported with cloned reference', weakrefs($obj), [$ar, $ar2]); 42 | $helper->line(); 43 | 44 | $obj = null; 45 | $helper->line(); 46 | 47 | $helper->assert('Cloned abstract reference matches original', $ar == $ar2); 48 | $helper->assert('Cloned abstract reference does not match original weak reference strictly', $ar !== $ar2); 49 | $helper->line(); 50 | 51 | 52 | ?> 53 | EOF 54 | --EXPECT-- 55 | weakrefcount($obj): integer: 0 56 | softrefcount($obj): integer: 0 57 | object(WeakTests\TestAbstractReference)#4 (2) { 58 | ["referent":"Ref\AbstractReference":private]=> 59 | NULL 60 | ["notifier":"Ref\AbstractReference":private]=> 61 | object(Closure)#3 (0) { 62 | } 63 | } 64 | 65 | Cloned abstract reference matches original: ok 66 | Cloned abstract reference does not match original abstract reference strictly: ok 67 | 68 | weakrefcount($obj): integer: 0 69 | softrefcount($obj): integer: 0 70 | object(WeakTests\TestAbstractReference)#5 (2) { 71 | ["referent":"Ref\AbstractReference":private]=> 72 | NULL 73 | ["notifier":"Ref\AbstractReference":private]=> 74 | object(Closure)#3 (0) { 75 | } 76 | } 77 | 78 | Abstract references reported with cloned reference: failed 79 | 80 | 81 | Cloned abstract reference matches original: ok 82 | Cloned abstract reference does not match original weak reference strictly: ok 83 | 84 | EOF 85 | -------------------------------------------------------------------------------- /tests/002-WeakReference-notifier_change.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - changing notifier 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier is null by default', $wr->notifier(), null); 23 | $helper->assert('Notifier was null', $wr->notifier($callback_notifier_1), NULL); 24 | $helper->assert('Notifier is callback', $wr->notifier(), $callback_notifier_1); 25 | 26 | $obj = null; 27 | $helper->line(); 28 | 29 | 30 | $obj = new stdClass(); 31 | $wr = new Ref\WeakReference($obj, $callback_notifier_1); 32 | 33 | $helper->assert('Notifier is callback by default', $wr->notifier(), $callback_notifier_1); 34 | $helper->assert('Notifier was callback', $wr->notifier($callback_notifier_2), $callback_notifier_1); 35 | $helper->assert('Notifier is callback', $wr->notifier(), $callback_notifier_2); 36 | 37 | $obj = null; 38 | $helper->line(); 39 | 40 | $obj = new stdClass(); 41 | $wr = new Ref\WeakReference($obj, $callback_notifier_1); 42 | 43 | $helper->assert('Notifier is callback by default', $wr->notifier(), $callback_notifier_1); 44 | $helper->assert('Notifier was callback', $wr->notifier($callback_notifier_2), $callback_notifier_1); 45 | $helper->assert('Notifier is callback', $wr->notifier(), $callback_notifier_2); 46 | 47 | $obj = null; 48 | $helper->line(); 49 | 50 | 51 | $notifier = 'var_dump'; 52 | $wr->notifier($notifier); 53 | 54 | try { 55 | $wr->notifier('nonexistent'); 56 | } catch (Error $e) { 57 | $helper->exception_export($e); 58 | } 59 | 60 | $helper->assert('Notifier stays the same', $wr->notifier(), $notifier); 61 | $helper->line(); 62 | 63 | ?> 64 | EOF 65 | --EXPECT-- 66 | Notifier is null by default: ok 67 | Notifier was null: ok 68 | Notifier is callback: ok 69 | Callback notifier 1 70 | 71 | Notifier is callback by default: ok 72 | Notifier was callback: ok 73 | Notifier is callback: ok 74 | Callback notifier 2 75 | 76 | Notifier is callback by default: ok 77 | Notifier was callback: ok 78 | Notifier is callback: ok 79 | Callback notifier 2 80 | 81 | TypeError: Argument 1 passed to Ref\AbstractReference::notifier() must be callable or null, string given 82 | Notifier stays the same: ok 83 | 84 | EOF 85 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notifier_change.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - changing notifier 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier is null by default', $sr->notifier(), null); 23 | $helper->assert('Notifier was null', $sr->notifier($callback_notifier_1), NULL); 24 | $helper->assert('Notifier is callback', $sr->notifier(), $callback_notifier_1); 25 | 26 | $obj = null; 27 | $helper->line(); 28 | 29 | 30 | $obj = new stdClass(); 31 | $sr = new Ref\SoftReference($obj, $callback_notifier_1); 32 | 33 | $helper->assert('Notifier is callback by default', $sr->notifier(), $callback_notifier_1); 34 | $helper->assert('Notifier was callback', $sr->notifier($callback_notifier_2), $callback_notifier_1); 35 | $helper->assert('Notifier is callback', $sr->notifier(), $callback_notifier_2); 36 | 37 | $obj = null; 38 | $helper->line(); 39 | 40 | $obj = new stdClass(); 41 | $sr = new Ref\SoftReference($obj, $callback_notifier_1); 42 | 43 | $helper->assert('Notifier is callback by default', $sr->notifier(), $callback_notifier_1); 44 | $helper->assert('Notifier was callback', $sr->notifier($callback_notifier_2), $callback_notifier_1); 45 | $helper->assert('Notifier is callback', $sr->notifier(), $callback_notifier_2); 46 | 47 | $obj = null; 48 | $helper->line(); 49 | 50 | 51 | $notifier = 'var_dump'; 52 | $sr->notifier($notifier); 53 | 54 | try { 55 | $sr->notifier('nonexistent'); 56 | } catch (Error $e) { 57 | $helper->exception_export($e); 58 | } 59 | 60 | $helper->assert('Notifier stays the same', $sr->notifier(), $notifier); 61 | $helper->line(); 62 | 63 | ?> 64 | EOF 65 | --EXPECT-- 66 | Notifier is null by default: ok 67 | Notifier was null: ok 68 | Notifier is callback: ok 69 | Callback notifier 1 70 | 71 | Notifier is callback by default: ok 72 | Notifier was callback: ok 73 | Notifier is callback: ok 74 | Callback notifier 2 75 | 76 | Notifier is callback by default: ok 77 | Notifier was callback: ok 78 | Notifier is callback: ok 79 | Callback notifier 2 80 | 81 | TypeError: Argument 1 passed to Ref\AbstractReference::notifier() must be callable or null, string given 82 | Notifier stays the same: ok 83 | 84 | EOF 85 | -------------------------------------------------------------------------------- /tests/002-WeakReference-spl_object_storage_hash_consistent.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - SplObjectStorage::getHash() still consistent before and after wrapping 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getHash($obj); 16 | 17 | $wr = new Ref\WeakReference($obj); 18 | 19 | $current_hash = $s->getHash($obj); 20 | 21 | $helper->assert('Wrapped object hash matches origin one', $original_hash == $current_hash); 22 | 23 | 24 | $wr2 = new Ref\WeakReference($obj); 25 | 26 | $double_hash = $s->getHash($obj); 27 | $helper->assert('Repeatedly wrapped object hash does not changes', $current_hash == $double_hash); 28 | 29 | $wr = null; 30 | 31 | $again_hash = $s->getHash($obj); 32 | $helper->assert('Repeatedly wrapped object hash does not changes after some reference death', $current_hash == $again_hash); 33 | 34 | $wr2 = null; 35 | 36 | $nullified_hash = $s->getHash($obj); 37 | $helper->assert('Wrapped object hash still not changed even after all references died', $current_hash == $original_hash); 38 | $helper->assert('Wrapped object hash still the same even after all references died', $current_hash == $nullified_hash); 39 | 40 | $helper->line(); 41 | 42 | $s = new SplObjectStorage(); 43 | $obj = new stdClass(); 44 | $original_hash = spl_object_hash($obj); 45 | $s->attach($obj); 46 | $current_hash = spl_object_hash($obj); 47 | $helper->assert('Stored in SplObjectStorage object hash matches origin one', $original_hash == $current_hash); 48 | 49 | $s = new SplObjectStorage(); 50 | $obj = new stdClass(); 51 | $original_hash = spl_object_hash($obj); 52 | $wr = new Ref\WeakReference($obj); 53 | $s->attach($wr); 54 | $current_hash = spl_object_hash($obj); 55 | $helper->assert('Stored in SplObjectStorage weak-referenced object hash matches origin one', $original_hash == $current_hash); 56 | 57 | $helper->line(); 58 | 59 | ?> 60 | EOF 61 | --EXPECT-- 62 | Wrapped object hash matches origin one: ok 63 | Repeatedly wrapped object hash does not changes: ok 64 | Repeatedly wrapped object hash does not changes after some reference death: ok 65 | Wrapped object hash still not changed even after all references died: ok 66 | Wrapped object hash still the same even after all references died: ok 67 | 68 | Stored in SplObjectStorage object hash matches origin one: ok 69 | Stored in SplObjectStorage weak-referenced object hash matches origin one: ok 70 | 71 | EOF 72 | -------------------------------------------------------------------------------- /tests/004-SoftReference-spl_object_storage_hash_consistent.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - SplObjectStorage::getHash() still consistent before and after wrapping 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getHash($obj); 16 | 17 | $sr = new Ref\SoftReference($obj); 18 | 19 | $current_hash = $s->getHash($obj); 20 | 21 | $helper->assert('Wrapped object hash matches origin one', $original_hash == $current_hash); 22 | 23 | 24 | $sr2 = new Ref\SoftReference($obj); 25 | 26 | $double_hash = $s->getHash($obj); 27 | $helper->assert('Repeatedly wrapped object hash does not changes', $current_hash == $double_hash); 28 | 29 | $sr = null; 30 | 31 | $again_hash = $s->getHash($obj); 32 | $helper->assert('Repeatedly wrapped object hash does not changes after some reference death', $current_hash == $again_hash); 33 | 34 | $sr2 = null; 35 | 36 | $nullified_hash = $s->getHash($obj); 37 | $helper->assert('Wrapped object hash still not changed even after all references died', $current_hash == $original_hash); 38 | $helper->assert('Wrapped object hash still the same even after all references died', $current_hash == $nullified_hash); 39 | 40 | $helper->line(); 41 | 42 | $s = new SplObjectStorage(); 43 | $obj = new stdClass(); 44 | $original_hash = spl_object_hash($obj); 45 | $s->attach($obj); 46 | $current_hash = spl_object_hash($obj); 47 | $helper->assert('Stored in SplObjectStorage object hash matches origin one', $original_hash == $current_hash); 48 | 49 | $s = new SplObjectStorage(); 50 | $obj = new stdClass(); 51 | $original_hash = spl_object_hash($obj); 52 | $sr = new Ref\SoftReference($obj); 53 | $s->attach($sr); 54 | $current_hash = spl_object_hash($obj); 55 | $helper->assert('Stored in SplObjectStorage soft-referenced object hash matches origin one', $original_hash == $current_hash); 56 | 57 | $helper->line(); 58 | 59 | ?> 60 | EOF 61 | --EXPECT-- 62 | Wrapped object hash matches origin one: ok 63 | Repeatedly wrapped object hash does not changes: ok 64 | Repeatedly wrapped object hash does not changes after some reference death: ok 65 | Wrapped object hash still not changed even after all references died: ok 66 | Wrapped object hash still the same even after all references died: ok 67 | 68 | Stored in SplObjectStorage object hash matches origin one: ok 69 | Stored in SplObjectStorage soft-referenced object hash matches origin one: ok 70 | 71 | EOF 72 | -------------------------------------------------------------------------------- /stubs/src/functions.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 9 | * 10 | * For the full copyright and license information, please view the 11 | * LICENSE file that was distributed with this source or visit 12 | * http://opensource.org/licenses/MIT 13 | */ 14 | 15 | 16 | namespace Ref; 17 | 18 | /** 19 | * Check whether value is refcounted 20 | * 21 | * @param mixed $value 22 | * 23 | * @return bool 24 | */ 25 | function refcounted(mixed $value): bool 26 | { 27 | } 28 | 29 | /** 30 | * Get real object references count (not counting value reference passed as a function argument) 31 | * 32 | * @param object $object 33 | * 34 | * @return int 35 | */ 36 | function refcount(object $object): int 37 | { 38 | } 39 | 40 | /** 41 | * Check whether object has soft references 42 | * 43 | * @param object $value 44 | * 45 | * @return bool 46 | */ 47 | function softrefcounted(object $value): bool 48 | { 49 | } 50 | 51 | /** 52 | * Get object's soft references count 53 | * 54 | * @param object $value 55 | * 56 | * @return int 57 | */ 58 | function softrefcount(object $value): int 59 | { 60 | } 61 | 62 | /** 63 | * Get object's soft references 64 | * 65 | * @param object $value 66 | * 67 | * @return mixed 68 | */ 69 | function softrefs(object $value): array 70 | { 71 | } 72 | 73 | /** 74 | * Check whether object has weak references 75 | * 76 | * @param object $value 77 | * 78 | * @return bool 79 | */ 80 | function weakrefcounted(object $value): bool 81 | { 82 | } 83 | 84 | /** 85 | * Get object's weak references count 86 | * 87 | * @param object $value 88 | * 89 | * @return int 90 | */ 91 | function weakrefcount(object $value): int 92 | { 93 | } 94 | 95 | /** 96 | * Get object's weak references 97 | * 98 | * @param object $value 99 | * 100 | * @return mixed 101 | */ 102 | function weakrefs(object $value): array 103 | { 104 | } 105 | 106 | /** 107 | * Get object's handle id 108 | * 109 | * @param object $value 110 | * 111 | * @return int 112 | */ 113 | function object_handle(object $value): int 114 | { 115 | } 116 | 117 | /** 118 | * Check whether object's destructor was called 119 | * 120 | * @param object $value 121 | * 122 | * @return bool 123 | */ 124 | function is_obj_destructor_called(object $value): bool 125 | { 126 | } 127 | -------------------------------------------------------------------------------- /stubs/src/AbstractReference.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 9 | * 10 | * For the full copyright and license information, please view the 11 | * LICENSE file that was distributed with this source or visit 12 | * http://opensource.org/licenses/MIT 13 | */ 14 | 15 | namespace Ref; 16 | 17 | 18 | /** 19 | * Class AbstractReference 20 | * 21 | * Abstract base class for reference objects. This class defines the operations common to all reference objects. 22 | * Because of reference objects implementation details which depends on how GC in PHP works, this class may not be 23 | * subclassed directly. 24 | */ 25 | abstract class AbstractReference 26 | { 27 | /** 28 | * @param object $referent Referent object 29 | * @param callable|null $notify Optional notifier to signal when referent object destroyed. 30 | * 31 | * If notifier is callback, it will be called with a current reference object as a first argument. 32 | * 33 | * For SoftReference, notifiers are called before object will (or will not) be destructed. If referent object 34 | * prevented from being destroyed (regular reference to it created during SoftReference notifiers calling), original 35 | * object's destructor will not be called and next time referent object refcount will reach 0 it will be a subject of 36 | * full destructing cycle again. For WeakReference, referent object will be already destroyed at the time of 37 | * notifiers calling. 38 | */ 39 | public function __construct(object $referent, callable $notify = null) 40 | { 41 | } 42 | 43 | /** 44 | * Get referent object 45 | * 46 | * @return object|null 47 | */ 48 | public function get() 49 | { 50 | } 51 | 52 | /** 53 | * Whether referent object exists 54 | * 55 | * @return bool 56 | */ 57 | public function valid(): bool 58 | { 59 | } 60 | 61 | /** 62 | * Get notifier 63 | * 64 | * @param callable|null $notify Notifier to replace existent notifier with. Same as in constructor. 65 | * 66 | * If any value provided, any existent notifier will be replaced and returned. 67 | * 68 | * @return callable|null Current notifier or the old one when replacing it with provided value. 69 | */ 70 | public function notifier(callable $notify = null): ?callable 71 | { 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/.stubs.php: -------------------------------------------------------------------------------- 1 | storage[$offset]); 35 | } 36 | 37 | public function offsetGet($offset) 38 | { 39 | echo static::class, '::offsetGet called', PHP_EOL; 40 | return $this->storage[$offset]; 41 | } 42 | 43 | public function offsetSet($offset, $value) 44 | { 45 | echo static::class, '::offsetSet called', PHP_EOL; 46 | $this->storage[$offset] = $value; 47 | } 48 | 49 | public function offsetUnset($offset) 50 | { 51 | echo static::class, '::offsetUnset called', PHP_EOL; 52 | unset($this->storage[$offset]); 53 | } 54 | } 55 | 56 | class TestArrayAccessCountable extends TestArrayAccess implements \Countable { 57 | 58 | public function count() 59 | { 60 | return count($this->storage); 61 | } 62 | } 63 | 64 | class TestProperties { 65 | public $public = 'public'; 66 | public $public_for_test = 'public for test'; 67 | public $public_for_proxy = 'public for proxy'; 68 | 69 | protected $protected = 'protected'; 70 | 71 | private $private = 'private'; 72 | } 73 | 74 | class ExtendedWeakReference extends \Ref\WeakReference { 75 | /** 76 | * @var 77 | */ 78 | private $test = []; 79 | 80 | public function __construct($referent, $notify, $test) 81 | { 82 | parent::__construct($referent, $notify); 83 | $this->test = $test; 84 | } 85 | } 86 | 87 | class ExtendedSoftReference extends \Ref\SoftReference { 88 | /** 89 | * @var 90 | */ 91 | private $test = []; 92 | 93 | public function __construct($referent, $notify, $test) 94 | { 95 | parent::__construct($referent, $notify); 96 | $this->test = $test; 97 | } 98 | } 99 | 100 | class TestAbstractReference extends \Ref\AbstractReference 101 | { 102 | } 103 | -------------------------------------------------------------------------------- /ref.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #ifdef HAVE_CONFIG_H 14 | #include "config.h" 15 | #endif 16 | 17 | #include "php_ref_functions.h" 18 | #include "php_ref_reference.h" 19 | #include "php_ref_notifier_exception.h" 20 | #include "php_ref.h" 21 | 22 | #include "ext/standard/info.h" 23 | 24 | ZEND_DECLARE_MODULE_GLOBALS(ref) 25 | 26 | /* True global resources - no need for thread safety here */ 27 | static int le_ref; 28 | 29 | PHP_MINIT_FUNCTION(ref) 30 | { 31 | PHP_MINIT(php_ref_notifier_exception)(INIT_FUNC_ARGS_PASSTHRU); 32 | PHP_MINIT(php_ref_reference)(INIT_FUNC_ARGS_PASSTHRU); 33 | 34 | return SUCCESS; 35 | } 36 | 37 | PHP_MSHUTDOWN_FUNCTION(ref) 38 | { 39 | return SUCCESS; 40 | } 41 | 42 | PHP_RINIT_FUNCTION(ref) 43 | { 44 | #if defined(COMPILE_DL_REF) && defined(ZTS) 45 | ZEND_TSRMLS_CACHE_UPDATE(); 46 | #endif 47 | return SUCCESS; 48 | } 49 | 50 | PHP_RSHUTDOWN_FUNCTION(ref) 51 | { 52 | if (NULL != PHP_REF_G(referents)) { 53 | zend_hash_destroy(PHP_REF_G(referents)); 54 | FREE_HASHTABLE(PHP_REF_G(referents)); 55 | PHP_REF_G(referents) = NULL; 56 | } 57 | 58 | return SUCCESS; 59 | } 60 | 61 | 62 | PHP_MINFO_FUNCTION(ref) 63 | { 64 | php_info_print_table_start(); 65 | php_info_print_table_header(2, "Ref support", "enabled"); 66 | php_info_print_table_row(2, "Version", PHP_REF_VERSION); 67 | php_info_print_table_row(2, "Revision", PHP_REF_REVISION); 68 | php_info_print_table_row(2, "Compiled", __DATE__ " @ " __TIME__); 69 | 70 | php_info_print_table_end(); 71 | } 72 | 73 | static PHP_GINIT_FUNCTION(ref) 74 | { 75 | ref_globals->referents = NULL; 76 | } 77 | 78 | zend_module_entry php_ref_module_entry = { 79 | STANDARD_MODULE_HEADER, 80 | "ref", 81 | php_ref_functions, 82 | PHP_MINIT(ref), 83 | PHP_MSHUTDOWN(ref), 84 | PHP_RINIT(ref), 85 | PHP_RSHUTDOWN(ref), 86 | PHP_MINFO(ref), 87 | PHP_REF_VERSION, 88 | PHP_MODULE_GLOBALS(ref), 89 | PHP_GINIT(ref), 90 | NULL, 91 | NULL, 92 | STANDARD_MODULE_PROPERTIES_EX 93 | }; 94 | 95 | #ifdef COMPILE_DL_REF 96 | #ifdef ZTS 97 | ZEND_TSRMLS_CACHE_DEFINE(); 98 | #endif 99 | ZEND_GET_MODULE(php_ref) 100 | #endif 101 | -------------------------------------------------------------------------------- /tests/001-verify-methods-signature.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check whether methods signature is valid 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getClasses(); 10 | 11 | 12 | class Verifier 13 | { 14 | private $invalid = []; 15 | 16 | public function verifyClass(ReflectionClass $class) 17 | { 18 | foreach ($class->getMethods() as $m) { 19 | $this->verifyMethod($m); 20 | } 21 | } 22 | 23 | public function verifyMethod(ReflectionMethod $method) 24 | { 25 | foreach ($method->getParameters() as $p) { 26 | $this->verifyParameter($p); 27 | } 28 | 29 | if ($method->getReturnType()) { 30 | $type = $method->getReturnType(); 31 | 32 | if (!$type || $type->isBuiltin()) { 33 | return; 34 | } 35 | 36 | if(!class_exists($type) && !interface_exists($type)) { 37 | $method_name = $method->getDeclaringClass()->getName() . '::' . $method->getName(); 38 | $shortcut = $method_name . '/return type'; 39 | if (isset($this->invalid[$shortcut])) { 40 | return; 41 | } 42 | 43 | $this->invalid[$shortcut] = true; 44 | 45 | echo "{$method_name}() method's return type is invalid ($type)", PHP_EOL; 46 | } 47 | } 48 | 49 | } 50 | 51 | protected function verifyReturnType(ReflectionType $rt) { 52 | 53 | } 54 | 55 | public function verifyParameter(ReflectionParameter $parameter) 56 | { 57 | $type = $parameter->getType(); 58 | 59 | if (!$type || $type->isBuiltin()) { 60 | return; 61 | } 62 | 63 | if (!class_exists($type) && !interface_exists($type)) { 64 | $method_name = $parameter->getDeclaringClass()->getName() . '::' . $parameter->getDeclaringFunction()->getName(); 65 | $param_name = $parameter->getName(); 66 | 67 | $shortcut = $method_name . '/' . $param_name; 68 | 69 | if (isset($this->invalid[$shortcut])) { 70 | return; 71 | } 72 | 73 | $this->invalid[$shortcut] = true; 74 | 75 | echo "{$method_name}() method's parameter {$parameter->getName()} has invalid type ($type)", PHP_EOL; 76 | } 77 | } 78 | 79 | public function isValid() 80 | { 81 | return empty($this->invalid); 82 | } 83 | } 84 | 85 | $v = new Verifier(); 86 | 87 | 88 | foreach ($classes as $c) { 89 | $v->verifyClass($c); 90 | } 91 | 92 | if ($v->isValid()) { 93 | echo 'All method parameters are valid', PHP_EOL; 94 | } 95 | 96 | ?> 97 | --EXPECT-- 98 | All method parameters are valid 99 | -------------------------------------------------------------------------------- /provision/provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo Provisioning... 4 | sudo apt-get update 5 | sudo apt-get -y autoremove 6 | 7 | # Make sure these tools installed 8 | sudo DEBIAN_FRONTEND=noninteractive apt-get install -y git htop curl tshark pkgconf 9 | # Add PPA with fresh PHP 7: 10 | sudo add-apt-repository -u -y ppa:ondrej/php 11 | 12 | # Install available php from packages 13 | sudo apt-get install -y php7.2 php7.2-dev php7.2-fpm 14 | 15 | # Configure php-fpm 16 | sudo cp ~/php-ref/provision/php/www.conf /etc/php/7.2/fpm/pool.d/www.conf 17 | sudo service php7.2-fpm restart 18 | 19 | # Fix php executable detection with PHP7 (https://github.com/oerdnj/deb.sury.org/issues/142) 20 | sudo sed -i -e 's/^php_cli_binary=NONE$/php_cli_binary="\/usr\/bin\/php"/g' /usr/bin/php-config 21 | 22 | # Install phpbrew to manage php versions 23 | curl -L -O -s https://github.com/phpbrew/phpbrew/raw/master/phpbrew 24 | chmod +x phpbrew 25 | sudo mv phpbrew /usr/bin/phpbrew 26 | phpbrew init 27 | 28 | cp ~/php-ref/provision/.bashrc ~/.bashrc 29 | 30 | sudo mkdir -p /var/www/html/ 31 | sudo chown -R vagrant:vagrant /var/www 32 | 33 | # Requirements to build php from sources, uncomment if you decide to do so 34 | sudo apt-get install -y \ 35 | libxml2-dev \ 36 | libcurl4-openssl-dev \ 37 | libjpeg-dev \ 38 | libpng-dev \ 39 | libxpm-dev \ 40 | libmcrypt-dev \ 41 | libmysqlclient-dev \ 42 | libpq-dev \ 43 | libicu-dev \ 44 | libfreetype6-dev \ 45 | libldap2-dev \ 46 | libxslt-dev \ 47 | libbz2-dev \ 48 | libreadline-dev \ 49 | autoconf \ 50 | libtool \ 51 | pkg-config 52 | 53 | # Valgrind for investigating and plumbing memory-related problems 54 | sudo apt-get install valgrind 55 | 56 | # Benchmarking... 57 | sudo apt-get install -y apache2-utils 58 | # For Apache-based installation 59 | sudo apt-get install -y apache2 libapache2-mod-php 60 | 61 | # Move Apache to port 8080 62 | sudo cp ~/php-ref/provision/apache/000-default.conf /etc/apache2/sites-available/000-default.conf 63 | sudo cp ~/php-ref/provision/apache/ports.conf /etc/apache2/ports.conf 64 | sudo service apache2 restart 65 | 66 | sudo cp -f /var/www/html/index.html /var/www/html/index-apache.html 67 | 68 | sudo bash -c "echo ' /var/www/html/index.php" 69 | 70 | # For Nginx-based installation 71 | sudo apt-get install -y nginx 72 | sudo cp ~/php-ref/provision/nginx/default /etc/nginx/sites-available/default 73 | sudo service nginx restart 74 | sudo cp -f /usr/share/nginx/html/index.html /var/www/html/index-nginx.html 75 | 76 | 77 | # Do it manually when you need it, 78 | #cd ~/php-ref 79 | #phpize --clean && phpize && ./configure && sudo make install 80 | #sudo cp ~/php-ref/provision/php/ref.ini /etc/php/mods-available/ 81 | #sudo phpenmod -v ALL ref 82 | #sudo service php7.2-fpm restart 83 | 84 | # For debugging segfault when extension fails in php-fpm mode: 85 | #sudo sh -c "echo '/home/vagrant/php-ref/coredump-%e.%p' > /proc/sys/kernel/core_pattern" 86 | 87 | # To test with typical dev configuration - with xdebug: 88 | #sudo apt-get install -y php-xdebug 89 | 90 | # Cleanup unused stuff 91 | sudo apt-get autoremove -y 92 | 93 | # This is for the future 94 | # At this point it is good idea to do `phpbrew install 7.2` (or other version you want to test extension with) 95 | # and `phpbrew ext install ~/php-ref/` 96 | 97 | date > /home/vagrant/vagrant_provisioned_at 98 | -------------------------------------------------------------------------------- /tests/003-functions-soft.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\functions - test functions 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('Test Ref\softrefcounted'); 39 | $helper->export_annotated('softrefcounted($obj1)', softrefcounted($obj1)); 40 | $helper->export_annotated('softrefcounted($obj2)', softrefcounted($obj2)); 41 | $helper->export_annotated('softrefcounted($obj3)', softrefcounted($obj3)); 42 | $helper->line(); 43 | 44 | $helper->header('Test Ref\softrefcount'); 45 | $helper->export_annotated('softrefcount($obj1)', softrefcount($obj1)); 46 | $helper->export_annotated('softrefcount($obj2)', softrefcount($obj2)); 47 | $helper->export_annotated('softrefcount($obj3)', softrefcount($obj3)); 48 | $helper->line(); 49 | 50 | $helper->header('Test Ref\softrefs'); 51 | 52 | $helper->assert('Multiple soft refs reported for object with softrefs()', softrefs($obj1), [$soft_ref1a, $soft_ref1b]); 53 | $helper->dump(softrefs($obj1)); 54 | $helper->line(); 55 | 56 | $helper->assert('Single soft ref reported for object with softrefs()', softrefs($obj2), [$soft_ref2]); 57 | $helper->dump(softrefs($obj2)); 58 | $helper->line(); 59 | 60 | $helper->assert('No soft refs reported for object with softrefs()', softrefs($obj3), []); 61 | $helper->dump(softrefs($obj3)); 62 | $helper->line(); 63 | 64 | ?> 65 | --EXPECT-- 66 | Test Ref\softrefcounted: 67 | ------------------------ 68 | softrefcounted($obj1): boolean: true 69 | softrefcounted($obj2): boolean: true 70 | softrefcounted($obj3): boolean: false 71 | 72 | Test Ref\softrefcount: 73 | ---------------------- 74 | softrefcount($obj1): integer: 2 75 | softrefcount($obj2): integer: 1 76 | softrefcount($obj3): integer: 0 77 | 78 | Test Ref\softrefs: 79 | ------------------ 80 | Multiple soft refs reported for object with softrefs(): ok 81 | array(2) refcount(2){ 82 | [0]=> 83 | object(Ref\SoftReference)#5 (2) refcount(2){ 84 | ["referent":"Ref\AbstractReference":private]=> 85 | object(stdClass)#2 (0) refcount(3){ 86 | } 87 | ["notifier":"Ref\AbstractReference":private]=> 88 | NULL 89 | } 90 | [1]=> 91 | object(Ref\SoftReference)#6 (2) refcount(2){ 92 | ["referent":"Ref\AbstractReference":private]=> 93 | object(stdClass)#2 (0) refcount(3){ 94 | } 95 | ["notifier":"Ref\AbstractReference":private]=> 96 | NULL 97 | } 98 | } 99 | 100 | Single soft ref reported for object with softrefs(): ok 101 | array(1) refcount(2){ 102 | [0]=> 103 | object(Ref\SoftReference)#7 (2) refcount(2){ 104 | ["referent":"Ref\AbstractReference":private]=> 105 | object(stdClass)#3 (0) refcount(2){ 106 | } 107 | ["notifier":"Ref\AbstractReference":private]=> 108 | NULL 109 | } 110 | } 111 | 112 | No soft refs reported for object with softrefs(): ok 113 | array(0) refcount(2){ 114 | } 115 | -------------------------------------------------------------------------------- /scripts/replace_expect.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 7 | * 8 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 9 | * 10 | * For the full copyright and license information, please view the 11 | * LICENSE file that was distributed with this source or visit 12 | * http://opensource.org/licenses/MIT 13 | */ 14 | 15 | $tests_dir = realpath(__DIR__ . '/../tests'); 16 | 17 | 18 | $mode = 'write'; 19 | 20 | $args = $argv; 21 | unset($args[0]); 22 | 23 | foreach ($argv as $i => $item) { 24 | if ($item == '--pretend') { 25 | $mode = 'pretend'; 26 | unset($args[$i]); 27 | } 28 | } 29 | 30 | if (count($args) > 1) { 31 | echo 'Invalid options', PHP_EOL; 32 | exit(1); 33 | } 34 | 35 | if ($args) { 36 | $mask = str_replace(['tests/', '.phpt', '.diff'], '', array_pop($args)); 37 | } else { 38 | $mask = '*'; 39 | } 40 | 41 | $iterator = new GlobIterator($tests_dir . "/{$mask}.out", FilesystemIterator::KEY_AS_FILENAME); 42 | 43 | foreach ($iterator as $item) { 44 | //var_dump($item); 45 | $out_file = $iterator->key(); 46 | $base_name = preg_replace('/\.out$/i', '', $iterator->key()); 47 | $test_file = $base_name . '.phpt'; 48 | 49 | $test_content = file_get_contents($tests_dir . '/' . $test_file); 50 | 51 | if (false !== ($pos = strpos($test_content, '--EXPECT--'))) { 52 | printf("--EXPECT-- [%s]" . PHP_EOL, $iterator->key()); 53 | 54 | $test_content = substr($test_content, 0, $pos); 55 | $test_content .= '--EXPECT--' . PHP_EOL; 56 | $test_content .= file_get_contents($tests_dir . '/' . $out_file); 57 | $test_content .= PHP_EOL; 58 | file_put_contents($tests_dir . '/' . $test_file, $test_content); 59 | 60 | foreach (['.diff', '.exp', '.log', '.mem', '.out', '.php', '.sh'] as $ext) { 61 | @unlink($tests_dir . '/' . $base_name . $ext); 62 | } 63 | 64 | continue; 65 | //} elseif (0) { 66 | } elseif (false !== ($pos = strpos($test_content, '--EXPECTF--'))) { 67 | 68 | printf("--EXPECTF-- [%s]" . PHP_EOL, $iterator->key()); 69 | 70 | // get replacements 71 | 72 | $tests = substr($test_content, 0, $pos); 73 | $result = file_get_contents($tests_dir . '/' . $out_file); 74 | 75 | preg_match_all('#// EXPECTF: \-\-\-(.+)#', $tests, $expectf_search); 76 | preg_match_all('#// EXPECTF: \+\+\+(.+)#', $tests, $expectf_replace); 77 | 78 | if (count($expectf_search) != count($expectf_replace)) { 79 | printf("please, edit manually [%s]: searches and replaces count doesn't match" . PHP_EOL, $iterator->key()); 80 | continue; 81 | } 82 | 83 | foreach (array_combine($expectf_search[1], $expectf_replace[1]) as $search => $replace) { 84 | $replace = str_replace('\\\\n', '!!(ಠ_ಠ)!!', $replace); 85 | $replace = str_replace('\\n', "\n", $replace); 86 | $replace = str_replace('!!(ಠ_ಠ)!!', '\\\\n', $replace); 87 | 88 | $result = preg_replace($search, $replace, $result); 89 | } 90 | 91 | $test_content = $tests; 92 | $test_content .= '--EXPECTF--' . PHP_EOL; 93 | $test_content .= $result; 94 | $test_content .= PHP_EOL; 95 | 96 | if ($mode == 'pretend') { 97 | echo $result, PHP_EOL; 98 | echo PHP_EOL; 99 | 100 | } elseif ($mode = 'write') { 101 | file_put_contents($tests_dir . '/' . $test_file, $test_content); 102 | 103 | foreach (['.diff', '.exp', '.log', '.mem', '.out', '.php', '.sh'] as $ext) { 104 | @unlink($tests_dir . '/' . $base_name . $ext); 105 | } 106 | } 107 | 108 | continue; 109 | } 110 | 111 | printf("please, edit manually [%s]" . PHP_EOL, $iterator->key()); 112 | } 113 | -------------------------------------------------------------------------------- /tests/002-WeakReference-clone.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\WeakReference - clone reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | dump($ref); 21 | }; 22 | 23 | $wr = new \Ref\WeakReference($obj, $notifier); 24 | 25 | $helper->export_annotated('weakrefcount($obj)', weakrefcount($obj)); 26 | $helper->dump($wr); 27 | $helper->line(); 28 | 29 | $wr2 = clone $wr; 30 | 31 | $helper->assert('Cloned weak reference matches original', $wr == $wr2); 32 | $helper->assert('Cloned weak reference does not match original weak reference strictly', $wr !== $wr2); 33 | $helper->line(); 34 | 35 | $helper->export_annotated('weakrefcount($obj)', weakrefcount($obj)); 36 | $helper->dump($wr2); 37 | $helper->line(); 38 | 39 | $helper->assert('Weak references reported with cloned reference', weakrefs($obj), [$wr, $wr2]); 40 | $helper->line(); 41 | 42 | $obj = null; 43 | $helper->line(); 44 | 45 | $helper->assert('Cloned weak reference matches original', $wr == $wr2); 46 | $helper->assert('Cloned weak reference does not match original weak reference strictly', $wr !== $wr2); 47 | $helper->line(); 48 | 49 | 50 | ?> 51 | EOF 52 | --EXPECT-- 53 | weakrefcount($obj): integer: 1 54 | object(Ref\WeakReference)#4 (2) refcount(3){ 55 | ["referent":"Ref\AbstractReference":private]=> 56 | object(stdClass)#2 (0) refcount(2){ 57 | } 58 | ["notifier":"Ref\AbstractReference":private]=> 59 | object(Closure)#3 (2) refcount(3){ 60 | ["static"]=> 61 | array(1) refcount(1){ 62 | ["helper"]=> 63 | object(Testsuite)#1 (0) refcount(4){ 64 | } 65 | } 66 | ["parameter"]=> 67 | array(1) refcount(1){ 68 | ["$ref"]=> 69 | string(10) "" refcount(1) 70 | } 71 | } 72 | } 73 | 74 | Cloned weak reference matches original: ok 75 | Cloned weak reference does not match original weak reference strictly: ok 76 | 77 | weakrefcount($obj): integer: 2 78 | object(Ref\WeakReference)#5 (2) refcount(3){ 79 | ["referent":"Ref\AbstractReference":private]=> 80 | object(stdClass)#2 (0) refcount(2){ 81 | } 82 | ["notifier":"Ref\AbstractReference":private]=> 83 | object(Closure)#3 (2) refcount(4){ 84 | ["static"]=> 85 | array(1) refcount(1){ 86 | ["helper"]=> 87 | object(Testsuite)#1 (0) refcount(4){ 88 | } 89 | } 90 | ["parameter"]=> 91 | array(1) refcount(1){ 92 | ["$ref"]=> 93 | string(10) "" refcount(1) 94 | } 95 | } 96 | } 97 | 98 | Weak references reported with cloned reference: ok 99 | 100 | Notified: object(Ref\WeakReference)#5 (2) refcount(7){ 101 | ["referent":"Ref\AbstractReference":private]=> 102 | NULL 103 | ["notifier":"Ref\AbstractReference":private]=> 104 | object(Closure)#3 (2) refcount(5){ 105 | ["static"]=> 106 | array(1) refcount(1){ 107 | ["helper"]=> 108 | object(Testsuite)#1 (0) refcount(5){ 109 | } 110 | } 111 | ["parameter"]=> 112 | array(1) refcount(1){ 113 | ["$ref"]=> 114 | string(10) "" refcount(1) 115 | } 116 | } 117 | } 118 | Notified: object(Ref\WeakReference)#4 (2) refcount(7){ 119 | ["referent":"Ref\AbstractReference":private]=> 120 | NULL 121 | ["notifier":"Ref\AbstractReference":private]=> 122 | object(Closure)#3 (2) refcount(5){ 123 | ["static"]=> 124 | array(1) refcount(1){ 125 | ["helper"]=> 126 | object(Testsuite)#1 (0) refcount(5){ 127 | } 128 | } 129 | ["parameter"]=> 130 | array(1) refcount(1){ 131 | ["$ref"]=> 132 | string(10) "" refcount(1) 133 | } 134 | } 135 | } 136 | 137 | Cloned weak reference matches original: ok 138 | Cloned weak reference does not match original weak reference strictly: ok 139 | 140 | EOF 141 | -------------------------------------------------------------------------------- /tests/004-SoftReference-clone.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - clone reference 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | dump($ref); 21 | }; 22 | 23 | $ref = new \Ref\SoftReference($obj, $notifier); 24 | 25 | $helper->export_annotated('softrefcount($obj)', softrefcount($obj)); 26 | $helper->dump($ref); 27 | $helper->line(); 28 | 29 | $ref2 = clone $ref; 30 | 31 | $helper->assert('Cloned soft reference matches original', $ref == $ref2); 32 | $helper->assert('Cloned soft reference does not match original soft reference strictly', $ref !== $ref2); 33 | $helper->line(); 34 | 35 | $helper->export_annotated('softrefcount($obj)', softrefcount($obj)); 36 | $helper->dump($ref2); 37 | $helper->line(); 38 | 39 | $helper->assert('Soft references reported with cloned reference', softrefs($obj), [$ref, $ref2]); 40 | $helper->line(); 41 | 42 | $obj = null; 43 | $helper->line(); 44 | 45 | $helper->assert('Cloned soft reference matches original', $ref == $ref2); 46 | $helper->assert('Cloned soft reference does not match original soft reference strictly', $ref !== $ref2); 47 | $helper->line(); 48 | 49 | 50 | ?> 51 | EOF 52 | --EXPECT-- 53 | softrefcount($obj): integer: 1 54 | object(Ref\SoftReference)#4 (2) refcount(3){ 55 | ["referent":"Ref\AbstractReference":private]=> 56 | object(stdClass)#2 (0) refcount(2){ 57 | } 58 | ["notifier":"Ref\AbstractReference":private]=> 59 | object(Closure)#3 (2) refcount(3){ 60 | ["static"]=> 61 | array(1) refcount(1){ 62 | ["helper"]=> 63 | object(Testsuite)#1 (0) refcount(4){ 64 | } 65 | } 66 | ["parameter"]=> 67 | array(1) refcount(1){ 68 | ["$ref"]=> 69 | string(10) "" refcount(1) 70 | } 71 | } 72 | } 73 | 74 | Cloned soft reference matches original: ok 75 | Cloned soft reference does not match original soft reference strictly: ok 76 | 77 | softrefcount($obj): integer: 2 78 | object(Ref\SoftReference)#5 (2) refcount(3){ 79 | ["referent":"Ref\AbstractReference":private]=> 80 | object(stdClass)#2 (0) refcount(2){ 81 | } 82 | ["notifier":"Ref\AbstractReference":private]=> 83 | object(Closure)#3 (2) refcount(4){ 84 | ["static"]=> 85 | array(1) refcount(1){ 86 | ["helper"]=> 87 | object(Testsuite)#1 (0) refcount(4){ 88 | } 89 | } 90 | ["parameter"]=> 91 | array(1) refcount(1){ 92 | ["$ref"]=> 93 | string(10) "" refcount(1) 94 | } 95 | } 96 | } 97 | 98 | Soft references reported with cloned reference: ok 99 | 100 | Notified: object(Ref\SoftReference)#5 (2) refcount(7){ 101 | ["referent":"Ref\AbstractReference":private]=> 102 | object(stdClass)#2 (0) refcount(2){ 103 | } 104 | ["notifier":"Ref\AbstractReference":private]=> 105 | object(Closure)#3 (2) refcount(5){ 106 | ["static"]=> 107 | array(1) refcount(1){ 108 | ["helper"]=> 109 | object(Testsuite)#1 (0) refcount(5){ 110 | } 111 | } 112 | ["parameter"]=> 113 | array(1) refcount(1){ 114 | ["$ref"]=> 115 | string(10) "" refcount(1) 116 | } 117 | } 118 | } 119 | Notified: object(Ref\SoftReference)#4 (2) refcount(7){ 120 | ["referent":"Ref\AbstractReference":private]=> 121 | object(stdClass)#2 (0) refcount(2){ 122 | } 123 | ["notifier":"Ref\AbstractReference":private]=> 124 | object(Closure)#3 (2) refcount(5){ 125 | ["static"]=> 126 | array(1) refcount(1){ 127 | ["helper"]=> 128 | object(Testsuite)#1 (0) refcount(5){ 129 | } 130 | } 131 | ["parameter"]=> 132 | array(1) refcount(1){ 133 | ["$ref"]=> 134 | string(10) "" refcount(1) 135 | } 136 | } 137 | } 138 | 139 | Cloned soft reference matches original: ok 140 | Cloned soft reference does not match original soft reference strictly: ok 141 | 142 | EOF 143 | -------------------------------------------------------------------------------- /php_ref_notifier_exception.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #include "php_ref_notifier_exception.h" 14 | #include "php_ref.h" 15 | #include "zend_exceptions.h" 16 | 17 | 18 | zend_class_entry *php_ref_notifier_exception_class_entry; 19 | #define this_ce php_ref_notifier_exception_class_entry 20 | 21 | 22 | static zend_object *php_ref_notifier_exception_ctor(zend_class_entry *ce) 23 | { 24 | zval obj, thrown; 25 | zend_object *object; 26 | 27 | Z_OBJ(obj) = object = ce->parent->create_object(ce); 28 | 29 | array_init_size(&thrown, 0); 30 | zend_update_property(php_ref_notifier_exception_class_entry, &obj, ZEND_STRL("exceptions"), &thrown); 31 | 32 | return object; 33 | } 34 | 35 | 36 | void php_ref_create_notifier_exception(zval *exception, const char *message, zval *thrown) 37 | { 38 | object_init_ex(exception, this_ce); 39 | zend_update_property_string(zend_ce_exception, exception, ZEND_STRL("message"), message); 40 | zend_update_property(php_ref_notifier_exception_class_entry, exception, ZEND_STRL("exceptions"), thrown); 41 | } 42 | 43 | static PHP_METHOD(NotifierException, __construct) 44 | { 45 | zend_string *message = NULL; 46 | zend_long code = 0; 47 | 48 | zval tmp; 49 | zval *exceptions = NULL; 50 | zval *previous = NULL; 51 | 52 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SalO!", &message, &exceptions, &code, &previous, zend_ce_throwable) == FAILURE) { 53 | return; 54 | } 55 | 56 | if (message) { 57 | zend_update_property_str(zend_ce_exception, getThis(), ZEND_STRL("message"), message); 58 | } 59 | 60 | if (exceptions) { 61 | zend_update_property(this_ce, getThis(), ZEND_STRL("exceptions"), exceptions); 62 | } else { 63 | array_init_size(&tmp, 0); 64 | zend_update_property(this_ce, getThis(), ZEND_STRL("exceptions"), &tmp); 65 | zval_dtor(&tmp); 66 | } 67 | 68 | if (code) { 69 | zend_update_property_long(zend_ce_exception, getThis(), ZEND_STRL("code"), code); 70 | } 71 | 72 | if (previous) { 73 | zend_update_property(zend_ce_exception, getThis(), ZEND_STRL("previous"), previous); 74 | } 75 | } 76 | 77 | static PHP_METHOD(NotifierException, getExceptions) 78 | { 79 | zval rv; 80 | 81 | if (zend_parse_parameters_none() == FAILURE) { 82 | return; 83 | } 84 | 85 | RETVAL_ZVAL(zend_read_property(php_ref_notifier_exception_class_entry, getThis(), ZEND_STRL("exceptions"), 0, &rv), 1, 0); 86 | } 87 | 88 | 89 | ZEND_BEGIN_ARG_INFO_EX(arginfo_notifier_exception___construct, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 0) 90 | ZEND_ARG_INFO(0, message) 91 | ZEND_ARG_INFO(0, exceptions) 92 | ZEND_ARG_INFO(0, code) 93 | ZEND_ARG_INFO(0, previous) 94 | ZEND_END_ARG_INFO() 95 | 96 | ZEND_BEGIN_ARG_INFO_EX(arginfo_notifier_exception_getExceptions, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 0) 97 | ZEND_END_ARG_INFO() 98 | 99 | 100 | static const zend_function_entry php_ref_notifier_exception_methods[] = { 101 | PHP_ME(NotifierException, __construct, arginfo_notifier_exception___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 102 | PHP_ME(NotifierException, getExceptions, arginfo_notifier_exception_getExceptions, ZEND_ACC_PUBLIC) 103 | 104 | PHP_FE_END 105 | }; 106 | 107 | 108 | PHP_MINIT_FUNCTION (php_ref_notifier_exception) 109 | { 110 | zend_class_entry ce; 111 | 112 | INIT_NS_CLASS_ENTRY(ce, PHP_REF_NS, "NotifierException", php_ref_notifier_exception_methods); 113 | this_ce = zend_register_internal_class_ex(&ce, zend_ce_exception); 114 | /*this_ce->create_object = php_ref_notifier_exception_ctor;*/ 115 | 116 | zend_declare_property_null(this_ce, ZEND_STRL("exceptions"), ZEND_ACC_PRIVATE); 117 | 118 | 119 | return SUCCESS; 120 | } 121 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified_prevent_destoying.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - prevent original object from being destroyed 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier called', true); 23 | $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); 24 | $helper->assert('Notifier get soft reference as it argument', $reference instanceof Ref\SoftReference); 25 | $helper->assert('Original object is null', null === $obj); 26 | $helper->assert('Soft reference in notifier is not null', null !== $reference->get()); 27 | $helper->assert('Soft reference in notifier points to original object', $reference->get() instanceof \WeakTests\TrackingDtor); 28 | $helper->space(); 29 | 30 | $obj_copy = $reference->get(); 31 | }); 32 | 33 | $helper->header('When referent object alive'); 34 | $helper->assert('Referent object alive', $sr->get() === $obj); 35 | 36 | $helper->line(); 37 | $helper->dump($sr); 38 | $helper->space(); 39 | 40 | $obj = null; 41 | 42 | $helper->header('When referent object was nullified but reference to it stored from notifier'); 43 | $helper->assert('Original variable holds null', null === $obj); 44 | $helper->assert('Object copy is not null', null !== $obj_copy); 45 | $helper->assert('Object copy dtor was not called', false === is_obj_destructor_called($obj_copy)); 46 | $helper->assert('Referent object alive', $sr->get() === $obj_copy); 47 | 48 | $helper->line(); 49 | $helper->dump($sr); 50 | $helper->line(); 51 | 52 | $sr = null; 53 | $obj_copy = null; 54 | 55 | ?> 56 | EOF 57 | --EXPECT-- 58 | When referent object alive: 59 | --------------------------- 60 | Referent object alive: ok 61 | 62 | object(Ref\SoftReference)#3 (2) refcount(3){ 63 | ["referent":"Ref\AbstractReference":private]=> 64 | object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 65 | } 66 | ["notifier":"Ref\AbstractReference":private]=> 67 | object(Closure)#4 (2) refcount(2){ 68 | ["static"]=> 69 | array(3) refcount(1){ 70 | ["obj"]=> 71 | &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 72 | } 73 | ["obj_copy"]=> 74 | &NULL 75 | ["helper"]=> 76 | &object(Testsuite)#1 (0) refcount(2){ 77 | } 78 | } 79 | ["parameter"]=> 80 | array(1) refcount(1){ 81 | ["$reference"]=> 82 | string(10) "" refcount(1) 83 | } 84 | } 85 | } 86 | 87 | 88 | Notifier called: ok 89 | Notifier get 1 argument: ok 90 | Notifier get soft reference as it argument: ok 91 | Original object is null: ok 92 | Soft reference in notifier is not null: ok 93 | Soft reference in notifier points to original object: ok 94 | 95 | 96 | When referent object was nullified but reference to it stored from notifier: 97 | ---------------------------------------------------------------------------- 98 | Original variable holds null: ok 99 | Object copy is not null: ok 100 | Object copy dtor was not called: ok 101 | Referent object alive: ok 102 | 103 | object(Ref\SoftReference)#3 (2) refcount(3){ 104 | ["referent":"Ref\AbstractReference":private]=> 105 | object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 106 | } 107 | ["notifier":"Ref\AbstractReference":private]=> 108 | object(Closure)#4 (2) refcount(2){ 109 | ["static"]=> 110 | array(3) refcount(1){ 111 | ["obj"]=> 112 | &NULL 113 | ["obj_copy"]=> 114 | &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 115 | } 116 | ["helper"]=> 117 | &object(Testsuite)#1 (0) refcount(2){ 118 | } 119 | } 120 | ["parameter"]=> 121 | array(1) refcount(1){ 122 | ["$reference"]=> 123 | string(10) "" refcount(1) 124 | } 125 | } 126 | } 127 | 128 | WeakTests\TrackingDtor's destructor called 129 | EOF 130 | -------------------------------------------------------------------------------- /scripts/refresh-package-xml.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 8 | * 9 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 10 | * 11 | * For the full copyright and license information, please view the 12 | * LICENSE file that was distributed with this source or visit 13 | * http://opensource.org/licenses/MIT 14 | */ 15 | 16 | 17 | chdir(__DIR__ . DIRECTORY_SEPARATOR . '..'); 18 | 19 | $contents = [ 20 | 'stubs' => 'doc', 21 | 'tests' => 'test', 22 | 'config.m4' => 'src', 23 | 'config.w32' => 'src', 24 | 'LICENSE' => 'doc', 25 | 'php_ref.h' => 'src', 26 | 'php_ref_functions.c' => 'src', 27 | 'php_ref_functions.h' => 'src', 28 | 'php_ref_notifier_exception.c' => 'src', 29 | 'php_ref_notifier_exception.h' => 'src', 30 | 'php_ref_reference.c' => 'src', 31 | 'php_ref_reference.h' => 'src', 32 | 'README.md' => 'doc', 33 | 'ref.c' => 'src', 34 | ]; 35 | 36 | $rules = [ 37 | 'test' => [ 38 | '/\.phpt$/', 39 | '/^\..+\.php$/', 40 | ], 41 | ]; 42 | 43 | 44 | $files = []; 45 | 46 | $files[] = ''; 47 | 48 | foreach ($contents as $location => $role) { 49 | if (is_dir($location)) { 50 | 51 | $dir_files = []; 52 | /** @var SplFileInfo $filename */ 53 | foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($location)) as $filename) { 54 | if ($filename->isDir()) { 55 | continue; 56 | } 57 | 58 | $location = $filename->getPathname(); 59 | 60 | if (!is_file($location)) { 61 | throw new Exception("'{$location}' is not a file"); 62 | } 63 | 64 | 65 | if (isset($rules[$role])) { 66 | $matches = false; 67 | foreach ($rules[$role] as $rule) { 68 | if ($matches = preg_match($rule, $filename->getFilename())) { 69 | break; 70 | } 71 | } 72 | 73 | if (!$matches) { 74 | continue; 75 | } 76 | } 77 | 78 | $dir_files[] = " "; 79 | } 80 | 81 | sort($dir_files); 82 | 83 | $files = array_merge($files, $dir_files); 84 | 85 | continue; 86 | } 87 | 88 | if (!is_file($location)) { 89 | throw new Exception("'{$location}' is not a file"); 90 | } 91 | 92 | $files[] = " "; 93 | } 94 | 95 | $files[] = ' '; 96 | 97 | $package = file_get_contents('package.xml'); 98 | 99 | $start = preg_quote(''); 100 | $end = preg_quote(''); 101 | 102 | $package = preg_replace("/{$start}.+{$end}/s", implode("\n", $files), $package); 103 | 104 | $datetime = new DateTime(); 105 | $package = preg_replace("/\.+\<\/date\>/", '' . $datetime->format('Y-m-d') . '', $package); 106 | $package = preg_replace("/\.+\<\/time\>/", '', $package); 107 | 108 | $new_package_filename = 'package-new.xml'; 109 | if (isset($argv[1]) && '-f' == $argv[1]) { 110 | $new_package_filename = 'package.xml'; 111 | } 112 | 113 | // Replace version: 114 | 115 | $header = file_get_contents('php_ref.h'); 116 | 117 | if (!preg_match('/#define PHP_REF_VERSION "(.+)"/', $header, $matches)) { 118 | throw new RuntimeException("Unable to get release version"); 119 | } 120 | $version = $matches[1]; 121 | 122 | $package = preg_replace("/\\d+\.\d+.\d+\<\/release\>/", '' . $version . '', $package); 123 | $package = preg_replace("/\\d+\.\d+.\d+\<\/api\>/", '' . $version . '', $package); 124 | 125 | 126 | file_put_contents($new_package_filename, $package); 127 | -------------------------------------------------------------------------------- /tests/004-SoftReference-notified_prevent_destoying_forever.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\SoftReference - prevent original object from being destroyed forever 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | assert('Notifier called', true); 23 | $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); 24 | $helper->assert('Notifier get soft reference as it argument', $reference instanceof Ref\SoftReference); 25 | $helper->assert('Original object is null', null === $obj); 26 | $helper->assert('Soft reference in notifier is not null', null !== $reference->get()); 27 | $helper->assert('Soft reference in notifier points to original object', $reference->get() instanceof \WeakTests\TrackingDtor); 28 | $helper->space(); 29 | 30 | $obj_copy = $reference->get(); 31 | }); 32 | 33 | $helper->header('When referent object alive'); 34 | $helper->assert('Referent object alive', $sr->get() === $obj); 35 | 36 | $helper->line(); 37 | $helper->dump($sr); 38 | $helper->space(); 39 | 40 | $obj = null; 41 | 42 | $helper->header('When referent object was nullified but reference to it stored from notifier'); 43 | $helper->assert('Original variable holds null', null === $obj); 44 | $helper->assert('Object copy is not null', null !== $obj_copy); 45 | $helper->assert('Object copy dtor was not called', false === is_obj_destructor_called($obj_copy)); 46 | $helper->assert('Referent object alive', $sr->get() === $obj_copy); 47 | 48 | $helper->line(); 49 | $helper->dump($sr); 50 | $helper->line(); 51 | 52 | $obj_copy = null; 53 | 54 | ?> 55 | EOF 56 | --EXPECT-- 57 | When referent object alive: 58 | --------------------------- 59 | Referent object alive: ok 60 | 61 | object(Ref\SoftReference)#3 (2) refcount(3){ 62 | ["referent":"Ref\AbstractReference":private]=> 63 | object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 64 | } 65 | ["notifier":"Ref\AbstractReference":private]=> 66 | object(Closure)#4 (2) refcount(2){ 67 | ["static"]=> 68 | array(3) refcount(1){ 69 | ["obj"]=> 70 | &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 71 | } 72 | ["obj_copy"]=> 73 | &NULL 74 | ["helper"]=> 75 | &object(Testsuite)#1 (0) refcount(2){ 76 | } 77 | } 78 | ["parameter"]=> 79 | array(1) refcount(1){ 80 | ["$reference"]=> 81 | string(10) "" refcount(1) 82 | } 83 | } 84 | } 85 | 86 | 87 | Notifier called: ok 88 | Notifier get 1 argument: ok 89 | Notifier get soft reference as it argument: ok 90 | Original object is null: ok 91 | Soft reference in notifier is not null: ok 92 | Soft reference in notifier points to original object: ok 93 | 94 | 95 | When referent object was nullified but reference to it stored from notifier: 96 | ---------------------------------------------------------------------------- 97 | Original variable holds null: ok 98 | Object copy is not null: ok 99 | Object copy dtor was not called: ok 100 | Referent object alive: ok 101 | 102 | object(Ref\SoftReference)#3 (2) refcount(3){ 103 | ["referent":"Ref\AbstractReference":private]=> 104 | object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 105 | } 106 | ["notifier":"Ref\AbstractReference":private]=> 107 | object(Closure)#4 (2) refcount(2){ 108 | ["static"]=> 109 | array(3) refcount(1){ 110 | ["obj"]=> 111 | &NULL 112 | ["obj_copy"]=> 113 | &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ 114 | } 115 | ["helper"]=> 116 | &object(Testsuite)#1 (0) refcount(2){ 117 | } 118 | } 119 | ["parameter"]=> 120 | array(1) refcount(1){ 121 | ["$reference"]=> 122 | string(10) "" refcount(1) 123 | } 124 | } 125 | } 126 | 127 | Notifier called: ok 128 | Notifier get 1 argument: ok 129 | Notifier get soft reference as it argument: ok 130 | Original object is null: ok 131 | Soft reference in notifier is not null: ok 132 | Soft reference in notifier points to original object: ok 133 | 134 | 135 | EOF 136 | WeakTests\TrackingDtor's destructor called 137 | -------------------------------------------------------------------------------- /provision/.bashrc: -------------------------------------------------------------------------------- 1 | # ~/.bashrc: executed by bash(1) for non-login shells. 2 | # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) 3 | # for examples 4 | 5 | # If not running interactively, don't do anything 6 | case $- in 7 | *i*) ;; 8 | *) return;; 9 | esac 10 | 11 | # don't put duplicate lines or lines starting with space in the history. 12 | # See bash(1) for more options 13 | HISTCONTROL=ignoreboth 14 | 15 | # append to the history file, don't overwrite it 16 | shopt -s histappend 17 | 18 | # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) 19 | HISTSIZE=1000 20 | HISTFILESIZE=2000 21 | 22 | # check the window size after each command and, if necessary, 23 | # update the values of LINES and COLUMNS. 24 | shopt -s checkwinsize 25 | 26 | # If set, the pattern "**" used in a pathname expansion context will 27 | # match all files and zero or more directories and subdirectories. 28 | #shopt -s globstar 29 | 30 | # make less more friendly for non-text input files, see lesspipe(1) 31 | [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" 32 | 33 | # set variable identifying the chroot you work in (used in the prompt below) 34 | if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then 35 | debian_chroot=$(cat /etc/debian_chroot) 36 | fi 37 | 38 | # set a fancy prompt (non-color, unless we know we "want" color) 39 | case "$TERM" in 40 | xterm-color) color_prompt=yes;; 41 | esac 42 | 43 | # uncomment for a colored prompt, if the terminal has the capability; turned 44 | # off by default to not distract the user: the focus in a terminal window 45 | # should be on the output of commands, not on the prompt 46 | #force_color_prompt=yes 47 | 48 | if [ -n "$force_color_prompt" ]; then 49 | if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then 50 | # We have color support; assume it's compliant with Ecma-48 51 | # (ISO/IEC-6429). (Lack of such support is extremely rare, and such 52 | # a case would tend to support setf rather than setaf.) 53 | color_prompt=yes 54 | else 55 | color_prompt= 56 | fi 57 | fi 58 | 59 | if [ "$color_prompt" = yes ]; then 60 | PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' 61 | else 62 | PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 63 | fi 64 | unset color_prompt force_color_prompt 65 | 66 | # If this is an xterm set the title to user@host:dir 67 | case "$TERM" in 68 | xterm*|rxvt*) 69 | PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" 70 | ;; 71 | *) 72 | ;; 73 | esac 74 | 75 | # enable color support of ls and also add handy aliases 76 | if [ -x /usr/bin/dircolors ]; then 77 | test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" 78 | alias ls='ls --color=auto' 79 | #alias dir='dir --color=auto' 80 | #alias vdir='vdir --color=auto' 81 | 82 | alias grep='grep --color=auto' 83 | alias fgrep='fgrep --color=auto' 84 | alias egrep='egrep --color=auto' 85 | fi 86 | 87 | # some more ls aliases 88 | alias ll='ls -alF' 89 | alias la='ls -A' 90 | alias l='ls -CF' 91 | 92 | # Add an "alert" alias for long running commands. Use like so: 93 | # sleep 10; alert 94 | alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' 95 | 96 | # Alias definitions. 97 | # You may want to put all your additions into a separate file like 98 | # ~/.bash_aliases, instead of adding them here directly. 99 | # See /usr/share/doc/bash-doc/examples in the bash-doc package. 100 | 101 | if [ -f ~/.bash_aliases ]; then 102 | . ~/.bash_aliases 103 | fi 104 | 105 | # enable programmable completion features (you don't need to enable 106 | # this, if it's already enabled in /etc/bash.bashrc and /etc/profile 107 | # sources /etc/bash.bashrc). 108 | if ! shopt -oq posix; then 109 | if [ -f /usr/share/bash-completion/bash_completion ]; then 110 | . /usr/share/bash-completion/bash_completion 111 | elif [ -f /etc/bash_completion ]; then 112 | . /etc/bash_completion 113 | fi 114 | fi 115 | 116 | PHPBREW_SET_PROMPT=1 117 | 118 | source ~/.phpbrew/bashrc 119 | 120 | if [ $(id -u) -eq 0 ]; 121 | then 122 | PS1='${debian_chroot:+()}\[\e[01;31m\]\u\[\e[0m\]@\[\e[01;31m\]\h\[\e[0m\] (\[\e[0;36m\]$(phpbrew_current_php_version)\[\e[0m\]):\[\033[01;34m\]\w\[\033[00m\]# ' 123 | else 124 | PS1='${debian_chroot:+()}\[\e[01;32m\]\u\[\e[0m\]@\[\e[01;32m\]\h\[\e[0m\] (\[\e[0;36m\]$(phpbrew_current_php_version)\[\e[0m\]):\[\033[01;34m\]\w\[\033[00m\]$ ' 125 | fi 126 | 127 | EDITOR=vim 128 | 129 | export NO_INTERACTION=1 130 | -------------------------------------------------------------------------------- /tests/003-functions-soft-and-weak.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\functions - test soft* and weak* functions together 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('Test Ref\softrefcounted'); 40 | $helper->export_annotated('softrefcounted($obj1)', softrefcounted($obj1)); 41 | $helper->export_annotated('softrefcounted($obj2)', softrefcounted($obj2)); 42 | $helper->export_annotated('softrefcounted($obj3)', softrefcounted($obj3)); 43 | $helper->line(); 44 | 45 | 46 | $helper->header('Test Ref\softrefcount'); 47 | $helper->export_annotated('softrefcount($obj1)', softrefcount($obj1)); 48 | $helper->export_annotated('softrefcount($obj2)', softrefcount($obj2)); 49 | $helper->export_annotated('softrefcount($obj3)', softrefcount($obj3)); 50 | $helper->line(); 51 | 52 | 53 | $helper->header('Test Ref\softrefs'); 54 | 55 | $helper->assert('Multiple soft refs reported for object with softrefs()', softrefs($obj1), [$soft_ref1a, $soft_ref1b]); 56 | $helper->dump(softrefs($obj1)); 57 | $helper->line(); 58 | 59 | $helper->assert('Single soft ref reported for object with softrefs()', softrefs($obj2), [$soft_ref2]); 60 | $helper->dump(softrefs($obj2)); 61 | $helper->line(); 62 | 63 | $helper->assert('No soft refs reported for object with softrefs()', softrefs($obj3), []); 64 | $helper->dump(softrefs($obj3)); 65 | $helper->line(); 66 | 67 | 68 | 69 | $weak_ref1a = new WeakReference($obj1); 70 | $weak_ref1b = new WeakReference($obj1); 71 | $weak_ref2 = new WeakReference($obj2); 72 | 73 | 74 | $helper->header('Test Ref\weakrefcounted'); 75 | $helper->export_annotated('weakrefcounted($obj1)', weakrefcounted($obj1)); 76 | $helper->export_annotated('weakrefcounted($obj2)', weakrefcounted($obj2)); 77 | $helper->export_annotated('weakrefcounted($obj3)', weakrefcounted($obj3)); 78 | $helper->line(); 79 | 80 | 81 | $helper->header('Test Ref\weakrefcount'); 82 | $helper->export_annotated('weakrefcount($obj1)', weakrefcount($obj1)); 83 | $helper->export_annotated('weakrefcount($obj2)', weakrefcount($obj2)); 84 | $helper->export_annotated('weakrefcount($obj3)', weakrefcount($obj3)); 85 | $helper->line(); 86 | 87 | 88 | $helper->header('Test Ref\weakrefs'); 89 | 90 | $helper->assert('Multiple weak refs reported for object with weakrefs()', weakrefs($obj1), [$weak_ref1a, $weak_ref1b]); 91 | $helper->dump(weakrefs($obj1)); 92 | $helper->line(); 93 | 94 | $helper->assert('Single weak ref reported for object with weakrefs()', weakrefs($obj2), [$weak_ref2]); 95 | $helper->dump(weakrefs($obj2)); 96 | $helper->line(); 97 | 98 | $helper->assert('No weak refs reported for object with weakrefs()', weakrefs($obj3), []); 99 | $helper->dump(weakrefs($obj3)); 100 | $helper->line(); 101 | 102 | ?> 103 | --EXPECT-- 104 | Test Ref\softrefcounted: 105 | ------------------------ 106 | softrefcounted($obj1): boolean: true 107 | softrefcounted($obj2): boolean: true 108 | softrefcounted($obj3): boolean: false 109 | 110 | Test Ref\softrefcount: 111 | ---------------------- 112 | softrefcount($obj1): integer: 2 113 | softrefcount($obj2): integer: 1 114 | softrefcount($obj3): integer: 0 115 | 116 | Test Ref\softrefs: 117 | ------------------ 118 | Multiple soft refs reported for object with softrefs(): ok 119 | array(2) refcount(2){ 120 | [0]=> 121 | object(Ref\SoftReference)#5 (2) refcount(2){ 122 | ["referent":"Ref\AbstractReference":private]=> 123 | object(stdClass)#2 (0) refcount(3){ 124 | } 125 | ["notifier":"Ref\AbstractReference":private]=> 126 | NULL 127 | } 128 | [1]=> 129 | object(Ref\SoftReference)#6 (2) refcount(2){ 130 | ["referent":"Ref\AbstractReference":private]=> 131 | object(stdClass)#2 (0) refcount(3){ 132 | } 133 | ["notifier":"Ref\AbstractReference":private]=> 134 | NULL 135 | } 136 | } 137 | 138 | Single soft ref reported for object with softrefs(): ok 139 | array(1) refcount(2){ 140 | [0]=> 141 | object(Ref\SoftReference)#7 (2) refcount(2){ 142 | ["referent":"Ref\AbstractReference":private]=> 143 | object(stdClass)#3 (0) refcount(2){ 144 | } 145 | ["notifier":"Ref\AbstractReference":private]=> 146 | NULL 147 | } 148 | } 149 | 150 | No soft refs reported for object with softrefs(): ok 151 | array(0) refcount(2){ 152 | } 153 | 154 | Test Ref\weakrefcounted: 155 | ------------------------ 156 | weakrefcounted($obj1): boolean: true 157 | weakrefcounted($obj2): boolean: true 158 | weakrefcounted($obj3): boolean: false 159 | 160 | Test Ref\weakrefcount: 161 | ---------------------- 162 | weakrefcount($obj1): integer: 2 163 | weakrefcount($obj2): integer: 1 164 | weakrefcount($obj3): integer: 0 165 | 166 | Test Ref\weakrefs: 167 | ------------------ 168 | Multiple weak refs reported for object with weakrefs(): ok 169 | array(2) refcount(2){ 170 | [0]=> 171 | object(Ref\WeakReference)#8 (2) refcount(2){ 172 | ["referent":"Ref\AbstractReference":private]=> 173 | object(stdClass)#2 (0) refcount(3){ 174 | } 175 | ["notifier":"Ref\AbstractReference":private]=> 176 | NULL 177 | } 178 | [1]=> 179 | object(Ref\WeakReference)#9 (2) refcount(2){ 180 | ["referent":"Ref\AbstractReference":private]=> 181 | object(stdClass)#2 (0) refcount(3){ 182 | } 183 | ["notifier":"Ref\AbstractReference":private]=> 184 | NULL 185 | } 186 | } 187 | 188 | Single weak ref reported for object with weakrefs(): ok 189 | array(1) refcount(2){ 190 | [0]=> 191 | object(Ref\WeakReference)#10 (2) refcount(2){ 192 | ["referent":"Ref\AbstractReference":private]=> 193 | object(stdClass)#3 (0) refcount(2){ 194 | } 195 | ["notifier":"Ref\AbstractReference":private]=> 196 | NULL 197 | } 198 | } 199 | 200 | No weak refs reported for object with weakrefs(): ok 201 | array(0) refcount(2){ 202 | } 203 | -------------------------------------------------------------------------------- /tests/003-functions.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Ref\functions - test functions 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | header('Test Ref\refcounted'); 35 | $helper->export_annotated('refcounted($obj1)', refcounted($obj1)); 36 | $helper->export_annotated('refcounted($obj2)', refcounted($obj2)); 37 | $helper->export_annotated('refcounted(new stdClass())', refcounted(new stdClass())); 38 | // in zts strings are refcounted, in non-zts - not 39 | $helper->export_annotated('refcounted(null)', refcounted(null)); 40 | $helper->export_annotated('refcounted(42)', refcounted(42)); 41 | $helper->line(); 42 | 43 | $helper->header('Test Ref\refcount'); 44 | $helper->export_annotated('refcount($obj1)', refcount($obj1)); 45 | $helper->export_annotated('refcount($obj2)', refcount($obj2)); 46 | $helper->export_annotated('refcount(new stdClass())', refcount(new stdClass())); 47 | 48 | try { 49 | $helper->export_annotated('refcount(null)', refcount(null)); 50 | } catch (TypeError $e) { 51 | $helper->exception_export($e); 52 | } 53 | 54 | try { 55 | $helper->export_annotated('refcount(42)', refcount(42)); 56 | } catch (TypeError $e) { 57 | $helper->exception_export($e); 58 | } 59 | 60 | $helper->line(); 61 | 62 | 63 | $weak_ref1a = new WeakReference($obj1); 64 | $weak_ref1b = new WeakReference($obj1); 65 | $weak_ref2 = new WeakReference($obj2); 66 | 67 | 68 | $helper->header('Test Ref\weakrefcounted'); 69 | $helper->export_annotated('weakrefcounted($obj1)', weakrefcounted($obj1)); 70 | $helper->export_annotated('weakrefcounted($obj2)', weakrefcounted($obj2)); 71 | $helper->export_annotated('weakrefcounted($obj3)', weakrefcounted($obj3)); 72 | $helper->line(); 73 | 74 | $helper->header('Test Ref\weakrefcount'); 75 | $helper->export_annotated('weakrefcount($obj1)', weakrefcount($obj1)); 76 | $helper->export_annotated('weakrefcount($obj2)', weakrefcount($obj2)); 77 | $helper->export_annotated('weakrefcount($obj3)', weakrefcount($obj3)); 78 | $helper->line(); 79 | 80 | 81 | $helper->header('Test Ref\weakrefs'); 82 | 83 | $helper->assert('Multiple weak refs reported for object with weakrefs()', weakrefs($obj1), [$weak_ref1a, $weak_ref1b]); 84 | $helper->dump(weakrefs($obj1)); 85 | $helper->line(); 86 | 87 | $helper->assert('Single weak ref reported for object with weakrefs()', weakrefs($obj2), [$weak_ref2]); 88 | $helper->dump(weakrefs($obj2)); 89 | $helper->line(); 90 | 91 | $helper->assert('No weak refs reported for object with weakrefs()', weakrefs($obj3), []); 92 | $helper->dump(weakrefs($obj3)); 93 | $helper->line(); 94 | 95 | 96 | $helper->header('Test Ref\object_handle'); 97 | $helper->export_annotated('object_handle($obj1)', object_handle($obj1)); 98 | $helper->export_annotated('object_handle($obj2)', object_handle($obj2)); 99 | $helper->line(); 100 | 101 | 102 | $helper->header('Test Ref\is_obj_destructor_called'); 103 | 104 | class ObjectWithSoftDestructor { 105 | 106 | private $external; 107 | 108 | public function __construct(&$external) 109 | { 110 | $this->external = &$external; 111 | } 112 | 113 | public function __destruct() 114 | { 115 | echo __METHOD__ . ' called', PHP_EOL; 116 | $this->external = $this; 117 | } 118 | } 119 | 120 | $external = null; 121 | $obj = new ObjectWithSoftDestructor($external); 122 | 123 | $helper->export_annotated('is_obj_destructor_called($obj)', is_obj_destructor_called($obj)); 124 | $obj = null; 125 | $helper->assert('Object stored to external value during destructor call', is_object($external)); 126 | $helper->export_annotated('is_obj_destructor_called($external)', is_obj_destructor_called($external)); 127 | 128 | ?> 129 | --EXPECTF-- 130 | Test Ref\refcounted: 131 | -------------------- 132 | refcounted($obj1): boolean: true 133 | refcounted($obj2): boolean: true 134 | refcounted(new stdClass()): boolean: true 135 | refcounted(null): boolean: false 136 | refcounted(42): boolean: false 137 | 138 | Test Ref\refcount: 139 | ------------------ 140 | refcount($obj1): integer: 2 141 | refcount($obj2): integer: 1 142 | refcount(new stdClass()): integer: 0 143 | TypeError: Argument 1 passed to Ref\refcount() must be an object, null given 144 | TypeError: Argument 1 passed to Ref\refcount() must be an object, int%S given 145 | 146 | Test Ref\weakrefcounted: 147 | ------------------------ 148 | weakrefcounted($obj1): boolean: true 149 | weakrefcounted($obj2): boolean: true 150 | weakrefcounted($obj3): boolean: false 151 | 152 | Test Ref\weakrefcount: 153 | ---------------------- 154 | weakrefcount($obj1): integer: 2 155 | weakrefcount($obj2): integer: 1 156 | weakrefcount($obj3): integer: 0 157 | 158 | Test Ref\weakrefs: 159 | ------------------ 160 | Multiple weak refs reported for object with weakrefs(): ok 161 | array(2) refcount(2){ 162 | [0]=> 163 | object(Ref\WeakReference)#5 (2) refcount(2){ 164 | ["referent":"Ref\AbstractReference":private]=> 165 | object(stdClass)#2 (0) refcount(3){ 166 | } 167 | ["notifier":"Ref\AbstractReference":private]=> 168 | NULL 169 | } 170 | [1]=> 171 | object(Ref\WeakReference)#7 (2) refcount(2){ 172 | ["referent":"Ref\AbstractReference":private]=> 173 | object(stdClass)#2 (0) refcount(3){ 174 | } 175 | ["notifier":"Ref\AbstractReference":private]=> 176 | NULL 177 | } 178 | } 179 | 180 | Single weak ref reported for object with weakrefs(): ok 181 | array(1) refcount(2){ 182 | [0]=> 183 | object(Ref\WeakReference)#8 (2) refcount(2){ 184 | ["referent":"Ref\AbstractReference":private]=> 185 | object(stdClass)#3 (0) refcount(2){ 186 | } 187 | ["notifier":"Ref\AbstractReference":private]=> 188 | NULL 189 | } 190 | } 191 | 192 | No weak refs reported for object with weakrefs(): ok 193 | array(0) refcount(2){ 194 | } 195 | 196 | Test Ref\object_handle: 197 | ----------------------- 198 | object_handle($obj1): integer: 2 199 | object_handle($obj2): integer: 3 200 | 201 | Test Ref\is_obj_destructor_called: 202 | ---------------------------------- 203 | is_obj_destructor_called($obj): boolean: false 204 | ObjectWithSoftDestructor::__destruct called 205 | Object stored to external value during destructor call: ok 206 | is_obj_destructor_called($external): boolean: true 207 | -------------------------------------------------------------------------------- /tests/.testsuite.php: -------------------------------------------------------------------------------- 1 | space(1); 28 | } 29 | 30 | public function exception_export(\Throwable $e) 31 | { 32 | echo get_class($e), ': ', $e->getMessage(), PHP_EOL; 33 | } 34 | 35 | public function ref_exception_export(\Ref\NotifierException $e) 36 | { 37 | $this->exception_export($e); 38 | 39 | if ($e->getPrevious()) { 40 | echo 'previous: '; 41 | $this->exception_export($e->getPrevious()); 42 | } 43 | 44 | if (!empty($e->getExceptions())) { 45 | echo 'thrown:', PHP_EOL; 46 | foreach ($e->getExceptions() as $id => $thrown) { 47 | echo ' #' . $id . ': '; 48 | $this->exception_export($thrown); 49 | if ($thrown->getPrevious()) { 50 | echo ' previous: ', $this->exception_export($thrown->getPrevious()); 51 | } 52 | } 53 | } 54 | } 55 | 56 | public function export($value) 57 | { 58 | echo gettype($value), ': ', var_export($value, true), PHP_EOL; 59 | } 60 | 61 | public function export_annotated($message, $value) 62 | { 63 | echo $message, ': '; 64 | $this->export($value); 65 | } 66 | 67 | public function dump($value, bool $detailed = true) 68 | { 69 | $detailed ? debug_zval_dump($value) : var_dump($value); 70 | } 71 | 72 | public function dump_annotated($message, $value, bool $detailed = true) 73 | { 74 | echo $message, ': '; 75 | $this->dump($value, $detailed); 76 | } 77 | 78 | public function object_type($object) 79 | { 80 | echo get_class($object), PHP_EOL; 81 | } 82 | 83 | public function function_export($func, array $args = []) 84 | { 85 | echo $func, '(): ', var_export(call_user_func_array($func, $args), true), PHP_EOL; 86 | } 87 | 88 | public function method_export($object, $method, array $args = []) 89 | { 90 | echo get_class($object), '::', $method, '(): ', var_export(call_user_func_array([$object, $method], $args), true), PHP_EOL; 91 | } 92 | 93 | public function assert($message, $actual, $expected = true, $identical = true) 94 | { 95 | echo $message, ': '; 96 | 97 | if ($identical) { 98 | echo($expected === $actual ? 'ok' : 'failed'), PHP_EOL; 99 | } else { 100 | echo($expected == $actual ? 'ok' : 'failed'), PHP_EOL; 101 | } 102 | } 103 | 104 | public function value_matches($expected, $actual, $identical = true) 105 | { 106 | if ($identical) { 107 | echo 'Expected ', var_export($expected, true), ' value is ', ($expected === $actual ? 'identical to' : 'not identical to'), ' actual value ', var_export($actual, true), PHP_EOL; 108 | } else { 109 | echo 'Expected ', var_export($expected, true), ' value is ', ($expected == $actual ? 'matches' : 'doesn\'t match'), ' actual value ', var_export($actual, true), PHP_EOL; 110 | } 111 | } 112 | 113 | public function value_instanceof($value, $expected) 114 | { 115 | echo 'Value', ($value instanceof $expected ? ' is' : ' not an'), ' instance of ', $expected, PHP_EOL; 116 | } 117 | 118 | public function value_matches_with_no_output($expected, $actual, $identical = true) 119 | { 120 | if ($identical) { 121 | echo 'Expected value is ', ($expected === $actual ? 'identical to' : 'not identical to'), ' actual value', PHP_EOL; 122 | } else { 123 | echo 'Expected value ', ($expected == $actual ? 'matches' : 'doesn\'t match'), ' actual value', PHP_EOL; 124 | } 125 | } 126 | 127 | public function method_matches($object, $method, $expected, array $args = []) 128 | { 129 | echo get_class($object), '::', $method, '()', ' ', ($expected === call_user_func_array([$object, $method], $args) ? 'matches' : 'doesn\'t match'), ' expected value', PHP_EOL; 130 | } 131 | 132 | public function method_throws($object, $method, $exception, $message = null, array $args = []) 133 | { 134 | try { 135 | call_user_func_array([$object, $method], $args); 136 | } catch (\Throwable $e) { 137 | 138 | if ($e instanceof $exception) { 139 | 140 | if ($message !== null) { 141 | if ($message == $e->getMessage()) { 142 | echo get_class($object), '::', $method, '()', ' throw expected exception and messages match', PHP_EOL; 143 | } else { 144 | echo get_class($object), '::', $method, '()', ' throw expected exception, but messages doesn\'t match', PHP_EOL; 145 | } 146 | } else { 147 | echo get_class($object), '::', $method, '()', ' throw expected exception', PHP_EOL; 148 | } 149 | } else { 150 | echo get_class($object), '::', $method, '()', ' throw unexpected exception', PHP_EOL; 151 | } 152 | 153 | return; 154 | } 155 | 156 | echo get_class($object), '::', $method, '()', ' ', 'doesn\'t throw any exception', PHP_EOL; 157 | 158 | } 159 | 160 | 161 | public function method_matches_instanceof($object, $method, $expected) 162 | { 163 | echo get_class($object), '::', $method, '() result', ($object->$method() instanceof $expected ? ' is' : ' not an'), ' instance of ', $expected, PHP_EOL; 164 | } 165 | 166 | 167 | public function method_matches_with_output($object, $method, $expected) 168 | { 169 | echo get_class($object), '::', $method, '()', ' ', ($expected === $object->$method() ? 'matches' : 'doesn\'t match'), ' expected ', var_export($expected, true), PHP_EOL; 170 | } 171 | 172 | public function class_constants_export($class_or_object) 173 | { 174 | $refl = new ReflectionClass($class_or_object); 175 | 176 | $class_name = $refl->getName(); 177 | foreach ($refl->getConstants() as $name => $value) { 178 | echo $class_name, '::', $name, ' = ', var_export($value, true), PHP_EOL; 179 | } 180 | } 181 | } 182 | 183 | return new Testsuite(); 184 | -------------------------------------------------------------------------------- /tests/001-verify_extension_entities.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check all extension entities 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | getName(), PHP_EOL; 15 | // echo 'Version: ', $re->getVersion(), PHP_EOL; 16 | 17 | echo PHP_EOL; 18 | echo 'Extension-global functions:', PHP_EOL; 19 | 20 | if ($re->getFunctions()) { 21 | foreach ($re->getFunctions() as $rf) { 22 | $this->dumpFunction($rf); 23 | } 24 | } else { 25 | echo 'none', PHP_EOL; 26 | } 27 | 28 | echo PHP_EOL; 29 | echo 'Extension-global constants:', PHP_EOL; 30 | 31 | if ($re->getConstants()) { 32 | foreach ($re->getConstants() as $name => $value) { 33 | echo "$name = ", var_export($value, true), PHP_EOL; 34 | } 35 | } else { 36 | echo 'none', PHP_EOL; 37 | } 38 | 39 | echo PHP_EOL; 40 | echo 'Extension-global classes:', PHP_EOL; 41 | 42 | if ($re->getClasses()) { 43 | foreach ($re->getClasses() as $rc) { 44 | $this->dumpClass($rc); 45 | echo PHP_EOL; 46 | } 47 | } else { 48 | echo 'none', PHP_EOL; 49 | } 50 | 51 | 52 | } 53 | 54 | protected function dumpClass(ReflectionClass $rc) 55 | { 56 | if ($rc->isTrait()) { 57 | echo 'trait '; 58 | } elseif ($rc->isInterface()) { 59 | echo 'interface '; 60 | } else { 61 | if ($rc->isAbstract()) { 62 | echo 'abstract '; 63 | } 64 | 65 | if ($rc->isFinal()) { 66 | echo 'final '; 67 | } 68 | 69 | echo 'class '; 70 | } 71 | 72 | echo $rc->getName(), PHP_EOL; 73 | 74 | if ($rc->getParentClass()) { 75 | echo ' extends ', $rc->getParentClass()->getName(), PHP_EOL; 76 | } 77 | 78 | foreach ($rc->getInterfaces() as $ri) { 79 | echo ' implements ', $ri->getName(), PHP_EOL; 80 | } 81 | 82 | foreach ($rc->getTraits() as $rt) { 83 | echo ' use ', $rt->getName(), PHP_EOL; 84 | } 85 | 86 | foreach ($rc->getConstants() as $name => $value) { 87 | echo " const {$name} = ", var_export($value, true), PHP_EOL; 88 | } 89 | 90 | foreach ($rc->getProperties() as $rp) { 91 | if ($rp->getDeclaringClass() != $rc) { 92 | continue; 93 | } 94 | 95 | echo ' '; 96 | 97 | if ($rp->isStatic()) { 98 | echo 'static '; 99 | } 100 | 101 | 102 | if ($rp->isPublic()) { 103 | echo 'public '; 104 | } 105 | 106 | if ($rp->isProtected()) { 107 | echo 'protected '; 108 | } 109 | 110 | if ($rp->isPrivate()) { 111 | echo 'private '; 112 | } 113 | 114 | echo '$', $rp->getName(); 115 | echo PHP_EOL; 116 | } 117 | 118 | 119 | 120 | foreach ($rc->getMethods() as $rm) { 121 | if ($rm->getDeclaringClass() != $rc) { 122 | continue; 123 | } 124 | 125 | echo ' '; 126 | 127 | if ($rm->isAbstract()) { 128 | echo 'abstract '; 129 | } 130 | 131 | if ($rm->isFinal()) { 132 | echo 'final '; 133 | } 134 | 135 | if ($rm->isPublic()) { 136 | echo 'public '; 137 | } 138 | 139 | if ($rm->isProtected()) { 140 | echo 'protected '; 141 | } 142 | 143 | if ($rm->isPrivate()) { 144 | echo 'private '; 145 | } 146 | 147 | if ($rm->isStatic()) { 148 | echo 'static '; 149 | } 150 | 151 | echo 'function ', $rm->getName(); 152 | echo $this->dumpPartialFunction($rm), PHP_EOL; 153 | } 154 | } 155 | 156 | protected function dumpFunction(ReflectionFunction $rf) 157 | { 158 | if ($rf->inNamespace()) { 159 | echo $rf->getNamespaceName(), ': '; 160 | } 161 | 162 | echo 'function ', $rf->getName(); 163 | 164 | echo $this->dumpPartialFunction($rf); 165 | echo PHP_EOL; 166 | } 167 | 168 | protected function dumpPartialFunction(ReflectionFunctionAbstract $rf) 169 | { 170 | $ret = '('; 171 | 172 | $parameters = []; 173 | foreach ($rf->getParameters() as $parameter) { 174 | $parameters[] = $this->dumpParameter($parameter); 175 | } 176 | 177 | $ret .= implode(', ', $parameters); 178 | 179 | $ret .= ')'; 180 | 181 | if ($rf->hasReturnType()) { 182 | $ret .= ': ' . ($rf->getReturnType()->allowsNull() ? '?' : '') . $rf->getReturnType(); 183 | } 184 | 185 | return $ret; 186 | } 187 | 188 | protected function dumpParameter(ReflectionParameter $rp) 189 | { 190 | $ret = []; 191 | 192 | $ret[] = $rp->hasType() ? ($rp->allowsNull() ? '?' : '') . (string)$rp->getType() : null; 193 | $ret[] = ($rp->isVariadic() ? '...' : '') . "\${$rp->getName()}"; 194 | 195 | // $ret[] = $rp->isOptional() ? '= ?' : ''; 196 | 197 | return trim(implode(' ', $ret)); 198 | } 199 | } 200 | 201 | $d = new Dumper(); 202 | 203 | $d->dumpExtension(); 204 | 205 | ?> 206 | --EXPECT-- 207 | Extension-global functions: 208 | Ref: function Ref\refcounted($value): bool 209 | Ref: function Ref\refcount(object $object): int 210 | Ref: function Ref\softrefcounted(object $object): bool 211 | Ref: function Ref\softrefcount(object $object): int 212 | Ref: function Ref\softrefs(object $object): array 213 | Ref: function Ref\weakrefcounted(object $object): bool 214 | Ref: function Ref\weakrefcount(object $object): int 215 | Ref: function Ref\weakrefs(object $object): array 216 | Ref: function Ref\object_handle(object $object): int 217 | Ref: function Ref\is_obj_destructor_called(object $object): bool 218 | 219 | Extension-global constants: 220 | none 221 | 222 | Extension-global classes: 223 | class Ref\NotifierException 224 | extends Exception 225 | implements Throwable 226 | private $exceptions 227 | public function __construct($message, $exceptions, $code, $previous) 228 | public function getExceptions() 229 | 230 | abstract class Ref\AbstractReference 231 | public function __construct(object $referent, ?callable $notify) 232 | public function get() 233 | public function valid(): bool 234 | public function notifier(?callable $notify): ?callable 235 | 236 | class Ref\SoftReference 237 | extends Ref\AbstractReference 238 | 239 | class Ref\WeakReference 240 | extends Ref\AbstractReference 241 | -------------------------------------------------------------------------------- /php_ref_functions.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the pinepain/php-ref PHP extension. 3 | * 4 | * Copyright (c) 2016-2018 Bogdan Padalko 5 | * 6 | * Licensed under the MIT license: http://opensource.org/licenses/MIT 7 | * 8 | * For the full copyright and license information, please view the 9 | * LICENSE file that was distributed with this source or visit 10 | * http://opensource.org/licenses/MIT 11 | */ 12 | 13 | #include "php_ref_functions.h" 14 | #include "php_ref_reference.h" 15 | #include "php_ref.h" 16 | 17 | PHP_FUNCTION(refcounted) 18 | { 19 | zval *zv; 20 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 21 | return; 22 | } 23 | 24 | RETURN_BOOL(Z_REFCOUNTED_P(zv)); 25 | } 26 | 27 | PHP_FUNCTION(refcount) 28 | { 29 | zval *zv; 30 | 31 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 32 | return; 33 | } 34 | 35 | if (Z_REFCOUNTED_P(zv)) { 36 | RETURN_LONG(Z_REFCOUNT_P(zv) - 1); /* -1 to skip passed argument counting */ 37 | } 38 | 39 | RETURN_LONG(0); 40 | } 41 | 42 | PHP_FUNCTION(softrefcounted) 43 | { 44 | zval *zv; 45 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 46 | return; 47 | } 48 | 49 | if (IS_OBJECT == Z_TYPE_P(zv)) { 50 | php_ref_referent_t *referent = php_ref_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); 51 | 52 | RETURN_BOOL(NULL != referent && zend_hash_num_elements(&referent->soft_references)); 53 | } 54 | 55 | RETURN_BOOL(0); 56 | } 57 | 58 | PHP_FUNCTION(softrefcount) 59 | { 60 | zval *zv; 61 | 62 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 63 | return; 64 | } 65 | 66 | if (IS_OBJECT == Z_TYPE_P(zv)) { 67 | 68 | php_ref_referent_t *referent = php_ref_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); 69 | 70 | if (NULL == referent) { 71 | RETURN_LONG(0); 72 | } 73 | 74 | RETURN_LONG(zend_hash_num_elements(&referent->soft_references)); 75 | } 76 | 77 | RETURN_LONG(0); 78 | } 79 | 80 | PHP_FUNCTION(softrefs) 81 | { 82 | zval *zv; 83 | zval softrefs; 84 | 85 | php_ref_reference_t *reference; 86 | 87 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 88 | return; 89 | } 90 | 91 | ZVAL_UNDEF(&softrefs); 92 | 93 | if (IS_OBJECT == Z_TYPE_P(zv)) { 94 | php_ref_referent_t *referent = php_ref_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); 95 | 96 | if (NULL != referent) { 97 | array_init_size(&softrefs, zend_hash_num_elements(&referent->soft_references)); 98 | 99 | ZEND_HASH_FOREACH_PTR(&referent->soft_references, reference) { 100 | add_next_index_zval(&softrefs, &reference->this_ptr); 101 | Z_ADDREF(reference->this_ptr); 102 | } ZEND_HASH_FOREACH_END(); 103 | } 104 | } 105 | 106 | if (IS_UNDEF == Z_TYPE(softrefs)) { 107 | array_init_size(&softrefs, 0); 108 | } 109 | 110 | RETURN_ZVAL(&softrefs, 1, 1); 111 | } 112 | 113 | PHP_FUNCTION(weakrefcounted) 114 | { 115 | zval *zv; 116 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 117 | return; 118 | } 119 | 120 | if (IS_OBJECT == Z_TYPE_P(zv)) { 121 | php_ref_referent_t *referent = php_ref_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); 122 | 123 | RETURN_BOOL(NULL != referent && zend_hash_num_elements(&referent->weak_references)); 124 | } 125 | 126 | RETURN_BOOL(0); 127 | } 128 | 129 | PHP_FUNCTION(weakrefcount) 130 | { 131 | zval *zv; 132 | 133 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 134 | return; 135 | } 136 | 137 | if (IS_OBJECT == Z_TYPE_P(zv)) { 138 | 139 | php_ref_referent_t *referent = php_ref_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); 140 | 141 | if (NULL == referent) { 142 | RETURN_LONG(0); 143 | } 144 | 145 | RETURN_LONG(zend_hash_num_elements(&referent->weak_references)); 146 | } 147 | 148 | RETURN_LONG(0); 149 | } 150 | 151 | PHP_FUNCTION(weakrefs) 152 | { 153 | zval *zv; 154 | zval weakrefs; 155 | 156 | php_ref_reference_t *reference; 157 | 158 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { 159 | return; 160 | } 161 | 162 | ZVAL_UNDEF(&weakrefs); 163 | 164 | if (IS_OBJECT == Z_TYPE_P(zv)) { 165 | php_ref_referent_t *referent = php_ref_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); 166 | 167 | if (NULL != referent) { 168 | array_init_size(&weakrefs, zend_hash_num_elements(&referent->weak_references)); 169 | 170 | ZEND_HASH_FOREACH_PTR(&referent->weak_references, reference) { 171 | add_next_index_zval(&weakrefs, &reference->this_ptr); 172 | Z_ADDREF(reference->this_ptr); 173 | } ZEND_HASH_FOREACH_END(); 174 | } 175 | } 176 | 177 | if (IS_UNDEF == Z_TYPE(weakrefs)) { 178 | array_init_size(&weakrefs, 0); 179 | } 180 | 181 | RETURN_ZVAL(&weakrefs, 1, 1); 182 | } 183 | 184 | PHP_FUNCTION(object_handle) 185 | { 186 | zval *zv; 187 | 188 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zv) == FAILURE) { 189 | return; 190 | } 191 | 192 | RETURN_LONG((uint32_t)Z_OBJ_HANDLE_P(zv)); 193 | } 194 | 195 | PHP_FUNCTION(is_obj_destructor_called) 196 | { 197 | zval *zv; 198 | 199 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zv) == FAILURE) { 200 | return; 201 | } 202 | 203 | zend_object *obj = Z_OBJ_P(zv); 204 | 205 | uint32_t flags = GC_FLAGS(obj); 206 | 207 | RETURN_BOOL(flags & IS_OBJ_DESTRUCTOR_CALLED); 208 | } 209 | 210 | 211 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(refcounted_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, 0) 212 | ZEND_ARG_INFO(0, value) 213 | ZEND_END_ARG_INFO() 214 | 215 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(refcount_arg, ZEND_RETURN_VALUE, 1, IS_LONG, 0) 216 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 217 | ZEND_END_ARG_INFO() 218 | 219 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(softrefcounted_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, 0) 220 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 221 | ZEND_END_ARG_INFO() 222 | 223 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(softrefcount_arg, ZEND_RETURN_VALUE, 1, IS_LONG, 0) 224 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 225 | ZEND_END_ARG_INFO() 226 | 227 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(softrefs_arg, ZEND_RETURN_VALUE, 1, IS_ARRAY, 0) 228 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 229 | ZEND_END_ARG_INFO() 230 | 231 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(weakrefcounted_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, 0) 232 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 233 | ZEND_END_ARG_INFO() 234 | 235 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(weakrefcount_arg, ZEND_RETURN_VALUE, 1, IS_LONG, 0) 236 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 237 | ZEND_END_ARG_INFO() 238 | 239 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(weakrefs_arg, ZEND_RETURN_VALUE, 1, IS_ARRAY, 0) 240 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 241 | ZEND_END_ARG_INFO() 242 | 243 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(object_handle_arg, ZEND_RETURN_VALUE, 1, IS_LONG, 0) 244 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 245 | ZEND_END_ARG_INFO() 246 | 247 | ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(is_obj_destructor_called_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, 0) 248 | ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) 249 | ZEND_END_ARG_INFO() 250 | 251 | const zend_function_entry php_ref_functions[] = { 252 | ZEND_NS_FE(PHP_REF_NS, refcounted, refcounted_arg) 253 | ZEND_NS_FE(PHP_REF_NS, refcount, refcount_arg) 254 | 255 | ZEND_NS_FE(PHP_REF_NS, softrefcounted, softrefcounted_arg) 256 | ZEND_NS_FE(PHP_REF_NS, softrefcount, softrefcount_arg) 257 | ZEND_NS_FE(PHP_REF_NS, softrefs, softrefs_arg) 258 | 259 | ZEND_NS_FE(PHP_REF_NS, weakrefcounted, weakrefcounted_arg) 260 | ZEND_NS_FE(PHP_REF_NS, weakrefcount, weakrefcount_arg) 261 | ZEND_NS_FE(PHP_REF_NS, weakrefs, weakrefs_arg) 262 | 263 | ZEND_NS_FE(PHP_REF_NS, object_handle, object_handle_arg) 264 | ZEND_NS_FE(PHP_REF_NS, is_obj_destructor_called, is_obj_destructor_called_arg) 265 | 266 | PHP_FE_END 267 | }; 268 | --------------------------------------------------------------------------------