#1
  1. No Profile Picture
    Dazed&Confused
    Devshed Novice (500 - 999 posts)

    Join Date
    Jun 2002
    Location
    Tempe, AZ
    Posts
    506
    Rep Power
    128

    Traits + __NAMESPACE__ (and others)


    (not actually in need of help, but thought this perhaps worthy of discussion...)

    So I ran into a snag while trying to put a common method into a Trait to be usable across classes, and I'm curious if this would be seen as a PHP bug or a working-as-intended.

    Among other ways, Traits have been described as a way to reuse code across multiple classes as though that code is explicitly written in each one.

    However, I've found that if you try to use keywords like __NAMESPACE__, __FILE__, or __DIR__, they all operate based on the location of the Trait and not the class that's using them.

    test.php
    PHP Code:
    <?php
        
    namespace humanity;

        include_once(
    "trait.php");
        class 
    human {
            use \
    Smells;
        }

        (new 
    human())->smell();
    trait.php
    PHP Code:
    <?php
        trait Smells
        
    {
            public function 
    smell(){
                print 
    __FILE__.' ... smells funny.';
            }
        }
    Output: C:\wamp\www\trait.php ... smells funny.

    As you can see, __FILE__ is reporting trait.php where I would have thought it'd behave based on the class using it, in which case it would have been test.php.

    It's worth noting that similar occurs when subclassing, but Traits being "horizontal", I somehow expected different.

    Anyway, opinions?

    Clearly it's not quite the equivalent of runtime copy&pasting logic.
  2. #2
  3. Did you steal it?
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    13,997
    Rep Power
    9397
    Most magic constants like __FILE__ and __DIR__ have always been for the file containing them and not from where they're being executed (like what could happen with anonymous functions). It may be explained like that's how it happens but you're right: PHP does not actually "copy and paste" trait code into the importing classes.
    In fact pretty much immediately after parsing a file those constants have already been replaced with their appropriate values.

    So is there a specific need you're trying to meet?
  4. #3
  5. No Profile Picture
    Dazed&Confused
    Devshed Novice (500 - 999 posts)

    Join Date
    Jun 2002
    Location
    Tempe, AZ
    Posts
    506
    Rep Power
    128
    Originally Posted by requinix
    Most magic constants like __FILE__ and __DIR__ have always been for the file containing them and not from where they're being executed (like what could happen with anonymous functions). It may be explained like that's how it happens but you're right: PHP does not actually "copy and paste" trait code into the importing classes.
    In fact pretty much immediately after parsing a file those constants have already been replaced with their appropriate values.

    So is there a specific need you're trying to meet?
    Oh, in my case I was using __DIR__ and __NAMESPACE__ for some dynamic class loading and instantiation. I was able to work around it by creating respective methods on the Trait to pull the info leveraging ReflectionClass().

    I suppose it would be bad form if I didn't show the workaround for those who might pass by...

    In the Trait, and called from within the Trait to get the directory path and namespace of the class using it:
    PHP Code:
    private function getDirectory(){
        return 
    dirname((new ReflectionClass(get_class($this)))->getFileName());
    }
    private function 
    getNamespace(){
        return (new 
    ReflectionClass(get_class($this)))->getNamespaceName();

    So not an problem per se. I was just surprised to see things break when I moved the logic into a Trait and didn't find a single text about this from Googling.
  6. #4
  7. --
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,957
    Rep Power
    1046
    Hi,

    Originally Posted by dmittner
    Among other ways, Traits have been described as a way to reuse code across multiple classes as though that code is explicitly written in each one.
    In my opinion, that's a very naive and misleading description. It's similar to a typical misunderstanding many people have when they first encounter inheritance: They think of it as some kind of preprocessing, which literally inserts all code of the parent class into the subclass. But that's not how it works, and if you think this way, you'll have a hard time understanding features like method overriding.

    It's the same with traits: A trait doesn't duplicate code or something like that. It holds a bunch of methods which other classes can use. If you fetch "static data" like __FILE__ or __LINE__ in those methods, you'll get static data. If you fetch dynamic data like $this, you'll get dynamic data -- just like everywhere else in PHP.
    The 6 worst sins of security ē How to (properly) access a MySQL database with PHP

    Why canít I use certain words like "drop" as part of my Security Question answers?
    There are certain words used by hackers to try to gain access to systems and manipulate data; therefore, the following words are restricted: "select," "delete," "update," "insert," "drop" and "null".
  8. #5
  9. Mad Scientist
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Oct 2007
    Location
    North Yorkshire, UK
    Posts
    3,661
    Rep Power
    4123
    In a pre release of PHP 5.4 the __CLASS__ returned the name of the trait, not the name of the class.

    I reported this as a bug and it was accepted - __CLASS__ in a trait gives the calling class and a new magic const __TRAIT__ was added.

    The dirty work around is to either set a property to the magic constant before using the trait or pass the values into the methods,eg

    PHP Code:
    //untested
    trait TestTrait {
         public function 
    __construct() {
              
    $this->path $this->namespace.$this->dir;
         }
    }


    class 
    NeedsATrait {
         use 
    TestTrait {
              
    TestTrait::__construct as TestTraitConstruct;
         }

         public function 
    __construct() {
              
    $this->namespace __NAMESPACE__;
              
    $this->dir __DIR__;
              
    $this->TestTraitConstruct();
         }


    I said I didn't like ORM!!! <?php $this->model->update($this->request->resources[0])->set($this->request->getData())->getData('count'); ?>

    PDO vs mysql_* functions: Find a Migration Guide Here

    [ Xeneco - T'interweb Development ] - [ Are you a Help Vampire? ] - [ Read The manual! ] - [ W3 methods - GET, POST, etc ] - [ Web Design Hell ]

IMN logo majestic logo threadwatch logo seochat tools logo