Reflection Docs: Basic Usage
The concept of reflection of course is not new, and the ability to have a class, or in the case of ColdFusion a Component, reveal information about itself can be a very useful programmatic utility. That is expressely what the cfcommons reflection API is intended to do.
If you haven't already, review the class documentation located here. There are two primary interfaces that expose the reflection API, Class and Method.
Asking a Component Or Interface to Describe Itself
Exposing information for a component or an interface is easy and starts with an instance of a ComponentClass. This is the core reflection API component. Instantiating a ComponentClass instance can happen in one of three ways; The first of which is providing the actual dot-notation class name with the class constructor argument;
Or with an actual object instance with the object constructor argument. This will only work with actual concrete component instances - not interfaces for obvious reasons;
Lastly you can provide a system file path with the resolvedPath constructor argument;
The information encapsulated in the resulting ComponentClass instance, held in the "component" variable is the same regardless of which method you use.
It's important to note that the resulting object in the "component" variable is NOT an instance of MyComponent, but an instance of ComponentClass exposing all of the methods prescribed in the org.cfcommons.reflection.Class interface. You now have access to all of the Class methods with provide a rich API for querying component information.
Sample Component
We'll use the following sample .cfc as the MyComponent.cfc template to demonstrate what to expect from the API.
In our first example, we'll pull out all component-level attributes and their assocaited values;
Will produce;
Fairly straight-forward with probably the exception of the "extends" information. All components inherently extend WEB-INF.cftags.component, even if you don't explicitly declare it in your component file. While the getAttributes() method is helpful for returning a representation of all component-level attribute information, you can also ask more fine-grained questions about the component with the associated hasAttribute() and getAttribute() methods which operate on a per-attribute basis like this;
Which would return it's respective value;
MyComponent
You may be asking yourself at this point why the myInlineAnnotation key and associated value were not returned in the resulting structure - after all, it was declared in the same way as the other attributes.
This is where the reflection API exibits a degree of intelligence, the myInlineAnnotation key and associated value is not returned as an attribute because "myInlineAnnotation" is not a formal, documented attribute of the <cfcomponent /> tag (even though we're using the script equivalent). The component attribute management portion of the API will only return information on those documented tag attributes. Never fear though, you can easily and intuitively grab the information associated with "myInlineAnnotation" as well with the Annotations portion of the API. We simply have to call this;
Which returns;
As you can see, not only was the "myInlineAnnotation" key/value returned, but the other comment-based annotation (declared with "@" in the comments) was returned correctly as well.
Functions are Just as Enlightening
The Class components equally powerful and introspective counterpart is the ComponentMethod- the implementation of the Method interface. You'll notice when you explore the documentation, that there are methods on Class such as getMethods(), getAnnotatedMethods() and getMethodsAnnotatedAs(). These methods return arrays consisting of Method instances, which provide very similar functions that return encapsulated information regarding the composition of functions declared on your component.
For example, per our MyComponent component, we purposefully annotated the echo() method with "@annotatedMethod anotherValue". We can easily return an instance of ComponentMethodrepresenting echo with a call to getMethodsAnnotatedAs() from the Class instance.
In our case, we'll get an array back with only one Method element - representing echo(), but if there would have been other methods on the class expressing the same annotation, then they would have been returned in the same call. Taking it one step further, we can now invoke the getAnnotation() function on the Method instance to return the value of the annotation; name
Which will return;
anotherValue