SmartObject
SmartObject enhanced PHP object behavior for many years. Since PHP 8.4, all of its features have become native parts of PHP itself, thus completing its historic mission as a pioneer of modern object-oriented approach in PHP.
Installation:
SmartObject was introduced in 2007 as a revolutionary solution to the shortcomings of PHP's object model at the time. In an era when PHP faced numerous issues with object-oriented design, it brought significant improvements and simplified developers' workflows. It became a legendary component of the Nette framework. SmartObject offered functionality that PHP only acquired many years later – from access control for object properties to sophisticated syntactic sugar. With the release of PHP 8.4, it fulfilled its historical mission, as all its features became a native part of the language. It was ahead of PHP's development by an impressive 17 years.
SmartObject went through an interesting technical evolution. Initially, it was implemented as the Nette\Object
class, from which other classes inherited the needed functionality. A significant change came with PHP 5.4, which introduced
trait support. This enabled transformation into the Nette\SmartObject
trait, bringing greater flexibility –
developers could use the functionality even in classes that already inherited from another class. While the original
Nette\Object
class ceased to exist with PHP 7.2 (which prohibited naming classes with the word ‘Object’), the
Nette\SmartObject
trait lives on.
Let's explore the features that Nette\Object
and later Nette\SmartObject
offered. Each of these
functions represented a significant step forward in PHP object-oriented programming at the time.
Consistent Error States
One of the most pressing issues of early PHP was inconsistent behavior when working with objects. Nette\Object
brought order and predictability to this chaos. Let's look at how PHP originally behaved:
Fatal error would terminate the application without any possibility to react. Silent writing to non-existent members without
warning could lead to serious errors that were difficult to detect. Nette\Object
caught all these cases and threw a
MemberAccessException
, allowing programmers to react to and handle these errors:
Since PHP 7.0, the language no longer causes uncatchable fatal errors, and since PHP 8.2, access to undeclared members is considered an error.
“Did you mean?” Helper
Nette\Object
came with a very convenient feature: intelligent suggestions for typos. When a developer made a
mistake in a method or variable name, it not only reported the error but also offered help by suggesting the correct name. This
iconic message, known as “did you mean?”, saved programmers hours of hunting for typos:
While PHP itself doesn't have any form of “did you mean?”, this feature is now provided by Tracy. It can even auto-fix such errors.
Properties with Controlled Access
A significant innovation that SmartObject brought to PHP was properties with controlled access. This concept, common in languages like C# or Python, allowed developers to elegantly control access to object data and ensure their consistency. Properties are a powerful tool of object-oriented programming. They function like variables but are actually represented by methods (getters and setters). This allows input validation or value generation at the time of reading.
To use properties, you must:
- Add the annotation
@property <type> $xyz
to the class - Create a getter named
getXyz()
orisXyz()
, a setter namedsetXyz()
- Ensure the getter and setter are public or protected. They are optional – thus can exist as read-only or write-only properties
Let's look at a practical example using the Circle class, where we'll use properties to ensure that the radius is always
non-negative. We'll replace public $radius
with a property:
Since PHP 8.4, the same functionality can be achieved using property hooks, which offer a much more elegant and concise syntax:
Extension Methods
Nette\Object
brought another interesting concept to PHP inspired by modern programming languages – extension
methods. This feature, borrowed from C#, allowed developers to elegantly extend existing classes with new methods without
modifying them or inheriting from them. For instance, you could add an addDateTime()
method to a form that adds a
custom DateTimePicker:
Extension methods proved impractical because editors wouldn't suggest their names and would instead report that the method doesn't exist. Therefore, their support was discontinued. Today, it's more common to use composition or inheritance to extend class functionality.
Getting Class Name
SmartObject offered a simple method for getting the class name:
Reflection and Annotation Access
Nette\Object
provided access to reflection and annotations through methods getReflection()
and
getAnnotation()
. This approach significantly simplified working with class meta-information:
Since PHP 8.0, it's possible to access meta-information through attributes, which offer even more possibilities and better type checking:
Method Getters
Nette\Object
offered an elegant way to pass methods as if they were variables:
Since PHP 8.1, you can use the first-class callable syntax, which takes this concept even further:
Events
SmartObject offers simplified syntax for working with events. Events allow objects to inform other parts of the application about changes in their state:
The code $this->onChange($this, $radius)
is equivalent to the following loop:
For clarity, we recommend avoiding the magic $this->onChange()
method. A practical replacement is the Nette\Utils\Arrays::invoke function: