php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #70106 Inheritance by string, ReflectionClass and anonymous class
Submitted: 2015-07-21 14:34 UTC Modified: 2015-07-21 21:11 UTC
From: soevilcat at mail dot ru Assigned: bwoebi
Status: Closed Package: Reflection related
PHP Version: 7.0.0beta2 OS: any
Private report: No CVE-ID:
 [2015-07-21 14:34 UTC] soevilcat at mail dot ru
Description:
------------
Class inheritance is done in runtime, judging from how you can do:

if (rand(0,1)==1) { class Lifeform extends Animal {}; }
else { class Lifeform extends Plant {}; }

You can even make it inherit class features by string by doing eval().

It would be much more convenient to have an official syntax for that, since using eval() has so many drawbacks - and is impossible with anonymous classes from PHP7 which have no name to reference. Jbviously they have _some_ internal handle...

Actual use case: a framework designed to be able to run multiple copies of the same extension, but of different versions, for test purposes or otherwise (let's say, "Game 1.2" and "Game 1.3"). Files of each are located in different subfolders. The class/namespace structure would be almost identical, so all the classes are handled anonymously and accessed by factories or properties (much like can be done in Ruby or JavaScript). Another extension is desined to operate with "Game" extension - let's say, "SubGame". It was configured by admin to extend "Game 1.3" classes and not "Game 1.2" classes. Another "SubGame" instance could be configured to extend "Game 1.2". But for now, there's no way to inherit an anonymous class... The only solution would be to use tricky composition to emulate inheritance.

Test script:
---------------
// inheritance by string

class Animal {
	public function eat() { echo 'Yummy!'; }
}

class Plant {
	public $size=0;
	public function grow() { echo 'I\'m level '.++$this->size; }
}

if (rand(0,1)==0) $base='Animal'; else $base='Plant';
class Lifeform extends $base { }

// inheritance by ReflectionClass

$reflection=new ReflectionClass('Animal');
class Predator extends $reflection->getClassEntry() {
	public $prey;
}

// inheritance by anonymous class; also requires an object representing the anonymous class, like Closure is for anonymous functions.

function get_base_animal_class() {
	return class extends Animal {
		public $color;
		public function hiss() { echo 'Hiss!'; }
	};
}
$tiger_class=class extends get_base_animal_class() {
	public $color='orange';
}

Expected result:
----------------
No errors; valid syntax for inheriting classes.

Actual result:
--------------
Fatal errors: the suggested syntax in currently impossible.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-07-21 18:33 UTC] bwoebi@php.net
-Status: Open +Status: Assigned -Type: Feature/Change Request +Type: Bug -PHP Version: 7.0.0beta1 +PHP Version: 7.0.0beta2 -Assigned To: +Assigned To: bwoebi
 [2015-07-21 18:33 UTC] bwoebi@php.net
You actually can do a workaround via

class_alias(get_class($the_anon_class_instance), "AnonBase");

and then do

new class extends AnonBase { ... }


See also http://3v4l.org/SKWdW

(Assigning to myself as there's a bug when anything in the file path is uppercase)
 [2015-07-21 18:34 UTC] bwoebi@php.net
Automatic comment on behalf of bobwei9@hotmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2141ab9be5c2b07c24534552753f8227473efa07
Log: Fix bug #70106 (Inheritance by anonymous class)
 [2015-07-21 18:34 UTC] bwoebi@php.net
-Status: Assigned +Status: Closed
 [2015-07-21 20:30 UTC] soevilcat at mail dot ru
Thank you for quick response! A haven't realized that anonymous classes can be aliased (if it's not mentioned in the docs yet, it's worth mentioning).

I see two problems, though... First of all, by returning an object belonging to an anonymous class instead of a Closure-like representation of the class, you need to instantiate the class at least once... It might not be optimal. Consider this example:

function get_base_animal_class() {
 return new class('placeholder_name') {
  public $name;
  public __construct($name) { $this->name=$name; }
 }
}

We have to force a placeholder name on the temporary instance, though from the logical standpoint it's completely unnecessary. I know this is looked down upon in JavaScript where it was used to be a pattern for inheriting.

Secondly, is class_alias() scoped? Or, maybe, an alias can be removed? (Sorry, I can't verify this until tomorrow for PHP7 and I don't see it in docs.) If not, then after I use "class_alias(get_class($proto), 'AnonBase')" in one place, I can't use the same line in another place... I could alias to a different, generated class name each time, but I still have to state "new class() extends SpecificName" unless I can use strings in inheritance. I can still do this with eval()... But it's quite ugly.
 [2015-07-21 21:11 UTC] bwoebi@php.net
Yeah, that is part of anon classes that the constructor is called upon their declaration. No way around that. But really, just use an init() method or something along these lines if you need that.

And yes, that's the only issue, class_alias is persistent. So, yes, to use the name dynamically, you'll have to recur to eval() with a generated unique name.
But I'd honestly rethink the application design if you need such hacks... ;-)
 [2015-07-21 23:19 UTC] soevilcat at mail dot ru
I'll have to rethink it, of course... But they're only hacks because current PHP lacks these patterns; or even only lacks syntax sugar for them, since technically it's absolutely possible. Still, PHP has tendency to implement useful features from other languages, so maybe someday. Thanks! :)
 [2015-07-24 10:36 UTC] soevilcat at mail dot ru
I made a proof-of-concept repo for a kind of framework I mentioned.

https://github.com/Katemare/ClassClosure-proof-of-concept
 [2015-08-04 20:54 UTC] ab@php.net
Automatic comment on behalf of bobwei9@hotmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2141ab9be5c2b07c24534552753f8227473efa07
Log: Fix bug #70106 (Inheritance by anonymous class)
 [2016-07-20 11:37 UTC] davey@php.net
Automatic comment on behalf of bobwei9@hotmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2141ab9be5c2b07c24534552753f8227473efa07
Log: Fix bug #70106 (Inheritance by anonymous class)
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Fri Jul 21 08:01:41 2017 UTC