Table of Contents
Getting Started
Composer has become the de facto standard for installing libraries in the php world. Aura does the same.
Installation
There are 3 types of skeletons
- aura/web-project : only web application, no cli support built in.
- aura/cli-project : only for command line applications.
- aura/framework-project : supports both web and cli
We are going to install aura/framework-project
, so we can show command line examples also.
It will create the {$PROJECT_PATH}
directory and install the dependencies
in vendor folder.
Structure
The directory structure looks something similar to this. The list is not complete for we have removed some of the files and directories.
The web/index.php
is where you need to point your virtual host. Check the
chapter setting up your virtual host
for more information.
Alternatively you can start the built-in PHP server.
If you point your web browser to http://localhost:8000
you can see
the message Hello World!
.
Great! Everything is working fine.
Exploring the Hello World!
Open the file config/Common.php
. Look into the modifyWebRouter()
and
modifyWebDispatcher()
methods.
The modifyWebRouter()
gets the shared router service and add a named route
hello
which points to /
. So any request to http://localhost:8000
is satisfied by route named hello
.
Now we have the route, the router don’t know what to do when a request come. The dispatcher is what helps to dispatch things.
We get the shared dispatcher service, set the same name as in the
controller of route in setObject
, and use a Closure or Callable.
In this example we are using a Closure, which get the di container and use it as a service, get the shared web response and set the content.
Don’t worry too much about dependency injection and dependency injection container. We will be talking more details in the coming chapter.
Configuration
Although configuration is a project-level concern, each Aura kernel and project handles it in the same way.
Setting The Config Mode
Set the configuration mode using $_ENV['AURA_CONFIG_MODE']
,
either via a server variable or the project-level config/_env.php
file.
Each Aura project comes with dev
(local development), test
(shared testing/staging), and prod
(production) modes pre-defined.
Config File Location
Project-level configuration files are located in the project-level
config/
directory. Each configuration file is a class that extends
Aura\Di\Config, and represents a configuration mode. Each
configuration class has two methods:
-
define()
, which allows you to define params, setters, and services in the project Container; and -
modify()
, which allows you to pull objects out of the Container for programmatic modification. (This happens after the Container is locked, so you cannot add new services or change params and setters here.)
The two-stage configuration system loads all the configuration classes
in order by library, kernel, and project, then runs all the define()
methods, locks the container, and finally runs all the modify()
methods.
Mapping Config Modes To Classes
The config modes are mapped to their related config class files via the
project-level composer.json
file in the extra:aura:config
block.
The entry key is the config mode, and the entry value is the class to use for that mode.
Config classes are autoloaded via a PSR-4 entry for that project namespace.
The “common” config class is always loaded regardless of the actual
config mode. For example, if the config mode is dev
, first the
Common class is loaded, and then the Dev class.
Changing Config Settings
First, open the config file for the related config mode. To change
configuration params, setters, and services, edit the define()
method.
To programmatically change a service after all definitions are complete,
edit the modify()
method.
Adding A Config Mode
If you want to add a new configuration mode, say qa
, you need to do three things.
First, create a config class for it in config/
:
Next, edit the project-level composer.json
file to add the new config
mode with its related class:
Finally, run composer update
so that Composer makes the necessary changes
to the autoloader system.
Routing
Configuration of routing and dispatching is done via the project-level config/
class files. If a route needs to be available in every config mode,
edit the project-level config/Common.php class file. If it only needs
to be available in a specific mode, e.g. dev, then edit the config file
for that mode (config/Dev.php
).
The modify()
method is where we get the router service (‘aura/web-kernel:router’)
and add routes to the application.
The aura/web-kernel:router
is an object of type Aura\Router\Router . So if you are
familiar with Aura.Router then
you are done with this chapter, else read on.
Aura framework can act both as a micro framework or full stack framework. If you are using it as a micro framework, you can set a Closure as the action value, else set the same name of the action in the dispatcher. Don’t worry, we will cover dispatching in next chapter.
Note: This chapter gives you a basic understanding of the different types of methods available in router.
Adding a Route
We will not be showing the whole config file to reduce the space used.
This document assumes you are adding the route in the modify()
method after
getting the router service.
To create a route, call the add()
method on the Router. Named path-info
params are placed inside braces in the path.
You can create a route that matches only against a particular HTTP method
as well. The following Router methods are identical to add()
but require
the related HTTP method:
$router->addGet()
$router->addDelete()
$router->addOption()
$router->addPatch()
$router->addPost()
$router->addPut()
Advanced Usage
Extended Route Specification
You can extend a route specification with the following methods:
-
addTokens()
– Adds regular expression subpatterns that params must match.Note that
setTokens()
is also available, but this will replace any previous subpatterns entirely, instead of merging with the existing subpatterns. -
addServer()
– Adds regular expressions that server values must match.Note that
setServer()
is also available, but this will replace any previous expressions entirely, instead of merging with the existing expressions. -
addValues()
– Adds default values for the params.Note that
setValues()
is also available, but this will replace any previous default values entirely, instead of merging with the existing default value. -
setSecure()
– Whentrue
the$server['HTTPS']
value must be on, or the request must be on port 443; whenfalse
, neither of those must be in place. -
setWildcard()
– Sets the name of a wildcard param; this is where arbitrary slash-separated values appearing after the route path will be stored. -
setRoutable()
– Whenfalse
the route will be used only for generating paths, not for matching (true
by default). -
setIsMatchCallable()
– A custom callable with the signaturefunction(array $server, \ArrayObject $matches)
that returns true on a match, or false if not. This allows developers to build any kind of matching logic for the route, and to change the$matches
for param values from the path. -
setGenerateCallable()
– A custom callable with the signaturefunction(\ArrayObject $data)
. This allows developers to modify the data for path interpolation.
Here is a full extended route specification named read
:
Default Route Specifications
You can set the default route specifications with the following Router methods; the values will apply to all routes added thereafter.
Simple Routes
You don’t need to specify an extended route specification. With the following simple route …
… the Router will use a default subpattern that matches everything except slashes for the path params. Thus, the above simple route is equivalent to the following extended route:
Automatic Params
The Router will automatically populate values for the action
route param if one is not set manually.
Optional Params
Sometimes it is useful to have a route with optional named params. None, some, or all of the optional params may be present, and the route will still match.
To specify optional params, use the notation {/param1,param2,param3}
in the
path. For example:
With that, the following routes will all match the ‘archive’ route, and will set the appropriate values:
Optional params are sequentially optional. This means that, in the above example, you cannot have a “day” without a “month”, and you cannot have a “month” without a “year”.
Only one set of optional params per path is recognized by the Router.
Optional params belong at the end of a route path; placing them elsewhere may result in unexpected behavior.
Wildcard Params
Sometimes it is useful to allow the trailing part of the path be anything at
all. To allow arbitrary trailing params on a route, extend the route
definition with setWildcard()
to specify param name under which the
arbitrary trailing param values will be stored.
Attaching Route Groups
You can add a series of routes all at once under a single “mount point” in
your application. For example, if you want all your blog-related routes to be
mounted at /blog
in your application, you can do this:
Each of the route names will be prefixed with ‘blog.’, and each of the route paths
will be prefixed with /blog
, so the effective route names and paths become:
blog.browse => /blog{format}
blog.read => /blog/{id}{format}
blog.edit => /blog/{id}/edit{format}
You can set other route specification values as part of the attachment specification; these will be used as the defaults for each attached route, so you don’t need to repeat common information. (Setting these values will not affect routes outside the attached group.)
Attaching REST Resource Routes
The router can attach a series of REST resource routes for you with the
attachResource()
method:
That method call will result in the following routes being added:
Route Name | HTTP Method | Route Path | Purpose |
---|---|---|---|
blog.browse | GET | /blog{format} | Browse multiple resources |
blog.read | GET | /blog/{id}{format} | Read a single resource |
blog.edit | GET | /blog/{id}/edit | The form for editing a resource |
blog.add | GET | /blog/add | The form for adding a resource |
blog.delete | DELETE | /blog/{id} | Delete a single resource |
blog.create | POST | /blog | Create a new resource |
blog.update | PATCH | /blog/{id} | Update part of an existing resource |
blog.replace | PUT | /blog/{id} | Replace an entire existing resource |
The {id}
token is whatever has already been defined in the router; if not
already defined, it will be any series of numeric digits. Likewise, the
{format}
token is whatever has already been defined in the router; if not
already defined, it is an optional dot-format file extension (including the
dot itself).
The action
value is the same as the route name.
If you want calls to attachResource()
to create a different series of REST
routes, use the setResourceCallable()
method to set your own callable to
create them.
The example will cause only four CRUD routes, using hexadecimal resource IDs,
to be added for the resource when you call attachResource()
.
Dispatching
Aura web/framework projects can handle different variations of dispatching with the help of Aura.Dispatcher.
So if your application starts small and grows, it is easy to modify the application routes acting as a micro framework to a full-stack style.
Microframework
The following is an example of a micro-framework style route, where the
action logic is embedded in the route params. In the modify()
config method, we retrieve the shared aura/web-kernel:request
and aura/web-kernel:response
services, along with the aura/web-kernel:router
service. We then add a route names
blog.read
and embed the action code as a closure.
Modified Micro-Framework Style
We can modify the above example to put the action logic in the dispatcher instead of the route itself.
Extract the action closure to the dispatcher under the name blog.read
.
Then, in the route, use a action
value that matches the name in
the dispatcher.
Full-Stack Style
You can migrate from a micro-framework style to a full-stack style (or start with full-stack style in the first place).
First, define a action class and place it in the project src/
directory.
Next, tell the project how to build the BlogRead through the DI
Container. Edit the project config/Common.php
file to configure the
Container to pass the aura/web-kernel:request
and aura/web-kernel:response
service objects to
the BlogRead constructor.
After that, put the App\Actions\BlogRead
object in the dispatcher
under the name blog.read
as a lazy-loaded instantiation …
… and finally, point the router to the blog.read
action object:
Request
The Request object describes the current web execution context for PHP. Note
that it is not an HTTP request object proper, since it includes things
like $_ENV
and various non-HTTP $_SERVER
keys.
You can get the Request object from the DI,
The Request object contains several property objects. Some represent a copy of the PHP superglobals …
-
$request->cookies
for$_COOKIES
-
$request->env
for$_ENV
-
$request->files
for$_FILES
-
$request->post
for$_POST
-
$request->query
for$_GET
-
$request->server
for$_SERVER
… and others represent more specific kinds of information about the request:
-
$request->client
for the client making the request -
$request->content
for the raw body of the request -
$request->headers
for the request headers -
$request->method
for the request method -
$request->accept
for content negotiation -
$request->params
for path-info parameters -
$request->url
for the request URL
The Request object has only one method, isXhr()
, to indicate if the
request is an XmlHttpRequest or not.
Superglobals
Each of the superglobal representation objects has a single method, get()
,
that returns the value of a key in the superglobal, or an alternative value
if the key is not present. The values here are read-only.
Client
The $request->client
object has these methods:
-
getForwardedFor()
returns the values of theX-Forwarded-For
headers as an array. -
getReferer()
returns the value of theReferer
header. -
getIp()
returns the value of$_SEVER['REMOTE_ADDR']
, or the appropriate value ofX-Forwarded-For
. -
getUserAgent()
return the value of theUser-Agent
header. -
isCrawler()
returns true if theUser-Agent
header matches one of a list of bot/crawler/robot user agents (otherwise false). -
isMobile()
returns true if theUser-Agent
header matches one of a list of mobile user agents (otherwise false).
Content
The $request->content
object has these methods:
-
getType()
returns the content-type of the request body -
getRaw()
return the raw request body -
get()
returns the request body after decoding it based on the content type
The Content object has two decoders built in.
If the request specified a content type of application/json
,
the get()
method will automatically decode the body with json_decode()
.
Likewise, if the content type is application/x-www-form-urlencoded
, the
get()
method will automatically decode the body with parse_str()
.
Headers
The $request->headers
object has a single method, get()
, that returns the
value of a particular header, or an alternative value if the key is not
present. The values here are read-only.
Method
The $request->method
object has these methods:
-
get()
: returns the request method value -
isDelete()
: Did the request use a DELETE method? -
isGet()
: Did the request use a GET method? -
isHead()
: Did the request use a HEAD method? -
isOptions()
: Did the request use an OPTIONS method? -
isPatch()
: Did the request use a PATCH method? -
isPut()
: Did the request use a PUT method? -
isPost()
: Did the request use a POST method?
You can also call is*()
on the Method object; the part after is
is
treated as custom HTTP method name, and checks if the request was made using
that HTTP method.
Sometimes forms use a special field to indicate a custom HTTP method on a
POST. By default, the Method object honors the _method
form field.
Params
Unlike most Request property objects, the Params object is read-write (not read-only). The Params object allows you to set application-specific parameter values. These are typically discovered by parsing a URL path through a router of some sort (e.g. Aura.Router).
The $request->params
object has two methods:
-
set()
to set the array of parameters -
get()
to get back a specific parameter, or the array of all parameters
For example:
Url
The $request->url
object has two methods:
-
get()
returns the full URL string; or, if a component constant is passed, returns only that part of the URL -
isSecure()
indicates if the request is secure, whether via SSL, TLS, or forwarded from a secure protocol
Response
The Response object describes the web response that should be sent to the client. It is not an HTTP response object proper. Instead, it is a series of hints to be used when building the HTTP response with the delivery mechanism of your choice.
Setting values on the Response object does not cause values to be sent to the client. The Response can be inspected during testing to see if the correct values have been set without generating output.
You can get the Response object from the DI,
The Response object is composed of several property objects representing different parts of the response:
-
$response->status
for the status code, status phrase, and HTTP version -
$response->headers
for non-cookie headers -
$response->cookies
for cookie headers -
$response->content
for describing the response content, and for convenience methods related to content type, charset, disposition, and filename -
$response->cache
for convenience methods related to cache headers -
$response->redirect
for convenience methods related to Location and Status
Status
Use the $response->status
object as follows:
Headers
The $response->headers
object has these methods:
-
set()
to set a single header, resetting previous values on that header -
get()
to get a single header, or to get all headers
Setting a header value to null, false, or an empty string will remove that header; setting it to zero will not remove it.
Cookies
The $response->cookies
object has these methods:
-
setExpire()
sets the default expiration for cookies -
setPath()
sets the default path for cookies -
setDomain()
sets the default domain for cookies -
setSecure()
sets the default secure value for cookies -
setHttpOnly()
sets the default for whether or not cookies will be sent by HTTP only. -
set()
sets a cookie name and value along with its meta-data. This method mimics the setcookie() PHP function. If meta- data such as path, domain, secure, and httponly are missing, the defaults will be filled in for you. -
get()
returns a cookie by name, or all the cookies at once.
The cookie descriptor array looks like this:
Content
The $response->content
object has these convenience methods related to the
response content and content headers:
-
set()
sets the body content of the response (this can be anything at all, including an array, a callable, an object, or a string – it is up to the sending mechanism to translate it properly) -
get()
get the body content of the response which has been set viaset()
-
setType()
sets theContent-Type
header -
getType()
returns theContent-Type
(not including the charset) -
setCharset()
sets the character set for theContent-Type
-
getCharset()
returns thecharset
portion of theContent-Type
header -
setDisposition()
sets theContent-Disposition
type and filename -
setEncoding()
sets theContent-Encoding
header
Cache
The $response->cache
object has several convenience methods related to HTTP
cache headers.
-
reset()
removes all cache-related headers -
disable()
turns off caching by removing all cache-related headers, then sets the following: -
setAge()
sets theAge
header value in seconds -
setControl()
sets an array ofCache-Control
header directives all at once; alternatively, use the individual directive methods:-
setPublic()
andsetPrivate()
set thepublic
andprivate
cache control directives (each turns off the other) -
setMaxAge()
andsetSharedMaxAge()
set themax-age
ands-maxage
cache control directives (set to null or false to remove them) -
setNoCache()
andsetNoStore()
set theno-cache
andno-store
cache control directives (set to null or false to remove them) -
setMustRevalidate()
andsetProxyRevalidate()
to set themust-revalidate
andproxy-revalidate
directives (set to null or false to remove them)
-
-
setEtag()
andsetWeakEtag()
set theETag
header value -
setExpires()
sets theExpires
header value; will convert recognizable date formats andDateTime
objects to a correctly formatted HTTP date -
setLastModified()
sets theLast-Modified
header value; will convert recognizable date formats andDateTime
objects to a correctly formatted HTTP date -
setVary()
sets theVary
header; pass an array for comma-separated values
For more information about caching headers, please consult the HTTP 1.1 headers spec along with these descriptions from Palizine.
Redirect
The $response->redirect
object has several convenience methods related to
status and Location
headers for redirection.
-
to($location, $code = 302, phrase = null)
sets the status and headers for redirection to an arbitrary location with an arbitrary status code and phrase -
afterPost($location)
redirects to the$location
with a303 See Other
status; this automatically disables HTTP caching -
created($location)
redirects to$location
with201 Created
-
movedPermanently($location)
redirects to$location
with301 Moved Permanently
-
found($location)
redirects to$location
with302 Found
-
seeOther($location)
redirects to$location
with303 See Other
; this automatically disables HTTP caching -
temporaryRedirect($location)
redirects to$location
with307 Temporary Redirect
-
permanentRedirect($location)
redirects to$location
with308 Permanent Redirect
Dependency Injection
Aura.Di is a dependency injection container system with the following features:
- constructor and setter injection
- explicit and implicit auto-resolution of typehinted constructor parameter values
- configuration of setters across interfaces and traits
- inheritance of constructor parameter and setter method values
- lazy-loaded services, values, and instances
- instance factories
We will concentrate on constructor and setter injection in this chapter for easiness. It is recommend you should read Aura.Di documentation
Setting And Getting Services
A “service” is an object stored in the Container under a unique name.
Any time you get()
the named service, you always get back the same object instance.
That usage is great if we want to create the Example instance at the same time we set the service. However, we generally want to create the service instance at the moment we get it, not at the moment we set it.
The technique of delaying instantiation until get()
time is called “lazy loading.” To lazy-load an instance, use the lazyNew()
method on the Container and give it the class name to be created:
Now the service is created only when we we get()
it, and not before.
This lets us set as many services as we want, but only incur the overhead of creating the instances we actually use.
Constructor Injection
When we use the Container to instantiate a new object, we often need to inject (i.e., set) constructor parameter values in various ways.
Default Parameter Values
We can define default values for constructor parameters using the $di->params
array on the Container.
Let’s look at a class that takes some constructor parameters:
If we were to try to set a service using $di->lazyNew('ExampleWithParams')
,
the instantiation would fail. The $foo
param is required, and the Container
does not know what to use for that value.
To remedy this, we tell the Container what values to use for
each ExampleWithParams constructor parameter by name using the $di->params
array:
Now when a service is defined with $di->lazyNew('ExampleWithParams')
,
the instantiation will work correctly. Each time we create an
ExampleWithParams instance through the Container, it will apply
the $di->params['ExampleWithParams']
values.
Instance-Specific Parameter Values
If we want to override the default $di->params
values for a specific
new instance, we can pass a $params
array as the second argument to
lazyNew()
to merge with the default values. For example:
This will leave the $foo
parameter default in place, and override
the $bar
parameter value, for just that instance of the ExampleWithParams.
Lazy-Loaded Services As Parameter Values
Sometimes a class will need another service as one of its parameters. By way of example, the following class needs a database connection:
To inject a shared service as a parameter value, use $di->lazyGet()
so that the service object is not created until the ExampleNeedsService object is created:
This keeps the service from being created until the very moment it is needed. If we never instantiate anything that needs the service, the service itself will never be instantiated.
Setter Injection
This package supports setter injection in addition to constructor injection. (These can be combined as needed.)
Setter Method Values
After the Container constructs a new instance of an object, we can specify that certain methods should be called with certain values immediately after instantiation by using the $di->setter
array. Say we have class like the following:
We can specify that, by default, the setFoo()
method should be called with a specific value after construction like so:
The value can be any valid value: a literal, a call to lazyNew()
or lazyGet()
, and so on.
Note, however, that auto-resolution does not apply to setter methods. This is because the Container does not know which methods are setters and which are “normal use” methods.
Note also that this works only with explicitly-defined setter methods.
Setter methods that exist only via magic __call()
will not be honored.
Instance-Specific Setter Values
As with constructor injection, we can note instance-specific setter
values to use in place of the defaults. We do so via the third
argument to $di->lazyNew()
. For example:
View
Aura web framework doesn’t come packaged with any templating. The reason is love for templating differs from person to person.
With the help of foa/responder-bundle, we can integrate
The advantage of using foa/responder-bundle
is you have a common method render
, which helps you to switch between template engine with less overhead.
It also helps integrating the Action Domain Responder which we will cover on a different chapter.
Installation
Choose your templating engine and install the same. In this example we are going to make use of foa/html-view-bundle
which integrates aura/view and aura/html.
Configuration
Add the below lines in {PROJECT_PATH}/config/Common.php
in the define
method.
Setting the paths to
Aura\View\TemplateRegistry
will only work for version >= 2.1
Integration with actions
Let us integrate the above renderer to the full stack framework example shown in previous chapter.
Edit the {$PROJECT_PATH}/src/App/Actions/BlogRead.php
to inject an instance of FOA\Responder_Bundle\Renderer\RendererInterface
.
Modify the config/Common.php
for the DI container to
pass an instance that satisfies FOA\Responder_Bundle\Renderer\RendererInterface
to App\Actions\BlogRead
.
Create the file templates/read.php
with contents
Please refer respective library documentation on usage inside template file.
Forms
Forms are an integral part of web application. Aura.Input is a tool to describe HTML fields and values.
Installation
Even though Aura.Input has a base filter implementation, it is good to integrate a powerful filter system like Aura.Filter.
The foa/filter-input-bundle
, and foa/filter-intl-bundle
already
have done the heavy lifting integrating the aura/input
, aura/filter
,
aura/intl
and having the necessary DI configuration.
Add those bundles to your composer.json
.
and run
Usage
Inorder to create a form, we need to extend the Aura\Input\Form
class
and override the init()
method.
An example is shown below.
We will talk about Aura.Filter in next chapter.
Note : We are using v1 components of input, intl, filter.
Configuration
If we create App\Input\ContactForm
object via the new operator
we need to pass the dependencies manually as
Creating object via DI configuration helps us not to add the dependencies, add the necessary rules to the filter.
We only need to figure out where we need the form, and use params or setter injection.
Populating
Form can be populated using fill()
method.
In aura term it will be $this->request->post->get()
Validating User Input
You can validate the form via the filter()
method.
Rendering
Inorder to render the form, we need to pass the ContactForm object and use the
Aura.Html
helpers.
Assuming you have passed the ContactForm
object, and the variable assigned
is contact_form
you can use the get
method on the form object to
get the hints of field, and pass to input helper.
An example is given below :
Read more on form helpers here.
In the session chapter we will learn how to set flash message when the form submission was success.
Validation
Aura.Filter is a tool to validate and sanitize data.
We are going to look into version 1 of Aura.Filter.
Installation
We have already installed aura/filter
in the previous chapter about forms. If you have not installed please do the same.
Applying Rules to Data Objects
Soft, Hard, and Stop Rules
There are three types of rule processing we can apply:
- The
addSoftRule()
method adds a soft rule: if the rule fails, the filter will keep applying all remaining rules to that field and all other fields. - The
addHardRule()
method adds a hard rule: if the rule fails, the filter will not apply any more rules to that field, but it will keep filtering other fields. - The
addStopRule()
method adds a stopping rule: if the rule fails, the filter will not apply any more filters to any more fields; this stops all filtering on the data object.
Validating and Sanitizing
We validate data by applying a rule with one of the following requirements:
-
RuleCollection::IS
means the field value must match the rule. -
RuleCollection::IS_NOT
means the field value must not match the rule. -
RuleCollection::IS_BLANK_OR
means the field value must either be blank, or match the rule. This is useful for optional field values that may or may not be filled in.
We sanitize data by applying a rule with one of the following transformations:
-
RuleCollection::FIX
to force the field value to comply with the rule; this may forcibly transform the value. Some transformations are not possible, so sanitizing the field may result in an error message. -
RuleCollection::FIX_BLANK_OR
will convert blank values tonull
; non-blank fields will be forced to comply with the rule. This is useful for sanitizing optional field values that may or may not match the rule.
Each field is sanitized in place; i.e., the data object property will be modified directly.
Blank Values
Aura Filter incorporates the concept of “blank” values, as distinct from
isset()
and empty()
. A value is blank if it is null
, an empty string, or
a string composed of only whitespace characters. Thus, the following are
blank:
Integers, floats, booleans, and other non-strings are never counted as blank, even if they evaluate to zero:
Available Rules
-
alnum
: Validate the value as alphanumeric only. Sanitize to leave only alphanumeric characters. Usage: -
alpha
: Validate the value as alphabetic only. Sanitize to leave only alphabetic characters. Usage: -
between
: Validate the value as being within or equal to a minimum and maximum value. Sanitize so that values lower than the range are forced up to the minimum; values higher than the range are forced down to the maximum. Usage: -
blank
: Validate the value as being blank. Sanitize tonull
. Usage: -
bool
: Validate the value as being a boolean, or a pseudo-boolean. Pseudo-true values include the strings ‘1’, ‘y’, ‘yes’, and ‘true’; pseudo-false values include the strings ‘0’, ‘n’, ‘no’, and ‘false’. Sanitize to a strict PHP boolean. Usage: -
creditCard
: Validate the value as being a credit card number. The value cannot be sanitized. Usage: -
dateTime
: Validate the value as representing a date and/or time. Sanitize the value to a specified format, default'Y-m-d H:i:s'
. Usage (note that this is to sanitize, not validate): -
email
: Validate the value as being a properly-formed email address. The value cannot be sanitized. Usage: -
equalToField
: Validate the value as loosely equal to the value of another field in the data object. Sanitize to the value of that other field. Usage: -
equalToValue
: Validate the value as loosely equal to a specified value. Sanitize to the specified value. Usage: -
float
: Validate the value as representing a float. Sanitize the value to transform it into a float; for weird strings, this may not be what you expect. Usage: -
inKeys
: Validate that the value is loosely equal to a key in a given array. The value cannot be sanitized. Usage: -
inValues
: Validate that the value is strictly equal to at least one value in a given array. The value cannot be sanitized. Usage: -
int
: Validate the value as representing an integer Sanitize the value to transform it into an integer; for weird strings, this may not be what you expect. Usage: -
ipv4
: Validate the value as an IPv4 address. The value cannot be sanitized. Usage: -
locale
: Validate the given value against a list of locale strings. If it’s not found returns false. The value cannot be sanitized. Usage: -
max
: Validate the value as being less than or equal to a maximum. Sanitize so that values higher than the maximum are forced down to the maximum. Usage: -
min
: Validate the value as being greater than or equal to a minimum. Sanitize so that values lower than the minimum are forced up to the minimum. Usage: -
regex
: Validate the value usingpreg_match()
. Sanitize the value usingpreg_replace()
. -
strictEqualToField
: Validate the value as strictly equal to the value of another field in the data object. Sanitize to the value of that other field. Usage: -
strictEqualToValue
: Validate the value as strictly equal to a specified value. Sanitize to the specified value. Usage: -
string
: Validate the value can be represented by a string. Sanitize the value by casting to a string and optionally usingstr_replace().
Usage (note that this is to sanitize, not validate): -
strlen
: Validate the value has a specified length. Sanitize the value to cut off longer values at the right, andstr_pad()
shorter ones. Usage: -
strlenBetween
: Validate the value length as being within or equal to a minimum and maximum value. Sanitize the value to cut off values longer than the maximum, longer values at the right, andstr_pad()
shorter ones. Usage: -
strlenMax
: Validate the value length as being no longer than a maximum. Sanitize the value to cut off values longer than the maximum. Usage: -
strlenMin
: Validate the value length as being no shorter than a minimum. Sanitize the value tostr_pad()
values shorter than the minimum. Usage: -
trim
: Validate the value istrim()
med. Sanitize the value totrim()
it. Optionally specify characters to trim. Usage: -
upload
: Validate the value represents a PHP upload information array, and that the file is an uploaded file. The value cannot be sanitized. Usage: -
url
: Validate the value is a well-formed URL. The value cannot be sanitized. Usage: -
word
: Validate the value as being composed only of word characters. Sanitize the value to remove non-word characters. Usage: -
isbn
: Validate the value is a correct ISBN (International Standard Book Number). Usage: -
any
: Validate the value passes at-least one of the rules. These rules are the ones added in rule locator. -
all
: Validate the value against a set of rules. These rules are should be added in rule locator. You will not get seprate error messages for which all rules it failed.
Custom Messages
By default when a rule fails, the messages you will be getting are from the
intl/en_US.php
. But you can also provide a single custom message for
all the failures.
Example:
As you have used useFieldMessage
you will see
instead of
Creating and Using Custom Rules
There are three steps to creating and using new rules:
- Write a rule class
- Set that class as a service in the
RuleLocator
- Use the new rule in our filter chain
Writing a Rule Class
Writing a rule class is straightforward:
- Extend
Aura\Filter\AbstractRule
with two methods:validate()
andsanitize()
. - Add params as needed to each method.
- Each method should return a boolean: true on success, or false on failure.
- Use
getValue()
to get the value being validated, andsetValue()
to change the value being sanitized. - Add a property
$message
to indicate a string that should be translated as a message when validation or sanitizing fails.
Here is an example of a hexadecimal rule:
Set The Class As A Service
Now we set the rule class into the RuleLocator
.
Apply The New Rule
Finally, we can use the rule in our filter:
That is all!
Internationalization
The Aura.Intl package provides internationalization (I18N) tools, specifically package-oriented per-locale message translation.
Installation
Service
In your modify method you can get the service intl_translator_locator
like
Setting Localized Messages For A Package
We can set localized messages for a package through the PackageLocator
object
from the translator locator. We create a new Package
with messages and place
it into the locator as a callable. The messages take the form of a message key and
and message string.
Setting The Default Locale
We can set the default locale for translations using the setLocale()
method:
Getting A Localized Message
Now that the translator locator has messages and a default locale, we can get an individual package translator. The package translator is suitable for injection into another class, or for standalone use. You will neeed to create a tanslator helper which can return the service.
You can get a translator for a non-default locale as well:
Replacing Message Tokens With Values
We often need to use dynamic values in translated messages. First, the message string needs to have a token placeholder for the dynamic value:
Then, when we translate the message, we provide an array of tokens and replacement values. These will be interpolated into the message string.
Pluralized Messages
Usually, we need to use different messages when a value is singular or plural.
The BasicFormatter
is not capable of presenting different messages based on
different token values. The IntlFormatter
is capable, but the PHP
intl
extension must be loaded to take advantage of
it, and we must specify the 'intl'
formatter for the package in the catalog.
When using the IntlFormatter
, we can build our message strings to present
singular or plural messages, as in the following example:
Note that you can use other tokens within a pluralized token string to build more complex messages. For more information, see the following:
http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html
Session
Provides session management functionality, including lazy session starting, session segments, next-request-only (“flash”) values, and CSRF tools.
Installation
We are going to install aura/session
.
Service
Aura.Session already have aura/session:session
service which is an object of
Aura\Session\Session . You can get inject the service to responder or
view helper and make use of the Aura\Session\Session object.
Segments
In normal PHP, we keep session values in the $_SESSION
array. However, when different libraries and projects try to modify the same keys, the resulting conflicts can result in unexpected behavior. To resolve this, we use Segment objects. Each Segment addresses a named key within the $_SESSION
array for deconfliction purposes.
For example, if we get a Segment for Vendor\Package\ClassName
, that Segment will contain a reference to $_SESSION['Vendor\Package\ClassName']
. We can then set()
and get()
values on the Segment, and the values will reside in an array under that reference.
The benefit of a session segment is that we can deconflict the keys in the
$_SESSION
superglobal by using class names (or some other unique name) for
the segment names. With segments, different packages can use the $_SESSION
superglobal without stepping on each other’s toes.
To clear all the values on a Segment, use the clear()
method.
Lazy Session Starting
Merely instantiating the Session manager and getting a Segment from it does not call session_start()
. Instead, session_start()
occurs only in certain circumstances:
- If we read from a Segment (e.g. with
get()
) the Session looks to see if a session cookie has already been set. If so, it will callsession_start()
to resume the previously-started session. If not, it knows there are no previously existing$_SESSION
values, so it will not callsession_start()
. - If we write to a Segment (e.g. with
set()
) the Session will always callsession_start()
. This will resume a previous session if it exists, or start a new one if it does not.
This means we can create each Segment at will, and session_start()
will not be invoked until we actually interact with a Segment in a particular way. This helps to conserve the resources involved in starting a session.
Of course, we can force a session start or reactivation by calling the Session start()
method, but that defeats the purpose of lazy-loaded sessions.
Saving, Clearing, and Destroying Sessions
To save the session data and end its use during the current request, call the commit()
method on the Session manager:
To clear all session data, but leave the session active during the current request, use the clear()
method on the Session manager.
To clear all flash values on a segment, use the clearFlash()
method:
To clear the data and terminate the session for this and future requests, thereby destroying it completely, call the destroy()
method:
Calling destroy()
will also delete the session cookie via setcookie()
. If we have an alternative means by which we delete cookies, we should pass a callable as the second argument to the SessionFactory method newInstance()
. The callable should take three parameters: the cookie name, path, and domain.
Session Security
Session ID Regeneration
Any time a user has a change in privilege (that is, gaining or losing access rights within a system) be sure to regenerate the session ID:
Cross-Site Request Forgery
A “cross-site request forgery” is a security issue where the attacker, via malicious JavaScript or other means, issues a request in-the-blind from a client browser to a server where the user has already authenticated. The request looks valid to the server, but in fact is a forgery, since the user did not actually make the request (the malicious JavaScript did).
http://en.wikipedia.org/wiki/Cross-site_request_forgery
Defending Against CSRF
To defend against CSRF attacks, server-side logic should:
- Place a token value unique to each authenticated user session in each form; and
- Check that all incoming POST/PUT/DELETE (i.e., “unsafe”) requests contain that value.
For this example, the form field name will be __csrf_value
. In each form
we want to protect against CSRF, we use the session CSRF token value for that
field:
When processing the request, check to see if the incoming CSRF token is valid for the authenticated user:
CSRF Value Generation
For a CSRF token to be useful, its random value must be cryptographically
secure. Using things like mt_rand()
is insufficient. Aura.Session comes with
a Randval
class that implements a RandvalInterface
, and uses either the
openssl
or the mcrypt
extension to generate a random value. If you do not
have one of these extensions installed, you will need your own random-value
implementation of the RandvalInterface
. We suggest a wrapper around
RandomLib.
Flash Values
Segment values persist until the session is cleared or destroyed. However, sometimes it is useful to set a value that propagates only through the next request, and is then discarded. These are called “flash” values.
Setting And Getting Flash Values
To set a flash value on a Segment, use the setFlash()
method.
Then, in subsequent requests, we can read the flash value using getFlash()
:
Using setFlash()
makes the flash value available only in the next request, not the current one. To make the flash value available immediately as well as in the next request, use setFlashNow($key, $val)
.
Using getFlash()
returns only the values that are available now from having been set in the previous request. To read a value that will be available in the next request, use getFlashNext($key, $alt)
.
Keeping and Clearing Flash Values
Sometimes we will want to keep the flash values in the current request for the next request. We can do so on a per-segment basis by calling the Segment keepFlash()
method, or we can keep all flashes for all segments by calling the Session keepFlash()
method.
Similarly, we can clear flash values on a per-segment basis or a session-wide bases. Use the clearFlash()
method on the Segment to clear flashes just for that segment, or the same method on the Session to clear all flash values for all segments.
Authentication
Authentication is made possible with the help of aura/auth.
Aura.Auth supports below adapters :
- Apache htpasswd files
- SQL tables via the PDO extension
- IMAP/POP/NNTP via the imap extension
- LDAP and Active Directory via the ldap extension
- OAuth via customized adapters
We will concentrate on authentication via PDO adapter.
Building Service class
Configuration
Consider reading adapters if you need changes/improvements.
Calling Auth Methods
You can retrieve authentication information using the following methods on the AuthService instance :
-
getUserName()
: returns the authenticated username string -
getUserData()
: returns the array of optional arbitrary user data -
getFirstActive()
: returns the Unix time of first activity (login) -
getLastActive()
: return the Unix time of most-recent activity (generally that of the current request) -
getStatus()
: returns the current authentication status constant. These constants are:-
Status::ANON
– anonymous/unauthenticated -
Status::IDLE
– the authenticated session has been idle for too long -
Status::EXPIRED
– the authenticated session has lasted for too long in total -
Status::VALID
– authenticated and valid
-
-
isAnon()
,isIdle()
,isExpired()
,isValid()
: these return true or false, based on the current authentication status.
You can also use the set*()
variations of the get*()
methods above to force the Auth object to whatever values you like.
Eg : Calling methods inside action
Action Domain Responder
It is recommend to read Action Domain Responder in short ADR.
Aura framework v2 promote the usage of one action per class.
In ADR there are 3 components.
- Action is the logic that connects the Domain and Responder. It uses the request input to interact with the Domain, and passes the Domain output to the Responder.
- Domain is the logic to manipulate the domain, session, application, and environment data, modifying state and persistence as needed.
- Responder is the logic to build an HTTP response or response description. It deals with body content, templates and views, headers and cookies, status codes, and so on.
Basically
- The web handler receives a client request and dispatches it to an Action.
- The Action interacts with the Domain.
- The Action feeds data to the Responder. (N.b.: This may include results from the Domain interaction, data from the client request, and so on.)
- The Responder builds a response using the data fed to it by the Action.
- The web handler sends the response back to the client.
Responder Bundle
We have FOA.Responder_Bundle which helps to render different template engines like Aura.View, Twig, Mustache etc. See full list.
Installation
Note : Current version 0.4
Let us modify our previous example of BlogRead action class to render the contents via BlogRead responder.
Save at {$PROJECT_PATH}/src/App/Responder/BlogRead.php
Now modify the actions class {$PROJECT_PATH}/src/App/Actions/BlogRead.php
to inject the BlogRead
responder. You also need to inject a
Domain service which can fetch the details of the id.
We are skipping the service and assume you have some way to get the data.
Remove the View and Response objects from the action class because the responder is responsible for rendering the view and set the response.
Now your modified action class will look like
Modify our Closure as a view file and save in
{$PROJECT_PATH}/src/App/Responders/views/read.php
.
Time to edit your configuration file {$PROJECT_PATH}/config/Common.php
.
Modify the class params for App\Actions\BlogRead
to reflect
the changes made to the constructor.
Browse the http://localhost:8000/blog/read/1
.
Questions
What have we achieved other than creating lots of classes ?
That is really a good question. We are moving the responsibility to its own layers which will help us in testing the application. Web applications get evolved even we start small, so testing each and every part is always a great way to move forward.
This help us in to test the action classes, services etc.
Command line / cli / console
In this chapter we assume you have installed either aura/framework-project
or aura/cli-project
.
Both aura/framework-project
and aura/cli-project
make use of the aura/cli
library. Aura.Cli can be used as standalone library. Please
refer getting started
if you looking for standalone usage.
Features of Aura.Cli
Context Discovery
The Context object provides information about the command line environment, including any option flags passed via the command line. (This is the command line equivalent of a web request object.)
Please have a look at services
You can access the $_ENV
, $_SERVER
, and $argv
values with the $env
,
$server
, and $argv
property objects, respectively. (Note that these
properties are copies of those superglobals as they were at the time of
Context instantiation.) You can pass an alternative default value if the
related key is missing.
Getopt Support
The Context object provides support for retrieving command-line options and params, along with positional arguments.
To retrieve options and arguments parsed from the command-line $argv
values,
use the getopt()
method on the Context object. This will return a
GetoptValues object for you to use as as you wish.
Defining Options and Params
To tell getopt()
how to recognize command line options, pass an array of
option definitions. The definitions array format is similar to, but not
exactly the same as, the one used by the getopt()
function in PHP. Instead of defining short flags in a string and long options
in a separate array, they are both defined as elements in a single array.
Adding a *
after the option name indicates it can be passed multiple times;
its values will be stored in an array.
Use the get()
method on the returned GetoptValues object to retrieve the
option values. You can provide an alternative default value for when the
option is missing.
If you want alias one option name to another, comma-separate the two names. The values will be stored under both names;
If you want to allow an option to be passed multiple times, add a ‘*’ to the end of the option name.
If the user passes options that do not conform to the definitions, the
GetoptValues object retains various errors related to the parsing
failures. In these cases, hasErrors()
will return true
, and you can then
review the errors. (The errors are actually Aura\Cli\Exception
objects,
but they don’t get thrown as they occur; this is so that you can deal with or
ignore the different kinds of errors as you like.)
Positional Arguments
To get the positional arguments passed to the command line, use the get()
method and the argument position number:
Defined options will be removed from the arguments automatically.
Standard Input/Output Streams
The Stdio object allows you to work with standard input/output streams. (This is the command line equivalent of a web response object.)
Please have a look at services
It defaults to using php://stdin
, php://stdout
, and php://stderr
, but
you can pass whatever stream names you like as parameters to the newStdio()
method.
The Stdio object methods are …
-
getStdin()
,getStdout()
, andgetStderr()
to return the respective Handle objects; -
outln()
andout()
to print to stdout, with or without a line ending; -
errln()
anderr()
to print to stderr, with or without a line ending; -
inln()
andin()
to read from stdin until the user hits enter;inln()
leaves the trailing line ending in place, whereasin()
strips it.
You can use special formatting markup in the output and error strings to set text color, text weight, background color, and other display characteristics. See the formatter cheat sheet below.
Exit Codes
This library comes with a Status class that defines constants for exit
status codes. You should use these whenever possible. For example, if a
command is used with the wrong number of arguments or improper option flags,
exit()
with Status::USAGE
. The exit status codes are the same as those
found in sysexits.h.
Writing Commands
The Aura.Cli library does not come with an abstract or base command class to
extend from, but writing commands for yourself is straightforward. The
following is a standalone command script, but similar logic can be used in a
class. Save it in a file named hello
and invoke it with
php hello [-v,--verbose] [name]
.
Writing Command Help
Sometimes it will be useful to provide help output for your commands. With Aura.Cli, the Help object is separate from any command you may write. It may be manipulated externally or extended.
For example, extend the Help object and override the init()
method.
Then instantiate the new class and pass its getHelp()
output through Stdio:
- We keep the command name itself outside of the help class, because the command name may be mapped differently in different projects.
- We pass a GetoptParser to the Help object so it can parse the option defintions.
- We can get the option definitions out of the Help object using
getOptions()
; this allows us to pass a Help object into a hypothetical command object and reuse the definitions.
The output will look something like this:
Formatter Cheat Sheet
On POSIX terminals, <<markup>>
strings will change the display
characteristics. Note that these are not HTML tags; they will be converted
into terminal control codes, and do not get “closed”. You can place as many
space-separated markup codes between the double angle-brackets as you like.
For example, to set bold white text on a red background, add <<bold white redbg>>
into your output or error string. Reset back to normal with <<reset>>
.
Services
Aura.Cli_Kernel defines the following service objects in the Container:
-
aura/cli-kernel:dispatcher
: an instance of Aura\Dispatcher\Dispatcher -
aura/cli-kernel:context
: an instance of Aura\Cli\Context -
aura/cli-kernel:stdio
: an instance of Aura\Cli\Stdio -
aura/cli-kernel:help_service
: an instance of Aura\Cli_Kernel\HelpService -
aura/project-kernel:logger
: an instance ofMonolog\\Logger
Quick Start
The dependency injection Container is absolutely central to the operation of an Aura project. Please be familiar with the DI docs before continuing.
You should also familiarize yourself with Aura.Dispatcher, as well as the Aura.Cli Context, Stdio, and Status objects.
Project Configuration
Every Aura project is configured the same way. Please see the shared configuration docs for more information.
Logging
The project automatically logs to {$PROJECT_PATH}/tmp/log/{$mode}.log
.
If you want to change the logging behaviors for a particular config mode,
edit the related config file (e.g., config/Dev.php
) file to modify
the aura/project-kernel:logger
service.
Commands
We configure commands via the project-level config/
class files.
If a command needs to be available in every config mode, edit the
project-level config/Common.php
class file. If it only needs to
be available in a specific mode, e.g. dev
, then edit the config
file for that mode.
Here are two different styles of command definition.
Micro-Framework Style
The following is an example of a command where the logic is embedded in the dispatcher, using the aura/cli-kernel:context
and aura/cli-kernel:stdio
services along with standard exit codes. (The dispatcher object name doubles as the command name.)
You can now run the command to see its output.
(If you do not pass an ID argument, you will see an error message.)
Full-Stack Style
You can migrate from a micro-controller style to a full-stack style (or start with full-stack style in the first place).
First, define a command class and place it in the project src/
directory.
Next, tell the project how to build the FooCommand through the DI
Container. Edit the project config/Common.php
file to configure the
Container to pass the aura/cli-kernel:context
and aura/cli-kernel:stdio
service objects to
the FooCommand constructor. Then put the AppCommandFooCommand object in the dispatcher under the name foo
as a lazy-loaded instantiation.
You can now run the command to see its output.
(If you do not pass an ID argument, you will see an error message.)
Setting up your virtual host
Apache
We are going to point the virtual host to aura.localhost
.
If you are in a debain based OS, you want to create a file
/etc/apache2/sites-available/aura2.localhost.conf
with the below contents.
path/to/project
is where you installed the aura/web-project
or
aura/framework-project
.
NOTE: Apache 2.4 users might have to add Require all granted
below AllowOverride all
in order to prevent a 401 response caused by the changes in access control.
Enable the site using
and reload the apache
Before we go and check in browser add one more line in the /etc/hosts
Nginx
In ubuntu 12.04 the configuration file is under /etc/nginx/sites-available
Check http://aura2.localhost in your favourite browser.