Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Catchable "Call to a member function bar() on a non-object" #647

Closed
wants to merge 41 commits into from

5 participants

@thekid

This pull request turns fatal errors generated from calls to methods on a non-object into E_RECOVERABLE_ERRORs.

Example

set_error_handler(function($code, $message) {
  var_dump($code, $message);
});

$x= null;
var_dump($x->method());
echo "Alive\n";

The above produces the following output:

int(4096)
string(50) "Call to a member function method() on a non-object"
NULL
Alive

Reasoning

If you want to run PHP as a webserver itself, fatal errors are problematic. For a long story on why you would want to do that in the first place, see http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html

Consistency

This behavior is consistent with how type hints work. Framework authors can turn this into exceptions if they wish.

Inner workings

The essence of this PR is a patch to the Zend Engine, which raises an E_RECOVERABLE_ERROR and then skips over all opcodes until it finds the one ending the current call, and sets return value to NULL.

See this gist for the patch to Zend/zend_vm_def.h only, the full diff view contains tests as well as generated code.

Further reading

@smalyshev
Owner

This is a very nicely made pull request. However, I would say this would require an RFC or at least discussion on internals list to see if we have support for it.

@thekid

@smalyshev - thanks for the feedback! I will come up with an RFC ASAP.

@thekid

Set the RFC to :one:.:zero: and started discussion on internals mailing list.

Zend/zend_vm_def.h
((9 lines not shown))
+ CHECK_EXCEPTION();
+
+ /* Skip over arguments until fcall opcode, return NULL */
+ do {
+ ZEND_VM_INC_OPCODE();
+ opline++;
+ } while (ZEND_DO_FCALL_BY_NAME != opline->opcode);
+
+ MAKE_STD_ZVAL(EX_T(opline->result.var).var.ptr);
+ ZVAL_NULL(EX_T(opline->result.var).var.ptr);
+ Z_UNSET_ISREF_P(EX_T(opline->result.var).var.ptr);
+ Z_SET_REFCOUNT_P(EX_T(opline->result.var).var.ptr, 1);
+ EX_T(opline->result.var).var.fcall_returned_reference = 0;
+ EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr;
+ ZEND_VM_NEXT_OPCODE();
+ return;
@nikic Owner
nikic added a note

This return; will break the goto and switch VMs. You can just drop it, as ZEND_VM_NEXT_OPCODE() will already transfer control from here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Zend/zend_vm_def.h
((5 lines not shown))
+
+ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on a non-object", function_name_strval);
+ FREE_OP2();
+ FREE_OP1_IF_VAR();
+ CHECK_EXCEPTION();
+
+ /* Skip over arguments until fcall opcode, return NULL */
+ do {
+ ZEND_VM_INC_OPCODE();
+ opline++;
+ } while (ZEND_DO_FCALL_BY_NAME != opline->opcode);
+
+ MAKE_STD_ZVAL(EX_T(opline->result.var).var.ptr);
+ ZVAL_NULL(EX_T(opline->result.var).var.ptr);
+ Z_UNSET_ISREF_P(EX_T(opline->result.var).var.ptr);
+ Z_SET_REFCOUNT_P(EX_T(opline->result.var).var.ptr, 1);
@nikic Owner
nikic added a note

The Z_UNSET_ISREF and Z_SET_REFCOUNT are unnecessary here - this is already handled by MAKE_STD_ZVAL. You can replace all four lines with

ALLOC_INIT_ZVAL(EX_T(opline->result.var).var.ptr);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Zend/zend_vm_def.h
@@ -2463,7 +2463,26 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMP|VAR|UNUSED|CV, CONST|TMP|VAR|CV)
FREE_OP2();
HANDLE_EXCEPTION();
}
- zend_error_noreturn(E_ERROR, "Call to a member function %s() on a non-object", function_name_strval);
+
+ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on a non-object", function_name_strval);
+ FREE_OP2();
+ FREE_OP1_IF_VAR();
+ CHECK_EXCEPTION();
+
+ /* Skip over arguments until fcall opcode, return NULL */
+ do {
+ ZEND_VM_INC_OPCODE();
+ opline++;
+ } while (ZEND_DO_FCALL_BY_NAME != opline->opcode);
@nikic Owner
nikic added a note

What would happen here if one of the arguments is a function call as well?

$null->method(foo());

Wouldn't this only skip to the ZEND_DO_FCALL_BY_NAME of the call in the args and wreak havoc?

@thekid
thekid added a note

You're right, theoretically this should be problematic, the opcodes go along the lines of:

L0-0 {main}() /home/friebe/devel/php-src/nested.php
L2      0x7f852c033da0 ZEND_ASSIGN                    $nul                 C0                   @0
L3      0x7f852c033dd0 ZEND_INIT_METHOD_CALL          $nul                 C2                   <unused>
L3      0x7f852c033e00 ZEND_INIT_FCALL_BY_NAME        <unused>             C4                   <unused>
L3      0x7f852c033e30 ZEND_DO_FCALL_BY_NAME          <unused>             <unused>             @1
L3      0x7f852c033e60 ZEND_SEND_VAR_NO_REF           @1                   <unused>             <unused>
L3      0x7f852c033e90 ZEND_DO_FCALL_BY_NAME          <unused>             <unused>             @2

...so looks like we need to modify the skipping part a bit. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Zend/zend_vm_def.h
((7 lines not shown))
+ FREE_OP2();
+ FREE_OP1_IF_VAR();
+ CHECK_EXCEPTION();
+
+ /* Skip over arguments until fcall opcode, return NULL */
+ do {
+ ZEND_VM_INC_OPCODE();
+ opline++;
+ } while (ZEND_DO_FCALL_BY_NAME != opline->opcode);
+
+ MAKE_STD_ZVAL(EX_T(opline->result.var).var.ptr);
+ ZVAL_NULL(EX_T(opline->result.var).var.ptr);
+ Z_UNSET_ISREF_P(EX_T(opline->result.var).var.ptr);
+ Z_SET_REFCOUNT_P(EX_T(opline->result.var).var.ptr, 1);
+ EX_T(opline->result.var).var.fcall_returned_reference = 0;
+ EX_T(opline->result.var).var.ptr_ptr = &EX_T(opline->result.var).var.ptr;
@nikic Owner
nikic added a note

You are unconditionally setting the opline->result of DO_FCALL_BY_NAME here - however that opcode does not always use a return operand (RETURN_VALUE_USED(opline)). I would expect that this will result in a memory leak when writing $null->method() without using the return value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nikic
Owner

Another thing to consider is how this interacts with EXT_FCALL_BEGIN/END. As currently implemented, I think this will result in EXT_FCALL_BEGIN to be skipped, but EXT_FCALL_END being run, which doesn't sounds right. You can test this using sapi/cli/php -E, if I remember correctly.

From the tests side, It would be nice to test calls with non-cv operands, e.g. $arr[0]->{$arr[1]}(), as well as nested calls.

@thekid

@nikic - thanks for the detailled feedback! I'll look into this ASAP and give you feedback one by one.

@nikic

This is still missing ZEND_INIT_STATIC_METHOD_CALL and ZEND_INIT_NS_FCALL_BY_NAME and ZEND_NEW - maybe an alternative approach is to check whether the opline->op2.num (nested_calls) matches?

thekid added some commits
@thekid thekid Add support for PHP's 'extended information for debugger/profiler' mode ab4ded9
@thekid thekid Handle ZEND_EXT_FCALL_END, skipping if necessary
Verified with running tests with new "-e" run-tests arg:
$ make test TESTS=Zend/tests/*-on-non-objects-*phpt TEST_PHP_ARGS=-e
# Tests passed    :   11 (100.0%)

$ make test TESTS=Zend/tests/*-on-non-objects-*phpt
# Tests passed    :   11 (100.0%)

Before, this would cause a SEGV. Thanks @nikic for raising this concern
df90827
@thekid thekid Support nested static calls ff66e59
@thekid thekid QA: Refactor: Split tests a bit to make them more comprehendable 7cd8ecb
@thekid thekid Handle ZEND_INIT_NS_FCALL_BY_NAME nesting 9dbc91f
@thekid thekid Also verify nesting with dynamically called static methods d7156c1
@thekid thekid Handle ZEND_NEW nesting 7e2e4eb
@thekid

maybe an alternative approach is to check whether the opline->op2.num (nested_calls) matches

This worked out well until I added a test for ZEND_INIT_NS_FCALL_BY_NAME, here the nesting level in ZEND_DO_FCALL_BY_NAME's op2.num field is inconsistent with what I manually counted :/

thekid added some commits
@thekid
  • Another thing to consider is how this interacts with EXT_FCALL_BEGIN / END - verified by running the tests with TEST_PHP_ARGS=-e (support added to run_tests.php in thekid/php-src@ab4ded9)
  • It would be nice to test calls with non-cv operands - Done, see thekid/php-src@e0cf63f
  • [...] as well as nested calls. - Done, in various commits above for all opcodes that are closed with ZEND_DO_FCALL_BY_NAME: ZEND_INIT_METHOD_CALL, ZEND_INIT_FCALL_BY_NAME, ZEND_INIT_NS_FCALL_BY_NAME, ZEND_INIT_STATIC_METHOD_CALL and ZEND_NEW
  • alternative approach is to check whether the opline->op2.num (nested_calls) matches - failed when I tried, see above
@thekid
  • alternative approach is to check whether the opline->op2.num (nested_calls) matches - Tried again, after quite a bit of reverse engineering I understood how CG(context).nested_calls works. So here we go
thekid added some commits
@thekid thekid QA: Simplify code to find matching ZEND_DO_FCALL_BY_NAME
CG(context).nested_calls is stored inside the initializer's result.num
and inside the finalizer's op2.num, by comparing these we don't need
to count manually, and are thus safer from future expansion with
specialized opcodes e.g.
dacd4c8
@thekid thekid Merge branch 'master' into catchable-fatals/methods-on-non-objects ded935e
@thekid thekid Merge branch 'master' into catchable-fatals/methods-on-non-objects be957f8
@thekid thekid Merge branch 'master' into catchable-fatals/methods-on-non-objects 671f02a
@thekid thekid Merge branch 'catchable-fatals/methods-on-non-objects' of github.com:…
…thekid/php-src into catchable-fatals/methods-on-non-objects
a9653b6
Zend/zend_vm_execute.h
((6 lines not shown))
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
}
- zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object)));
+
+ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object)));
+
+
+ if (EG(exception) != NULL) {
+ HANDLE_EXCEPTION();
+ }
+
+ /* No exception raised: Skip over arguments until fcall opcode with correct
+ * nesting level. Return NULL (except when return value unused) */
+ do {
+ ZEND_VM_INC_OPCODE();
+ opline++;
@nikic Owner
nikic added a note

This won't work correctly with switch/goto VMs, because both lines will increment the opline variable. I would suggest to instead only increment opline here (and also on line 9347) and replace the CHECK_EXCEPTION() below with SAVE_OPLINE(). (Actually check_exception and save_opline are the same, but save_opline is probably more clear about what you want here)

@thekid
thekid added a note

Using the following didn't work for me:

diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 42d3f60..628db06 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -2477,7 +2477,6 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMP|VAR|UNUSED|CV, CONST|TMP
                /* No exception raised: Skip over arguments until fcall opcode with correct
                 * nesting level. Return NULL (except when return value unused) */
                do {
-                       ZEND_VM_INC_OPCODE();
                        opline++;
                } while (opline->opcode == ZEND_DO_FCALL_BY_NAME ? opline->op2.num > nesting :

@@ -2488,10 +2487,10 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMP|VAR|UNUSED|CV, CONST|T
                }

                if ((opline + 1)->opcode == ZEND_EXT_FCALL_END) {
-                       ZEND_VM_INC_OPCODE();
+                       opline++;
                }

-               CHECK_EXCEPTION();
+               SAVE_OPLINE();
                ZEND_VM_NEXT_OPCODE();
        }

The SAVE_OPLINE() macro only sets EX(opline) whereas ZEND_VM_INC_OPCODE(); affects OPLINE which is authorative for the executor AFAIS.

Seems ZEND_VM_SET_OPCODE() might be what I'm looking for.

@nikic Owner
nikic added a note

Yeah, sorry, I forgot that SAVE_OPLINE only sets EX(opline) for GOTO/SWITCH, but is a noop for CALL. ZEND_VM_SET_OPCODE sounds more like it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Zend/zend_vm_execute.h
((6 lines not shown))
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
}
- zend_error_noreturn(E_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object)));
+
+ zend_error(E_RECOVERABLE_ERROR, "Call to a member function %s() on %s", function_name_strval, zend_get_type_by_const(Z_TYPE_P(call->object)));
+
+
+ if (EG(exception) != NULL) {
+ HANDLE_EXCEPTION();
+ }
+
+ /* No exception raised: Skip over arguments until fcall opcode with correct
+ * nesting level. Return NULL (except when return value unused) */
+ do {
+ ZEND_VM_INC_OPCODE();
+ opline++;
+ } while (opline->opcode == ZEND_DO_FCALL_BY_NAME ? opline->op2.num > nesting : 1);
@nikic Owner
nikic added a note

I'd rephrase this condition as while (opline->opcode != ZEND_DO_FCALL_BY_NAME || opline->op2.num > nesting) to avoid the ternary - had some trouble understanding that ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nikic
Owner

@thekid Thanks for updating everything so quickly! I've added two last notes, though one is purely cosmetic, so feel free to ignore it ;)

@thekid

This RFC has now been accepted! I've updated the RFC to reflect this.

Last step: Merge it. @nikic - can you do this? I don't have karma for Zend/...

thekid added some commits
@thekid thekid MFH c3d214a
@thekid thekid Make list of opcodes used for nesting calculation consistent
  with `zend_do_convert_call_user_func()` in Zend/zend_compile.c
a1a4ba9
@thekid

I've brought this pull request up-to-date so it's once again mergeable with master. I had to resort back to counting ZEND_INIT_*CALL* and ZEND_DO_FCALL opcodes for handling nesting correctly again since the nesting level no longer is in op2.num. The code now uses the same list of opcodes as in zend_do_convert_call_user_func() in Zend/zend_compile.c...

$ make test TESTS=Zend/tests/*methods-*phpt
# [...]
=====================================================================
Number of tests :   16                16
Tests skipped   :    0 (  0.0%) --------
Tests warned    :    0 (  0.0%) (  0.0%)
Tests failed    :    0 (  0.0%) (  0.0%)
Expected fail   :    0 (  0.0%) (  0.0%)
Tests passed    :   16 (100.0%) (100.0%)
---------------------------------------------------------------------
Time taken      :    1 seconds
=====================================================================
@nikic

I think ZEND_INIT_USER_CALL should remain in this list, otherwise something like $obj->method(call_user_func('foo')) may fail.

I thought the same but as I didn't find it in zend_do_convert_call_user_func() from zend_compile.c I removed it. Do you know if it's an oversight there, or is there a reason it's not in that switch/case statement?

There is a test for this here inside the pull request, by the way.

Owner

@thekid What happens if you move the nested declaration to the end of the file? INIT_USER_CALL is not used for ct-bound functions.

Good one, thanks; tested in thekid/php-src@971d369 and fixed by thekid/php-src@3db6731

@thekid

From https://www.mail-archive.com/internals@lists.php.net/msg68933.html


On Aug 16, 2014, at 9:09, Timm Friebe p...@thekid.de wrote:
two weeks ago, the RFC Catchable "Call to a member function bar() on a
non-object"
was accepted by a vote. I don't have commit access to
php-src/Zend,

If it's not sorted by someone else, I land this when I get back home later
today.


@sgolemon any news?

@datibbaw

Could you squash the branch into a single commit and edit the NEWS and UPGRADING files?

@thekid

I'm off to a conference for the next couple of days and will look into getting this done ASAP after I get back; the earliest next Sunday

@thekid

Could you squash the branch into a single commit

No matter how hard I try git rebase always fails somewhere in the middle. I'll create a new PR with a single commit.

@thekid thekid referenced this pull request from a commit in thekid/php-src
@thekid thekid Implemented the RFC `Catchable "Call to a member function bar()..."`
Changed ZEND_INIT_METHOD_CALL to raise an E_RECOVERABLE_ERROR and handle
error handler outcome, returning NULL if error handler doesn't terminate
the script.

Adjusted outcome in MySQL / PDO_MySQL and ext/date
Added various examples and tests
Added tests for indirect calls - see http://news.php.net/php.internals/73823
Added tests verifying method calls on non-objects work inside eval()
Originally developed inside php#647
596ed22
@thekid thekid referenced this pull request from a commit in thekid/php-src
@thekid thekid Implemented the RFC `Catchable "Call to a member function bar()..."`
Changed ZEND_INIT_METHOD_CALL to raise an E_RECOVERABLE_ERROR and handle
error handler outcome, returning NULL if error handler doesn't terminate
the script.

Adjusted outcome in MySQL / PDO_MySQL and ext/date
Added various examples and tests
Added tests for indirect calls - see http://news.php.net/php.internals/73823
Added tests verifying method calls on non-objects work inside eval()
Originally developed inside php#647
ac9b9ec
@thekid thekid referenced this pull request from a commit in thekid/php-src
@thekid thekid Implemented the RFC `Catchable "Call to a member function bar()..."`
Changed ZEND_INIT_METHOD_CALL to raise an E_RECOVERABLE_ERROR and handle
error handler outcome, returning NULL if error handler doesn't terminate
the script.

Adjusted outcome in MySQL / PDO_MySQL and ext/date
Added various examples and tests
Added tests for indirect calls - see http://news.php.net/php.internals/73823
Added tests verifying method calls on non-objects work inside eval()
Originally developed inside php#647
ac2ede7
@TazeTSchnitzel

Is this going to be merged?

@thekid

@TazeTSchnitzel I' m trying, but everytime it is a mergeable state there's noone around to do so. I myself don't have commit access to the upstream project...

@thekid

Closing this, #847 is the new place

@thekid thekid closed this
@php-pulls php-pulls referenced this pull request from a commit
@datibbaw datibbaw Merge branch 'pr/647'
* pr/647: (33 commits)
  zend_uint -> uint32_t
  Fix nesting for *non*-compile-time-resolveable functions See thekid@a1a4ba9#commitcomment-7414223
  Add tests for calls to nested, *non*-compile-time-resolveable functions See thekid@a1a4ba9#commitcomment-7414362
  Make list of opcodes used for nesting calculation consistent   with `zend_do_convert_call_user_func()` in Zend/zend_compile.c
  Rewrite code to use ZEND_VM_JMP() instead of repeated ZEND_VM_INC_OPCODE() calls
  QA: Simplify code to find matching ZEND_DO_FCALL_BY_NAME CG(context).nested_calls is stored inside the initializer's result.num and inside the finalizer's op2.num, by comparing these we don't need to count manually, and are thus safer from future expansion with specialized opcodes e.g.
  Fix expected fatal error, now is catchable fatal
  Adjust expected fatal error message Now also includes "on [TYPE]" after merge from master
  Check for memory leaks when not using return value
  Adjust expected fatal error message Now also includes "on [TYPE]" after merge from master
  Add tests with arrays as parameters
  Handle ZEND_NEW nesting
  Also verify nesting with dynamically called static methods
  Handle ZEND_INIT_NS_FCALL_BY_NAME nesting
  QA: Refactor: Split tests a bit to make them more comprehendable
  Support nested static calls
  Handle ZEND_EXT_FCALL_END, skipping if necessary Verified with running tests with new "-e" run-tests arg: $ make test TESTS=Zend/tests/*-on-non-objects-*phpt TEST_PHP_ARGS=-e # Tests passed    :   11 (100.0%)
  Add support for PHP's 'extended information for debugger/profiler' mode
  Verify non-CV-operands also work See discussion #647 (comment)
  Only allocate NULL return value if it's actually used
  ...

Conflicts:
	ext/date/tests/bug67118.phpt
82523c0
@datibbaw

Oh shit, the numbers looked so alike that I had merged in this PR instead of the newer one.

They're the same changes minus the added test cases, so instead of reverting what I have already done I shall just add the additional test cases.

My bad =(

@thekid

I [...] merged in this PR

\o/

@thekid thekid deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 19, 2014
  1. @thekid

    Initial implementation

    thekid authored
  2. @thekid

    Adjust expected outcome

    thekid authored
  3. @thekid

    Regenerate

    thekid authored
  4. @thekid
  5. @thekid
  6. @thekid
Commits on Apr 20, 2014
  1. @thekid
Commits on Apr 28, 2014
  1. @thekid
  2. @thekid
Commits on Jul 6, 2014
  1. @thekid

    Handle nesting correctly

    thekid authored
    See feedback by @nikic, php#647 (comment)
  2. @thekid
  3. @thekid
  4. @thekid
  5. @thekid
  6. @thekid

    Verify non-CV-operands also work

    thekid authored
    See discussion php#647 (comment)
  7. @thekid
  8. @thekid

    Handle ZEND_EXT_FCALL_END, skipping if necessary

    thekid authored
    Verified with running tests with new "-e" run-tests arg:
    $ make test TESTS=Zend/tests/*-on-non-objects-*phpt TEST_PHP_ARGS=-e
    # Tests passed    :   11 (100.0%)
    
    $ make test TESTS=Zend/tests/*-on-non-objects-*phpt
    # Tests passed    :   11 (100.0%)
    
    Before, this would cause a SEGV. Thanks @nikic for raising this concern
  9. @thekid

    Support nested static calls

    thekid authored
  10. @thekid
  11. @thekid
  12. @thekid
  13. @thekid

    Handle ZEND_NEW nesting

    thekid authored
  14. @thekid
  15. @thekid

    Merge in changes from master

    thekid authored
    Now also includes "on [TYPE]" in "Call to a member function" error
  16. @thekid

    Adjust expected fatal error message

    thekid authored
    Now also includes "on [TYPE]" after merge from master
  17. @thekid
  18. @thekid

    Adjust expected fatal error message

    thekid authored
    Now also includes "on [TYPE]" after merge from master
  19. @thekid
  20. @thekid

    QA: Simplify code to find matching ZEND_DO_FCALL_BY_NAME

    thekid authored
    CG(context).nested_calls is stored inside the initializer's result.num
    and inside the finalizer's op2.num, by comparing these we don't need
    to count manually, and are thus safer from future expansion with
    specialized opcodes e.g.
  21. @thekid
Commits on Jul 7, 2014
  1. @thekid
Commits on Jul 8, 2014
  1. @thekid
  2. @thekid

    Merge branch 'catchable-fatals/methods-on-non-objects' of github.com:…

    thekid authored
    …thekid/php-src into catchable-fatals/methods-on-non-objects
Commits on Jul 12, 2014
  1. @thekid
Commits on Aug 16, 2014
  1. @thekid

    MFH

    thekid authored
  2. @thekid

    Make list of opcodes used for nesting calculation consistent

    thekid authored
      with `zend_do_convert_call_user_func()` in Zend/zend_compile.c
  3. @thekid
  4. @thekid
Commits on Sep 25, 2014
  1. @thekid
  2. @thekid

    zend_uint -> uint32_t

    thekid authored
Commits on Sep 27, 2014
Something went wrong with that request. Please try again.