0% found this document useful (0 votes)
561 views

PHP Apis

PHP

Uploaded by

johnatan17
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
561 views

PHP Apis

PHP

Uploaded by

johnatan17
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 72

WE’RE HIRING

test self starter


develop security minded
collaborate experienced
improve attention to detail
CONTENTS
JANUARY 2016
Volume 15 - Issue 1

APIs
Doorways to Your Apps
Features
4 Discovering API Platform 30 Writing Better CLI Tools with
Kévin Dunglas Symfony Components
Juan Manuel Torres
18 Build APIs on Existing
Web Applications with Apigility 42 Raspberry Pi with PHP on Top
Michelangelo van Dam Dylan Intorf

Columns
3 Editorial: 60 Security Corner:
Doorways to Your Apps Passwords are Dead,
Oscar Merida Long Live Passwords!
Chris Cornutt
48 Education Station:
Date and Time Handling 63 Community Corner:
with Carbon Interview with Jeffrey Carouth
Matthew Setter Joe Devon

55
Leveling Up: 66 MONTH IN REVIEW:
Finding the Solution to the DECEMBER HAPPENINGS
Problem
David Stockton 67 finally{}:
Different Paths for the P’s in LAMP
Eli White

CODE PACKAGE

The accompanying code to this issue


may be downloaded from:
http://phpa.me/January2016_code
www.phparch.com July 2016 | 1
Editor-in-Chief Advertising
Oscar Merida
Each month, php[architect] magazine brings news
Managing Editor and information to an audience of thousands of
Eli White PHP professionals worldwide. Advertising through
php|architect] is a great opportunity to showcase your
Creative Director products and company to potential customers who have
Kevin Bruce already shown their dedication to PHP and willingness to
invest in their careers and knowledge.
Technical Editors
Oscar Merida, Sandy Smith Ads are accepted for php[architect] magazine in varied
formats (PDF, Adobe Illustrator, JPEG, PNG, TIFF) and are
Issue Authors each priced based upon their relative sizing.
Chris Cornutt, Joe Devon, Kévin Dunglas, Dylan Intorf,
David Stockton, Matthew Setter, Juan Manuel Torres, To learn more and receive the full advertising prospectus,
Michelangelo van Dam contact us at [email protected] today!

Subscriptions WRITE FOR US


Digital Edition - DRM-Free If you want to bring a PHP-related topic to the attention
Get 12 issues in PDF, ePub, and Mobi format with our of the professional PHP community, whether it is personal
digital subscription for only $49 a year. research, company software, or anything else, why
not write an article for php[architect]? If you would like
Print + DRM-Free Digital to contribute, contact us, and one of our editors will
Get 12 issues printed and shipped to you as well as the be happy to help you hone your idea and turn it into a
digital copies for only $129 a year (add $45 outside of the beautiful article for our magazine.
U.S.A.).
Visit http://www.phparch.com/editorial/write-for-us or
Business Print + DRM-Free Digital contact our editorial team at [email protected] and get
Are you at a business and want multiple print copies to started!
share around the office? Business packages start at
3 copies a month annually for $299 (add $75 outside of
the USA).

Visit http://www.phparch.com/magazine to subscribe or


email [email protected] for more information.

musketeers.me, LLC is the parent company and owner of the php[architect] brand.

CEO php[architect], php[a], the php[architect] logo,


Sandy Smith musketeers.me, LLC and the musketeers.me, LLC logo are
trademarks of musketeers.me, LLC.
Managing Partners
Kevin Bruce, Oscar Merida, Eli White Contact Information:
General mailbox: [email protected]
php[architect] (ISSN 1709-7169) is published twelve Editorial: [email protected]
times a year by musketeers.me, LLC, 201 Adams Avenue, Sales & advertising: [email protected]
Alexandria, VA 22301, USA.
Print ISSN 1709-7169
Although all possible care has been placed in assuring the Digital ISSN 2375-3544
accuracy of the contents of this magazine, including all
associated source code, listings and figures, the publisher Copyright © 2002-2016
assumes no responsibilities with regards of use of the musketeers.me, LLC
information contained herein or in all associated material. All Rights Reserved

2 | January 2016 www.phparch.com


Editorial
Oscar Merida

Doorways to Your Apps


First, Happy 2016! The web development world is
as vibrant as ever and I hope the new year brings you
interesting projects, that you make new friends and
colleagues, and that you endeavor to share whatever
cool things you learn. I’m sure the places where our
applications are expected to run will continue to
proliferate in new and unexpected ways. Soon, your
service may be called by someone’s refrigerator—a
PHP backed REST API is sure to make life easier. No
matter what, we’ll be rounding up articles to help
you stay at up to date.

In the first issue of the New Year, we feature two


articles to help you quickly build an API. If you need
to add one to a legacy codebase, check out Build
API’s on Existing Web Applications with Apigility by
Michelangelo van Dam. Discovering API Platform
by Kévin Dunglas will introduce you to a framework
that helps create a fully featured API in minutes. Are
you writing more command line scripts to automate
tasks? Juan Manuel Torres will show you how to
leverage existing components in Writing Better
CLI Tools with Symfony Components. And if you’re
looking to put PHP on a small, portable computer,
be sure to read Raspberry Pi with PHP on Top by
Dylan Intorf.

Explore an easier way to work with Dates and Times,


see this month’s Education Station by Matthew Setter
to learn about Date and Time Handling with Carbon.
David Stockton will guide you in Finding the Solution
to the Problem in Leveling up. Joe Devon speaks with
Jeffrey Carouth in Community Corner. Learn why
Passwords are Dead, Long Live Passwords! in Security
Corner by Chris Cornutt. Finally{}, Eli White looks
at how Perl and Python have fared in releasing new
versions of their languages.

www.phparch.com January 2016 | 3


FEATURE

Discovering API Platform


Kévin Dunglas

API Platform is an open-source PHP


web framework focusing on web API
development (yeah, the name is pretty
explicit). It is built on top of the famous
Symfony full-stack framework and is
100% compliant with it: you can install
any Symfony bundle and you can
add API Platform to existing Symfony
projects. In this article, you will learn
how to use API Platform together
with some Symfony bundles to create
a small but fully featured API, by
leveraging its basic features. This API,
built step by step, will be a customer
and prospect directory.

API Platform includes all you need • Set CORS headers easily.
to build a high quality (HATEOAS • Set HTTP cache headers to improve performance.
https://en.wikipedia.org/wiki/HATEOAS) API: • Specify and test your API using Behat contexts
dedicated to APIs.
• Bootstrap a data model reusing Schema.org
vocabularies, including Doctrine ORM mapping, It looks daunting but you will be amazed to see how
Symfony validation constraints, and PHPDoc. easy it is.
• Expose—in a few seconds—a fully working
level 3 REST API with a ton of features: CRUD, API Platform is developed by Les-Tilleuls.coop, a
pagination for collections, server-side validation, French worker cooperative providing professional
relation support, filtering, sorting, JSON-LD, and services around web applications and e-commerce.
Hydra Core Vocabulary support. Full disclosure: I’m the creator of API Platform and the
• Get an automatically generated Swagger-like API CEO of this company.
doc that includes a sandbox.
• Easily add stateless JSON Web Token or OAuth 2
authentication.

4 | January 2016 www.phparch.com


DisplayInfo()

Requirements:
• PHP: 5.5+
• API Platform—https://api-platform.com
• Composer—https://getcomposer.org
• OpenSSL—https://www.openssl.org
Other Software:
• A MySQL or a PostgreSQL DBMS
• Postman (optional)—https://www.getpostman.com
Related URLs:
• Creating Custom Data Providers—http://phpa.me/api-platform-custom-providers
• EasyRdf library—http://www.easyrdf.org
• Guzzle—https://github.com/guzzle/guzzle
• DBPedia—http://dbpedia.org
• FosHttpCache—https://foshttpcache.readthedocs.org
• Hydra Console—https://github.com/lanthaler/HydraConsole
• Hydra Core—https://github.com/bergos/hydra-core
• Hydra Core Vocabulary—http://hydra-cg.com
• IRI—https://en.wikipedia.org/wiki/Internationalized_resource_identifier
• JSON-LD—http://json-ld.org
• JSON-LD context http://www.w3.org/TR/json-ld/#the-context
• JSON Web Token—http://jwt.io
• Les-Tilleuls.coop—https://les-tilleuls.coop
• LexikJWTAuthenticationBundle http://phpa.me/LexikJWTAuth
• Linked Open Vocabularies—http://lov.okfn.org
• MySQL—http://www.mysql.com
• NelmioCorsBundle—https://github.com/nelmio/NelmioCorsBundle
• OAuth 2—http://oauth.net
• PostgreSQL—http://www.postgresql.org
• Running API Platform in Docker–http://phpa.me/run-api-platform-docker
• Schema.org—http://schema.org
• SPARQL—https://en.wikipedia.org/wiki/SPARQL
• Symfony Best Practices—http://phpa.me/symfony-best-practices
• Writing Behat Features—http://phpa.me/writing-features-behat

Architecture Considerations doc contains an Angular integration example),


traditional (but separated) server-side PHP websites
API Platform can be used to create any kind of web retrieving data from the API using Guzzle, native
API. It is especially useful when developing Single mobile apps, heavy desktop apps, or any other type
Page Applications, mobile apps, and microservices- of client, such as command line tools or connected
oriented architectures. devices.

The philosophy advocated by the framework is to first Another strength of the API-first approach is that
build an API enclosing the whole business logic of the any partner, customer, or provider with access to the
application(s) (API-first). This API will be the unique API can leverage the exact same set of features as the
entry-point for accessing and modifying data. official website or app. This approach can be summed
up by the motto write your business logic once, use it
The presentation logic (including forms) is stored in from any client.
client applications. Clients can be modern HTML5/
Angular/React/whatever responsive web apps It enforces by design low coupling, separation of
querying the API trough AJAX (the official API Platform

www.phparch.com January 2016 | 5


Discovering API Platform

concerns, and high application interoperability. It helps avoid the typical business logic duplication that occurs
during the application growth: features are added (first a website, then a mobile site, then an API for third-party
apps…) and, of course, all the copy/paste/adapt that occurs in each application component so the maintenance
becomes a nightmare.

Installing the Framework


The more straightforward way to get an API Platform app up and running is to download the preconfigured
distribution. This distribution includes Symfony and all API Platform’s components, installed, configured, and
integrated together.

To do so, use Composer, the well-known PHP package manager:

composer create-project api-platform/api-platform customer-directory-api

The installer asks some configuration parameters, starting with the database driver to use and DB credentials.
By default, API Platform comes with the Doctrine ORM, but it can be integrated with any other persistence
library. Doctrine supports MySQL, Oracle, Microsoft SQL Server, SAP Sybase, SQLite, and Drizzle, but I strongly
recommend using MySQL or Postgres for this tutorial and for your APIs in general: those DBMS have the best
support by Doctrine, and a lot of community libraries and bundles support only them. The default prompts are
shown below. Depending on your database setup, you may need to specify database_host as localhost.

database_driver (pdo_mysql):
database_host (127.0.0.1):
database_port (null):
database_name (api):
database_user (root):
database_password (null):

Then the installer asks for SMTP credentials. This is only useful if you want to send email from your API (as with
Symfony, the Swift Mailer library is included).

mailer_transport (smtp):
mailer_host (127.0.0.1):
mailer_user (null):
mailer_password (null):

Finally, you’ll be asked for some configuration parameters specific to the API.

The default locale:

locale (en):

API Platform has native CORS support through NelmioCorsBundle (see related URLs). Set the URL of your default
web client and API Platform will automatically set CORS headers that allow the webapp to query the API:

cors_allow_origin ('http://localhost'):

The installer also asks you to name and describe your API. These details will be used in the Hydra documentation
of the API:

api_name ('Your API name'):


api_description ('The full description of your API'):

Finally enter a long (32 characters) and complex random token. It will be used by security features:

secret (ThisTokenIsNotSoSecretChangeIt):

6 | January 2016 www.phparch.com


Discovering API Platform

Once the installation is complete, go to the new the default AppBundle. It will enable some
my-directory-api directory that appears. It magic: for instance, your Doctrine entities will
contains the skeleton of your new API. All required be automatically detected and registered, and
dependencies (basically Symfony, the Doctrine validation constraints will apply.
ORM, and API Platform components) have been • We already described the vendor/ directory;
downloaded in the vendor/ directory. finally, the web/ directory must be the document
root of your project. It’s the only directory
The installer also created a file called that must be publicly exposed. It contains
app/config/parameters.yml containing the two front controllers (app.php for the prod
parameters it asked for. This file can be tweaked at any environment and app_dev.php for developing
time of the project life. It must not be versioned, as it and debugging) and public assets such as the
allows adapting the configuration of the current install robots.txt file.
to its environment. You can add new configuration
The root directory also contains some useful files:
parameters with default values by editing the
composer.json tracks installed PHP libraries and
app/config/parameters.yml.dist file.
their version and docker-compose.yaml allows
API Platform to be instantly run in a Docker container
In fact, this skeleton is already a working app. It
http://phpa.me/run-api-platform-docker.
contains some example code that just works. Run the
built-in PHP web server to give API Platform a try:
When you are done playing with the demo app,
php app/console server:run empty the file app/config/schema.yml and the
src/AppBundle/Entity directory. Don’t worry—
The demo API is now available at we’ll fill them again in the next section.
http://127.0.0.1:8000. I recommend using Postman
https://www.getpostman.com to query the API. It’s
an API client on steroids that will drastically increase
Generating the Data Model
your productivity when developing and debugging. Interoperability of databases at web scale—also
known as the semantic web—is an old dream of
Go to http://127.0.0.1:8000/doc to browse the internet scientific and technical communities. Thanks
beautiful auto-generated API documentation and the to the joint efforts of search engine giants, universities,
sandbox. and the e-commerce industry (under the W3C), this
dream is coming true: its name is the Linked Data
Take a look at the content of the project directory. If movement.
you are familiar with Symfony, you will recognize the
directory structure of the well-known standard edition. Imagine querying in a single request most public
This is not a coincidence, as the API Platform edition databases on the Internet. Imagine a search engine
you just installed is a perfectly valid Symfony crawler able to easily index and find all publications of
app following Symfony’s best practices. If you are a given author across all public websites, or imagine
new to Symfony, you deserve some more detailed a price comparator extracting with ease products and
explanation: offers data from e-commerce websites or, more in sync
with our small project, imagine a CRM able to import
• The app/ directory contains configuration files
customer data from any source.
and assets (non PHP-code) for the project. It also
contains logs and a cache (inside directories of
To enable such possibilities, websites and apps
the same name).
must expose their data in a standard format (RDF/
• The bin/ directory stores command line tools
JSON-LD) and embrace a common vocabulary. If such
provided with API Platform. You’ll learn how to
prerequisites are met, it becomes possible to run
use the schema generator in a few lines. It also
queries on multiple data sources with languages like
contains the behat command.
SPARQL (see Related URLs). Open web standards and
• The features/ directory contains specifications
API Platform help a lot in all these fields, but let’s focus
of the API written in the Gherkin language (see
on the common vocabulary for now.
Related URLs) that will be executed by Behat; it
will not be covered in this article.
Schema.org is a vocabulary defining a lot of
• The src/ directory is the most important one, as
things frequently exposed on the web, including
it contains all the source code of your application.
publications, people, organizations, events, product,
For a typical app, your code will be stored in
offers, and reviews, among many, many other things.

www.phparch.com January 2016 | 7


Discovering API Platform

LISTING 1
These data models are well-designed and widely used,
01. types:
especially to add search engine readable structured
02. Person:
markup through microdata and to expose raw data
03. parent: false
(through APIs and large dumps such as DCPedia—see
04. properties:
Related URLs) in a common and popular vocabulary.
05. givenName: ~
06. additionalName: ~
Guess what? API Platform comes with a handy
07. familyName: ~
data model generator allowing the transformation
08. worksFor: { range: Corporation }
of Schema.org data models into a set of PHP entity
09. jobTitle: ~
classes, including:
10. gender: ~
11. address: { range: PostalAddress } • selected properties with corresponding mutator
12. telephone: ~ methods
13. faxNumber: ~ • relations
14. url: ~ • Doctrine ORM mappings (guessed from Schema.
15. description: ~ org type information)
16. internalScore: { range: Integer } • Symfony validation constraints
17. Corporation: • Full and extensive PHPDoc
18. parent: false • API Platform @Iri annotations (to use the
19. properties: Schema.org namespace during the JSON-LD
20. name: ~ serialization)
21. legalName: ~
Schema.org provides some types that will allow
22. address: { range: PostalAddress }
us to bootstrap our customer directory with the
23. email: ~
API Platform schema generator. The Schema.org
24. telephone: ~
website has a convenient list of all available types
25. faxNumber: ~
http://schema.org/docs/full.html. We will use this to
26. url: ~
bootstrap our data model generically:
27. description: ~
28. PostalAddress: • http://schema.org/Person: a person (a customer
29. parent: false for our app)
30. properties: • http://schema.org/Organization: an
31. addressCountry: { range: Text } organization (a company for our app)
32. addressLocality: ~ • http://schema.org/PostalAddress: an address
33. addressRegion: ~
Once you have chosen types applicable for your data
34. postOfficeBoxNumber: ~
model, report them in the app/config/schema.yml
35. postalCode: ~
file, see Listing 1.
36. streetAddress: ~
37. rdfa:
38. - https://raw.githubusercontent.com/schemaorg/schemaorg/sdo-phobos/data/schema.rdfa
39. namespaces:
40. entity: AppBundle\Entity
41. annotationGenerators:
42. - ApiPlatform\SchemaGenerator\AnnotationGenerator\PhpDocAnnotationGenerator
43. - ApiPlatform\SchemaGenerator\AnnotationGenerator\DoctrineOrmAnnotationGenerator
44. - ApiPlatform\SchemaGenerator\AnnotationGenerator\ConstraintAnnotationGenerator
45. - ApiPlatform\SchemaGenerator\AnnotationGenerator\DunglasApiAnnotationGenerator

In the types section of this file, all types you want to generate must be reported. For each type, also report the
list of properties you want (the default behavior is to generate all properties).

The generator is able to generate a class hierarchy (using abstract classes and the @MappedSuperclass
Doctrine annotation), but another solution—preferred here—is to disable the parent generation (with
parent: false) and directly include properties from the parent type in its child. Here the Thing type is ignored
(no related PHP class will be generated), but its name property will be generated directly in the Corporation
class. By doing so, we limit the number of generated classes and keep our data model simple. We also avoid the
numerous problems that can occur when dealing with Doctrine mapped superclasses.

8 | January 2016 www.phparch.com


Discovering API Platform

You can also see that we force the type of some properties by using the range keyword. This is necessary when
a type has several types: it’s perfectly valid from a RDF point of view, but can lead to problems and add extra
complexity in a PHP class model. The generator does not allow a property to have several types (by default, the
first one defined will be the one that’s picked). This feature is also useful when generating custom properties: the
internalScore property of the Person class is not part of Schema.org, but the generator allows you to define
custom fields. In such cases, specifying a type is mandatory.

Note: the development version of the generator also allows you to specify in the configuration file whether a
property can be null or not, whether it must be unique, and its Symfony Serializer @Groups annotation.

The rdfa section allows you to specify which RDFa files to extract the schema from. If omitted, it uses the Schema.
org production file but—at time of writing—this file is buggy, so the developement version is used here.

The development version of the schema generator contains another interesting feature: it can generate a data
model from any vocab defined in RDF, including the bunch of open domain vocabs referenced in Linked Open
Vocabularies (see Related URLs). It supports RDFa, JSON-LD, RDF/XML, RDF/JSON, Turtle, and N-Triples formats,
thanks to the underlying EasyRdf library (see Related URLs). It opens new horizons. For example, define your
whole data model in RDF, using Schema.org as the foundation and specialized with custom extensions, then
generate related PHP classes with the API Platform schema generator.

The namespace section indicates the PHP namespace for generated classes. Here we keep with Symfony best
practices but, as the generator is a standalone component, you can use it to generate PHP classes for any project,
using Symfony, another framework, or no framework at all.

Finally, some annotation generators are registered in the annotationGenerators section:


• PhpDocAnnotationGenerator: for PHPDoc annotations
• DoctrineOrmAnnotationGenerator: for the Doctrine ORM mapping
• ConstraintAnnotationGenerator: for Symfony validation constraints
• DunglasApiAnnotationGenerator: to add vocabularies metadata (IRI) exposed by API Platform’s API
bundle
If you need a custom annotation generator, just add it in this section.

It’s time to generate the PHP classes. Run the following command:

bin/schema generate-types src/ app/config/schema.yml

A working set of PHP classes will appear in the src/AppBundle/Entity directory. Open some files: you’ll
see that they respect PSR specifications and contain properties, getters, setters, exhaustive PHPDoc, Doctrine
mapping, annotation constraints, and API bundle metadata! API Platform saved us from a lot of tedious work.

Now create the project database:

app/console doctrine:database:create

And generate the tables related to the entities:

app/console doctrine:schema:create

Easy, isn’t it?

Keep in mind that this schema generator is only intended to bootstrap the data model. Then you will need to
write your domain specific types and properties, add more validation constraints, and tweak Doctrine mapping.
Once files are generated and you start to edit them, remove api-platform/schema-generator from your
composer.json file. It is not required at runtime.

www.phparch.com January 2016 | 9


Discovering API Platform

Creating the API


The next step is to expose the entities we just generated. Notice that the API bundle works with any PHP data
model; it is 100% independent of the schema generator. Doctrine ORM is also not a requirement. If you don’t use
it, you will need to create a custom data provider (see Related URLs).

To expose your entities through your API, you need to define a Resource service by entity class. Replace the
content of app/config/services.yml with the lines in Listing 2.

If you get an error, try running LISTING 2


app/console cache:clear. 01. services:
02. resource.person:
Guess what? You now have a fully 03. parent: "api.resource"
working API! Use your HTTP client 04. arguments: [ "AppBundle\\Entity\\Person" ]
(Postman if you followed my advice) 05. tags: [ { name: "api.resource" } ]
and go to http://127.0.0.1:8000. 06.
An updated entry point listing the 07. resource.corporation:
three available collections (people, 08. parent: "api.resource"
corporations, and postal addresses) 09. arguments: [ "AppBundle\\Entity\\Corporation" ]
should show up. 10. tags: [ { name: "api.resource" } ]
11.
Let’s play with your new API. Create a 12. resource.postal_address:
corporation by issuing a POST request 13. parent: "api.resource"
to http://127.0.0.1:8000/corporations 14. arguments: [ "AppBundle\\Entity\\PostalAddress" ]
with the following JSON raw body: 15. tags: [ { name: "api.resource" } ]

{
"name": "Les-Tilleuls.coop",
"description": "Professional services",
"email": "contact/les-tilleuls.coop",
"url": "http://les-tilleuls.coop"
}

I’m sure you spotted the error: the email is invalid. The API bundle automatically validates submitted data using
the Symfony Validator Component. Here the schema generator was smart enough to add an email validation
constraint. The API leverages them and returns the following:

{
"@context": "/contexts/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description":
"email: This value is not a valid email address.\n",
"violations": [
{
"propertyPath": "email",
"message": "This value is not a valid email address."
}
]
}

As you can see, the error is descriptive enough to be rendered client-side. As with all data returned by the API, it
is a valid JSON-LD document. It follows the error serialization format defined in the Hydra Core Vocabulary spec.

10 | January 2016 www.phparch.com


Discovering API Platform

Correct the email address and send the request again. you’ll see the output in Listing 3.
The output you see is:
The API bundle automatically paginates collections
{ and serializes them accordingly to Hydra’s Paged
"@context": "/contexts/Corporation", Collection specification. Navigate in the collection
"@id": "/corporations/1", using metadata present starting with the hydra prefix.
"@type": "http://schema.org/Corporation",
"address": null, Last but not least, API Platform automatically
"description": "Professional services", generates machine-readable documentation of all
"email": "[email protected]", exposed services. This description follows the Hydra
"faxNumber": null, standard.
"legalName": null,
"name": "Les-Tilleuls.coop", It enables the API to be auto-discovered by software
"telephone": null, agents aware of the Hydra specification. Give Hydra
"url": "http://les-tilleuls.coop" console a try (see Related URLs): install it and enter
} your API URL, and you’ll browse a 100 percent
dynamic, auto-generated administration interface
This time the new item was correctly saved in the for your API. If you want to play with the auto-
database. Again, the returned document is in JSON- discoverability of the API in a Javascript program, try
LD. It points to an automatically generated JSON-LD out hydra-core (see Related URLs), a library able to
context through the @context key. API Platform parse the exposed Hydra documentation.
automatically assigned an IRI to the document, based
on the database identifier: /corporations/1. This
identifier is unique across the Internet and enables Embedding Resources
this particular corporation to be identified in any other
When you play with your API, you’ll see that
system (internal or external). The API bundle also
retrieving a person (or a corporation) and the
leveraged @Iri annotations added by the schema
associated address requires two HTTP requests:
generator (or manually) to generate a @type key in the
they are separated collections. This is bad from a
JSON document referencing a Schema.org type.
performance point of view. But don’t panic: the API
bundle supports embedding relations (in read and
Thanks to the @type key, a software
write modes). To do so, you need to
supporting RDF, such as a triple store, will
know the type of the document LISTING 3
(http://schema.org/Corporation). 01. {
Thanks to the mapping defined in the 02. "@context": "/contexts/Corporation",
JSON-LD context, it will know—for 03. "@id": "/corporations",
instance—that the type of the email 04. "@type": "hydra:PagedCollection",
property is https://schema.org/email. 05. "hydra:totalItems": 1,
Such information allows SPARQL queries 06. "hydra:itemsPerPage": 30,
to be run against data coming from 07. "hydra:firstPage": "/corporations",
different databases using the Schema.org 08. "hydra:lastPage": "/corporations",
vocabulary. 09. "hydra:member": [
10. {
Thanks to API Plaform, you got all this 11. "@id": "/corporations/1",
next-generation semantic stuff for free, 12. "@type": "http://schema.org/Corporation",
with ease and powered only by open and 13. "address": null,
standard web technologies: raw JSON 14. "description": "ProfesSional services",
(all JSON-LD documents are valid JSON 15. "email": "[email protected]",
parseable with any language), HTTP, PHP, 16. "faxNumber": null,
MySQL, no Java ;)! 17. "legalName": null,
18. "name": "Les-Tilleuls.coop",
The API bundle supports POST, 19. "telephone": null,
PUT, DELETE, GET on items 20. "url": "http://les-tilleuls.coop"
and, more interestingly, GET 21. }
queries on collections. Go to 22. ]
http://127.0.0.1:8000/corporations— 23. }

www.phparch.com January 2016 | 11


Discovering API Platform

use Symfony serialization groups. Open the Person entity class and add the following use statement:

use Symfony\Component\Serializer\Annotation\Groups;

Do the same for the PostalAddress class, then add the following annotation to all properties of Person you
want to expose (including the address property) and all properties of the PostalAddress entity class you want
to be embedded in the Person JSON-LD document:
/**
* ...
* @Groups({"person_read"})
*/
private $address;

I’ve chosen to add the @Groups annotation to all properties of Person and PostalAddress except for $id (it is
automatically handled by the IRI system).

The last step is to indicate in the service definition which groups to use for the serialization (and optionally for the
deserialization). In app/config/services.yml, update the resource.person definition as shown:
resource.person:
parent: "api.resource"
arguments: [ "AppBundle\\Entity\\Person" ]
calls:
- method: "initNormalizationContext"
arguments: [ { groups: [ "person_read" ] } ]
tags: [ { name: "api.resource" } ]
Add an address by issuing a POST request to /postal_addresses:
{
"addressCountry": "FR",
"addressLocality": "Lomme",
"postalCode": "59160",
"streetAddress":
"Euratechnologies - bâtiment Canal, 2 rue Hegel"
}

Start a habit of Continuous Learning

Nomad PHP® is a virtual user group for PHP developers who


understand that they need to keep learning to grow professionally.

We meet online twice a month to hear some of the best speakers


Stay Current in the community share what they’ve learned.

Grow Professionally Join us for the next meeting – start your habit of continuous learning.
Stay Connected
Check out our upcoming meetings at nomadphp.com
Or follow us on Twitter @nomadphp

12 | January 2016 www.phparch.com


Discovering API Platform

Then add a person with this address and working for the corporation we added earlier with a POST to /people:

{
"givenName": "Kévin",
"familyName": "Dunglas",
"address": "/postal_addresses/1",
"worksFor": "/corporations/1",
"email": "[email protected]",
"internalScore": 5
}

You’ll get the following output when browsing to http://127.0.0.1:8000/person/1:

"@context": "/contexts/Person",
"@id": "/people/1",
"@type": "http://schema.org/Person",
"additionalName": null,
"address": {
"@id": "/postal_addresses/1",
"@type": "http://schema.org/PostalAddress",
"addressCountry": "FR",
"addressLocality": "Lomme",
"addressRegion": null,
"postalCode": "59160",
"postOfficeBoxNumber": null,
"streetAddress": "Euratechnologies - bâtiment Canal, 2 rue Hegel"
},
"description": null,
"familyName": "Dunglas",
"gender": null,
"givenName": "Kévin",
"jobTitle": null,
"telephone": null,
"url": null,
"worksFor": "/corporations/1",
"internalScore": 5
}

You retrieved the person document with its associated address in one request!

Adding HTTP Cache Headers


API Platform is bundled with the awesome FosHttpCache library (see Related URLs). Using
an expiration strategy is the easiest way to improve LISTING 4
the performance of our API and reduce the server load. 16. # FosHttpCache
FosHttpCache supports more powerful strategies, such as 17. fos_http_cache:
tag-based invalidation, but for now we will just tell clients 18. cache_control:
to cache all data returned by the API 1 minute. Add the 19. rules:
lines in Listing 4 to app/config/config.yml: 20. -
21. match:
As our API is intended to require authentication, we 22. path: ^/$
disable public caching using software such as reverse 23. headers:
proxies. If the exposed data is public, be sure to mark it 24. cache_control:
as such and put something like Varnish in front of your API. 25. public: false
That way your API will be able to support very high loads: 26. max_age: 60
the reverse proxy will absorb most of the traffic. 27. etag: true

www.phparch.com January 2016 | 13


Discovering API Platform

If you inspect HTTP headers sent by the API, you’ll now see a Cache-Control and an E-tag header. If your client
honors such headers, data will be stored in cache for 1 minute.

Adding a JWT Authentication Mechanism


Finally, we will make our new API private and require users to authenticate with JSON Web Token. To do so, we
will use the Symfony Security Component in conjunction with another great bundle from the Symfony ecosystem:
LexikJWTAuthenticationBundle (see Related URLs).

This bundle is not shipped directly with API Platform; you need to install it through Composer:

composer require lexik/jwt-authentication-bundle

Then register it in the app/AppKernel.php file:

public function registerBundles()


{
$bundles = [
// ...
// all on one line:
new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
new AppBundle\AppBundle(),
];
// ...
}

Basically, the concept behind JWT is to send a signed token to the client. Then the client must send this token at
each request and the server will verify its validity. To do so, the bundle requires public and private OpenSSL keys.
Generate them:

mkdir -p app/var/jwt
openssl genrsa -out app/var/jwt/private.pem -aes256 4096
openssl rsa -pubout -in app/var/jwt/private.pem -out app/var/jwt/public.pem

Configure the bundle in app/config/config.yml:

lexik_jwt_authentication:
private_key_path: %kernel.root_dir%/var/jwt/private.pem
public_key_path: %kernel.root_dir%/var/jwt/public.pem
pass_phrase: %jwt_key_pass_phrase%
token_ttl: 86400

To not hard code the secret key, we create a new parameter. Fill the jwt_key_pass_phrase with the pass
phrase chosen during the OpenSSL key generation. Do it only in app/config/parameters.yml and never in
app/config/parameters.yml.dist, as this secret phrase must not be versioned.

parameters:
#...
jwt_key_pass_phrase: MySecretPassPhrase

Register the authentication route in app/config/routing.yml:

api_login:
path: /login

14 | January 2016 www.phparch.com


Discovering API Platform

Finally, the Symfony firewall must be configured. Our users will be stored in a config file. There are two types of
users, a regular and an admin. The regular user can read data but has no modification credential. The admin can
do everything. Anonymous users are not allowed to access the API at all.

Replace the content of app/config/security.yml with Listing 5.

In the providers section we configure the Symfony Security Component to use the list of users defined in our
parameters file. Passwords must be hashed with the bcrypt algorithm (encoders section). Add users in your
app/config/parameters.yml like this:

parameters:
# ...
users:
olivier:
password: '$2y$12$uTd0HB/cfygjzVEQm28FRuirIaRctp7uUBnvOdQFsl0/MhmPqwBi.'
roles: 'ROLE_USER'
kevin:
password: '$2y$12$uTd0HB/cfygjzVEQm28FRuirIaRctp7uUBnvOdQFsl0/MhmPqwBi.'
roles: 'ROLE_ADMIN''

Here we defined a regular user (role ROLE_USER) with olivier as username and an admin named kevin. The
password must be encoded using the app/console security:encode-password Symfony command (here it’s
test for both).
LISTING 5
Three firewalls are defined in
the firewalls section: 28. security:
29. providers:
• dev: no security at all, 30. in_memory:
used for routes available 31. memory:
only in dev environment, 32. users: %users%
such as the profiler 33.
• login: anonymous 34. encoders:
access allowed to the 35. Symfony\Component\Security\Core\User\User:
JWT endpoint allowing 36. algorithm: bcrypt
retrieval of a valid token 37. cost: 12
• api: all other URLs 38. firewalls:
require a valid JWT token 39. dev:
to be accessed 40. pattern: ^/(_(profiler|wdt|error)|css|images|js)/
41. security: false
42. login:
43. pattern: ^/login$
44. stateless: true
45. anonymous: true
46. form_login:
47. check_path: /login
48. success_handler:
49. lexik_jwt_authentication.handler.authentication_success
50. failure_handler:
51. lexik_jwt_authentication.handler.authentication_failure
52. require_previous_session: false
53. api:
54. pattern: ^/
55. stateless: true
56. lexik_jwt: ~
57. access_control:
58. - { path: ^/, roles: [ROLE_USER, ROLE_ADMIN], methods: [GET] }
59. - { path: ^/, roles: ROLE_ADMIN, methods: [POST, PUT] }

www.phparch.com January 2016 | 15


Discovering API Platform

Finally, in the access_control section we define that regular users can access all URLs with the GET method,
but all other methods are available only for admins. As for cache header definition, the REST architecture allows
security and cache policies to be configured very easily, based on URL patterns and HTTP methods.

Restart the server, and try to access any endpoint of the API: you will get a 401 HTTP status code and the
following error:

{"code":401,"message":"Invalid credentials"}

To get a valid JWT token, send a POST request to http://127.0.0.1:8000/login with two parameters encoded as
form data (unlike other endpoints, the JWT endpoint takes standard form data as input—no raw JSON documents):
_username containing the username and _password containing the raw password.

The JWT endpoint will return a JSON document similar to:

{"token":"header.payload.signature"}

Use the token value (and not the whole JSON document) to access the API. Just add an Authorization HTTP
header with Bearer the.full.token as value. Keep in mind that JWT is a stateless (cookie-less) authentication
mechanism, which means that you must add this header to every issued request.

Conclusion
In this introduction you discovered that API Platform eases the development of modern and extremely powerful
REST APIs. We mostly used RAD features allowing us to bootstrap a working hypermedia API in a very short time.

But API Platform comes with more advanced features, including collection filtering and sorting, a powerful event
system, and the ability to create custom operations and controllers. It is designed in a way that allows you to hook
your custom code almost everywhere. Give it a try!

KÉVIN DUNGLAS is the founder of Les-Tilleuls.coop and a freelance software


architect. He is a member of the Symfony core-team, the creator of API
Platform and have contributed to more than 100 open source projects
including the PHP language, ZendFramework, Doctrine, Sonata and
Prestashop.

Twitter: @dunglas

16 | January 2016 www.phparch.com


2016
php[tek]

The Premier Professional


PHP Conference
with a Community Flair
2016
July 17th-23rd, 2016
cruise.phparch.com Saint Louis
May 23rd - 27th
tek.phparch.com

2
FEATURE

Build APIs on Existing Web


Applications with Apigility
Michelangelo van Dam

DisplayInfo()

Requirements: Apigility is an API Builder,


Minimum requirement designed to simplify
• PHP 5.3.23 or higher
Recommended requirement
creating and maintaining
• PHP 5.4.8 or higher (to use built-in PHP web server) useful, easy to consume,


Other Software:
Apigility—http://www.apigility.org
and well-structured
• MySQL 5.0+ APIs. Regardless of your
• SQLite 3.6+
experience in API-building,
Related URLs:
with Apigility you can
• PHP—http://php.net
• MySQL—http://www.mysql.com build APIs that enable


PECL/PDO—http://php.net/pdo
Silex—http://silex.sensionlabs.org
mobile apps, developer
• Slim—http://www.slimframework.com communities, and any


Frapi—https://frapi.github.io
ZendCon—http://zendcon.com
other consumer-controlled
access to your applications.
18 | January 2016 www.phparch.com
Build APIs on Existing Web Applications with Apigility

Theialive Homepage FIGURE 1


We Already Have a Web
Application!
Let’s go back to the beginning of 2012. One of our
customers had a Zend Framework 1 application for
simple task management called TheiaLive; Figure 1
shows the homepage. REST APIs were a novelty at
the time and nobody really took notice of it.

Fast-forward to early 2013. The Internet was


moving toward a mobile consumer market and our
customer was eager to also move in that direction.
They asked me if our company could provide
an API so an external company could develop a
native mobile app.

Our first idea was to build it on our own using


a microframework like Slim, Silex, or plain PHP.
We had spent months building a REST-ish API
service on the side of our application, but ended up Apigility Announcement FIGURE 2
throwing it all away when Apigility was announced.
We hadn’t really thought about how to handle
versioning, providing hypermedia links, or having
documentation updated after each change.

We also looked at a variety of API managers


like Frapi to handle our REST API needs. Frapi
http://getfrapi.com is a native API framework
designed to build, maintain, and secure RESTful
endpoints. At first, Frapi seemed to be a perfect fit,
but we ended up missing some essential abilities to
reuse what we had already prepared in the original
application.
• event authentication using HTTP Basic/Digest or
OAuth2
The Big Announcement
It even provides API documentation (HTML,
Apigility was first announced at ZendCon Swagger)!
2013 by Matthew Weier O’Phinney
(http://twitter.com/mwop) during one of the
keynotes. It was immediately obvious that Apigility Setting Up Apigility
was a game-changer for everyone building and
When we started using Apigility we were a bit at a
managing APIs. Figure 2 shows my reaction on Twitter
loss: How could we integrate our old Zend Framework
http://phpa.me/tw-dragonbe-apigility.
1 application into this fancy Zend Framework 2 API
manager without having to duplicate our business
Apigility is, in essence, an API front end, but comes
logic all over? We first tried to tie Apigility into our
with a full-featured management interface. Apigility
existing application, but this didn’t work out well
provides RESTful or RPC services with JSON. Some of
as we got into conflict between the class name
its features include:
namespacing logic of ZF1 and the native PHP
• Hypertext Application Language (HAL) to offer namespace usage of ZF2. So we decided to set up
in-response links and resources Apigility on its own and bootstrap our ZF1 application
• selective HTTP verbs (GET, POST, PUT, DELETE, into the API manager. That worked out a lot better.
PATCH, etc.) access control
• application versioning through URI routes (e.g. / Getting Apiglity was easy. There are multiple ways
v1/user/1234) or through HTTP headers to install it, including from a release .tar file or using
• normalization and content validation, composer. All we needed to do was use the following

www.phparch.com January 2016 | 19


Build APIs on Existing Web Applications with Apigility

command to get the sources:

cd /path/to/workspace
curl -sS https://apigility.org/install | php Apigility Welcomes You FIGURE 3
After installation, Apigility was
launched on the built-in PHP web
server running on port 8888, so all
you had to do was point your browser
to http://localhost:8888/ to see
the nice welcome screen of Apigility.

Note for Very Old PHP


If you run a PHP version below 5.4,
you need to define a virtual host for
your web server; for Apache, it would
look like the configuration in Listing 1.

For other web servers, LISTING 1


please consult that web
server’s documentation to 01. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
configure a virtual host listening on port 02. # Configuration for Apigility #
8888 so the examples in this article will 03. # File: /etc/apache/vhosts/apigility.conf #
be similar to your own setup. Make 04. # #
sure the Apigility directory and 05. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
underlying files are writeable by 06. # #
your web server! 07. # WARNING: THIS IS DEVELOPMENT CONFIGURATION, DO NOT USE #
08. # IN A PRODUCTION ENVIRONMENT!!! #
09. # #
Pulling in the Sources 10. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
11.
of Our Web App 12. # Define the listen directive to match the examples
Once we had Apigility up and running, 13. Listen 8888
we still needed to have the API manager 14.
reusing the logic we already build in our 15. # Ensure Apache listens for named requests at port 8888
original application. Since Apigility uses 16. NameVirtualHost *:8888
Composer for managing its required 17.
packages, we decided to have our 18. # Define the virtual host configuration
web app’s source code be pulled in the 19. <VirtualHost *:8888>
vendor directory. 20. ServerName apigility.local
21. ServerAlias www.apigility.local
First we’re going to make the 22.
installation of Apigility a Git project. 23. DocumentRoot "/path/to/apigility/public"
24.
cd /path/to/apigility 25. <Directory "/path/to/apigility/public">
git init 26. Options Indexes FollowSymlinks
27. AllowOverride All
There are various ways to pull our 28. Order allow,deny
application sources into the Apigility 29. Allow from all
project, but the easiest way for us was to 30. DirectoryIndex index.php
add our repository using gitmodules. 31. </Directory>
32.
33. ErrorLevel Info
34. ErrorLog /var/log/apache/apigility-error_log
35. CustomLog /var/log/apache/apigility-access_log common
36. </VirtualHost>

20 | January 2016 www.phparch.com


Build APIs on Existing Web Applications with Apigility

WARNING: This is a private repository! Do not try to pull it in; use your own application for the purpose of
following along.

git submodule add -f git@server:mvdam/theialive.git vendor/theialive

Avoiding Collisions
LISTING 2
Since our application was written
in Zend Framework 1 and Apigility is 01. <?php
based on Zend Framework 2, we need 02.
to make sure both applications are 03. // Define path to application directory
not colliding with each other, as both 04. defined('APPLICATION_PATH')
use APPLICATION_PATH to define the 05. || define('APPLICATION_PATH',
location of the application. 06. realpath(__DIR__ . '/application'));
07.
Simply change APPLICATION_PATH 08. // Define application environment
to APIGILITY_PATH in the 09. defined('APPLICATION_ENV')
public/index.php bootstrap file 10. || define('APPLICATION_ENV',
using sed. 11. (getenv('APPLICATION_ENV') ?
12. getenv('APPLICATION_ENV') : 'production'));
sed -i '' 's/APPLICATION_PATH/'. 13.
'APIGILITY_PATH/g' 14. // Ensure library/ is on include_path
public/index.php 15. set_include_path(implode(PATH_SEPARATOR, array(
16. realpath(APPLICATION_PATH . '/../library'),
17.
Autoloading ZF1 18. )));
get_include_path(),

We have imported our web application 19.


using gitmodules, but that doesn’t 20. require_once 'Zend/Session.php';
mean that our application will be 21. Zend_Session::start();
automatically autoloaded. We need to 22.
bootstrap our ZF1 application without 23. require_once 'Zend/Config/Ini.php';
running it. We can copy most of our 24. $config = new Zend_Config_Ini(
vendor/theialive/public/ 25. APPLICATION_PATH . '/configs/application.ini',
index.php except the run() part. We 26. APPLICATION_ENV,
call this loader zf1for2.php and save 27. array ('allowModifications' => true)
it in the directory vendor/theialive, 28. );
see Listing 2. 29.
30. if (file_exists(APPLICATION_PATH . '/configs/local.ini')) {
Now we need to have Apigility 31. $localConfig = new Zend_Config_Ini(APPLICATION_PATH
autoload our application, so in the 32. . '/configs/local.ini');
Apigility public/index.php file we 33. $config->merge($localConfig);
just add this line: 34. }
35. $config->setReadOnly();
require_once __DIR__ . 36.
'/../vendor/theialive/' . 37. /** Zend_Application */
'zf1for2.php'; 38. require_once 'Zend/Application.php';
39.
40. // Create application, bootstrap, and run
41. $application = new Zend_Application(
42. APPLICATION_ENV,
43. $config
44. );
45. $application->bootstrap();

www.phparch.com January 2016 | 21


Build APIs on Existing Web Applications with Apigility

Theialive UML Use Case FIGURE 5


Authentication
Setting Up Our
First Endpoint Authenticate
uses View
Dashboard
uses
Update Profile

Now that the preparation


is done, it’s time to build Time Registration
the REST API. There are
several things we can Register Time uses
reuse from our existing
uses
application: Staff Select Task Select Project

• authentication Add uses


• listing of projects Description
• creating and
updating projects
• listing of tasks
Authentication
• creating and
updating tasks uses View uses Manage
Authenticate
Dashboard Profiles
To give you a better idea
of how all the components
work together, have a look Time Registration
at this basic UML Use Case
Manage
diagram, see Figure 5. Reports uses
uses Manage
Let’s start with Manager Manage Tasks
Projects
listing our projects uses
first. When we go back to Retrieve KPTs
http://localhost:8888 we see
the Apigility welcome screen. We’re creating a
new API, so we click on the New API button to
get started.

A dialog window pops up where we can fill out the name of our API, which we will call “theialive”. We click on
the Create button to continue.

The API management window now appears, allowing us to define our API endpoints.

There are two options we can use: REST or RPC; we choose REST as we’re building direct services to our existing
business logic. We click on create a new one within the REST section of the window.

New API FIGURE 6

New API title FIGURE 7

22 | January 2016 www.phparch.com


Build APIs on Existing Web Applications with Apigility

RPC is a good choice when your application requires tight coupling with the distributed architecture you build,
especially when you require quick and reliable communications between client and server(s). Most often you will
see payment processing, ordering systems, and medical applications preferring RPC over REST. Since this is not a
requirement for our use case, REST is our preferred choice.
New API window FIGURE 8

New REST service FIGURE 9

A dialogue screen pops up asking us to provide a New REST service popup FIGURE 10
name for our endpoint. We still can choose between
REST, RPC, and DB Connected. It’s true that all our data
is stored inside a database, but because we have a set
of business rules added to the retrieval of the data in
our web application, we’re going to choose the REST
endpoint.

www.phparch.com January 2016 | 23


Build APIs on Existing Web Applications with Apigility

Once we click on Create Service a skeleton REST service is generated for our endpoint. This window is where
we define what and how we will be retrieving our data.

New REST service window FIGURE 11

Let’s have a look at the options we can see in this window. First of all there are General Settings that give you an
idea about routing, parameters, and HTTP verbs to allow. We can leave the default options as they are.

Content negotiation defines how we negotiate with the client. In our case, we used the default settings as they
were more than sufficient for our goal.

The benefit of this default is that it offers the content type application/hal+json out of the box. This allows
application developers to use content links and resources provided with the response to auto-discover these
links and resources.
For mobile Project general settings FIGURE 12
development,
this means the
API endpoints can
change, but the
application itself
doesn’t need to be
updated as it uses
the links provided
in the response
to figure out the
location of the
resources created
or used.

24 | January 2016 www.phparch.com


Build APIs on Existing Web Applications with Apigility

More information about Hypertext Application Language and the usage of application/hal+json can be
found at http://stateless.co/hal_specification.html.

Project content negotiation FIGURE 13

Fields allows you define specific fields required in the client’s request. But as we’re only going to list our projects,
we don’t need to touch it now.

API project fields FIGURE 14

Authorization allows you to define specific ACLs on the endpoint and per HTTP verb. Since we’re going to use the
authorization from our original web application, we can skip this step, too.
API project authorization FIGURE 15

www.phparch.com January 2016 | 25


Build APIs on Existing Web Applications with Apigility

The Documentation tab is a great addition to Apigility as it allows you to create documentation about the
endpoint you’re managing. When a client connects to the endpoint and does not fulfill your specified
requirements, this documentation is returned in the error response to guide the user in making a successful
request.

Project documentation FIGURE 16

Of course, this documentation is also a nice feature to have when you need to provide user documentation or an
application manual to the mobile app development team.

Source code tab FIGURE 17

26 | January 2016 www.phparch.com


Build APIs on Existing Web Applications with Apigility

Code Editing FIGURE 18

The really important step now is in


the Source code tab, where you
can view the source code behind the
newly created endpoint.

This is the moment where we’re


going to connect our existing
business logic with Apigility. So open
up your favorite editor or IDE and
open the Resource Class that’s
mentioned as file location above the
source code.

LISTING 3
37. <?php
38. /**
39. * Fetch a resource
40. *
41. * @param mixed $id
42. * @return ApiProblem|mixed
43. */
44. public function fetch($id) {
45. $project = new \Project_Model_Project();
46. $projectMapper = new \Project_Model_ProjectMapper();
47. $projectMapper->fetchRow($project, [
48. // Change later in proper authentication
49. 'accountId = ?' => 1,
50. 'projectId = ?' => $id,
51. ]);
52. return $project->toArray();
53. }
54.
55. /**
56. * Fetch all or a subset of resources
57. *
58. * @param array $params
59. * @return ApiProblem|mixed
60. */
61. public function fetchAll($params = array())
62. {
63. $projectCollection = new \Project_Model_Collection();
64. $projectMapper = new \Project_Model_ProjectMapper();
65. $projectMapper->fetchAll(
66. $projectCollection,
67. '\Project_Model_Project',
68. array (
69. // Change later in proper authentication
70. 'accountId = ?' => 1,
71. )
72. );
73. return $projectCollection->toArray();
74. }

www.phparch.com January 2016 | 27


Build APIs on Existing Web Applications with Apigility

In apigility/module/theialive/
src/theialive/V1/Rest/Project/
ProjectResource.php you need to locate two methods that require a code change:
• fetch($id)
• fetchAll($params = array())
Listing 3 contains the full change in detail.

After the source code has been changed, you can use a REST client to test the endpoints. Just point your client to
http://localhost:8888/project for the collection of projects and http://localhost:8888/project/4 to
see the details of the project with ID 4.

Figure 19 has both calls with curl and their responses.

We’re Not Done Yet!


Of course we’re far from done at this point. But this is the basis for rapidly providing a REST API for your existing
application.

Authentication is probably something you need to see at the moment you start implementing it. We used a
user ID in the source code above, but we prefer to use an API token that is provided by Apigility’s authentication
management. However, authentication can quickly become very complex, so that’s
something you should figure out on your own. If
cURL call output FIGURE 19

28 | January 2016 www.phparch.com


Build APIs on Existing Web Applications with Apigility

you already have a solid authentication service update if it was older. It soon backfired, as some users
implemented, you can just tap into that service were not able to upgrade because of IT policies on
as we did with fetching project lists and details. their mobile devices. So we kept all versions active and
If not, you can choose to build one of your own backported security fixes to older versions.
or to use OAuth providers. The documentation
for Apigility has devoted a complete chapter on Deploying your Apigility REST API is similar to
choosing and implementing authentication. See deploying your other applications. One thing you
https://www.apigility.org/documentation/auth/intro need to watch out for is making sure you don’t deploy
for more information. it in development mode. By providing the following at
the command line you can disable development mode
Updating and creation of new entities is exactly the if required.
same, but instead of receiving data from a form on
the web application, the data now comes as a POST php public/index.php development disable
request with a JSON body. After json_decode you
have essentially the same data as you would get from Since Apigility is using the resources from your
your form, so it should be a breeze to implement that existing web application, it means that the server that
as well. runs Apigility requires the same access resources as
your web application. It wouldn’t be the first time to
have everything was ready to go, only to find out that
Important Considerations the web server running Apigility was not authorized to
access the database server.
Depending on your needs, you can run multiple
versions at the same time. But the moment a version
is released and in use by clients, you should be aware Further Steps
that you can’t break backwards compatibility. As long
as you don’t change the exposed endpoint behavior, I’ve become a passionate fan of Apigility, and thanks
you can continue development in the same version. to the efforts I made back in 2013 I’m adding REST
We didn’t want to take any risks and started a new services to legacy applications as an intermediate
version the moment a new version of the mobile app way to upgrade them from Zend Framework 1 to Zend
was released. Framework 2. But with DB Connected functionality I
can even attach Apigility to Java and .NET applications.
Running multiple versions allowed us to support
users who updated their mobile app to the latest If you like to know more about this or
version, but also users who are running an older have some questions, please contact me at
version of the app. Together with the external party [email protected]. I’m known not to respond
that developed the mobile app, we agreed that quickly, but your mail will get a response from me.
we would only support up to three versions Or come and see me at a PHP user group meeting or
and that the app would require an conference somewhere on this planet.

MICHELANGELO VAN DAM works at in2it as a professional PHP consultant assisting


businesses to automate their development processes, improve their code
quality and train their development teams. Michelangelo is also president of
the PHP user group PHPBenelux where he and a team organizes monthly
meetups, development events and an annual conference. He’s a devoted
member of the global PHP community and contributes to open-source
projects, mainly PHP related.
In his spare time, Michelangelo likes to spend all his time with his wife and 3
sons.
Twitter: @dragonbe

www.phparch.com January 2016 | 29


FEATURE

Writing Better CLI Tools with


Symfony Components
Juan Manuel Torres

DisplayInfo()

Requirements:
• PHP: 5.3+
• Composer
• Symfony Console Component—http://phpa.me/symfony-console-component
• Symfony Yaml Component—http://phpa.me/symfony-yaml-component
Related URLs:
• Symfony Best Practices—http://phpa.me/symfony-best-practices

Introduction
Symfony is awesome! Why? Because it is a “set of [decoupled,] reusable components.” When these com-
ponents work together as a cohesive unit, they become applications that are easy to maintain and extend. By
using Symfony components, you can quickly create great applications without having to bootstrap an entire
framework. In this article, we will learn to use some components to empower you to write better applications
faster—starting with the Symfony Console.

30 | January 2016 www.phparch.com


Writing Better CLI Tools with Symfony Components

Why should you care about or use components if you Interoperability


can just use a framework? There are several reasons:
If functions and classes can work in isolation and with
• As dependencies for libraries you intend to reuse others to create complex behavior, we can easily reuse
in multiple projects our code in different projects.
• In a new project that does not require a
framework A few years ago, the Framework Interop Group
• When refactoring old code (FIG) was established with the purpose of promoting
• To learn how to write better code collaboration between various PHP projects. They
have created a series of standards/recommendations
While the first three are practical approaches, the last
aimed at helping themselves and others write code
one is a bonus that requires a bit of curiosity and a few
that can be shared.
key concepts, which I will introduce below.
Developers who follow the Basic Coding Standard
Clean Code, Cohesion, and PSR-1 and the Coding Style Guide PSR-2 can write
Interoperability code that is and looks consistent across different
projects. PSR-0 and PSR-4 are autoloading standards/
Clean Code recommendations for autoloader interoperability.
If your projects follow one of these standards (PSR-
One of the books that has influenced how I code
0 is deprecated, so use PSR-4), your code can be
is Clean Code: A Handbook of Agile Software
loaded into any project that uses a PSR-4-compliant
Craftsmanship, by Robert C. Martin. In his book,
autoloader.
Martin presents many best practices, from how to
name variables to how classes should be, and he even
Unfortunately, while the standards address some
gives the reader pointers for identifying “smells” in the
practical aspects of coding and interoperability,
code. Following these practices will result in code that
they do not address high-level design to make code
is easy to read, maintain, and extend: Clean Code.
cohesive.

Cohesion Here is where Symfony shines! All components are


When writing clean code, one must move well-designed, well-written, and tested. You can use
away from practices that lead to inappropriate many of the components independently or together;
relationships between sets of code that have therefore, they can be used to build new applications
different responsibilities. A simple example of such or frameworks, as well as to improve existing ones.
relationships is when views mix database queries in The more you learn how to use Symfony and how it
their code. works internally, the more you learn how to design and
write better, cleaner applications.
Another example is when dependencies (classes
or functions that we create or install from third-party Introduction to the Symfony
libraries) intermingle with our code to the point that it
is hard to make changes without breaking it. We say Console Component
that these dependencies are tightly coupled, and PHP, as a general-purpose scripting language, can
the result is a big bowl of spaghetti code. be used to create CLI applications. The CLI officially
became a SAPI in version 4.3.0, which was released
Creating and using smaller single-responsibility in December 2002. As you can see, PHP and the CLI
classes that can be used in isolation and interact go way back, and its popularity continues to rise,
with others through well-defined interfaces is a best especially with tools like the Symfony Console.
practice and an important feature of clean code. The
result is loosely coupled code that can work together If you have never directly used the Symfony Console
with others to achieve system behavior, referred to as in your applications, chances are, you may have
cohesion. used it indirectly through other PHP projects such as
Composer, Laravel, or Behat, as all of these projects
Learn more about Clean Code in Martin’s book, or use it.
Google “SOLID principles.”
The Console component gives developers the ability
to quickly create amazing CLI applications. Let’s
take a look at how to create, test, and extend a new
application.

www.phparch.com January 2016 | 31


Writing Better CLI Tools with Symfony Components

To install it, use Composer: It is possible to define multiple arguments. If so, they
must be used in the same order in which they are
composer require symfony/console defined. The InputArgument::ARRAY mode is used
to allow any number of arguments. These arguments
Basic Console Concepts get parsed as an array, and for this to work correctly, it
must be defined as the last argument of the command.
There are two main classes that we use to assemble
a CLI application. The first one is the
Command class. Command is where we
LISTING 1
are going to define how commands are 01. <?php
configured (what arguments and options 02. //app
are available) and how they behave (the 03. require_once __DIR__.'/vendor/autoload.php';
code that is executed). 04.
05. use Symfony\Component\Console\Application;
The second is the Application class. 06. use Symfony\Component\Console\Command\Command;
It is used to add commands to the 07. use Symfony\Component\Console\Input\InputArgument;
application, and its responsibility is to run 08. use Symfony\Component\Console\Input\InputInterface;
the commands. 09. use Symfony\Component\Console\Output\OutputInterface;
10.
Creating a Simple Command 11. $say = function(InputInterface $in, OutputInterface $out) {
12. $phrase = $in->getArgument('phrase');
The first example is a simple say 13. $out->writeln($phrase);
program. The command takes an 14. };
argument, and it sends it to the given 15.
output. If there is no argument, 16. $sayCommand = new Command('say');
the command uses a default value: 17. $sayCommand->addArgument(
"I don't have anything to say". 18. 'phrase',
19. InputArgument::OPTIONAL,
The code needed for this simple 20. 'The phrase you want to say',
application is in Listing 1, which I’ve 21. 'I don\'t have anything to say');
saved as the file app. In line 16, a 22. $sayCommand->setCode($say);
new command object is instantiated. 23.
The name of the command is the first 24. $application = new Application();
argument of the constructor; as an 25. $application->add($sayCommand);
alternative, you can use the setName. 26. $application->run();

Command Arguments
The code that is executed by the command can be
Line 17 defines an argument for the command. set using the setCode method. This method only
Arguments are parameters that come after the accepts callables. Later in this article, an alternative
command name. The addArgument method takes four way to implement the code for the command will be
parameters: introduced. For now, this makes it simple to visualize
what is happening.
1. The argument name (required)
2. Argument mode: either The closure takes two parameters: $in (input) and
$out (output). The function uses the input object
• InputArgument::REQUIRED
to get the argument phrase that we defined in the
• InputArgument::OPTIONAL
command. Then the argument value is used to write it
• InputArgument::ARRAY
back to the screen using the output writeln method.
3. Argument description (optional)
4. Argument default value (optional) You can set custom input and output
objects by passing them as parameters of the
Application::run method.
NOTE: While the description parameter is optional,
you should make a mental note to always add it to all
of your arguments, as it is a good way to document
and remember what commands do.

32 | January 2016 www.phparch.com


Writing Better CLI Tools with Symfony Components

CLEAN CODE: The parameters passed to the closure must be instances of classes that implement the
InputInterface and OutputInterface. While the command depends on input and output, it does not depend
on a single implementation. Instead, different implementations can be used (think of an output that can write to a log
file or one that suppresses all outputs), and the command code should continue to work.

Finally, we instantiate the application (line 24), add the command (line 25), and run it (line 26).

To execute the code from the command line, you can type the following:

php app say 'Hello, Console!'

Also, try the following commands:

php app say


php app say --help

Moving Our Command to a Custom Class


Prefatory Matters
Before we continue diving into the code, I want to make sure that there is an understanding of how the files
are organized in this project. The app file remains at the root of the project. We will place all commands in
src/Command and the resources (files, configuration, and others) in src/Resources.

project
├─ app
└─ src/
├─ Command/
│ ├─SayCommand.php
│ └─OtherCommands.php
└─ Resources/
├─files/
└─config/

To load the classes from these locations, add an


autoloading mapping to the composer.json file and
don’t forget to issue a composer dump-autoload
to rebuild the autoloader:

{
"require": { ... },
"autoload": {
"psr-4": {
"PHPArch\\": "src/"
}
}
}

Say Command Class


In most cases, when creating console commands,
you would use custom classes that extend the
Command class. There are several reasons to use a

CLEAN CODE: Separate concerns, and keep


commands “thin,” as you would with a controller in
a framework.

www.phparch.com January 2016 | 33


Writing Better CLI Tools with Symfony Components

LISTING 2
class, rather than what we did in Listing 1. Mainly,
the command is contained in a single place where it
01. <?php
would be easier to maintain, test, and reuse.
02. // src/Command/SayCommand.php
03. namespace PHPArch\Command;
Listing 2 shows the same code from Listing 1, but it is
04.
now in the new class SayCommand.
05. use Symfony\Component\Console\Command\Command;
06. use Symfony\Component\Console\Input\InputArgument;
We place the code to configure the command in a
07. use Symfony\Component\Console\Input\InputInterface;
convenient method called configure and the code for
08. use Symfony\Component\Console\Output\OutputInterface;
the command in the execute method.
09.
10. class SayCommand extends Command
The app file can be improved by removing the extra
11. {
code and adding the brand-new command to the
12. protected function configure()
application.
13. {
14. $this->setName('say')
LISTING 3 15. ->setDescription('Write a short quote.')
16. ->addArgument(
01. <?php
17. 'phrase',
02. //app
18. InputArgument::OPTIONAL,
03. require_once __DIR__.'/vendor/autoload.php';
19. 'The phrase you want to say',
04.
20. 'I don\'t have anything to say'
05. use PHPArch\Command\SayCommand;
21. );
06. use Symfony\Component\Console\Application;
22. }
07.
23.
08. $application = new Application();
24. public function execute(
09. $application->add(new SayCommand());
25. InputInterface $input,
10. $application->run();
26. OutputInterface $output)
27. {
Testing the Say Command 28. $phrase = $input->getArgument('phrase');
The class can easily be tested by using the tools 29. $output->writeln($phrase);
provided by Symfony. The command tester is a 30. }
class that enables you to check if the output of the 31. }
command is correct.
LISTING 4
Listing 4 shows a simple PHPUnit test that
would go in a SayCommandTest class. 01. <?php
02. // test/TestSayCommand.php
Here, we use the application find method to 03. namespace PHPArch\Test;
get a command that gets passed to the tester. 04.
Then, we can use the PHPUnit assertRegExp 05. use PHPArch\Command\SayCommand;
method to test for the correct output of the 06. use Symfony\Component\Console\Application;
command. 07. use Symfony\Component\Console\Tester\CommandTester;
08.
09. class SayCommandTest extends \PHPUnit_Framework_TestCase
Creating a More 10. {
11.
Interesting Application 12.
public function testSayNothing()
{
In *nix systems, you can install a program 13. $application = new Application();
called Fortune. Fortune is a program that picks 14. $application->add(new SayCommand());
a “fortune” from a pool of quotes and prints it to 15.
the standard output. 16. $command = $application->find('say');
17. $tester = new CommandTester($command);
18. $tester->execute(['command' => $command->getName()]);
19. $this->assertRegExp(
20. '/I don\'t have anything to say/',
21. $tester->getDisplay());
22. }
23. }
34 | January 2016 www.phparch.com
Writing Better CLI Tools with Symfony Components

LISTING 5
While you may have interesting things to write in
01. <?php
the SayCommand, we are going to be a bit lazy and
02. // src/Command/FortuneCommand.php
automate the process of saying things.
03. namespace PHPArch\Command;
04.
A complete list of “fortunes” is
05. use Symfony\Component\Console\Command\Command;
available on a Github repository,
06. use Symfony\Component\Console\Input\ArrayInput;
https://github.com/bmc/fortunes/
07. use Symfony\Component\Console\Input\InputInterface;
blob/master/fortunes, as a file named fortunes.
08. use Symfony\Component\Console\Input\InputOption;
We are going to use it to build a new fortune
09. use Symfony\Component\Console\Output\OutputInterface;
command. Download it, and place it in the
10.
src/Resources directory.
11. class FortuneCommand extends Command
12. {
The fortune command is simple; it opens the
13. protected function configure() {
file, explodes it into a PHP array, and picks a
14. $this->setName('fortune')
random quote. Then the say command is used to
15. ->setDescription(
print out the fortune. Listing 5 shows the code for
16. 'A command to display random quotes.'
the new command.
17. )->addOption(
18. 'path-to-fortunes',
Don’t forget to add the FortuneCommand to the
19. 'p',
application in the app file:
20. InputOption::VALUE_REQUIRED,
// ...
21. 'Location of the fortunes archive',
use PHPArch\Command\FortuneCommand;
22. __DIR__.'/../Resources/fortunes'
23. );
// app
24. }
// ...
25.
26. public function execute(
$application->add(new FortuneCommand());
27. InputInterface $input,
$application->run();
28. OutputInterface $output) {
29. $pathToFortunes = $input->getOption('path-to-fortunes');
This command loads the fortunes from a default 30. $fortunes = explode('%',
or custom path in lines 30-31. Each quote is 31. file_get_contents($pathToFortunes));
separated by a % sign; we use the PHP explode 32. $count = count($fortunes)-1;
function to get an array of quotes and then select 33. $rand = rand(0, $count);
one at random. 34. $quote = $fortunes[$rand];
35.
A few important things to notice: 36. $sayCommand = $this->getApplication()->find('say');
37. $arrayInput = new ArrayInput(['phrase' => $quote]);
1. The command is not using any arguments; 38. $sayCommand->run($arrayInput, $output);
instead, we are adding an option called 39. }
path-to-fortunes. Options are explained 40. }
in more detail below. Command Options
2. The command say is used to send the fortunes to the The fortunes command introduces a new configuration
output. To have access to say, we need the application method: addOption. Here the option is used to load the
object that is available through the getApplication fortunes from a custom path or from the default location
method. within the Resources directory.
3. We do not use the same $input object (the one
passed to the execute method for the SayCommand), You can use options like flags (boolean), values, a
as it contains values for the FortuneCommand. combination of flags or values, and an array of values.
Instead, we use an alternative implementation of the Options can be used in any order we wish and are not
InputInterface where you can set command inputs required to run the command. Commands may contain any
as an associative array. number of arguments, options, or a combination of both.

www.phparch.com January 2016 | 35


Writing Better CLI Tools with Symfony Components

The method addOption takes five arguments:

1. The option name (required)


2. A shortcut or short-name (optional)
3. Option mode: either
• InputOption::VALUE_IS_ARRAY
• InputOption::VALUE_NONE (used as flag)
• InputOption::VALUE_REQUIRED (used as value)
• InputOption::VALUE_OPTIONAL (flag or value)
4. Option description (optional)
5. Option default value (optional)

NOTE: Just like argument descriptions, it is good to always add descriptions to the options.

Array Option
The input option VALUE_IS_ARRAY mode works differently from the argument ARRAY mode. With this one you
can add multiple values by defining the option multiple times like so:

php app command --option=val1 --option=val2 ...

Elephant Say Fortunes


If you are familiar with the original fortune program, you may have come across Cowsay. LISTING 6
Cowsay is a simple program
that displays an ASCII image 01. <?php
of a cow with a speech bubble 02. // src/Command/ElephantFortuneCommand.php
and some text. Let’s wrap up the 03. namespace PHPArch\Command;
CLI application by creating an 04.
elephant:fortune command. 05. use Symfony\Component\Console\Command\Command;
Listing 6 contains the code for this 06. use Symfony\Component\Console\Input\ArrayInput;
command. 07. use Symfony\Component\Console\Input\InputInterface;
08. use Symfony\Component\Console\Input\InputOption;
The elephant:fortune command 09. use Symfony\Component\Console\Output\OutputInterface;
calls the fortune command and 10.
then opens a file from the resource 11. class ElephantFortuneCommand extends Command
directory that contains an ASCII 12. {
elephant. Figure 1 shows what you 13. protected function configure() {
14. $this->setName('elephant:fortune')
15. ->setDescription('Make the Elephant say a fortune.');
16. }
17.
18. public function execute(InputInterface $input,
19. OutputInterface $output) {
20. $fortuneCommand = $this->getApplication()
21. ->find('fortune');
22. $arrayInput = new ArrayInput([
23. 'command' => 'fortunes',
24. ]);
25. $fortuneCommand->run($arrayInput, $output);
26. $output->writeln(
27. file_get_contents(__DIR__.'/../Resources/elephant')
28. );
29. }
30. }

36 | January 2016 www.phparch.com


Writing Better CLI Tools with Symfony Components

Elephant Fortunes FIGURE 1


should see. The rendering of
the speech bubble has been
omitted here for simplicity.
You can find several ASCII
elephants in the issue code
archive.

Verbosity Levels
The Symfony Console enable
us to print messages using
the output object, but what
if we want to print out more
information or nothing at all?
The Console components
support five verbosity levels: quiet, normal, verbose, very verbose, and debug. These verbosity levels can be
enabled by using the following options:

1. Quiet: -q
2. Normal: No option required
3. Verbose: -v
4. Very Verbose: -vv
5. Debug: -vvv
The quickest way to check the current verbosity level is by using one of the following output methods:

$output->isQuiet()
$output->isVerbose() LISTING 7
$output->isVeryVerbose()
$output->isDebug() 01. $dir = __DIR__.'/../Resources/';
02. if ($output->isVerbose()) {
These methods return either 03. $output->writeln(file_get_contents($dir.'v_elephant'));
true or false. Add flow control 04. }
statements to print out custom 05. if ($output->isVeryVerbose()) {
messages for each verbosity level at 06. $output->writeln(file_get_contents($dir.'vv_elephant'));
the end of the execute method in the 07. }
ElephantFortuneCommand. 08. if ($output->isDebug()) {
09. $output->writeln(file_get_contents($dir.'vvv_elephant'));
Now try adding the verbose 10. }
option to the command:
php app elephant:fortune -vvv. If all goes as planned, you should see a herd of ASCII elephants.

Console Logger
If any of your command dependencies uses a PSR-3 logger, the Console component provides a ConsleLogger
class that sends all messages to the console output. By providing this class, we can ensure that the dependency
sends all log messages to the same place. You may need to require psr/log with Composer.

composer require psr/log

Then add the class your use statement in the command file:

use Symfony\Component\Console\Logger\ConsoleLogger;

Let’s improve the elephant fortune command by separating some concerns. Currently, we are loading different

www.phparch.com January 2016 | 37


Writing Better CLI Tools with Symfony Components

files to display various types of elephants for each verbosity level. By moving that logic to a new class, we can
centralize the file-loading process from the src/Resources directory. The new class takes a PSR-3 logger and
displays a message before a file is loaded or if there is an issue loading the file.

Add the new loader to our command like so:

// ...
// in execute()
$logger = new ConsoleLogger($output);
$loader = new FileLoader($logger);
$value = $loader->load('elephant');
$output->writeln($value);

if ($output->isVerbose()) {
$value = $loader->load('v_elephant');
$output->writeln($value);
}
// ...
LISTING 8
Now try out the improved
ElephantCommand: 01. <?php
php app elephant:fortune -vvv. 02. // src/FileLoader.php
03. namespace PHPArch;
04.
05. use Psr\Log\LoggerInterface;
CLEAN CODE: Use 06.
dependency injection 07. class FileLoader
as the means to acquire 08. {
dependencies in your objects. 09. private $logger;
The FileLoader takes a 10.
PSR-3 compliant logger; for this 11. public function __construct(LoggerInterface $logger) {
example, we use constructor 12. $this->logger = $logger;
injection. 13. }
14.
15. public function load($file) {
16. $this->logger->info("About to load file: $file");
Simple Command 17. $dir = __DIR__.'/Resources';
18.
Configurations 19. if(!file_exists("$dir/$file")) {
As a standalone component, the 20. $this->logger->error("Invalid $file");
Console does not provide a way to 21. $file = false;
add configurations, but we can use 22. } else {
other components to complement the 23. $file = file_get_contents("$dir/$file");
functionality of the commands. The 24. }
YAML component is the ideal 25.
candidate for helping to add basic 26. return $file;
configuration features. 27. }
28. }

NOTE: For advanced configuration features, use the Symfony Config component. With it, you can find and load
resources, define and validate config values, and more.

38 | January 2016 www.phparch.com


Writing Better CLI Tools with Symfony Components

YAML is a human-readable serialization standard. It is language-agnostic, and developers use it as an alternative


to other serialization formats such as XML and JSON.

The YAML Symfony component implements most features of the YAML standard and is used mostly to parse
configuration files to PHP arrays.

To get started with the YAML component, install it using Composer:

composer require symfony/yaml

To parse a file, you can use the convenient static method Yaml::parse.

use Symfony\Component\Yaml\Yaml;

if (file_exists($pathToConfig)) {
$yamlString = file_get_contents($pathToConfig);
$config = Yaml::parse($yamlString);
} else {
throw new \RuntimeException(
"The file $pathToConfig does not exist."
);
}

You can find additional information about YAML and how to use it in the Symfony documentation:
http://phpa.me/symfony-yaml

For this article, we are only going to use a configuration file to define the elephant that is displayed in the
elephant:fortune command. By doing so, you will get a basic idea of how to use configuration files in
commands.

Create a configuration.yml file in the src/Resources/config directory.

# src/Resources/config/configuration.yml
#'small_elephant', 'medium_elephant'
elephant_file: elephant

Use the FileLoader we created earlier to retrieve the contents of the configuration file:

$yamlFile = $loader->load('config/configuration.yml');
$config = Yaml::parse($yamlFile);
$elephant = $loader->load($config['elephant_file'])

Try using the small_elephant or the medium_elephant included with the source code for the article by making
a simple modification to the config file—for example, see below:

elephant_file: small_elephant.

Helpers
The Console component has several helpers. These are useful classes that exist to improve and extend the
functionality of the application. From progress bars to tables, these classes are another set of features in the
console that you can use to create great applications.

While the helpers are outside the scope of this article, Listing 9 shows you how to use (what in my opinion is) the
most important helper to interact with a user: the QuestionHelper.

www.phparch.com January 2016 | 39


Writing Better CLI Tools with Symfony Components

Running this command would result in the following output:

$ php app greet:me


Your Name: Juan
Are you sure your name is Juan? y
Hello Juan

Using the default values:

$ php app greet:me


Your Name:
Are you sure your name is Stranger?
Hello Stranger
LISTING 9
For more information on helpers, 01. <?php
see the Symfony documentation: 02. namespace PHPArch\Command;
http://phpa.me/symfony-console-helpers 03.
04. use Symfony\Component\Console\Command\Command;
The Tip of the Iceberg 05. use Symfony\Component\Console\Input\InputInterface;
06. use Symfony\Component\Console\Output\OutputInterface;
There are many ways to use the Console 07. use Symfony\Component\Console\Question\Question;
component and many additional features I 08. use Symfony\Component\Console\Question\ConfirmationQuestion;
did not have time for. The console, paired 09.
with other components, can become 10. class GreetMeCommand extends Command
a powerful tool in your development 11. {
toolbox. I encourage you to find out more 12. protected function configure()
about this component by reading the 13. {
documentation, exploring the code, and 14. $this->setName('greet:me')
writing CLI applications. 15. ->setDescription('This command will greet me');
16. }
17.
18. public function execute(
19. InputInterface $input,
20. OutputInterface $output)
21. {
22. $helper = $this->getHelper('question');
23. $question = new Question('Your Name: ', 'Stranger');
24.
25. $name = $helper->ask($input, $output, $question);
26.
27. $question = new ConfirmationQuestion(
28. "Are you sure your name is $name? ",
29. false
30. );
31.
32. if ($helper->ask($input, $output, $question)) {
33. $output->writeln('Hello '.$name);
34. } else {
35. $output->writeln('Hello Stranger');
36. }
37. }
38. }

40 | January 2016 www.phparch.com


Writing Better CLI Tools with Symfony Components

Best Practices
While the Console component helps you write better CLI applications more quickly, it can be abused just like
any other library. These are a few guidelines to help you write better CLI applications:

6. Think of commands as controllers in an MVC framework; they are the glue of your application, not a bowl of
spaghetti where all the logic lives.
7. Separate concerns and create single-responsibility commands.
8. Use other Symfony components to add additional functionality to your commands: Config,
EventDispatcher, StopWatch, and Process, just to name a few.
9. Write your code using interfaces, not concrete implementations. By doing so, you can extend the
functionality of the application without having to refactor existing code.
10. Add as much information as possible to the command configuration; your future self will be glad that you
did.
11. Use the setHelp method in your command class to add additional information about the command and
how to use it properly.
12. The Console is a robust component; read the documentation and find out more about many additional
features and how to extend it effectively.

Up Next
Next month, I will introduce the EventDispatcher component. This component will enable us to add and
extend the functionality of our commands without having to make modifications to the code. I have found this to
be one of the most helpful components that I have used, and it is a great complement for any PHP project.

JUAN MANUEL lives in San Diego, California where he works for MindTouch as
Senior Software Engineer. He started programming with PHP in 2000 as a
hobby. He has worked professionally with PHP for several years and has
developed different applications, tools and services. His passions include
photography, reptiles and SOLID programming. You can find him on Twitter
@onema.

Twitter: @onema

41 | January 2016 www.phparch.com www.phparch.com January 2016 | 41


FEATURE

Raspberry Pi with PHP on Top


Dylan Intorf

The Raspberry Pi 2 Model B is


the newest form of Raspberry Pi
Foundation’s “credit card-sized
computer” that is most commonly
used for learning the ways of the
programmer. But don’t be too
quick to dismiss it based on size,
because this miniature machine
can be used for more than just
<?php echo 'Hello World!';.
Let’s talk about how to make
the most of the Raspberry Pi 2’s
power and create a PHP web
server.

DisplayInfo()

Requirements:
• Raspberry Pi 2 Model B
Other Software:
• Raspbian (or other distro) Installed
Related URLs:
• Raspberry Pi Quickstart—http://phpa.me/raspberrypi-quickstart
• Linux Installation (using NOOBS)—http://phpa.me/raspberrypi-noobs
• Apache Server Setup—http://phpa.me/raspberrypi-apache
• NGINX Server Setup—http://phpa.me/raspberrypi-nginx
• Raspberry Pi 2 Model B Specifications—https://www.raspberrypi.org/?p=11476
• Apache vs. NGINX Performance Comparison—http://www.theorganicagency.com/?p=161
• Apache vs. NGINX Practical Considerations—http://phpa.me/do-apache-vs-nginx
• NGINX vs. Apache Blog Post—https://anturis.com/blog/nginx-vs-apache/

42 | January 2016 www.phparch.com


Raspberry Pi with PHP on Top

Introduction
Owning a Raspberry Pi 2 (also referred to as RasPi 4. HDMI-compatible Raspberry Pi 2 Model B
2) is fun and exciting, especially if you’ve never display (i.e. TV, PC
fiddled around with any of the older models. There monitor, etc.)
are countless activities and projects that the pint-size 5. Ethernet cable or wifi dongle
computer has the ability to manage. Among these
projects are running a Minecraft server, hooking up a 6. Internet connection
security camera, and even the comical sort such as a 7. Programmer necessities (i.e. soda, tunes, epic
Santa detector; the possibilities are endless! Of course, nachos, etc.)
what we are interested in is spinning up a PHP web
server, because that’s what we do. What You’ll Do
1. Use the terminal.
Now let’s not get too carried away with the amount
of stress we put the RasPi 2 through as there are 2. Install Apache or NGINX.
some limitations in terms of running a web server 3. Install PHP packages for your chosen web server.
on it. Looking at the technical specifications (refer to
4. Test your web server with HTML and PHP pages.
related links), you can see that a 900MHz quad-core
ARM Cortex-A7 CPU and 1GB of RAM may not be 5. Show your friends and family how cool you are.
able to handle the concurrency we’re used to from That’s everything you will need and everything you
other hosting setups. Of course any type of website will do in the scope of this little tutorial. However,
can be hosted on the RasPi 2, however, the Pi may as previously stated, there are many other projects
not offer the speed you need for the site. A better and adventures you can embark on with your RasPi
suited website to be hosted on the RasPi 2 is one that 2. I suggest taking a peek at what the Raspberry Pi
is informational or low-traffic at best. For example, a website has to offer.
portfolio or resumé site, or one you can only access on
a private network, is perfectly suited for the RasPi 2.
Web Server Setup
This article is a tutorial is meant to show you the
capabilities of the RasPi 2 as a web server. In following Choosing a Web server
it, you get the opportunity to play with your own RasPi
We won’t go too in-depth about the differences
2 web server and blow up Twitter with how much fun
between Apache and NGINX. The most important
you are having. That being said, the assumption is that
difference that I can make out from reading through
your RasPi 2 is already set up with some sort of Linux
blogs and forums is the way each architecture handles
distro because it will not be covered here (for help
concurrent processing.
with this, refer to related links).
In Figure 1, from the Organic Agency benchmarks in
Now that we got that out of the way…Let’s do this!
Apache vs. NGINX Performance Comparison, NGINX
seems to stand ahead with its speed in managing
Getting Started many concurrent requests, while Apache seems to
sometimes fail when the request count is high. If you’re
I know you’re itching to get started with your very feeling really wild, you could always use a combination
own home PHP web server, but let’s make sure of the two web servers. This setup is usually configured
you have what you need first. Without the proper with NGINX on the front end handling client requests,
equipment you will be a lost puppy, so pay close taking advantage of its fast processing speed, while
attention to the list below. Apache sits in the backseat as a reverse proxy. Since
we are creating a pretty basic website for our RasPi 2
What You’ll Need due to the hardware limitations, either server will do
just fine.
1. Raspberry Pi 2 Model B with Linux installed
(follow the Quick Start Guide at related URL 1,
Before we try to install either web server, let’s update
generally you will install the Raspbian flavor of
our package installer by running
Linux)
2. USB keyboard and mouse sudo apt-get update
3. HDMI cable
Once that update completes, we are ready to start
installing our web server.

www.phparch.com January 2016 | 43


Raspberry Pi with PHP on Top

:Apache vs. NGINX Processing FIGURE 1

Apache This command will change your current working


directory to the root directory of your new Apache
The setup for Apache is very straightforward. You can
web server. If you enter the ls command, it should
do this on any Linux machine and the process won’t be
return index.html. You can edit this page using:
any different for a Raspberry Pi 2.
sudo nano index.html
sudo apt-get install apache2 -y
Go ahead and mess around with the HTML and
The installation for Apache 2.2 should begin on your
save the file. To see your changes, open up your web
RasPi 2! You’ll notice the extra option -y at the end
browser to your IP and you will notice the “It Works!” is
of the command. Adding -y just lets the package
now replaced with whatever your imagination cooked
installer know that you want to say yes to any prompts
up in the editor. The Raspberry Pi will automatically
that present themselves during the installation. If you
start up the Apache web server on boot-up so you
would rather see what the prompts are, go ahead
don’t have to worry about any extra setup for that.
and leave out -y in your command. When the install
completes you should see a couple of green “ok” lines
in the output. NGINX
Let’s take a look at installing NGINX. Again, this is a
To make sure that the install actually was successful, very straightforward setup, and it can be done on any
we can run the command apache2 -v shown in
Figure 2, which will tell us the version of the Apache
server, or test it out in our web browser. Apache Successful Installation FIGURE 2
To test in the web browser, enter
hostname -I in your terminal to find
out what your IP address is and open
up your web browser. Enter your IP in
the address bar and you should see
the ever-helpful Apache “It works!”
message shown in Figure 3.

If you’d like to put up a more exciting


page in place of Apache’s “It Works!” Apache Works FIGURE 3
let’s open up that terminal again. The
default root directory of an Apache web
server is /var/www.

cd /var/www

44 | January 2016 www.phparch.com


Raspberry Pi with PHP on Top

NGINX Successful Installation FIGURE 4


Linux machine. All you have to do is run
the command below:

sudo apt-get install nginx -y

The installation for NGINX is much


shorter than for Apache. When the
install completes, you should not see
any errors in the install log. NGINX Welcome FIGURE 5
To make sure that the install actually was
successful, we can run the command nginx -v
shown in Figure 4, which will tell us the version
of the NGINX server, or test it out in our web
browser. Before we test in the browser, we
actually need to start the server because the PHP Setup
install doesn’t automatically do this for us.
Now that you have your very own web server serving
sudo /etc/init.d/nginx start up HTML pages, let’s get to the final touches to serve
up PHP. Again, for this install, all you need to do is
To test in the web browser, you can use your IP in open up your terminal. The libraries needed for each
the address bar or http://localhost. You may server are different, so of course we have slightly
have an issue if you’ve installed Apache before this. different commands to run. One command that we can
NGINX will set the default port to 81 instead of use for either is the PHP install.
80, so you will need to navigate with that port (i.e.
http://localhost:81). When you load either of sudo apt-get install php5 -y
these you will see a very welcoming message as shown
Entering php -v, such as in Figure 6, will tell you
in Figure 5!
what version of PHP was installed and show you that it
was successful.
The default root directory of an NGINX web server is
/usr/share/nginx/www.
Apache
cd /usr/share/nginx/www
For Apache, we need the PHP 5 module built for use
If you enter the ls command, it should return with Apache.
50x.html index.html. You can edit the
sudo apt-get install libapache2-mod-php5 -y
index.html page using:

sudo nano index.html This sets up PHP using mod_php with Apache, which
is a very easy way to configure it. To use php-fm with
The Raspberry Pi will automatically start up the Apache, see http://phpa.me/linode-php-fpm.
NGINX web server on boot-up, so you don’t have to
worry about any extra setup for that.
The library takes only a second or so to install.
Once it completes, change directories into the
Apache root with cd /var/www. Run the command
sudo rm index.html, which will remove the index

PHP Version FIGURE 6

www.phparch.com January 2016 | 45


Raspberry Pi with PHP on Top

page for your Apache server. Enter sudo nano index.php and we can now enter some PHP code that will be
served up on the Apache server. You can go with the classic first program of:

<?php echo "Hello, World!";


PHP Info FIGURE 7
or you can go a little more
dynamic with:

<?php phpinfo();

Either way, press Ctrl+X


to save your changes, load
up your web browser to
http://localhost, and
see that your hard work
paid off!

NGINX
NGINX requires a
different library to run
PHP but the install is just
as quick and easy. There
is a bit of additional
configuration for NGINX,
so pay close attention
here. We need the
php5-fpm for NGINX and
we will use apt-get to
install it.

sudo apt-get install php5-fpm -y

When that completes, you can begin the additional setup by changing into the NGINX configuration directory
with cd /etc/nginx. Use the following command to begin editing the configuration file:

sudo nano sites-enabled/default

Now that the configuration file is open in nano, find the line that reads index index.html index.htm; (it
should be on line 25). Referring to Figure 8, change this line by adding index.php after index.

NGINX Configuration 1 FIGURE 8

Now scroll down to line 62 and find more PHP configuration code. You’ll know you’re in the right place when,
two lines above, you see:

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

46 | January 2016 www.phparch.com


Raspberry Pi with PHP on Top

Comment out the following lines to finish up the configuration:

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}

The final code should look like Figure 9.


NGINX Configuration 2 FIGURE 9

BOOM! NGINX is ready for action and set up to serve PHP. All that is left is to test it on the NGINX server. Change
into the NGINX root with cd /usr/share/nginx/www. Run the command sudo rm index.html and then
sudo nano index.php to create the PHP index page. Add some PHP code and save the file, then head over to
your web browser and enter your NGINX web address to observe the PHP magic!

Conclusion
That’s all it takes to install a web server, such as Apache or NGINX, that runs PHP! Congratulations on having
your own web server. Running a web server is a fantastic application of the Raspberry Pi 2, and what you do with
it is completely up to you! Try putting up your personal portfolio, running a WordPress site, or installing MySQL
and completing the LAMP stack. You could even run an ownCloud instance on your Raspberry Pi 2! No matter
what path you choose to forge, you can revel in the fact that you have set up a personal web server and it’s
running under your own roof!

DYLAN is a passionate web and mobile application developer with three years of
real world coding experience at Wise Agent. Having recently graduated from
Arizona State University with a degree in Computer Science and an emphasis in
Information Assurance, he looks forward to perfecting his skills on all web and
mobile platforms.
When he isn’t super glued to a keyboard, or tearing through millions of 0’s and
1’s, you could find him teaching the newest generation some wicked self-
defense moves.

Twitter: @dintorf

www.phparch.com January 2016 | 47


Education Station

Date and Time Handling


with Carbon
Matthew Setter

Welcome to Education Station for 2016. I hope you’re well rested and
recharged from the year that was and ready to crack on in 2016. If you
made any New Year’s resolutions, I hope you’ve not broken too many
of them already.

14

C
12 13

DisplayInfo()

Requirements
• Carbon—http://carbon.nesbot.com
• Composer—https://getcomposer.org

48 | January 2016 www.phparch.com


Education Station
Date and Time Handling with Carbon

Anyway, with all that said, let’s get stuck in to another year of Education Station. To start off the year, I want to talk
about dates. No, not the very tasty and sugary fruit. Dates, as in days, years, months, weekends, and so on. You
know, the kind that, despite having had to work with them since we were all kids, we can still find challenging.

Now before I go too deep down this rabbit hole, I don’t want to give you the wrong impression. If you’ve been
working with any semi-recent version of PHP, you’ll have used its excellent DateTime extension. With it, we can do
all kinds of date calculations, as well as handle for time zones, intervals, and formatting.

But despite that, dates, date calculations, time zone offsets, and intervals can still be a challenge to work with.
Perhaps everyone else is fine and it’s just me. But I suspect I’m not the only one who would love to have them be
just that much simpler to work with. It shouldn’t take too much to guess where I’m heading with this, right?

As with all things in the life of a PHP developer, when you have an itch that needs scratching, you do one of two
things: you check if someone’s already made a package available, either via Packagist or GitHub, or you write one
yourself and make it available. So I went to Packagist and started doing some searching. Boy, what a wealth of
options appeared!

One option in particular caught my eye, and it’s what I’m going to be talking about this month. It’s called Carbon.
The name might, at first, seem kinda strange. If it does, I’ll leave one term with you—carbon dating. Still seem
strange? Anyway, enough of the long-winded introduction.

Carbon extends PHP’s DateTime extension making object creation, comparison, printing, modification, addition,
and subtraction a lot easier. What’s more, there are a series of further packages, which again extend Carbon’s
capabilities. There are extensions that calculate which days are British bank holidays, calculate the next business
day, and determine the end of the fiscal (financial) year. If you want to make working with dates, times, time zones,
and intervals easy, let’s dive in and see how.

Installing Carbon
As with all good PHP libraries, Carbon is installable via Composer. You can either add
"nesbot/carbon": "^1.21" to the relevant section of your composer.json and run composer update. Or you
can run:

composer require nesbot/carbon

from the terminal in the root of your project.

If you don’t have Composer installed then, from the terminal in the root of your project, run:

curl -sS https://getcomposer.org/installer | php

That will download the Composer Phar file. After that, you can move Composer to a directory in your system path
and make it executable, by running the following command:

sudo mv composer.phar /usr/local/bin/composer;


sudo chmod +x /usr/local/bin/composer

You could alternatively change the commands above to be php composer.phar, instead of composer. Either
way, let’s install the extensions I mentioned. I’ll assume that you have Composer installed in your system path for
the remainder of these commands. From your terminal, run the following commands:

composer require citco/carbon


composer require advmaker/carbon-period
composer require rovangju/carbon-nbd
composer require rovangju/carbon-fy
composer require cherrypick/hammertime

www.phparch.com January 2016 | 49


Education Station
Date and Time Handling with Carbon

Creating a Carbon Instance


Now that we have everything ready to go, let’s write some code. The first thing we want to do is to get an
instance. As I live in Germany, I’m going to create an instance using that time zone. To do this is as simple as calling
the static today() method, passing in the time zone string, as follows:

<?php
require_once('vendor/autoload.php');
use Carbon\Carbon;

$dtBerlin = new Carbon('2015-12-01', 'Europe/Berlin');

I could have been more specific and passed in a DateTimeZone object, like the following snippet shows, but it’s
not strictly necessary. I could also use the today method, but that leads to unexpected results when comparing
time differences between time zones.

$berlinNow = Carbon::today(new \DateTimeZone('Europe/Berlin'));

Calculating the Time Difference Between Time Zones


Now here’s a common scenario, at least for me as a remote worker, working with teams in multiple time zones.
Let’s say I’m working with a team in Brisbane, Australia, and one in New York City. What’s a quick way to check the
time difference between these two places? Well, first let’s create two objects, one for each time zone:

$brisbane = new Carbon('2015-12-01', 'Australia/Brisbane');


$newYorkCity = new Carbon('2015-12-01', 'America/New_York');

Then let’s use the diffInHours method, along with the printf function to show the difference; see Listing 1.

When run, this will output the following to the console:

Time difference between Berlin & Brisbane, Australia: 9 hours.


Time difference between Berlin & New York City, America: 6 hours.
LISTING 1
Now, if you were paying attention,
01. $outputString = "Time difference between %s & %s: %s hours.\n";
you may have noticed that that’s
02. // Date difference
both correct, yet not quite accurate
03. printf(
at the same time. Take the time
04. $outputString,
difference between Berlin and
05. "Berlin", "Brisbane, Australia",
Brisbane, for instance. Yes, there
06. $berlinNow->diffInHours($brisbane)
is a 9-hour time difference, but
07. );
Brisbane is 9 hours ahead. This
08. printf(
isn’t always essential to know, but
09. $outputString,
there are times when it is.
10. "Berlin", "New York City, America",
11. $berlinNow->diffInHours($newYorkCity)
If you need to see the difference,
12. );
then you need to pass false as
the second parameter. By default,
true is passed, which means it returns the absolute difference. To see whether we’re ahead or behind another
time zone, we need to see the relative difference. If we’d done that, the output would have looked like this:

Time difference between Berlin & Brisbane, Australia: -9 hours.


Time difference between Berlin & New York City, America: 6 hours.

50 | January 2016 www.phparch.com


Education Station
Date and Time Handling with Carbon

Calculating the Difference in Multiple Forms


LISTING 2
Let’s look at something more
sophisticated. Let’s say that we want 01. $septEighteen2014 = Carbon::createFromDate(
to know the difference between today 02. 2014, 9, 18, $dtBerlin->getTimezone()
and a given date, for example, 18 03. );
September 2014. To do that, we need 04.
two things: a new Carbon object 05. printf(
initialized to that date and a way of 06. "difference between now and %s in \n\thours: %d, \n\t"
finding the difference. 07. . "days: %d, \n\tweeks: %d, \n\tweekend days: %d, \n\t"
08. . "+1week days: %s, \n\thuman readable: %s",
This might seem like a rather arbitrary 09. $septEighteen2014->toFormattedDateString(),
example, but it highlights one of the 10. $dtBerlin->diffInHours($septEighteen2014),
key reasons for the existence of the 11. $dtBerlin->diffInDays($septEighteen2014),
library, making working with dates 12. $dtBerlin->diffInWeeks($septEighteen2014),
almost trivial. In addition to the 13. $dtBerlin->diffInWeekendDays($septEighteen2014),
diffInHours function, which we’ve 14. $dtBerlin->diffInWeekDays($septEighteen2014),
already seen, Carbon comes with a 15. $dtBerlin->diffForHumans($septEighteen2014)
range of diff* functions. 16. );

They can show the difference between two Carbon objects in all kinds of ways, including hours, days, weeks,
weekends, week days, and even one that is perhaps a little less accurate, but more meaningful to us humans,
diffForHumans. Take a look at the example below where I’ve used them all:

Firstly, we created a new Carbon instance by specifying the year, month, and day, along with the Berlin time zone,
which we retrieved from $dtBerlin by calling its getTimezone method. Then, I called all the diff methods to
output the difference in the various ways. Running that, today, would give the following console output:

difference between now and Sep 18, 2014 in


hours: 10887,
days: 453,
weeks: 64,
weekend days: 130,
week days: 324,
human readable: 1 year after

Most of these are pretty standard. The last one, which is human readable, to me is the most interesting. It’s
debatable as to whether this is necessary, as it’s not exactly precise. But it’s handy to have a way of interpreting a
date in a way we’re all more likely used to.

Date Formatting
Now that’s the setup taken care of, along with some simple calculations. What about formatting? As with date
differences, and the underlying class, there are a host of methods for formatting a Carbon object.

There are ones that return the date in various combinations of the day, month, and year, with or without a time
component. And there are a whole host of them for formatting based on various specifications and RFCs, such as
Atom, RSS, and Cookie. Here’s a sample of them:

echo $dtBerlin->toDateString() . "\n";


echo $dtBerlin->toFormattedDateString() . "\n";
echo $dtBerlin->toTimeString() . "\n";
echo $dtBerlin->toDateTimeString() . "\n";
echo $dtBerlin->toDayDateTimeString() . "\n";
echo $dtBerlin->toRfc1036String() . "\n";
echo $dtBerlin->toAtomString() . "\n";
echo $dtBerlin->toCookieString() . "\n";
echo $dtBerlin->toRssString() . "\n";

www.phparch.com January 2016 | 51


Education Station
Date and Time Handling with Carbon

If we were to run that, then we’d get the following output:


2015-12-16
Dec 16, 2015
00:00:00
2015-12-16 00:00:00
Wed, Dec 16, 2015 12:00 AM
Wed, 16 Dec 15 00:00:00 +0100
2015-12-16T00:00:00+01:00
Wednesday, 16-Dec-2015 00:00:00 CET
Wed, 16 Dec 2015 00:00:00 +0100

Note that we’re not limited to just the available format functions, as Carbon extends DateTime, remember?
Technically, we could call DateTime’s format function directly if need be. Alternatively, if there’s a format that you
want to use repeatedly, you can call Carbon’s setToStringFormat() method, and pass in the desired format
string, then simply echo the object, which will invoke Carbon’s __toString() method. Here’s a quick example:

$dtBerlin->setToStringFormat('l jS \\of F Y');


echo $dtBerlin . "\n";

I hope you can see just how convenient the library is. If not, let’s have a look at a set of other functions. These are
the endOf*, firstOf*, is*, nthOf*, lastOf*, and startOf* methods. They are all handy methods for those
calculations that you might need, yet they can often be tricky to implement. In a nutshell, here’s how they work:
• endOf*, firstOf*, startOf*, and lastOf*: They find the relevant date for the century, date, decade,
month, week, or year (though not all of them support all these options).
• is*: This tests for a range of options, such as is the date a Friday, Monday, tomorrow, Tuesday, or a leap year.
• nthOf*: This finds the nth date within a month, quarter, or year.
Let’s have a look at a selection of them, starting with the is* methods. Given our $dtBerlin object, let’s ask the
following questions: Is the date yesterday, a Thursday, in the future, or a leap year? Here’s how we do it:

echo "Is yesterday? "


. ($dtBerlin->isYesterday() ? "yes" : "no") . PHP_EOL
echo "Is a Thursday? "
. ($dtBerlin->isThursday() ? "yes" : "no") . PHP_EOL
echo "Is in the future? "
. ($dtBerlin->isFuture()) ? "yes" : "no") . PHP_EOL;
echo "Is a leap year? "
. ($dtBerlin->isLeapYear() ? "yes" : "no") . . PHP_EOL;

Share your
smarts with
learners
worldwide
Pluralsight is looking for tech and creative experts
to author online courses. Take part in a rewarding
experience that allows you to teach a global
audience, join a network of passionate, talented
peers, and supplement or replace your income.
Evolve your career at pluralsight.com/teach.

52 | January 2016 www.phparch.com


Education Station
Date and Time Handling with Carbon

Now, what if we wanted to get the start and end of the current month, along with the end of the decade? Here’s
how:

echo "Start of the month ", $dtBerlin->startOfMonth() . PHP_EOL;


echo "End of the month ", $dtBerlin->endOfMonth() . PHP_EOL;
echo "End of the decade ", $dtBerlin->endOfDecade() . PHP_EOL;

Date Manipulation
Let’s round out the core functionality by doing some date manipulations. What if we wanted to see what 5 hours,
2 days, 1 week, and 3 months in the future is? Or what if we wanted to see what 4 years, 8 months, 7 hours in the
past was? For this, there are the add* and sub* methods. There’s one for all of the options you’d likely need. Let’s
see how to perform the manipulation I just mentioned:

// 5 hours, 2 days, 1 week, and 3 months in the future


echo $dtBerlin->addHours(5)->addDays(2)
->addWeeks(1)->addMonths(3) . PHP_EOL;

// 8 months, 7 hours in the past


echo $dtBerlin->subMonths(8)->subHours(7) . PHP_EOL;

Each of the add* and sub* methods returns a DateTime object, so implement the fluent interface, which allows
them to be chained together. Given that Carbon implements the __toString method, we can just chain them all
together and print the result. Doing so will give the following result (assuming you’ve not done both at the same
time):

Wednesday 1st of May 2019


Wednesday 16th of December 2015

That’s a good amount of coverage of the core library. Let’s finish up by having a look at two of the available
extensions, specifically the ones providing support for U.K. bank holidays and the fiscal year.

Finding U.K. Bank Holidays


One of the great things about living in the U.K. was the number of bank and public holidays. There are just so
many, primarily around May, including early May, spring, summer, New Year, Good Friday, Easter Monday, Boxing
Day, and the Christmas bank holidays.

They’re a wonderful time to go for long weekends away, either somewhere in the U.K. or to Europe. Let’s look at
a couple of the functions, specifically showing the bank holidays for 2016–2017, and find out when the next bank
holiday is, so if you’re living in the U.K., you can get planning. Here’s how:

use Citco\Carbon as CitcoCarbon;

$dtLondon = CitcoCarbon::today('Europe/London');
list($date, $name) = each($dtLondon->nextBankHoliday());
printf("The next bank holiday is %s on %s\n", $name, $date);

We first include the Citco\Carbon namespace, aliasing it to CitcoCarbon, if we’re already using Carbon. Then,
as with Carbon, we instantiate a new Carbon object, specifying the London time zone. From there, we call the
nextBankHoliday method. This returns an array where the key is the date the holiday falls on and the value is the
holiday’s name. Using list, each, and printf, we can quickly extract and display the information. Now, what
about the full list of bank holidays for 2016–2017? Here’s how to get that:

$holidays = $dtLondon->getBankHolidays([2016, 2017]);


foreach ($holidays as $date => $name) {
printf("The next bank holiday is %s on %s\n", $name, $date);
}

www.phparch.com January 2016 | 53


Education Station
Date and Time Handling with Carbon

We’ve called the getBankHolidays method and passed in an array of years. This returns an array like
nextBankHoliday does. So, this time we use a simple foreach loop to iterate over them. If you run this, you’ll get
output similar to the following:

The next bank holiday is New Year's Day Holiday on 2016-01-01


The next bank holiday is Good Friday on 2016-03-25
The next bank holiday is Easter Monday on 2016-03-28
The next bank holiday is Early May Bank Holiday on 2016-05-02
The next bank holiday is Spring Bank Holiday on 2016-05-30
The next bank holiday is Summer Bank Holiday on 2016-08-29
The next bank holiday is Boxing Day on 2016-12-26
The next bank holiday is Christmas Day Holiday on 2016-12-27
The next bank holiday is New Year's Day Holiday on 2017-01-02
The next bank holiday is Good Friday on 2017-04-14
The next bank holiday is Easter Monday on 2017-04-17
The next bank holiday is Early May Bank Holiday on 2017-05-01
The next bank holiday is Spring Bank Holiday on 2017-05-29
The next bank holiday is Summer Bank Holiday on 2017-08-28
The next bank holiday is Boxing Day on 2017-12-25
The next bank holiday is Christmas Day Holiday on 2017-12-26

Find the End of the Fiscal Year


The fiscal year is an odd thing, odd in that it isn’t consistent around the world. For example, in Australia the fiscal,
or financial, year starts on 1 July and ends on 30 June. And here’s a selection from the rest of the world.
• Canada’s starts on 1 April and ends on 31 March.
• For U.K. corporations, it starts on 1 April and runs to 31 March and for individual U.K. taxpayers, it starts on 6
April and ends on 5 April.
• In the U.S., it seems even more complicated. It runs from 1 October to 30 September at the federal level, July
1 to July 30 at the state level, and it can be different again for businesses and organizations.
Given all those differences, this extension provides an easy way to know which date you’re working with. Let’s
say I’m writing an application for an Australian business. Here’s how to find the end of the financial year:

use CarbonExt\FiscalYear\Calculator;

/* FY starts on July 1 */
$fyCalculator = new Calculator(7, 1);
print $fyCalculator->get($dtBerlin);

This would tell us that the end of our financial year is: Sunday 30th of June 2019.

And That’s a Wrap


Well, that’s been a whirlwind tour of Carbon and some of its extensions. While you could do a lot of what it offers
yourself, I hope you’ve seen just how simple it makes a host of the more commonly requested, and sometimes
trickier, calculations and manipulations. Let me know your thoughts on php[architects]’s Facebook Page, which
you can find at https://www.facebook.com/phparch/?fref=ts.

MATTHEW SETTER is a software developer specializing in PHP, Zend Framework, and


JavaScript. He’s also the host of http://FreeTheGeek.fm, the podcast about the business
of freelancing as a software developer and technical writer, and editor of Master Zend
Framework, dedicated to helping you become a Zend Framework master? Find out more
http://www.masterzendframework.com.

Twitter: @settermjd
54 | January 2016 www.phparch.com
Leveling Up

Finding the Solution to the


Problem
David Stockton

As software developers or engineers,


our job is not to write code. In fact, we
should be writing as little code as we
can. Every line of code we write is a
liability: a potential security hole, a new
bug, a tight coupling that makes future
modifications difficult or impossible. It
may surprise you to realize that writing
code isn’t even what your job is about.

Introduction look unprofessional. So how about changing the


framework to make it easier to ensure everything is
As a software engineer (I’m not getting into the aligned properly? Suddenly the solution may not be
debate about whether anything we do with software as simple, but it could be more robust and actually
can truly be considered “engineering,” but bear save time in the long run. Or it could be that the
with me), with very few exceptions, we are problem- button just needs to move down 4 pixels because it is
solvers. We need to hear or read a description of afraid of heights.
what is needed and figure out how to make it happen
in code. Except instead of jumping straight into our *
“Just” is a very dangerous word. It implies
editor and banging out code that we think does
simplicity and a completely thorough understanding
what is asked for, we often should take it further and
of the problem. I recommend calling out usages
ask questions. Many times, we are given a task and
of “just” whenever you hear them. “Oh, it’s ‘just’ a
expected to blindly follow and do what it says—the
simple change,” “It’s ‘just’ some CSS,” “It’s ‘just’ a
proverbial and (now) somewhat derogatory “code
drawn reciprocating dingle arm to reduce sinusoidal
monkey” tasks. “Move the button down 4 pixels,
depleneration,” etc. Until the change has been
change the asterisk to a different shade of green,
completed and tested and marked as done, we
make these 100 PDF reports output as a single
really don’t know what it “just” was. Is it possible that
report with 100 pages.” Each of these requests has a
it’s simple? Sure, absolutely. But the people making
different motivation, and the people requesting them
the change are nearly always in a better position
may well have good intentions. But let’s take a further
to make the call about what it “just” is, and even
look.
then, it may not be that great a position. Avoid using
the word “just” in anything dealing with software
Request for Changes development. It’s just a bad idea.

A request to move a button around on the page is


simple, right? Just* change the top CSS property or Changing an asterisk to a different shade of green
use any of numerous possible ways to move things sounds like a simple change. But unless it’s defined
around. But why? Perhaps where it is doesn’t align exactly what that shade is, why it should be that new
with other buttons on the same line and makes things shade, and the people asking for it know they will

www.phparch.com January 2016 | 55


Leveling Up
Finding the Solution to the Problem

be happy with it, the simple request for changing the infinitum. Our developer managed to take in the list
shade of green may turn into hours or days of back- of caregivers and combine the individual reports into
and-forth with the developer. Don’t laugh, I’ve seen one, but one particular issue was causing him fits. In
it happen. My former coworker ended up spending certain places in the report and at the end, a blank
nearly two days going back and forth with a business, page was inserted into the final PDF. We gathered
changing the size and color shade of a single character several developers and made all sorts of attempts to
on the site. Afterward, he decorated his cube with a get rid of these superfluous blank pages, but to no
large green asterisk drawn with a whiteboard marker avail. After a few days, we concluded that it was not
on paper and stapled to the wall. It was a testament going to allow us to remove these blank pages. We
to inefficiency. If you were told, as the owner of the reported this to the customer.
business, that hundreds or thousands of dollars had
been spent to get an asterisk to the right size and Their response was rather surprising. It didn’t matter
shade of green, it would be understandable if you to them because they were using this to print out each
were upset. of the individual calendars and then scan and email
them out to the individual caregivers. What?! Finally,
And yet, this sort of thing happens all the time. we had the real problem that needed a solution—the
electronic delivery of the calendars to the caregivers.
We could have simply iterated through the selected
PDF Combinatorics caregivers, generated each report in turn, and emailed
In a more recent example, the team was asked to the result to the caregiver. Instead, we were given a
allow a system to build a single large PDF report out potential “solution” but not given enough insight into
of what started as many individual small reports for a the problem to determine a better solution.
large number of medical caregivers. As it stood, the
system was capable of generating a calendar “report” I know that Agile software development practices
of each caregiver’s assignments for the month. The are typically either loved or hated, but regardless, the
request was to allow a user of our software to select standard story format does have a lot of value. “As
a bunch of caregivers and generate all of these a {role or user} I want {feature or change} so I can
individual calendars for use by the coordinators. {benefit}.” There’s a lot of potential value in a sentence
with that format. If the request above was spelled out
We were, and still are, using a reporting software this way, then it would have been the responsibility of
that allows us to build reports. One of the features is the developers to ask questions that would have led to
that reports can contain sub-reports, potentially ad the right solution.

“As a scheduling coordinator, I want to be able to

in 2 it
build combined caregiver schedules so I can print,
scan, and email them to the caregivers without
contracting arthritis from too many mouse clicks.”
In this case, the majority of the valuable information
in the request is in the “benefit” or supposed
benefit. If the goal is to deliver the report to each
caregiver, then printing and scanning are not
PROFESSIONAL  PHP  SERVICES necessary. The amount of effort could have been
reduced to selecting a list of caregivers and clicking
a button. The system could then have generated
PHP  Consul,ng  Services   and delivered those calendars. Enhancing the
solution even further, depending on the selections,
most or potentially all of the work could have been
Workflow  automa,on   automated. If the caregivers are associated with the
coordinator and they all need their calendars on
a specific schedule, the selection could be made
Training  and  coaching   via a database query and the triggering of all of
this could be done via a cron job. The scheduling
coordinator now has one less thing to worry about
and we’ve delivered a real solution to the real
www.in2it.be   problem instead of an imagined solution to an
unexplained and misunderstood concern.

56 | January 2016 www.phparch.com


Leveling Up
Finding the Solution to the Problem

Other misunderstood requirements have led to such solving the problems, and architecting maintainable
erroneous features as load-bearing notes, exceedingly solutions.
complex data structures that allowed employees, copy
machines, meeting rooms, and MRI machines to be If we’re creating code for fun, then it’s up to us to
treated in exactly the same way. By taking the time to determine how much time and effort we want to
understand exactly what is needed and why, we get spend. At the time I’m writing this, the Advent of
a better solution, and we’ll often get it cheaper and Code is running. Each day presents a new challenge
faster as well. Speaking of which… which can be solved with code. According to the
leaderboard, many of these problems could be
completed within a few minutes by a small set of elite
The Iron Triangle of Software developers. If you know your language of choice well,
Development and potentially know an algorithm or already have
a library with an implementation, the problems can
Many of you may be familiar with the so-called “iron be solved relatively quickly. If you’re not familiar with
triangle” of software development. On each of the some technologies, it may take a bit longer.
sides of this triangle are “Good,” “Fast,” and “Cheap,”
or, in other versions, “Quality,” “Speed,” and “Cost.” In one of the problems, you’re given a list of a group
In any given software project, you’re only able to of people along with a happiness change score for
pick up to two of them. If we pick “Good” and “Fast,” each pair of people. For instance, if Bob sits next to
we’re going to need an excellent team, which will Sue, his happiness may increase by 50 points, but hers
not be cheap. If we pick “Good” and “Cheap,” we drops by 10. The problem wants you to determine
can get something like open-source software: people the optimal seating of people around a table so that
volunteering what time they can, mostly for free. The the maximum increase in happiness is achieved. The
resulting software can be very good, but since it’s all solution I chose borrowed some permutation code
on volunteer time, we cannot dictate how long before from a previous day and generated all possible
it is done. If the choice is “Fast” and “Cheap,” perhaps permutations of the dinner guest order. Since this was
we’ve got a skilled team on a tight deadline or a team for fun, I decided to stop there. I was able to get the
of motivated beginners. Quality will likely suffer. correct answer. However, what I wrote was inefficient.

When we don’t take the time to determine what is


really needed and figure out the problem we want
to solve, there’s a good chance that we will only be
picking one (or fewer) of our two possibilities from

Sick of
the iron triangle. There’s no need for that.

Why Do We Build Software?


We build software for a number of reasons: to
scratch an itch, to learn, to solve a problem, to reduce
errors, to automate, to make money, or just for fun. shared
hosting?
Each of these reasons leads to a different answer
about how much effort we need to expend to answer
the questions about what to build and why.

If you’re building an MVP (minimum viable product)


or a prototype, fast and cheap may be the best
choices. Once it is determined that the product is
viable, then time and/or money may be more readily
available. The MVP can be scrapped and a better,
higher-quality replacement can be stood up in its
place. Control your destiny
If we’re building an application that we expect to
live for a while, then it makes sense to put more time
and effort into gathering the correct requirements,

www.phparch.com January 2016 | 57


Leveling Up
Finding the Solution to the Problem

In a circular seating arrangement, many of the After the code is cleaned up, the final step is “make
permutations are redundant. If everyone gets up it fast.” This means that we need to realize there is a
and moves a seat to the left, their happiness doesn’t trade-off in nearly everything we do. Sometimes we
change because they are still sitting next to the same trade runtime for memory. Other times, we might
people. If you flip the ordering from clockwise to call out to a custom C-compiled extension in order to
counter-clockwise, all of the guests are still next to speed things up, but perhaps at the expense of ease
the same people and their happiness is unchanged. of maintenance.
However, my solution didn’t account for this. This
means that with an eight-person table, my code Of course, the “make it right” and “make it fast” steps
was checking 40,320 possibilities. If it accounted are much easier and less scary if you’ve written tests
for rotations, only 5,040 possibilities would have in order to make it work. Whether you follow a TDD
remained. Since I was running this code once, and workflow (which I recommend) or you write the tests
then likely never again, the runtime being 8x over an afterward, having those tests in place as you refactor
optimized solution wasn’t that big of a deal (though will allow you to be confident that you haven’t traded
it did bother me a bit). If I had code that did this as a in your working solution for a better looking, but
service for customers, or for larger tables, the amount broken, solution.
of time needed to update the algorithm to only
generate what was needed could mean, on a popular
service, I may only need 1/8th of the servers and Why Being DRY is Not Always
resources. In that case, it makes sense to optimize. Best
As software developers, we have loads of acronyms
Make it Work, Make it Right, to help us remember various principles. There’s SOLID
(ironically, each letter in SOLID stands for its own
Make it Fast acronym), KISS (keep it simple, stupid), KICK (keep it
Once we’ve determine what to build and why, our complex, knucklehead), DRY (don’t repeat yourself),
next task is to make it happen. This means our first YAGNI (you ain’t gonna need it), GRASP (general
pass at the code can be messy, inefficient, and, to responsibility assignment software pattern), DDD
a certain extent, bad. But we should certainly not (domain-driven development), as well as DRY (don’t
stop at this phase. Once it is working, we move to repeat yourself).
the next step, “make it right.” This means we clean
up the code, put things right. In my throwaway code We are often taught that DRY is important. We are
above, I stopped after making it work. I got the answer told that if two bits of code are the same, they should
I needed. However, in our day jobs or hobbies, if we go in a function. If two bits are nearly the same, put the
want code that will last, we need to continue. Making common bits in a function, abstract out the variable
it work involves understanding the problem at a code parts to the parameters, lather, rinse, repeat. This
level—perhaps reaching the point that you know what means that often in my career I’ve seen designs and
libraries are needed, how the inputs fit together, even software go sideways when what was initially
understanding that if you’re dealing with a circular determined to be two different pieces of code doing
permutation, there are a lot fewer possibilities than a the same thing, followed by refactoring that to a
standard permutation. function, turns out to be two bits of code doing nearly
the same thing but for completely different reasons. In
The “make things right” phase is where refactoring that case, the redundant code is arguably less bad
can happen. You can DRY (don’t repeat yourself) than a single complex solution that must account for a
out your code by moving repeated functionality into plethora of different running conditions.
functions and methods, renaming variables and
functions to be more clear about what’s happening, “Duplication is far cheaper than the wrong
potentially simplifying what’s going on since our abstraction.”
understanding of the problem has likely increased. We
- Sandi Metz
can reorganize to make it easier to maintain. This can
include tasks like removing calls to creating objects
with new inside our constructors and providing those In some cases, DRYing out our code can actually be
objects via dependency injection. harmful. Just because two bits of code appear to be
doing the same thing doesn’t mean they are. The
quote above is by Sandi Metz, an outstanding speaker

58 | January 2016 www.phparch.com


Leveling Up
Finding the Solution to the Problem

and software engineer. I encourage you to find her videos and watch them. She is a Ruby developer, but the
talks I’ve seen apply no matter what language you’re working in. I highly recommend watching any of her talks or
reading what she has written.

The point here links back to the beginning. De-duplication of code just for the sake of having a lower score on
phpcpd serves no good purpose. It’s better to have identical code in a few places while we strive to understand
the real problem and determine the real solution than to DRY out the code to a point where we don’t understand
what’s really happening and modifications become costly and potentially impossible. Strive to make your code as
simple as you can, but no simpler.

Conclusion
I encourage you to look into the change requests, issues, tickets, stories, or whatever you may call them, and
determine what they are really asking. It’s our job as software developers to build solutions to real problems, not
just to turn one silly request into code after another. We are being paid to think more than we’re being paid to
type. This means we need to ask the right questions, dig deeper into understanding what we need to do, and
provide real solutions to the real problems our customers have. It’s not about changing the asterisk to another
shade of green, it’s about determining why that needs to happen and what problem is being solved.

David Stockton is a husband, father and Software Engineer. He builds software in


Colorado, leading a few teams of software developers creating a very diverse array of web
applications. His two daughters, age 11 and 10, are learning to code JavaScript, Python,
Scratch, a bit of Java and PHP as well as building electrical circuits. His 4 year old son has
been seen studying calculus, practicing linguistics with regards to human biology, and is
excelling at annoying his sisters. David is a conference speaker and an active proponent of
TDD, APIs and elegant PHP. He’s on twitter as @dstockto, youtube at
http://youtube.com/dstockto, and can be reached by email at
[email protected].

Twitter: @dstockto

Get up and running fast with


PHP, MySQL, & WordPress!
UPCOMING TRAINING COURSES

PHP Essentials MySQL Essentials for PHP


starts January 11, 2016 starts February 1, 2016

Advanced PHP Development PHP Foundations for Drupal 8


starts January 29, 2016 starts February 10, 2016

www.phparch.com/training
www.phparch.com January 2016 | 59
Security Corner

Passwords are Dead,


Long
123456
Live Passwords!
superman fdsa nicole
Chris Cornutt
password 696969 753951 killer
12345 123123 chocolate abcdef
12345678 batman soccer hannah
qwerty trustno1 tigger test
123456789 daniel asdasd alexander
1234 computer jennifer andrew
baseball michael jordan 222222
dragon 121212 abcd1234 joshua
football charlie trustno1 freedom
1234567 master buster samsung
monkey superman 555555 asdfghj
letmein qwertyuiop liverpool purple
abc123 112233 abc ginger
111111
Anyone who’s been aroundasdfasdf whatever
web applications (or really 123654
any applications) that
need to protect data or restrict access to only a certain group of users has matrix
mustang jessica 11111111
experience with passwords. They’re a de facto standard when it comes to
access applications. Along
protecting with usernames (or102030
1q2w3e4r email addresses, dependingsecret
on the system), they’re used
shadow to identify the end user
welcome and verify that they aresummer
123123123 who
they claim to be. Unfortunately, there are many things wrong with them that
master
make them one of the worst options in applicationandrea
1qaz2wsx protection. There are whole1q2w3e
michaelaround removing
industries 987654321
or reinforcing passwordspepper snoopy1
in applications, yet they’re
still a huge part of the security of most services out there. I’m getting ahead of
myself, though. First off, let’s review a little history of where passwords came from.

DisplayInfo()

Related URLs:
• PHP Password Hashing–http://php.net/book.password

60 | January 2016 www.phparch.com


Security Corner
Passwords are Dead, Long Live Passwords!

Where Did Passwords Come Password Reuse


from, Anyway? We’ve all done it before. If you say you haven’t,
you’re probably fibbing just a bit. There are so many
The concept of a password or passphrase has been services out there, and we’re constantly signing up
around about as long as there have been secrets to for more and more every day. With almost all of them
protect. Way back in history, this something you know using the same basic methods for authentication
was used for everything from sharing information (again, the infamous username+password combo),
between groups to allowing access to certain physical it’s very easy to slip into reusing the same password
areas not open to everyone. Fast-forward to more across multiple services.
recent times, and there’s no shortage of movies and
books out there in which spies use them to identify Why is this a bad thing? Well, imagine you signed up
each other or messages are protected with a password at SuperAwesomeHosting.com with an email address
only the intended recipient should know. for the username and a password that just happens
to be the same as the one for your email account. The
Passwords were first introduced into the world hosting company assures you that security is a top
of computers in the early 1960s. A group at MIT in priority for its customers and that your information is
Massachusetts decided that they needed a way to 100% safe in its systems. One day an attacker stumbles
segregate the time that people had to spend on the over an unprotected development copy of the site and
shared computing systems owned by the university. discovers an SQL injection vulnerability, and harvests
Passwords were used in their Compatible Time- all of the live user data—including your credentials.
Sharing System (CTSS). They even had the notion Now the attacker has the credentials not only for your
of protecting this password and not echoing it back SuperAwesomeHosting.com account but also for your
out to the user as he or she typed. Incidentally, many email account. Think of everything they could do if
Unix-based systems still do this, while most web they made the jump and tried to log in to your email!
applications use a password type form field that,
though masking, still gives a visual indication of how As a developer, it’s almost impossible to prevent this
long the password is. from happening, unfortunately. The only thing you can
do is try to enforce good password policies and hope
Move forward another 10 years or so, and another users don’t shoot themselves in the foot.
password improvement came along in the form
of hashed passwords. Robert Morris added this
Bad Passwords
functionality to the origins of the operating system
we know as Unix, which used a simpler version of the When I give presentations about security at
standard crypt() functionality to protect password conferences and online training, there’s one thing I say
contents. Even then they realized that having just a every single time: “People are terrible at passwords.”
plain-text, human-readable password somewhere As humans, there’s a built-in desire to make things as
wasn’t the best or most secure method for protecting simple as possible. This is the same habit that leads to
valuable resources. password reuse. Unfortunately, it also makes us lazy
about the passwords we come up with. We use things
Since then, password storage methods and usage like our pet’s name, the date we got married, or words
have evolved, but the heart of the usage is still the right from the dictionary. Take a quick look through
same. Passwords are still a single point of failure that’s anyone’s social media pages and you can find answers
usually combined with a much more public piece of to most of these and just start guessing. Even worse,
information, a username, to restrict access to portions when it comes time to reset your password, most
of applications. This is the real key to the problem: people will just tack on a 1 or ! and call it good.
they only offer a single point of protection that all too
often is easily compromised, leaving the system wide This is where password policies come in. These
open to attack. policies help guide users to create good passwords
that will effectively protect not only their own account
but also your service. There’s one key thing to
Common Password Problems remember when setting up your policies: the phrase
I’ve already mentioned one of the major problems “at least.” The real key to effective policies is to set
with passwords: the single point of failure they them up so that you define minimum requirements
provide. But there are a few others that contribute to and then let the user go wild from there. Sure, this can
most of the password-centric vulnerabilities out there. still lead to some pretty bad passwords, but there are
plenty of tools out there that will measure the entropy
of the password to ensure it’s strong enough.
www.phparch.com January 2016 | 61
Security Corner
Passwords are Dead, Long Live Passwords!

Bad Password Storage


I want to touch on one last topic that’s a bit more developer-focused than the others in this (non-exhaustive)
list. Users put their trust in you to keep their private information safe, including the passwords they provide.
They perceive this as one of the keys to your having a secure system, so it makes sense that you’d protect this
information accordingly. Unfortunately, we hear of service after service that was
either storing their users’ passwords in plain text or, while making an effort to
“encrypt” them, only thought that an md5 of the password value was enough. If you’re doing either if these
things, stop right now and go
Passwords should always be one-way hashed with a strong method prior fix your application. There are
to being stored. Any company that can send you a plain-text version of your a lot of reasons that people
password or that can read it back to you during a support call is without a doubt give for storing passwords
doing it wrong. There’s no need for anyone other than the user themselves to poorly, but they’re all
know their password. invalid.

So how can I do this effectively in my application? Fortunately, it’s been made super simple since PHP 5.5 with
the password hashing functions. It’s literally a two-line process to hash what the user gives you and verify if it’s
correct:

<?php
// To hash the password
$storeMe = password_hash($_POST['userInput'], PASSWORD_DEFAULT);

// To verify the password


if (password_verify($_POST['userPassword'], $storeMe) === true) {
echo 'yay!';
}

This method currently uses an algorithm called bcrypt that rehashes the string The cost affects how
provided a number of times based on the “cost” value. PHP’s default cost is 10 difficult a password hash
unless you define it as an option. is to crack and is meant
to increase as hardware
becomes more capable.
And Finally
Passwords are flawed, there’s no doubt about it. I mentioned tools and services that have come up around the
password ecosystem and that want to help remediate some of the risk associated with using passwords—two-
factor authentication and federated identity being two of the more popular options. These can do a lot to help
improve the overall security stance of the application and prevent other problems from happening, but with a
password at the core of it all, a lot of risk will still be involved.

If the only protection between your application and the outside world is a simple user-defined string of text, it
might be time to rethink and reinforce your systems. Trust me, a password just doesn’t cut it.

For the last 10+ years, CHRIS has been involved in the PHP community in one way or another.
These days he’s the Senior Editor of PHPDeveloper.org and lead author for Websec.io, a site
dedicated to teaching developers about security and the Securing PHP ebook series. He’s
written for several PHP publications and has spoken at conferences in both the U.S. and
Europe. He’s also an organizer of the DallasPHP User Group and the Lone Star PHP
Conference and works as an Application Security Engineer for Salesforce.

Twitter: @enygma

62 | January 2016 www.phparch.com


Community Corner

Interview with Jeffrey Carouth


Joe Devon

Q & A with Jeff Carouth


Hello, Jeff. Can you tell us a bit about yourself? As for people who get a lot of rejections, you are
This isn’t an area I’m very good at, but I’ll give it just like me. I don’t mean that to be flippant, but
a shot. I am a software developer. I’ve focused on it is just part of the reality. If you are getting only
developing web applications since the first time rejections, I would suggest seeking some advice on
I wrote some HTML in 1998. I was immediately what you’re submitting. Sometimes the wording is just
hooked. I’m now a Lead Platform Engineer at Liftopia, not conveying what you think. Sometimes the topic
which is a fancy title to say I work with a team of doesn’t fit with the conference’s objectives. There
developers to develop an API platform to power our are a million reasons. I wrote a blog post to cover this
e-commerce platform. I live in a smaller city in Texas topic titled My Proposal Was Rejected, Now What? at
named College Station. I moved here in 2003 to http://phpa.me/carouth-proposal-rejected.
attend Texas A&M University and never left. I’m now
The main thing to remember is rejection is an
married to a wonderful woman and have a three-year-
unfortunate part of this endeavor. It’s not personal—
old, a German Shepherd, and two cats. One of my
keep submitting and you’ll get accepted.
favorite things to do is talk about problems, which is
probably why I like to present at conferences, co-host Tell us about your experiences with the PHP
a podcast, and have written several articles sharing my Mentoring movement
experiences.
When the movement started out I was an apprentice
How many conferences do you speak at each to the huggable, lovable Grumpy Programmer Chris
year? Hartjes. We talked a little at Lone Star PHP about
the testing situation I was in, and then when he was
I’ve been speaking at conferences since 2011.
available as a mentor I was happy to be selected to
I’ve averaged four a year in that time frame, with a
be his apprentice. We had a lot of great conversations
maximum of seven in 2014.
about challenges with testing in legacy environments
About how many acceptances vs. rejections and how to effect change on codebases through tests.
do you get on CFP submissions? What do After that I took on a series of apprentices in various
you recommend to people who get a lot of forms. I’ve helped people with testing, application
rejections? architecture, career advancement (moving from
more junior to more senior), among other things. I
That’s a great question. I would be lying if I said I have to say the greatest thing about participating is
didn’t go research this in my inbox to find out. If you I have learned a lot by being a mentor to others. The
could total the number of submissions vs. rejected best feeling in the world is graduating someone and
talks, the number is quite high, but that is to be encouraging them to take on an apprentice.
expected. Over the past year I submitted to nine total
conferences and was accepted to four of them. The What is Loosely Coupled?
year before that I had a higher acceptance rate. It all
Loosely Coupled, http://looselycoupled.info, is a
balances out.
podcast I co-host with Matt Frost. It started out as a

www.phparch.com January 2016 | 63


Community Corner
Interview with Jeffrey Carouth

podcast I was going to host where I covered mostly How about the Dev Book Club?
architectural topics and so-called “best practices” of The Dev Book Club, http://devbookclub.org––which,
software design and development. It morphed into thank you, Joe, for the reminder that I need to get
a co-hosted show where we talk about developer that back in gear––was born out of a conversation
practices of all sorts. I think it’s an interesting show, but at ZendCon one year where we were talking about
I might be biased. Domain-Driven Design and the Red vs. Blue Book
question. If you aren’t familiar, there are two books
Was it easier or harder than you expected
about DDD, one with a red cover and one with a blue
to start a podcast? What have you gained or
cover. There is often discussion about which you
learned from the process?
should read, which first, etc. This same conversation
It was and is a little harder than I expected. We happened in real time at ZendCon and it got me
decided to start the show because we sat down in thinking, “What if we created a forum for developers
a hotel lobby at Midwest PHP 2014 and talked for from all over the world to read and discuss tech
about two hours with some other people about all books?” Hence, Dev Book Club was born.
sorts of things we were going through at the time. It
was a great conversation and I felt it was enjoyable The idea is to provide a low-friction forum for
to everyone listening and participating. But an actual discussing the ideas and practices presented in
podcast is a little more work than that. various tech-related books with peers from all levels
and situations. We pick a book, get a group together,
I have learned a lot and gained a lot of respect and meet regularly. There will be some more on Dev
for people who do this more regularly than me. Book Club coming soon.
Producing, hosting, marketing, planning, etc. a
podcast is not easy. But out of the podcast, just in What’s the PHP Community like in College
recording the shows, I have definitely refined my Station, Texas?
own practices. I guess you could say I like doing the College Station is a smaller town, home of Texas A&M
podcast because it teaches me things as much as it is University. The majority of the tech scene is from that
about me sharing my experiences. arena. However, there is a large group of WordPress
consultants and other language/framework groups as
well. That’s one thing I really like about the community
here: it’s very low-judgment and we tend to focus less
on languages and more on ideas that cross the barriers
of frameworks and languages.

You have spoken about Domain-Driven Design.


What is DDD?
That’s quite a big topic. I presented a talk on Domain-
Driven Design fundamentals at Lone Star PHP one
year. To answer your question, I would say DDD is a set
of practices and patterns aimed at focusing solutions
to complicated projects on the core domain model
through integration and conversation with domain
experts. That’s a convoluted way to say that DDD is
Open source partner Bring the open source
solutions in Marketplace stack and tools you love about gaining a deep understanding of the problem
domain by asking questions, thinking about models,
talking with the real experts, and translating that into a
conceptual model that can be implemented in code.

The talk I presented focused on the idea of modeling


and then the concrete implementation patterns that
are essential building blocks of understanding how to
take the model and turn it into software. There is a lot
more to it than could be presented in an hour long talk,
or even a three-hour tutorial, but that covers the gist of
what DDD attempts to teach you.

Thanks Jeff, pleasure talking to you! Jeff can be


reached on twitter at @jcarouth
64 | January 2016 www.phparch.com
Community Corner
Interview with Jeffrey Carouth

Past Events
PHP Conference Brazil Day Camp 4 Developers
December 2–6, Osasco, Brazil December 18, Online
http://www.phpconference.com.br http://daycamp4developers.com

Symfony Live Paris


December 3–5, Paris, France
http://pariscon2015.symfony.com

Upcoming Events
Ski PHP 2016 Midwest PHP 2016
January 14–15, Salt Lake City, UT March 4–5, Minneapolis, MN
http://www.skiphp.com http://2016.midwestphp.org

PHPBenelux IPC 2016


January 29–30, Antwerp, Belgium May 29–June 2, Berlin, Germany
http://phpbenelux.eu https://phpconference.com

SunshinePHP 2016 Lone Star PHP


February 4–6, Miami, FL April 7–9, Dallas, TX
http://sunshinephp.com http://lonestarphp.com

Joomla Day UK New Orleans DrupalCon


February 13, London, Victoria, UK May 9–13, New Orleans, LA
http://www.joomla-day.uk https://events.drupal.org/neworleans2016

PHP UK, phpDay 2016


February 18–19, London, UK May 12–14, Verona, Italy
http://phpconference.co.uk http://2016.phpday.it

DrupalCon Asia php[tek] 2016


February 18–21, Mumbai, India May 23–27, St. Louis, MO
https://events.drupal.org/asia2016 http://tek.phparch.com

ConFoo PHP South Coast


February 24–26, Montreal, Canada June 10-11, Portshmouth, UK
http://confoo.ca https://cfp.phpsouthcoast.co.uk

Drupalcamp London 2016 php[cruise]


March 4–6, London, U.K. July 17–24, Baltimore, MD (leaving from)
http://drupalcamp.london https://cruise.phparch.com

By the Community, for the Community


If there are particularly compelling talks I should cover in this column, or PHP-related conferences coming up,
please reach out and let me know at [email protected].

JOE DEVON has been developing in PHP since Version 3. He is an advisor or board member
at various companies, non-profits and conferences. He is Founding Partner of LAPHP
user group; Diamond, a Digital Agency in Venice; Television Four, a Digital Media startup
and Global Accessibility Awareness Day (#GAAD). He is passionate about Digital
Accessibility & encouraging the UX & Development Community to produce accessible
content. He blogs at http://dws.la/author/joedevon and may be reached on LinkedIn
http://linkedin.com/in/joedevon, Twitter http://twitter.com/joedevon and email at
[email protected].

Twitter: @ joedevon
www.phparch.com January 2016 | 65
MONTH IN REVIEW: DECEMBER HAPPENINGS
Brought to you in conjunction with <?PHPDeveloper.org
PHP Releases
PHP 7.0.1:
http://php.net/archive/2015.php#id2015-12-17-1

News
Community News: PHP 7 Has Arrived (and Everyone’s Talking About It)
The big news in the PHP ecosystem is the release of the stable version of PHP 7.0.0. This was officially released late
yesterday and the response has already been great. Members of the PHP community (and some companies) have
also posted about the release too.
http://phpdeveloper.org/news/23435

Laravel News: Laravel 5.2 is released!


The Laravel News site has posted about the release of Laravel 5.2, the next minor release in the 5.x series of the
framework. Along with this release comes several new features and additions to current ones. He then gets into a
bit of detail about these new features added including: Auth Scaffolding, Implicit model binding, Laravel 5.2 Form
Array Validation, and more.
http://phpdeveloper.org/news/23497

Jordi Boggiano: New Composer Patterns


Jordi Boggiano, lead developer on the Composer has posted about some of the new Composer patterns that have
been introduced into the tool this year, including some you might not even have realized. He includes five of these
features in his list (but something tells me these are just some of the more user-facing improvements the project
has introduced): Checking dependencies for bad patterns, Referencing scripts to avoid duplication, Defining your
target production environment in composer.json, Excluding paths from the optimized classmap, and Requiring
packages easily and safely.
http://phpdeveloper.org/news/23491

Larry Garfield: Drupal 8: Happy, but not Satisfied


In this new post to his site Drupal developer Larry Garfield talks about why he’s “happy, but not satisfied” about
the release of Drupal 8 and the current state of the code in this major milestone. Among the things on his “happy”
list are the fact that you can “teach an old codebase new tricks”, that there’s a real framework underneath it and
that more modern development styles are being followed.
http://phpdeveloper.org/news/23430

Zend Developer Zone: On Security and PHP


On the Zend Developer Zone Cal Evans has posted an article about a topic that’s always hot in any development
community - security. In his post, “On Security and PHP”, he comments on some recent metrics reported by a
larger application security company and provides a bit more realistic view into the world of PHP security (and
some possible downfalls of their metrics).
http://phpdeveloper.org/news/23464

Community News: Bulgaria PHP Conference 2015 Videos Posted


This year’s Bulgaria PHP Conference has officially posted the videos from all of their sessions at this year’s event
to the official conference website. There’s lots of great sessions from this year’s event including talks from Larry
Garfield, Ilia Alshanetsky and Beth Tucker Long.
http://phpdeveloper.org/news/23493

66 | January 2016 www.phparch.com


finally{}
Eli White

Different Paths for the


P’s in LAMP

As happened by complete accident in the English language, the P in LAMP can


represent three different languages: Python, Perl, and PHP. Recently, an article
was shared with me that was written in the Python community about Python 3
and why it exists; see Related URLs. But it had a very specific point that made me
take a step back and evaluate how PHP 7 came into being. It stated:

“Obviously it will take decades to see if Python 3 code in the world


outstrips Python 2 code in terms of lines of code.”

Python
So why does Python 3 exist? In one word: Unicode. However, as people who followed the PHP 6 days
That’s right, the same issue that plagued PHP 6 (and know, this comes with some issues. Primarily, it comes
more on that later). with performance hits that many people didn’t want.
So while the coders of Python originally assumed that
So in Python, a string and a blob of bytes were over time people would just start using the unicode
actually the exact same thing in memory. It was only if type instead of str, they didn’t.
you parsed them as type str or bytes that changed
how they were treated inside your program. This, of So the decision was made that for Python 3, it would
course, can lead to some interesting situations. To be 100% Unicode all the time. Just like PHP 6 was
meet the need of people wanting/needing to do going to be. In this case, with the implied statement
Unicode work inside Python, they created a separate of: “And we don’t care if this means that people will
type, unicode, which you could declare instead to stay on Python 2 because they don’t like that.”
mean that the variable was truly, 100%, definitely, text.
So now, the community for Python has fragmented,
with different groups clinging to 2 vs. 3.

DisplayInfo()

Related URLs:
• Why Python 3 exists—http://www.snarky.ca/why-python-3-exists

www.phparch.com January 2016 | 67


Different Paths for the P’s in LAMP

Perl PHP
For those of us with enough white hair on our Which leads us to PHP. In my opinion, PHP almost
heads, we can see parallels in Perl 6. Perl 6 is the suffered this same fate with the move from 5 to 6 to 7…
language update that has essentially never come. but we have some smart people on the internals team
At a conference in 2000, Larry Wall announced the who have kept this from happening.
concept of Perl 6 and that work would begin on it. To
this day, Perl 6 has never actually been released. In case you aren’t familiar with “What became of
PHP 6”: It was an attempt, very similar to Python 3
(and tangentially similar to Perl 6), to refactor the
To be pedantic, there is an
language completely to make it Unicode-compliant
implementation (Rakudo Perl) that is all throughout the language. Every place that used
under active development and can be strings would natively and consistently use Unicode.
used, but there has never really been
a true release of Perl 6, and Perl 5 A couple of things happened, however, which
scuttled this plan after many years in development.
continues to be the de facto version in
First of all, the choice was made to go UTF-16 instead
use. of UTF-8. This complicated a number of issues (and the
Web settled on UTF-8 as a default in the meantime).
From the beginning, the intent of Perl 6 was to Part of the problem is that C-libraries can’t natively
completely break backwards-compatibility with Perl handle UTF-8, and there is no direct indexing for UTF-
5—but at the same time still feel like a “Perl Language.” 16. So numerous decoding steps had to happen in
the code to make this work. This caused a number of
In Perl 6, we decided it would be better really crucial, everyday-use features of PHP to lag and
slow down: database access, input filtering, and string
to fix the language than fix the user. searches, to mention a few.
— Larry Wall In the end, the project was scrapped, and PHP
moved on. PHP 5.3 came out, adding in a number of
There is a lot of history, which I won’t cover. The effect features, many of which were back-ported from the
was that Perl 6 stagnated within the Perl community, work on PHP 6. Since then, we’ve followed a much
simply because it wasn’t a natural progression from more evolutionary approach. Now PHP 7 has come
Perl 5. Users stuck with the previous version because it and added some of the biggest new features that PHP
was meeting their needs, and Perl 5 has continued to has seen, such as static type hinting and return types.
iterate along that path (with their most recent release And through the collective powers of the internals
being Perl 5.22 in June 2015). team, it was smartly done in a very backwards-
compatible way.
Because of the drastic change in the language,
instead of a more natural evolution, the community
fragmented. While I don’t have the perfect chart of There are no shortcuts in evolution.
data to show you, anecdotally this relates to the time
frame (2000) when Perl really started to see a drop — Louis D. Brandeis
in usage, as more and more programmers started
moving over to Python and PHP at that time. Both There is no reason for someone to “Stay on PHP 5”
were growing and continuing to evolve. because they don’t like how the language changed.
That’s a good thing. It encourages a healthy upgrade
cycle. It keeps the community unified. Without a
natural evolution approach, splits in the community
(or even forks in the project) are inevitable. And that
would hurt us all.

ELI WHITE is the Managing Editor & Conference Chair


for php[architect] and a Founding Partner of
musketeers.me, LLC (php[architect]’s parent company).
He prefers his P in LAMP to be “pie” (and his waistline
shows it)
Zend Framework 1 to 2
Migration Guide
by Bart McLeod

Zend Framework 1 was one of the


first major frameworks for PHP 5
and, for many, introduced object-
oriented programming principles
for writing PHP applications. Many
developers looking to embrace a
well-architected and supported
framework chose to use it as the
foundation for their applications.
Zend Framework 2 is a
significant improvement over
its predecessor. It re-designed
key components, promotes
the re-use of code through
modules, and takes advantage
of features introduced in PHP 5.3 such as
namespaces.
The first release of ZF1 was in 2006. If you’re maintaining an application built on it, this
practical guide will help you to plan how to migrate to ZF2. This book addresses common
issues that you’ll encounter and provides advice on how best to update your application
to take advantage of ZF2’s features. It also compares how key components—including
Views, Database Access, Forms, Validation, and Controllers—have been updated and how
to address these changes in your application code.

Written by PHP professional and Zend Framework contributor, coach, and consultant Bart
McLeod, this book leverages his expertise to ease your application’s transition to Zend
Framework 2.

Purchase
http://phpa.me/ZFtoZF2
Borrowed this magazine?

Get php[architect] delivered to your


doorstep or digitally every month!

Each issue of php[architect]


magazine focuses on an important
topic that PHP developers face
every day.

We cover topics such as frameworks, security,


ecommerce, databases, scalability, migration,
API integration, devops, cloud services,
business development, content management
systems, and the PHP community.

Digital and Print+Digital


Subscriptions
magazine
Starting at $49/Year
books
conferences
training http://phpa.me/mag_subscribe
www.phparch.com

You might also like