PHP 8.4 Introduces Asymmetric Visibility: Impact on Development

NewsPHP 8.4 Introduces Asymmetric Visibility: Impact on Development

PHP 8.4: A New Era for Developers

The much-anticipated release of PHP 8.4 is scheduled for November 21, 2024, and it comes with an array of exciting features aimed at enhancing the capabilities of PHP developers. Among these new additions is the concept of asymmetric visibility, which, when used in conjunction with property hooks, is set to transform the way developers design data and message objects in PHP. Let’s delve into the details of PHP 8.4 and explore how asymmetric visibility can be implemented effectively.

Understanding PHP Visibility

The concept of visibility in PHP was first introduced in PHP 5, providing developers with a mechanism to control access to class members. Visibility in PHP can be classified into three types:

  1. Public: This visibility level allows access from anywhere, whether within the class instance itself or from any other part of the code where the instance is available.
  2. Protected: Access is restricted to within the class itself and any subclasses that extend from it.
  3. Private: Access is strictly confined to the class where the property or method is defined, preventing access from subclasses.

    Here’s a practical example to illustrate these visibility levels:

    php<br /> enum LogLevel: string<br /> {<br /> case INFO = 'info';<br /> case NOTICE = 'notice';<br /> case WARNING = 'warning';<br /> case ERROR = 'error';<br /> case CRITICAL = 'critical';<br /> }<br /> <br /> abstract class AbstractMessage<br /> {<br /> private const TEMPLATE = '[%s] (%s) %s';<br /> <br /> abstract public function __toString(): string;<br /> <br /> protected function formatMessage(string $message, LogLevel $level): string<br /> {<br /> return sprintf(<br /> self::TEMPLATE, <br /> (new DateTimeImmutable())->format('c'),<br /> $level->value,<br /> $message<br /> );<br /> }<br /> }<br /> <br /> class DefaultMessage extends AbstractMessage<br /> {<br /> public function __construct(<br /> private string $message,<br /> private LogLevel $level,<br /> ) {<br /> }<br /> <br /> public function __toString(): string<br /> {<br /> return $this->formatMessage($this->message, $this->level);<br /> }<br /> }<br /> <br /> class JsonMessage extends DefaultMessage<br /> {<br /> protected function formatMessage(string $message, LogLevel $level): string<br /> {<br /> return json_encode([<br /> 'message' => $message,<br /> 'severity' => $level->value,<br /> 'ts' => (new DateTimeImmutable())->format('c'),<br /> ]);<br /> }<br /> }<br /> <br /> $message = new DefaultMessage('A basic message', LogLevel::INFO);<br /> $jsonMessage = new JsonMessage('A JSON message', LogLevel::NOTICE);<br /> echo $message;<br /> echo $jsonMessage;<br />

    In this example, the constant TEMPLATE is private, meaning it can only be accessed within AbstractMessage. The formatMessage() method is protected, allowing it to be accessed within the class and its subclasses. The properties $message and $level are private, accessible only within DefaultMessage. However, JsonMessage can still use these properties indirectly through methods defined in DefaultMessage.

    The Problem Solved by Asymmetric Visibility

    Asymmetric visibility addresses a significant challenge: ensuring that objects representing messages or data remain valid and immutable after creation. In traditional designs, developers might face issues such as:

    • Ensuring a non-empty message.
    • Guaranteeing that severity and timestamp are set.
    • Preventing modification of properties representing specific states after instantiation.

      PHP 8.1 introduced readonly properties, which provide a way to enforce immutability, but they have limitations, especially when it comes to validation and inheritance. Asymmetric visibility offers a solution by allowing different levels of access for reading and writing properties.

      Introducing Asymmetric Visibility

      The concept of asymmetric visibility, or "aviz," permits properties to have separate visibility levels for read and write operations. This feature is defined using specific syntax:

      php<br /> {read visibility} {set visibility}(set) {propertyType} $name;<br />

      Key points to remember:

    • If no read visibility is specified, it defaults to public.
    • Write visibility is denoted by the term "(set)" and must be less or equally visible as the read visibility.
    • Asymmetric visibility necessitates a type declaration for the property.

      Here’s how you can use asymmetric visibility:

      php<br /> // Publicly accessible, writable internally only<br /> public private(set) string $message;<br /> <br /> // Publicly accessible, writable from instance and extending classes<br /> public protected(set) string $message;<br /> <br /> // Accessible within instance and extensions, writable only within defining instance<br /> protected private(set) string $message;<br />

      Implementing Asymmetric Visibility

      To demonstrate the practical use of asymmetric visibility, consider refactoring the Message class:

      php<br /> final class Message<br /> {<br /> public private(set) string $message {<br /> set (string $value) {<br /> if (preg_match('/^\s*$/', $value)) {<br /> throw new InvalidArgumentException('message must be non-empty');<br /> }<br /> $this->message = $value;<br /> }<br /> }<br /> <br /> public function __construct(<br /> string $message,<br /> public readonly LogLevel $severity,<br /> public readonly DateTimeInterface $timestamp,<br /> ) {<br /> $this->message = $message;<br /> }<br /> }<br />

      By adding private(set) to the $message property, we ensure it cannot be altered from outside the class, while still allowing internal modifications. Making the class final and excluding additional methods achieves full immutability.

      A Developer’s Guide to Asymmetric Visibility

      When implementing asymmetric visibility, developers need to consider several interactions and contexts:

      References

      Access to a reference requires set visibility, meaning the reference’s visibility must match the set visibility. For example:

      php<br /> class Access<br /> {<br /> public protected(set) int $counter = 0;<br /> }<br /> <br /> // Invalid reference access<br /> $counter = &$accessInstance->counter;<br /> <br /> // Valid method incrementing the counter<br /> class Access<br /> {<br /> public protected(set) int $counter = 0;<br /> <br /> public function increment(): void<br /> {<br /> $this->counter++;<br /> }<br /> }<br />

      Arrays

      Arrays pose challenges with asymmetric visibility due to implicit reference access when writing to them. For instance:

      php<br /> class Collection<br /> {<br /> public private(set) array $items = [];<br /> }<br /> <br /> // Invalid array append<br /> $collection = new Collection();<br /> $collection->items[] = new Item('value');<br />

      To manipulate arrays, provide a method within the class:

      php<br /> class Collection<br /> {<br /> public private(set) array $items = [];<br /> <br /> public function insert(Item $item): void<br /> {<br /> $this->items[] = $item;<br /> }<br /> }<br />

      Inheritance and Interfaces

      Asymmetric visibility behaves similarly to standard visibility in inheritance. Properties must maintain the same or higher visibility in derived classes. However, private(set) is treated as final, preventing redeclaration in subclasses.

      PHP readonly Properties

      The readonly flag introduced in PHP 8.1 functions similarly to private(set) but with a write-once rule. With asymmetric visibility, readonly properties can now be seen as protected(set), streamlining the engine’s behavior.

      Property Overloading

      PHP supports property overloading using magic methods like __get(), __set(), etc. Asymmetric visibility allows developers to avoid these methods by providing more direct control over property access.

      Reflection

      To accommodate asymmetric visibility, the ReflectionProperty class includes new methods: isProtectedSet() and isPrivateSet(). These methods help determine the set visibility of properties, ensuring comprehensive access controls.

      Final Thoughts

      The introduction of asymmetric visibility, alongside property hooks, empowers PHP developers to create robust, immutable data objects and messages. Prior to PHP 8.1, developers had to employ complex workarounds for property management, often leading to convoluted code. The readonly flag in PHP 8.1 offered a step forward, but with limitations regarding inheritance.

      With property hooks, developers gain the ability to validate property values and define virtual properties, while asymmetric visibility restricts when and how properties can be modified. This combination offers a powerful, native solution for previously challenging scenarios.

      As PHP 8.4 rolls out, it’s exciting to anticipate how developers will leverage these features to innovate and enhance the language’s capabilities. Whether you’re planning an upgrade or exploring new projects, understanding and utilizing asymmetric visibility will undoubtedly be a valuable asset in your PHP development toolkit.

For more Information, Refer to this article.

Neil S
Neil S
Neil is a highly qualified Technical Writer with an M.Sc(IT) degree and an impressive range of IT and Support certifications including MCSE, CCNA, ACA(Adobe Certified Associates), and PG Dip (IT). With over 10 years of hands-on experience as an IT support engineer across Windows, Mac, iOS, and Linux Server platforms, Neil possesses the expertise to create comprehensive and user-friendly documentation that simplifies complex technical concepts for a wide audience.
Watch & Subscribe Our YouTube Channel
YouTube Subscribe Button

Latest From Hawkdive

You May like these Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.