Which of the following is true about `self` and `static` in PHP 8.4? (Select all that apply)
PHP

Which of the following is true about `self` and `static` in PHP 8.4? (Select all that apply)

Symfony Certification Exam

Expert Author

January 29, 20266 min read
PHPSymfonyselfstaticPHP 8.4Symfony Certification

Which of the following is true about self and static in PHP 8.4? (Select all that apply)

As a Symfony developer preparing for the certification exam, mastering the nuances of self and static in PHP 8.4 is essential. Understanding how these keywords operate within the context of class inheritance, late static binding, and polymorphism can significantly impact your coding practices and application design. This article delves into the distinctions between self and static, highlighting their practical implications in Symfony applications.

The Basics of self and static

In PHP, self and static are keywords that refer to the class context in which they are used, but they behave differently based on the inheritance structure of classes.

The self Keyword

The self keyword always refers to the class in which it is defined. It is static and cannot be overridden in subclasses. When you use self, you are ensuring that the context remains fixed to the class itself, regardless of how or where the method is called.

Example of self

class ParentClass
{
    protected static string $name = 'ParentClass';

    public static function getClassName(): string
    {
        return self::$name;
    }
}

class ChildClass extends ParentClass
{
    protected static string $name = 'ChildClass';
}

echo ParentClass::getClassName(); // outputs: ParentClass
echo ChildClass::getClassName(); // outputs: ParentClass

The static Keyword

On the other hand, the static keyword refers to the class that is currently being instantiated. This is particularly useful in inheritance scenarios where you want to ensure that the method behaves according to the subclass context, enabling polymorphism.

Example of static

class ParentClass
{
    protected static string $name = 'ParentClass';

    public static function getClassName(): string
    {
        return static::$name;
    }
}

class ChildClass extends ParentClass
{
    protected static string $name = 'ChildClass';
}

echo ParentClass::getClassName(); // outputs: ParentClass
echo ChildClass::getClassName(); // outputs: ChildClass

Key Differences

To summarize the key differences between self and static:

  • Self: Always refers to the class where it is defined.
  • Static: Refers to the class that is being instantiated, allowing for late static binding.

Practical Implications in Symfony

Understanding the differences between self and static is crucial for Symfony developers, especially when dealing with service definitions, event dispatching, and dependency injection. Let’s explore some practical scenarios where these keywords come into play.

Service Definitions and Inheritance

When defining services in Symfony, you may create a base service class and extend it for specific functionalities. Using static allows child classes to inherit and modify behaviors without explicitly changing the parent class.

Example: Service Inheritance

namespace App\Service;

abstract class BaseService
{
    protected static string $serviceName;

    public static function getServiceName(): string
    {
        return static::$serviceName;
    }
}

class UserService extends BaseService
{
    protected static string $serviceName = 'User Service';
}

class ProductService extends BaseService
{
    protected static string $serviceName = 'Product Service';
}

echo UserService::getServiceName(); // outputs: User Service
echo ProductService::getServiceName(); // outputs: Product Service

In this example, UserService and ProductService both extend BaseService but can define their own service name. This pattern is vital in Symfony applications where services are extended and specialized.

Event Dispatching

In Symfony, events play a crucial role in decoupling components. When creating event subscribers, understanding self and static can help maintain proper event handling.

Example: Event Subscriber

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

class ExampleSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            'kernel.response' => 'onKernelResponse',
        ];
    }

    public function onKernelResponse(ResponseEvent $event)
    {
        // Manipulate the response here
    }
}

In this case, getSubscribedEvents uses self because the method is fixed to the class definition. If it were to use static, it could lead to unexpected behavior if overridden in a child class.

Dependency Injection

When defining dependency injections in Symfony, it's important to understand how these keywords can affect service behavior, especially when using factory methods.

Example: Factory Methods

namespace App\Factory;

use App\Service\UserService;

class ServiceFactory
{
    public static function createUserService(): UserService
    {
        return new UserService();
    }
}

$userService = ServiceFactory::createUserService();

Here, the createUserService method uses static to ensure that it can be overridden if necessary, allowing for flexible service creation.

Late Static Binding

PHP 8.4 continues to support late static binding, which enhances the behavior of the static keyword. Late static binding allows you to call a method from the parent class and still access the properties of the child class.

Example of Late Static Binding

class Animal
{
    protected static string $type = 'Animal';

    public static function getType(): string
    {
        return static::$type;
    }
}

class Dog extends Animal
{
    protected static string $type = 'Dog';
}

echo Animal::getType(); // outputs: Animal
echo Dog::getType();    // outputs: Dog

In this case, the getType method in Animal uses static to ensure that it returns the correct type for Dog, demonstrating how late static binding works.

Practical Use in Symfony

In Symfony applications, late static binding can simplify the creation of repositories and services, where you want to ensure that the correct class context is used.

Example: Repository Pattern

namespace App\Repository;

use Doctrine\ORM\EntityRepository;

class BaseRepository extends EntityRepository
{
    public static function create(): static
    {
        return new static();
    }
}

class UserRepository extends BaseRepository
{
    // Custom methods for UserRepository
}

$userRepo = UserRepository::create(); // Correctly creates an instance of UserRepository

This pattern is particularly useful when working with Doctrine, as it ensures that the correct repository type is instantiated.

Conclusion

Understanding the nuances of self and static in PHP 8.4 is essential for Symfony developers. These keywords impact class behavior, service definitions, event dispatching, and dependency injection. By leveraging the differences between self and static, you can create more flexible, maintainable, and robust Symfony applications.

As you prepare for the Symfony certification exam, practice using these keywords in your projects. Explore how they affect inheritance, late static binding, and service configurations. Mastering these concepts will not only help you pass the exam but also enhance your development skills and deepen your understanding of Symfony’s architecture.

Embrace the power of self and static, and let them guide you towards writing cleaner and more efficient code in your Symfony projects.