php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #55214 use of __CLASS__ within trait returns trait name not class name
Submitted: 2011-07-15 06:30 UTC Modified: 2011-10-17 22:12 UTC
From: chris dot rutledge at gmail dot com Assigned: gron (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: 5.4.0alpha1 OS: Ubuntu
Private report: No CVE-ID: None
 [2011-07-15 06:30 UTC] chris dot rutledge at gmail dot com
Description:
------------
use of __CLASS__ within trait returns trait name not class name. 

Test script:
---------------
trait Singleton { 
        private static $instance; 
        public static function Load() { 
                if(!isset(self::$instance)) { 
                        $c = __CLASS__; 
                        self::$instance = new $c; 
                } 
                return self::$instance; 
        } 
} 
class Test { 
        use Singleton; 
        private function __construct() { }
} 

Test::Load();

Expected result:
----------------
Expected __CLASS__ to return the name of the class that required the trait ('Test'), so that the singleton object could be instantiated

Actual result:
--------------
__CLASS__ returned 'Singleton', this caused a Fatel error:

Fatal error: Cannot instantiate trait Singleton in /home/dshed/public/northie/index.php on line 7

Patches

__CLASS__-in-traits.002.patch (last revision 2011-07-24 18:28 UTC by gron@php.net)
__CLASS__-in-traits.patch (last revision 2011-07-23 17:45 UTC by gron@php.net)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-07-15 06:32 UTC] felipe@php.net
-Package: *General Issues +Package: Documentation problem
 [2011-07-15 06:32 UTC] felipe@php.net
This is the expected behavior. It just need be documented.
 [2011-07-15 06:44 UTC] chris dot rutledge at gmail dot com
It may be expected from the point of view of those developing the 5.4 branch, but 
logically this approach seems flawed.

As one big advantage of traits seems to be as a replacement for copy-and-pasting 
code, maybe consider making __CLASS__ have the value of the class that called the 
trait and a new magic constant __TRAIT__ with the trait name?
 [2011-07-22 04:56 UTC] gron@php.net
Felipe: I tend to disagree, too.

I do not think this is expected behavior.
Will have a look at this, and another bug reported on the QA mailing list hopefully at the weekend.

Best regards
Stefan
 [2011-07-23 08:17 UTC] gron@php.net
-Status: Open +Status: Assigned -Package: Documentation problem +Package: Scripting Engine problem -Assigned To: +Assigned To: gron
 [2011-07-23 14:17 UTC] felipe@php.net
It's simple to add the __TRAIT__ one, just like __CLASS__ does.

But making a more magic __CLASS__ to reflect the class that called is too much magic, hacky. Whereas we are currently doing the __CLASS__ substitution on compile-time.
 [2011-07-23 17:45 UTC] gron@php.net
The following patch has been added/updated:

Patch Name: __CLASS__-in-traits.patch
Revision:   1311443128
URL:        https://bugs.php.net/patch-display.php?bug=55214&patch=__CLASS__-in-traits.patch&revision=1311443128
 [2011-07-23 17:53 UTC] gron@php.net
I attached a patch of how a fix could be done.

I have to admit that it is hacky, but I think this is the expected behavior with respect to the metaphor of compiler assisted copy'n'past.

However, the patch is problematic, since it introduces a new memory leak.
And I do not have a good strategy yet to fix it.

Not sure how another patch could work, but the general idea is that __CLASS__ is not actually defined inside a trait (BTW: we should add __TRAIT__, too, yes).
Thus, it resolves to a IS_NULL value.
And as it happens to be, IS_NULL makes all is data members invalid, and I use that to indicate that it isn't actually a NULL value but that I want to fix it up with the class name when the method is actually flattened/copied into the class.

The problem with the memory leak comes from the fact that copying the method is not actually done deeply but shallow. And, I do not know how to free only my fixed up names/ZVALs :-/.
 [2011-07-24 18:28 UTC] gron@php.net
The following patch has been added/updated:

Patch Name: __CLASS__-in-traits.002.patch
Revision:   1311532096
URL:        https://bugs.php.net/patch-display.php?bug=55214&patch=__CLASS__-in-traits.002.patch&revision=1311532096
 [2011-07-24 18:31 UTC] gron@php.net
Ok, updated the patch and would like to ask for a review.
This is still hacky, but now I use the literals of a function to be able to clean up the zval for the __CLASS__ name. Thus, the memory leak should be fixed.

Think we will still need a __TRAIT__ to mirror __CLASS__ and to get the trait name itself when that is required.

The test case is missing in the patch:

--TEST--
Bug #55214 (Use of __CLASS__ within trait returns trait name not class name)
--FILE--
<?php

trait ATrait {
  public static function get_class_name() {
    return __CLASS__;
  }
  
  public function get_class_name_obj() {
    return __CLASS__;
  }
}


class SomeClass {
   use ATrait;
}

$r = SomeClass::get_class_name();
var_dump($r);

$o = new SomeClass();
$r = $o->get_class_name_obj();
var_dump($r);

?>
--EXPECT--
string(9) "SomeClass"
string(9) "SomeClass"
 [2011-07-31 18:18 UTC] gron@php.net
Automatic comment from SVN on behalf of gron
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=313997
Log: Fixed Bug #55214 	use of __CLASS__ within trait returns trait name not class name [TRAITS] [DOC]
 [2011-07-31 18:23 UTC] gron@php.net
-Status: Assigned +Status: To be documented -Assigned To: gron +Assigned To:
 [2011-07-31 18:23 UTC] gron@php.net
Fixed in SVN per http://svn.php.net/viewvc?view=revision&revision=313997

Needs documentation: __CLASS__ gives now the class name in which the trait is eventually used.
 [2011-08-15 08:29 UTC] gron@php.net
Automatic comment from SVN on behalf of gron
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=314921
Log: Addendum for patch to bug #55214: Class name was freed before method literal referring to it.
# Thanks to Felipe for catching this.
# The fix duplicates the name into the literal to avoid the dependency
 [2011-10-16 18:46 UTC] gron@php.net
-Status: Open +Status: To be documented
 [2011-10-16 18:46 UTC] gron@php.net
This bug has been fixed in SVN.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 [2011-10-17 22:12 UTC] gron@php.net
-Status: To be documented +Status: Closed -Assigned To: +Assigned To: gron
 [2012-04-18 09:49 UTC] laruence@php.net
Automatic comment on behalf of gron
Revision: http://git.php.net/?p=php-src.git;a=commit;h=88f497f27d4432fa0f6b81cbbe61a1cde486aed2
Log: Fixed Bug #55214 	use of __CLASS__ within trait returns trait name not class name [TRAITS] [DOC]
 [2012-07-24 23:40 UTC] rasmus@php.net
Automatic comment on behalf of gron
Revision: http://git.php.net/?p=php-src.git;a=commit;h=88f497f27d4432fa0f6b81cbbe61a1cde486aed2
Log: Fixed Bug #55214 	use of __CLASS__ within trait returns trait name not class name [TRAITS] [DOC]
 [2013-11-17 09:37 UTC] laruence@php.net
Automatic comment on behalf of gron
Revision: http://git.php.net/?p=php-src.git;a=commit;h=88f497f27d4432fa0f6b81cbbe61a1cde486aed2
Log: Fixed Bug #55214 	use of __CLASS__ within trait returns trait name not class name [TRAITS] [DOC]
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 10 02:01:26 2024 UTC