Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

anon class objects #470

Closed
wants to merge 68 commits into from
@krakjoe

allow anonymous class objects

<?php
$var = new class {
    public $data;

    public function __construct($data) {
        $this->data = $data;
    }
} ("Hello World");

var_dump($var);

interface IObject {
    public function iMethod();
}

function my_function(IObject $object) {
    return $object->iMethod();
}

var_dump(my_function(new class implements IObject {
    public function iMethod() {
        return __METHOD__;
    }
}));
?>

Thoughts ??

@krakjoe

Last commit makes the following possible:

class My {
    public function Test() {
        return new class{};
    }
}
@Majkl578

:+1:
I'd really like to see implementing interfaces / extending classes (especially abstract) anonymously in PHP, like (Java does)[http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html].

Some questions:

  • How would reflection handle these anonymous classes (when using ReflectionClass($obj))?
  • How would var_dump represent this object?
  • What would happen when using instanceof on this object?
  • ...

Also note that the new class syntax may interfere the PR #438 (or not?).

@krakjoe

This supports the use of abstracts/interfaces/traits etc ...

new class implements Whatever {}
new class extends Something implements Whatever {}

is fine ...

Anonymous classes are normal classes without (programmer declared) names, so ReflectionClass/var_dump/instanceof will work as you expect

Don't see a reason for it to interfere with #438 ...

@krakjoe krakjoe cleanup patch:
	make sure use of class_token is correct
	revert changes to znode typedef, reuse op.ptr
f4b3b55
@lisachenko

Not sure that I like this proposal, because syntax is too bad, especially with defining new classes and interfaces inside functions. May be we choose a Java way of private|protected classes or interfaces inside the concrete class except anonymous classes? So classes and interfaces can be defined inside the body of class but will not be available for general use outside.

@kassner

Any clue about how object serialization will work with this?

@yagmikita

Can you provide any examples where one can not handle the situation without anon class ? Don't you think this will break some of the SOLID paradigms ?

@feketegy

How is this different from

$var = new stdClass;

?

@krakjoe

Hi chaps, please see the RFC and @internals for updates and answers ...

@kassner

@yagmikita Normally way I will expect serialize/unserialize working with any object that is not dependent of a resource. In this case, I will not able to unserialize an anon class, because there is not class name. Let me know if I could not explain well.

@krakjoe

@kassner there is not a programmer declared class name, but there is a class name ... serialization and unserialization will both work, dependent on the application obviously ...

@datibbaw

I think it's a great idea to allow more expressiveness in the language, and all things considered it's a rather small patch :)

@HallofFamer

I like it, its been a while I get so excited at a potential PHP new feature since namespace support(although PHP's namespace is still kinda premature). I was wondering though, is there a reason why you have to use this verbose syntax to create an anonymous class:

return new class extends superclass{

}

rather than this simpler syntax:

return new superclass{

}

@HallofFamer

@feketegy: A stdclass does not have any behaviors, it has no methods and since PHP does not allow metaprogramming, theres nothing you can do about this. Also stdclass does not inherit from a parent class or implement an interface.

@krakjoe

@HallofFamer you should probably try to restrain your excitement, there's no telling if this will be merged or not ...

I messed with both syntaxes, the latter feels like java:

return new Object(args)  { /* ... */ };

This just felt more PHP, this is also a tiny bit easier to implement, and so keeps the patch simple ...

@yagmikita

@feketegy: 'PHP does not allow metaprogramming'
what about Reflection API ?

@krakjoe

This will now work:

<?php
while ($i++ < 10) {
   var_dump(new class{});
}
?>
@HallofFamer

@krakjoe: I see, it would be a pity if it does not make it to PHP 5.6, especially considering how they actually added 'goto' in PHP 5.3, a common misfeature in all languages that implement it.

Id say its quite subjective to conclude that your proposed syntax feels more like PHP. PHP has been porting OO features from other languages, its really hard to say what feels like natural PHP(or perhaps the concept of 'natural PHP' does not exist at all). Maybe its nice to create a different syntax from Java's anonymous class to make it more unique, but I doubt its helpful if the syntax becomes a lot more verbose like that. I am more interested in the performance difference, like whether the simpler syntax or the proposed syntax causes more overhead.

Still, I am just stating my own opinion though. The bottom-line is, I really dig this piece of work and I'd like to see it make PHP 5.6. The syntax part is negotiable and can always be revised, perhaps its a good idea to start a poll on php rfc on which syntax PHP core developers prefer if Anonymous class does make it to voting phase? Again thats just my humble suggestion.

@HallofFamer

@yagmikita: To me Metaprogramming involves dynamically creating/editing/removing properties and methods for a class/object. Reflection merely allows you to see through existing properties/methods and get through the visibility barrier upon accessing/invoking existing properties/methods, but its definitely not what I'd envision for true metaprogramming. Perhaps reflection can be considered part of metaprogramming, and definitely not equivalent concept though.

In fact PHP has an extension called runkit, which achieves this purpose but its on PECL and does not work with PHP 5.4. Id like to see metaprogramming make it to the core of PHP, not just through an extension that only users with dedicated hosting/servers can use. Unfortunately, the proposal of bundling runkit with PHP core has been rejected, so no metaprogramming for at least until PHP 6(or more likely never).

@krakjoe

Another thing to consider about order, it doesn't really make sense to pass arguments to a constructor that you haven't yet declared, I know anonymous functions do it, but I didn't write that, if I had, I wouldn't have done it that way ... because it just doesn't feel right ...

Yeah syntax is negotiable I guess ... its not dead important though ...

@HallofFamer

True, Id definitely love to see this feature in PHP 5.6 no matter what the syntax is, it cant get worse than PHP's namespace syntax after all. XD If I may suggest though, would you mind making a poll of the choice of syntax if this RFC makes it to the voting phase? Also how do the other PHP core developers think about it? Again if they ended up adding a misfeature like 'goto' at some point we should be optimistic that a useful and beneficial feature like anonymous class will be added.

@krakjoe

I actually am beginning to think this syntax is less ambigious ... if we do it the other way, not only will you be passing arguments to a constructor that doesn't exist when you pass them, but you will be able to do new IFace for interfaces, which, however you look at it is wrong ...

I really think we should stick with this syntax ...

I'm not really sure to be honest what the others are thinking, some positive, some negative, some don't get it ... we'll see ...

@HallofFamer

I still like the short-hand syntax better, dont see a problem with argument passing if you extend from a concrete class(just pass the arguments inside the parent class constructor). Anyway, its a minor point so again Id say make a poll of the choice of syntax if this goes to RFC voting phase. For now, lets talk about something else shall we?

I wonder though, why did you allow extend $this but then drop this option? Just curious.

@robbytaylor

I'm not sure how sought after my opinion is but I'll stick my oar in anyway because I like the look this feature.

I can't see that I'd ever use something like this in production code but in the early stages of a project this would be really useful. I tend to spend as much time coding as deciding what best to call my classes early on so this could save me a lot of time!

I get the arguments for both syntax styles. It does seem a little odd to pass arguments to a constructor which hasn't been defined. On the other hand when debuggin some code (especially someone else's spaghetti code) I'd prefer to see which arguments the object is being instantiated with when reading the class definition to give the code some context. For that reason I'd prefer the new Object(args) { /* ... */ }; style.

I haven't looked at the code in the pull request but would it be possible to have both styles as an option and leave it up to the programmer's style guide to determine which is used?

@krakjoe

@HallofFamer I was in the middle of working something out that turned out to be intractable ... or too disruptive, and quite unnecessary anyway ...

@robbytaylor I will see what I can find out, I'm trying to avoid raising the number of expected conflicts which is 3 right now and 3 with this patch, with other syntax it can go up as far as 8, which is unacceptable ... this is the only syntax I'm able to work out that keeps everything as much as possible, the same, and uses existing functionality to work, rather than introducing a bunch of new stuff, untested, new stuff ... there's technical reasons for doing it this way, I have not mentioned them yet because they aren't usually of paramount importance; "that's my problem" kind of thing, but if its a difference between being rejected or accepted for the sake of syntax ....

All opinion is appreciated :+1:

@HallofFamer

I see, thats really cool. I looked at the RFC page, seems that you are making tons of progress lately, amazing job so far. I hope the core PHP coders will accept this into PHP 5.6, I dont see why not since it does not even have BC issue.

Zend/zend_compile.c
@@ -3738,11 +3773,18 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
} else if (ce->interfaces[i] == iface) {
if (i < parent_iface_num) {
ignore = 1;
- } else {
+ } else {
+ if (ce->ce_flags & ZEND_ACC_ANON_CLASS) {
+ if (ce->ce_flags & ZEND_ACC_FINAL_CLASS) {

not sure if you care but weird indentation going on here

@krakjoe
krakjoe added a note

just doing the housework now :D

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

Looks like you guys are proposing inner/nested class too, any discussion going on about that RFC and how can we make comment about it?

@krakjoe

You can post to the internals mailing list in reply to the message posted there concerning nested classes...

There's not really a proper discussion getting started, because this hasn't come up for vote yet so there doesn't seem much point ...

I have two nesting branches, one that is simple (the one currently RFC'd), and one that supports public/private/protected classes, I'm not sure which way the community will go ...

@HallofFamer

I see, thanks for telling me all these. I wonder if the nested class will be more Java-like or C#-like. In Java has inner classes who actually can use all fields and methods in the outer class, it's like horizontal inheritance. In C# nested class are simply namespaced and thus do not inherit outer class' properties and methods unless you explicitly pass an an instance of the outer class to the inner class' constructor. I think both ways are fine, just curious which path you guys are taking.

@krakjoe

It'll be the C# way, there's no reasonable way to share a symbol table ...

@HallofFamer

I see. How about class visibility, like public class, private class and protected class?

@HallofFamer

I see, looks really nice, but whats the colon : used in front of the visibility modifier? Its just for testing purpose, right? o_o

@krakjoe

Yeah, correct, it's gone now, we're over it ...

I'll be updating the rfc tomorrow with the new version information, couple of things left to do on it, though you should be able to build and pass tests ...

@HallofFamer

Wow this is in voting phase now, excellent, cant wait to see how it ends up with.

@HallofFamer

Why is the poll looking so horrible now? I dont remember there was much criticism about anonymous class before it went into voting phase, do people seriously reject a feature just because they either dont understand how it works or that they dont find it useful 'cause they are never gonna use it? How did goto get past the voting phase?

@krakjoe

We have a vote because it's difficult to extrapolate an opinion or consensus from mailing lists ... there's not much can be done, the community has spoken ... apparently ...

@HallofFamer

I see, guess you dont really know why they are opposing it? The only possible reason I think of is that they dont understand useful it is. I dont use generators or finally, but if I have a chance Id still vote yes for them both since I can see how they help certain developers to achieve their goals. The fact that you dont find something beneficial or that it does not make sense to you does not dictate that the other people feel the same way, Unfortunately, some people refuse to understand that... sigh

And if I may ask, would you turn this into a PECL extension if it gets rejected and continue to develop the inner class? Will you continue to work on it at times so that it will be compatible with PHP 5.6, 5.7 and onwards?

@robbytaylor

How strange. I wouldn't have imagined that there could be a problem with a feature which encourages people to use objects more.

@krakjoe

@HallofFamer not possible to do language changes in an extension, you must be able to patch parser ... so no ... pretty much end of the road ... maybe it can be brought up again later on but there's not, realistically, much point ...

@adrienbrault

Why do people vote against this thing but vote for https://wiki.php.net/rfc/variadics ... wtf

@HallofFamer

I see, thats too bad. I do hope you consider at least make it compatible with each major PHP releases beyond the next version(PHP 5.6, 5.7 etc). There are other features like operator overloading, which is available on PECL and got updated when PHP 5.3, 5.4 and then 5.5 was released.

@adrienbrault:
I guess some people are just stubborn or bored. I think the funnest thing you ever hear on PHP internals is a feature must 'fit the nature of PHP'. I was like, what is the nature of PHP? Does it have a nature? lol.

But seriously, variadics is a useful feature, like 'finally'. But just like 'finally', its a minor feature and PHP 5.6 is going to be a joke if variadics is labeled as its greatest new feature implemented. In PHP 5.3 we get namespace, and then PHP 5.4 with trait and PHP 5.5 with generator. Anonymous Class/Inner Class can make PHP 5.6 on par with PHP 5.3 with namespace, unfortunately we get so many stupid people saying no. sigh

@krakjoe

@HallofFamer operator is different, it doesn't require a syntax change (thought I believe the first versions did come with a patch for some features), you can implement operators from an extension just fine ... by the way it was updated because someone happened to come along and ask why it didn't work or compile anymore, despite the fact that operator overloading is clearly useful and can be implemented as part of Zend or self contained in an extension using the APIs that Zend exposes; there is no prolonged effort from within the community to maintain the functionality of that, or nearly any other extension on pecl ...

This cannot be reasonably maintained outside of php, nor can inner classes, they both require patches to the parser, there is no reasonable, compatible way to self contain this functionality in an extension, if there were I probably would have saved myself the headache and just done that in the first place ...

@adrienbrault you should not try to work out why people act the way they do, there's no evidence at all that our behaviour is logical, or driven by logical concerns, and the only means you have to work it out with is logic, so it cannot be done ...

@krakjoe krakjoe closed this
@prolic

I could cry that this is not accepted :-/

@HallofFamer

It cant be helped, PHP internals choose to be stupid and stubborn, now they are rejecting extended keyword support too. sigh Perhaps all they wish for the next version of PHP is a purely procedural amateurish script language like PHP 3.0 lol.

@craigjbass

I can't believe this didn't pass the vote. I thought we were supposed to be moving towards a better OO model inside PHP?

@craigjbass

Instead focussing on minor concerns like UTF support. sigh

@HallofFamer

Well UTF support is important, its nice they work on it. This should not rule out anonymous/inner class though in any way. According to what has been revealed so far, the PHP internals do not believe it is useful in this language. I can definitely find some usage myself, but apparently it aint enough to convince them.

Also some people speculate that that fact that anonymous class support is too java-like may be one important reason why it was rejected, since everything must fit in PHP's nature. I dont understand though, does PHP have a nature lol? And does it need one?

Anyway I just hope this project is maintained at least like some PECL packages that get updated every now and then. When PHP 5.6 officially comes out, I am not sure there will even be a feasible way to install this without breaking the other core functionality. sigh

@craigjbass

PHP's nature is to be blind to OO design patterns. Personally I think we should be moving towards a Non-BC break PHP with nicer OO and the ability to choose to use types if you need them.

Multipurpose, and helpful. Instead of backward and confused.

Still. There has been some progress.

@HallofFamer

umm I wonder though, if I have a custom build PHP and I want to add anonymous class support, do I only have to look for the file changes in this pull request and make these changes myself in the source C code? Or are there something else that needs to be modified, especially to make this work with later PHP versions?

@reeze

Yes, but this patch are againt a certain PHP version, it might have difference.
But this is highly not encouraged, it will be hard to maintain, you have to merge upstream to your fork

@HallofFamer

@reeze
Thanks, I understand the risk of course. I plan to port it to PHP 7 after it is released, and maintain a custom build PHP myself. sounds tricky, but I hope it will work, or at least @krakjoe will offer some advice if I run into issues incorporating this into PHP 7.

@reeze

if you need help, you can contact me too :)

@HallofFamer

@reeze :

Thank you so much, I very appreciate this.

@HallofFamer

@reeze:
umm it seems that it does not work with PHP 5.6.5 if I just manually edit the C source code. I am getting these error messages after including anonymous class in php compile test:

FAILED TEST SUMMARY

declare bare anonymous class [tests/anon/001.phpt]
declare anonymous class extending another [tests/anon/002.phpt]
reusing anonymous classes [tests/anon/003.phpt]
testing anonymous inheritance [tests/anon/004.phpt]
testing reusing anons that implement an interface [tests/anon/005.phpt]
testing anons inside namespaces [tests/anon/006.phpt]
testing anons in functions outside of classes in namespaces [tests/anon/007.phpt]

And of course, using anonymous class in an actual php file on apache server will fail, throwing T_CLASS kind of error. Did I do anything wrong? The changes seems to be perfectly compatible with PHP 5.5 or 5.6, but it does not seem to work for me... Do you have any ideas how I can get this to work? Thanks.

@reeze
@HallofFamer

@reeze :

No problem, thank you. Maybe I can get it to work before you come back, I hope for the best. And enjoy your vacation!

@reeze

@HalloFamer anything I could do? :)

@HallofFamer

@reeze: Sure, I'd like to see what was wrong with the modifications I made to a customized PHP 5.6.5 with anonymous class. It seems that all tests for anonymous class have failed, and I suspect the issue is more complicated. At this moment, would you mind trying to compile it on your machine and run the tests to see if it fails the tests too? And if so, I will think about what we can do next. I've uploaded the PHP 5.6.5 with anonymous class support below, thanks.
http://www.mediafire.com/download/a2xtf6pjs3wnp3f/php-5.6.5.tar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 19, 2013
  1. @krakjoe
  2. @krakjoe
  3. @krakjoe
  4. @krakjoe

    just send publics

    krakjoe authored
  5. @krakjoe

    TSRM

    krakjoe authored
  6. @krakjoe

    use unmangled names

    krakjoe authored
Commits on Apr 22, 2013
  1. @krakjoe
  2. @krakjoe
  3. @krakjoe
  4. @krakjoe
  5. @krakjoe

    reverse this

    krakjoe authored
Commits on Aug 5, 2013
  1. @krakjoe
Commits on Aug 12, 2013
  1. @krakjoe

    fix strangeness for pthreads

    krakjoe authored
  2. @krakjoe
  3. @krakjoe

    correction

    krakjoe authored
Commits on Aug 13, 2013
  1. @krakjoe
Commits on Sep 21, 2013
  1. @krakjoe

    anon class objects

    krakjoe authored
Commits on Sep 22, 2013
  1. @krakjoe
  2. @krakjoe
  3. @krakjoe

    cleanup patch:

    krakjoe authored
    	make sure use of class_token is correct
    	revert changes to znode typedef, reuse op.ptr
Commits on Sep 25, 2013
  1. @krakjoe
  2. @krakjoe

    don't need these

    krakjoe authored
  3. @krakjoe

    tidy patch

    krakjoe authored
  4. @krakjoe

    fix for zts

    krakjoe authored
  5. @krakjoe
  6. @krakjoe

    some tests

    krakjoe authored
  7. @krakjoe

    allow extends $this

    krakjoe authored
  8. @krakjoe
  9. @krakjoe

    make sensible test

    krakjoe authored
  10. @krakjoe
Commits on Sep 26, 2013
  1. @krakjoe

    correction

    krakjoe authored
  2. @krakjoe

    remove pointless stuff

    krakjoe authored
  3. @krakjoe

    tidy

    krakjoe authored
  4. @krakjoe
  5. @krakjoe
  6. @krakjoe
Commits on Sep 27, 2013
  1. @krakjoe

    remove

    krakjoe authored
  2. @krakjoe

    super:: support

    krakjoe authored
  3. @krakjoe

    more general static access

    krakjoe authored
  4. @krakjoe

    remove super::

    krakjoe authored
  5. @krakjoe

    cleanup whitespace etc

    krakjoe authored
  6. @krakjoe

    cleanup ...

    krakjoe authored
  7. @krakjoe

    cleanup ...

    krakjoe authored
  8. @krakjoe

    cleanup ...

    krakjoe authored
  9. @krakjoe

    cleanup ...

    krakjoe authored
  10. @krakjoe

    cleanup ...

    krakjoe authored
Commits on Sep 28, 2013
  1. @krakjoe
  2. @krakjoe

    fix namespacing issues

    krakjoe authored
  3. @krakjoe
  4. @krakjoe
  5. @krakjoe
  6. @krakjoe

    more bc

    krakjoe authored
  7. @krakjoe

    more bc, passing Zend/tests

    krakjoe authored
Commits on Sep 29, 2013
  1. @krakjoe
  2. @krakjoe

    another test

    krakjoe authored
  3. @krakjoe
  4. @krakjoe

    bring tests inline

    krakjoe authored
  5. @krakjoe
  6. @krakjoe
  7. @krakjoe

    ...

    krakjoe authored
  8. @krakjoe

    more precise

    krakjoe authored
  9. @krakjoe

    ...

    krakjoe authored
  10. @krakjoe

    ...

    krakjoe authored
  11. @krakjoe

    ...

    krakjoe authored
  12. @krakjoe

    ...

    krakjoe authored
  13. @krakjoe

    ...

    krakjoe authored
  14. @krakjoe

    ...

    krakjoe authored
Commits on Sep 30, 2013
  1. @krakjoe
This page is out of date. Refresh to see the latest.
View
10 Zend/tests/anon/001.phpt
@@ -0,0 +1,10 @@
+--TEST--
+declare bare anonymous class
+--FILE--
+<?php
+var_dump(new class{});
+?>
+--EXPECTF--
+object(%s$$1)#%d (0) {
+}
+
View
10 Zend/tests/anon/002.phpt
@@ -0,0 +1,10 @@
+--TEST--
+declare anonymous class extending another
+--FILE--
+<?php
+var_dump(new class extends stdClass{});
+?>
+--EXPECTF--
+object(%s$$1)#%d (0) {
+}
+
View
54 Zend/tests/anon/003.phpt
@@ -0,0 +1,54 @@
+--TEST--
+reusing anonymous classes
+--FILE--
+<?php
+while (@$i++<10) {
+ var_dump(new class{
+ public function __construct($i) {
+ $this->i = $i;
+ }
+ }($i));
+}
+?>
+--EXPECTF--
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(1)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(2)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(3)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(4)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(5)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(6)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(7)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(8)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(9)
+}
+object(%s$$1)#1 (1) {
+ ["i"]=>
+ int(10)
+}
+
View
34 Zend/tests/anon/004.phpt
@@ -0,0 +1,34 @@
+--TEST--
+testing anonymous inheritance
+--FILE--
+<?php
+class Outer {
+ protected $data;
+
+ public function __construct($data) {
+ $this->data = $data;
+ }
+
+ public function getArrayAccess() {
+ /* create a proxy object implementing array access */
+ return new class extends Outer implements ArrayAccess {
+ public function offsetGet($offset) { return $this->data[$offset]; }
+ public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); }
+ public function offsetUnset($offset) { unset($this->data[$offset]); }
+ public function offsetExists($offset) { return isset($this->data[$offset]); }
+ }($this->data);
+ }
+}
+
+$outer = new Outer(array(
+ rand(1, 100)
+));
+
+$proxy = $outer->getArrayAccess();
+
+var_dump($proxy[0]);
+?>
+--EXPECTF--
+int(%d)
+
+
View
39 Zend/tests/anon/005.phpt
@@ -0,0 +1,39 @@
+--TEST--
+testing reusing anons that implement an interface
+--FILE--
+<?php
+class Outer {
+ protected $data;
+
+ public function __construct(&$data) {
+ /* array access will be implemented by the time we get to here */
+ $this->data = &$data;
+ }
+
+ public function getArrayAccess() {
+ /* create a child object implementing array access */
+ /* this grants you access to protected methods and members */
+ return new class extends Outer implements ArrayAccess {
+ public function offsetGet($offset) { return $this->data[$offset]; }
+ public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); }
+ public function offsetUnset($offset) { unset($this->data[$offset]); }
+ public function offsetExists($offset) { return isset($this->data[$offset]); }
+ }($this->data);
+ }
+}
+
+$data = array(
+ rand(1, 100),
+ rand(2, 200)
+);
+$outer = new Outer($data);
+$proxy = $outer->getArrayAccess();
+
+unset($proxy[0]);
+var_dump(@$outer->getArrayAccess()[0]);
+?>
+--EXPECT--
+NULL
+
+
+
View
18 Zend/tests/anon/006.phpt
@@ -0,0 +1,18 @@
+--TEST--
+testing anons inside namespaces
+--FILE--
+<?php
+namespace lone {
+ $hello = new class{} ;
+}
+
+namespace {
+ var_dump ($hello);
+}
+?>
+--EXPECTF--
+object(%s$$1)#1 (0) {
+}
+
+
+
View
26 Zend/tests/anon/007.phpt
@@ -0,0 +1,26 @@
+--TEST--
+testing anons in functions outside of classes in namespaces
+--FILE--
+<?php
+namespace lone {
+ function my_factory() {
+ return new class{};
+ }
+
+ class Outer {
+
+ public function __construct() {
+ var_dump(
+ my_factory());
+ }
+ }
+
+ new Outer();
+}
+?>
+--EXPECT--
+object(my_factory$$1)#2 (0) {
+}
+
+
+
View
1  Zend/zend.c
@@ -534,6 +534,7 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals TSRMLS
compiler_globals->static_members_table = NULL;
}
compiler_globals->script_encoding_list = NULL;
+ compiler_globals->anon_class_id = 0L;
}
/* }}} */
View
121 Zend/zend_compile.c
@@ -2175,8 +2175,12 @@ void zend_resolve_class_name(znode *class_name TSRMLS_DC) /* {{{ */
zval **ns;
znode tmp;
int len;
-
+
compound = memchr(Z_STRVAL(class_name->u.constant), '\\', Z_STRLEN(class_name->u.constant));
+
+ if ((class_name->EA & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)
+ return;
+
if (compound) {
/* This is a compound class name that contains namespace prefix */
if (Z_STRVAL(class_name->u.constant)[0] == '\\') {
@@ -2219,7 +2223,8 @@ void zend_resolve_class_name(znode *class_name TSRMLS_DC) /* {{{ */
*class_name = tmp;
}
}
- } else if (CG(current_import) || CG(current_namespace)) {
+ } else if ((CG(current_import) || CG(current_namespace)) &&
+ !((class_name->EA & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) {
/* this is a plain name (without \) */
lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));
@@ -2237,11 +2242,64 @@ void zend_resolve_class_name(znode *class_name TSRMLS_DC) /* {{{ */
zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC);
*class_name = tmp;
}
+
efree(lcname);
}
}
/* }}} */
+void zend_do_create_anon_class(znode *result TSRMLS_DC) { /* {{{ */
+ char *class_name = NULL;
+ int class_name_len = 0;
+ char *prefix_name = NULL;
+
+ /* give each class a unique id */
+ CG(anon_class_id)++;
+
+ /* prefix anonymous class with current scope for reference */
+ if (CG(active_op_array)) {
+ if (CG(active_class_entry) || CG(active_op_array)->function_name) {
+ if (CG(active_class_entry)) {
+ prefix_name = (char*) CG(active_class_entry)->name;
+ } else {
+ prefix_name = (char*) CG(active_op_array)->function_name;
+ }
+
+ /* remove namespace from entry name */
+ if (CG(in_namespace)) {
+ prefix_name += Z_STRLEN_P(
+ CG(current_namespace)) + sizeof("\\")-1;
+ }
+ } else {
+ /* use filename as prefix for class */
+ prefix_name = (char*) CG(active_op_array)->filename;
+ }
+ }
+
+ if (!prefix_name) {
+ /* this shouldn't happen, possibly emit a warning ? */
+ prefix_name = "Class";
+ }
+
+ /* populate znode constant with generated anonymous class name */
+ Z_TYPE(result->u.constant) = IS_STRING;
+ class_name_len = snprintf(
+ NULL, 0, "%s$$%lu", prefix_name, CG(anon_class_id));
+ class_name = (char*) emalloc(class_name_len+1);
+ snprintf(
+ class_name, class_name_len+1,
+ "%s$$%lu", prefix_name, CG(anon_class_id)
+ );
+ class_name[class_name_len] = '\0';
+
+ Z_STRLEN(result->u.constant) = class_name_len;
+ Z_STRVAL(result->u.constant) = (char*) zend_new_interned_string(
+ class_name, class_name_len+1, 1 TSRMLS_CC);
+
+ result->op_type = IS_CONST;
+ result->EA = ZEND_ACC_ANON_CLASS;
+} /* }}} */
+
void zend_do_fetch_class(znode *result, znode *class_name TSRMLS_DC) /* {{{ */
{
long fetch_class_op_number;
@@ -3773,11 +3831,16 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
} else if (ce->interfaces[i] == iface) {
if (i < parent_iface_num) {
ignore = 1;
- } else {
+ } else {
+ if ((ce->ce_flags & (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) == (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) {
+ continue;
+ }
+
zend_error(E_COMPILE_ERROR, "Class %s cannot implement previously implemented interface %s", ce->name, iface->name);
}
}
}
+
if (ignore) {
/* Check for attempt to redeclare interface constants */
zend_hash_apply_with_arguments(&ce->constants_table TSRMLS_CC, (apply_func_args_t) do_interface_constant_check, 1, &iface);
@@ -4542,13 +4605,22 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze
op1 = opline->op1.zv;
op2 = opline->op2.zv;
}
+
if (zend_hash_quick_find(class_table, Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_HASH_P(op1), (void **) &pce)==FAILURE) {
zend_error(E_COMPILE_ERROR, "Internal Zend error - Missing class information for %s", Z_STRVAL_P(op1));
return NULL;
} else {
ce = *pce;
}
+
ce->refcount++;
+
+ /* return anonymous class */
+ if ((ce->ce_flags & (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) == (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) {
+ ce->refcount--;
+ return ce;
+ }
+
if (zend_hash_quick_add(class_table, Z_STRVAL_P(op2), Z_STRLEN_P(op2)+1, Z_HASH_P(op2), &ce, sizeof(zend_class_entry *), NULL)==FAILURE) {
ce->refcount--;
if (!compile_time) {
@@ -4564,6 +4636,11 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const ze
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
zend_verify_abstract_class(ce TSRMLS_CC);
}
+
+ /* set final anonymous class */
+ if ((ce->ce_flags & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS) {
+ ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
+ }
return ce;
}
}
@@ -4605,6 +4682,11 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
zend_error(E_COMPILE_ERROR, "Class %s cannot extend from trait %s", ce->name, parent_ce->name);
}
+ /* return anonymous class */
+ if ((ce->ce_flags & (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) == (ZEND_ACC_ANON_CLASS|ZEND_ACC_FINAL_CLASS)) {
+ return ce;
+ }
+
zend_do_inheritance(ce, parent_ce TSRMLS_CC);
ce->refcount++;
@@ -4613,6 +4695,12 @@ ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array
if (zend_hash_quick_add(class_table, Z_STRVAL_P(op2), Z_STRLEN_P(op2)+1, Z_HASH_P(op2), pce, sizeof(zend_class_entry *), NULL)==FAILURE) {
zend_error(E_COMPILE_ERROR, "Cannot redeclare class %s", ce->name);
}
+
+ /* set final anonymous class */
+ if ((ce->ce_flags & ZEND_ACC_ANON_CLASS) == (ZEND_ACC_ANON_CLASS)) {
+ ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
+ }
+
return ce;
}
/* }}} */
@@ -4962,7 +5050,7 @@ void zend_do_default_before_statement(const znode *case_list, znode *default_tok
}
/* }}} */
-void zend_do_begin_class_declaration(const znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC) /* {{{ */
+void zend_do_begin_class_declaration(znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC) /* {{{ */
{
zend_op *opline;
int doing_inheritance = 0;
@@ -4970,11 +5058,13 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name
char *lcname;
int error = 0;
zval **ns_name, key;
+ zend_uint opline_num = 0L;
+
+ /* save opline_num before changing node */
+ opline_num = class_token->u.op.opline_num;
- if (CG(active_class_entry)) {
- zend_error(E_COMPILE_ERROR, "Class declarations may not be nested");
- return;
- }
+ /* save active class address */
+ class_token->u.op.ptr = CG(active_class_entry) ? CG(active_class_entry) : NULL;
lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));
@@ -4988,8 +5078,13 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name
zend_hash_find(CG(current_import), lcname, Z_STRLEN(class_name->u.constant)+1, (void**)&ns_name) == SUCCESS) {
error = 1;
}
+
+ if (CG(active_class_entry) && !((class_token->EA & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) {
+ zend_error(E_COMPILE_ERROR, "Class declarations may not be nested");
+ return;
+ }
- if (CG(current_namespace)) {
+ if (CG(current_namespace) && !((class_token->EA & ZEND_ACC_ANON_CLASS) == ZEND_ACC_ANON_CLASS)) {
/* Prefix class name with name of current namespace */
znode tmp;
@@ -5019,7 +5114,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name
zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC);
new_class_entry->info.user.filename = zend_get_compiled_filename(TSRMLS_C);
- new_class_entry->info.user.line_start = class_token->u.op.opline_num;
+ new_class_entry->info.user.line_start = opline_num;
new_class_entry->ce_flags |= class_token->EA;
if (parent_class_name && parent_class_name->op_type != IS_UNUSED) {
@@ -5063,7 +5158,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name
CALCULATE_LITERAL_HASH(opline->op2.constant);
zend_hash_quick_update(CG(class_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &new_class_entry, sizeof(zend_class_entry *), NULL);
- CG(active_class_entry) = new_class_entry;
+ CG(active_class_entry) = new_class_entry;
opline->result.var = get_temporary_variable(CG(active_op_array));
opline->result_type = IS_VAR;
@@ -5075,6 +5170,7 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name
CG(doc_comment) = NULL;
CG(doc_comment_len) = 0;
}
+
}
/* }}} */
@@ -5146,7 +5242,8 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent
ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES;
}
- CG(active_class_entry) = NULL;
+ /* restore active class entry*/
+ CG(active_class_entry) = class_token->u.op.ptr;
}
/* }}} */
View
6 Zend/zend_compile.h
@@ -83,7 +83,7 @@ typedef union _znode_op {
zend_op *jmp_addr;
zval *zv;
zend_literal *literal;
- void *ptr; /* Used for passing pointers from the compile to execution phase, currently used for traits */
+ void *ptr; /* Used for passing pointers from the compile to execution phase, currently used for traits and nested anonymous class support */
} znode_op;
typedef struct _znode { /* used only during compilation */
@@ -159,6 +159,7 @@ typedef struct _zend_try_catch_element {
#define ZEND_ACC_FINAL_CLASS 0x40
#define ZEND_ACC_INTERFACE 0x80
#define ZEND_ACC_TRAIT 0x120
+#define ZEND_ACC_ANON_CLASS 0x400
/* op_array flags */
#define ZEND_ACC_INTERACTIVE 0x10
@@ -561,7 +562,8 @@ void zend_do_case_before_statement(const znode *case_list, znode *case_token, co
void zend_do_case_after_statement(znode *result, const znode *case_token TSRMLS_DC);
void zend_do_default_before_statement(const znode *case_list, znode *default_token TSRMLS_DC);
-void zend_do_begin_class_declaration(const znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC);
+void zend_do_create_anon_class(znode *result TSRMLS_DC);
+void zend_do_begin_class_declaration(znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC);
void zend_do_end_class_declaration(const znode *class_token, const znode *parent_token TSRMLS_DC);
void zend_do_declare_property(const znode *var_name, const znode *value, zend_uint access_type TSRMLS_DC);
void zend_do_declare_class_constant(znode *var_name, const znode *value TSRMLS_DC);
View
3  Zend/zend_globals.h
@@ -150,6 +150,9 @@ struct _zend_compiler_globals {
zend_bool multibyte;
zend_bool detect_unicode;
zend_bool encoding_declared;
+
+ /* anonymous classes */
+ zend_ulong anon_class_id;
#ifdef ZTS
zval ***static_members_table;
View
33 Zend/zend_language_parser.y
@@ -737,8 +737,25 @@ instance_call:
chaining_instance_call { zend_do_pop_object(&$$ TSRMLS_CC); zend_do_end_variable_parse(&$2, BP_VAR_R, 0 TSRMLS_CC); }
;
+new_object:
+ T_NEW class_name_reference { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$1, &$2 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$$, &$1, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
+;
+
+anon_class_type:
+ T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_ANON_CLASS; }
+;
+
+anon_class_decl:
+ anon_class_type { zend_do_create_anon_class(&$$ TSRMLS_CC); } extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list '{' class_statement_list '}' { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); zend_do_fetch_class(&$$, &$2 TSRMLS_CC); }
+;
+
+new_anon_object:
+ T_NEW anon_class_decl { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$1, &$2 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$$, &$1, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
+;
+
new_expr:
- T_NEW class_name_reference { zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$1, &$2 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$$, &$1, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
+ new_object
+ | new_anon_object
;
expr_without_variable:
@@ -875,16 +892,16 @@ function_call:
;
class_name:
- T_STATIC { $$.op_type = IS_CONST; ZVAL_STRINGL(&$$.u.constant, "static", sizeof("static")-1, 1);}
- | namespace_name { $$ = $1; }
- | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); }
- | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); $$ = $2; }
+ T_STATIC { $$.EA = 0; $$.op_type = IS_CONST; ZVAL_STRINGL(&$$.u.constant, "static", sizeof("static")-1, 1);}
+ | namespace_name { $$ = $1; $$.EA = 0; }
+ | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.EA = 0; $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); }
+ | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); $$ = $2; $$.EA = 0; }
;
fully_qualified_class_name:
- namespace_name { $$ = $1; }
- | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); }
- | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); $$ = $2; }
+ namespace_name { $$ = $1; $$.EA = 0; }
+ | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.EA = 0; $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); }
+ | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); $$ = $2; $$.EA = 0; }
;
View
8 Zend/zend_opcode.c
@@ -296,7 +296,13 @@ ZEND_API void destroy_zend_class(zend_class_entry **pce)
efree(ce->default_static_members_table);
}
zend_hash_destroy(&ce->properties_info);
- str_efree(ce->name);
+#ifdef ZTS
+ if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
+ str_efree(ce->name);
+ }
+#else
+ str_efree(ce->name);
+#endif
zend_hash_destroy(&ce->function_table);
zend_hash_destroy(&ce->constants_table);
if (ce->num_interfaces > 0 && ce->interfaces) {
Something went wrong with that request. Please try again.