Glutexo
Glutexo

Reputation: 566

Why does using nested traits change PHP behavior?

Using PHP 7.2, I have a class MyClass that uses a trait MyFirstTrait. It is defined like this:

class MyClass
{
    use MyFirstTrait;
}

This MyFirstTrait uses another trait MySecondTrait. It is defined like this:

trait MyFirstTrait
{
    use MySecondTrait;
}

MySecondTrait does not use any other traits. It is defined like this:

trait MySecondTrait
{
}

If I define MyClass first and then the traits (it does not matter in which order the traits are defined), an error is raised.

The file would be like:

// Not working
class MyClass { use MyFirstTrait; }

// These two lines can be swapped, the result is the same.
trait MyFirstTrait { use MySecondTrait; }
trait MySecondTrait { } 

And the error raised is this:

Fatal error: Trait 'MyFirstTrait' not found in …

However, if I remove the nesting and remove the use clause from MyFirstTrait, the script runs without errors. The same happens if I define the traits first and then the class.

In the first case, the file would look like this:

// Working
class MyClass { use MyFirstTrait; }
trait MyFirstTrait { }

In the second case like this:

// Working

// These two lines can be swapped, the result is the same.
trait MyFirstTrait { use MySecondTrait; }
trait MySecondTrait { } 

class MyClass { use MyFirstTrait; }

Why does the behavior change when the traits are nested?

It doesn’t make much sense to me, because in both cases, the used traits are defined later than they are referenced. When the nesting is removed and only single trait is used, there is no problem with the trait being defined after defining the class.

Upvotes: 2

Views: 989

Answers (1)

deceze
deceze

Reputation: 522042

Logically, everything you refer to should be defined before you refer to it. There are some exceptions to this though. Taking a step back, a PHP file is interpreted in two steps:

  1. Tokenisation and parsing.
  2. Runtime execution of the parsed code.

Generally speaking, classes will be defined at runtime, meaning when the code is executed in the order it is written. Here you must have defined the trait you're going to use in another trait first. However, some "simple" traits and classes can be generated and defined by the parser in the parsing step, so they will be available before runtime. That is purely a performance optimisation.

What exactly PHP regards as a "simple class/trait" is not something I would necessarily try to remember or count on, as the capabilities of the parser may expand from one version to another (for instance, at some point simple arithmetic expressions became supported for statements like static $foo = 4 + 2;, which was previously a parse error).

Upvotes: 4

Related Questions