Object is the base class for extensible
classes in the Recess. Object introduces a standard
for building a class descriptor through reflection and the realization of
Recess Annotations. Object also introduces the
ability to attach methods to a class at run-time and create wrappable
methods.
Object is the superclass for the major
components of Recess: Models,
Controllers, and Views. The
two most common types of classes created when using the Recess Framework
are Models and Controllers. There is nothing prohibiting developers from
extending Object in their own custom classes. In
fact, if you are a developer feeling particularly ambitious and needing to
roll a mini-framework with a different composition than Recess, starting
with Object and other primitives like
Library defined in Recess Core may make your job
much more pleasant!
A primary purpose of any framework is to remove boilerplate code
whereever possible. Object-oriented (OO) languages provide different means
for removing code-duplication. The most commonly used techniques in
well-known OO languages of Java and C++ are inheritance and composition.
PHP has support for these common techniques and Recess makes use of them
wherever possible. Other languages that support object-oriented
programming like SmallTalk, Scala, and Ruby go beyond composition and
inheritance and allow modules or traits to attach new units of
functionality to class definitions (like properties and methods)
dynamically. Recess Core provides a systematic way for simulating similar
language features and allows for methods to be
attached to classes that extend
Object at run-time. This technique in combination
with annotations is how the Recess ORM provides
methods to access relationships, for example. Further discussion of
attached methods see the section called “Attached Methods”.
Another form of extensibility in Recess that draws inspiration from
a more sophisticated Aspect-Oriented Programming and the 'similar in
spirit' Decorator OO pattern are wrappable
methods. Classes extending Object
can declare a method to be !Wrappable. Once wrappable,
other classes implementing the IWrapper
interface can dynamically wrap functionality around calls to the method.
Key methods in the framework, like Controller's
serve and Model's
insert, update,
delete, are wrappable. Why is this useful? It
allows user-defined functionality to inject behavior just before or just
after core framework method calls in a simple, standard way. For example,
validations on a Model may take place just before
insert or update, using
wrappable methods the validation system does not need to be closely
coupled to the Model class. Similarly with Controllers, authentication
could happen before serve is called and the
wrapping authentication class can subvert a request by redirecting to a
login page. Further discussion on wrappable methods see the section called “!Wrappable
Methods and the IWrapper Interface”
The final major unit of functionality exposed in
Object is a system for realizing Recess Annotations.
Annotations give programmers the ability to work in a more declarative,
meta-programming style. What does this mean? Rather than telling PHP how
to do something, you tell Recess what you want with an annotation and it
is then up to Recess and the annotation's class to expand your declarative
statement into PHP. Annotations can be written on three PHP constructs:
classes, methods, and properties. Because PHP does not have first-class
support for annotations, Recess Annotations are placed inside of
doccomments, comments that begin with /**, which are a
first-class construct in PHP available through PHP's Reflection API.
Annotations often make use of attached and wrappable methods to inject
units of functionality. For further discussion on annotations see the section called “Annotations”
Underlying attached
methods, wrappable
methods, and annotations
is fundamental data structure, the ClassDescriptor.
The ClassDescriptor is where the information used
to describe Recess' language features like attached methods are stored.
There is one ClassDescriptor per class and, when
Recess is running in production mode, this data-structure is computed once
and cached. Sub-classes of Object can use the class
descriptor as a store for computed data structures by overriding hooks in
the Object class. For example,
Model uses this cache to hold database information
and the meta-data for relationships, and Controller
uses it to store routing information. Annotations expand to 'shape' a
class's ClassDescriptor. The following section
describes the hooks available to sub-classes of
Object for shaping class descriptors.
Initialize Class Descriptor - A class's
descriptor may need to initialize certain properties. For example
Model's descriptor has default properties
initialized for database table based on convention by the name of the
class, primary key, etc.
protected static function
initClassDescriptor($class) - Parameters:
$class is the class' name as a string. Returns a
ClassDescriptor.
Shape Descriptor with Method - Prior to expanding the annotations for a class method this hook is called to give a subclass an opportunity to manipulate its descriptor. For example Controller uses this in able to create default routes for methods which do not have explicit Route annotations.
protected static function
shapeDescriptorWithMethod($class, $method, $descriptor,
$annotations)- Parameters: $class string
name of class whose descriptor is being initialized.
$method is of type
ReflectionMethod.
$descriptor is the
ClassDescriptor.
$annotations is an array of
Annotations found on method. Returns a
ClassDescriptor.
Shape Descriptor with Property - Prior to
expanding the annotations for a class property this hook is called to
give a subclass an opportunity to manipulate its class descriptor. For
example Model uses this to initialize the
datastructure for a property before a !Column annotation applies
metadata.
protected static function
shapeDescriptorWithProperty($class, $property, $descriptor,
$annotations) - Parameters: $class string
name of class whose descriptor is being initialized.
$property is of type
ReflectionProperty.
$descriptor is the
ClassDescriptor.
$annotations is an array of
Annotations found on property. Returns a
ClassDescriptor.
Finalize Class Descriptor - After all methods
and properties of a class have been visited and annotations expanded
this hook provides a sub-class a final opportunity to do post-processing
and sanitization. For example, Model uses this
hook to ensure consistency between Model's
descriptor and the actual database table's columns.
protected static function
finalClassDescriptor($class, $descriptor) - Parameters:
$class is the class' name as a string.
$descriptor is the
ClassDescriptor after all properties and methods
have been visited. Returns a
ClassDescriptor.