The Components implement common features needed to develop websites.
+
They are the foundation of the Symfony full-stack framework, but they can
+also be used standalone even if you don't use the framework as they don't
+have any mandatory dependencies.
+
There are ~30 components, including:
+
BrowserKit EventDispatcher OptionsResolver Templating
+ClassLoader ExpressionLanguage Process Translation
+Config Filesystem PropertyAccess VarDumper
+Console Finder PropertyInfo Yaml
+CssSelector Form Routing
+Debug HttpFoundation Security
+DependencyInjection HttpKernel Serializer
+DomCrawler Intl Stopwatch
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Getting Ready With Components
+
+
+
Say you want to play with YAML files, start by requiring the symfony/yaml
+component into your composer.json file:
+
{
+ "require":{
+ "symfony/yaml":"~3.0"
+ }
+}
+
+
+
Install it by running php composer.phar install, and use it:
The Symfony Framework accomplishes two distinct tasks:
+
+
Provides a selection of components;
+
Provides sensible configuration and a "glue" library that ties all of these
+ pieces together.
+
+
The goal of the framework is to integrate many independent tools in order to
+provide a consistent experience for the developer. Even the framework itself is
+a Symfony bundle (i.e. a plugin) that can be configured or replaced entirely.
+
Symfony provides a powerful set of tools for rapidly developing web
+applications without imposing on your application.
useSymfony\Component\HttpFoundation\Request;
+
+$request=Request::createFromGlobals();
+
+// the URI being requested (e.g. /about) minus any query parameters
+$request->getPathInfo();
+
+// the HTTP verb
+$request->getMethod();
+
+// GET variables
+$request->query->get('foo');
+
+// POST variables
+$request->request->get('bar');
+
+// SERVER variables
+$request->server->get('HTTP_HOST');
+
+// retrieve an HTTP request header, with normalized, lowercase keys
+$request->headers->get('host');
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Symfony Response
+
+
+
useSymfony\Component\HttpFoundation\Response;
+
+$response=newResponse();
+
+$response->setContent(<<<HTML
+<html>
+ <body>
+ <h1>Hello world!</h1>
+ </body>
+</html>
+HTML
+);
+
+$response->setStatusCode(200);
+
+$response->headers->set('Content-Type','text/html');
+
+// prints the HTTP headers followed by the content
+$response->send();
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Simplest Front Controller Ever
+
+
+
// index.php
+useSymfony\Component\HttpFoundation\Request;
+useSymfony\Component\HttpFoundation\Response;
+
+$request=Request::createFromGlobals();
+$path=$request->getPathInfo();
+
+if(in_array($path,['','/'])){
+ $response=newResponse('Welcome to the homepage.');
+}elseif('/hello'===$path){
+ $response=newResponse('hello, World!');
+}else{
+ $response=newResponse('Page not found.',404);
+}
+
+$response->send();
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Symfony Application Flow
+
+
+
It's all about transforming a Request into a Response:
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Routing Definition
+
+
+
The routing system determines which PHP function should be executed based on
+information from the request and routing configuration you've created.
An application consists of a collection of "bundles" representing all of the
+features and capabilities of your application.
+
Each "bundle" can be customized via configuration files written in YAML, XML
+or PHP.
+
By default, the main configuration file lives in the app/config/
+directory and is called either config.yml, config.xml or config.php
+depending on which format you prefer.
+
Symfony is all about configuring everything, and you can do pretty much
+everything you want. That's why people agreed on some conventions, but then
+again, a convention is just A way to do things, not THE way to do them.
A Bundle is a directory containing a set of files (PHP files, stylesheets,
+JavaScripts, images, ...) that implement a single feature (a blog, a forum,
+etc).
+
It should be reusable, so that you don't reinvent the wheel each time you
+need a common feature. In Symfony, (almost) everything lives inside a bundle.
+
In order to use a bundle in your application, you need to register it in the
+AppKernel, using the registerBundles() method:
The web root directory is the home of all public and static files including
+images, stylesheets, and JavaScript files. It is also where each front
+controller lives:
The front controller file (app.php in this example) is the actual PHP file
+that's executed when using a Symfony application and its job is to use a
+Kernel class, AppKernel, to bootstrap the application, for a given
+environment.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Summary
+
+
+
Creating a page is a three-step process involving a route, a controller, and
+(optionally) a template.
+
Each project contains just a few main directories: web/ (web assets and the
+front controllers), app/ (configuration), src/ (your bundles), and vendor/
+(third-party code).
+
Each feature in Symfony (including the Symfony framework core) is organized
+into a bundle, which is a structured set of files for that feature.
+
The configuration for each bundle lives in the Resources/config directory of the
+bundle and can be specified in YAML, XML or PHP.
+
The global application configuration lives in the app/config/ directory.
+
Each environment is accessible via a different front controller (e.g. app.php
+and app_dev.php) and loads a different configuration file.
A controller is a PHP function you create that takes information from the
+HTTP request and constructs and returns an HTTP response.
+
Every request handled by a Symfony project goes through the same lifecycle:
+
+
Each request is handled by a single front controller file (e.g. app.php or
+app_dev.php) that bootstraps the application;
+
The Router reads information from the request (e.g. the URI), finds a route
+that matches that information, and reads the _controller parameter from the
+route;
+
The controller from the matched route is executed and the code inside the
+controller creates and returns a Response object;
+
The HTTP headers and content of the Response object are sent back to the
+client.
Every route must have a _controller parameter, which dictates which controller
+should be executed when that route is matched.
+
This parameter uses a simple string pattern called the logical controller name.
+The pattern has three parts, each separated by a colon: bundle:controller:action.
+
For example, a _controller value of AcmeBlogBundle:Blog:show means:
+
+
Bundle: AcmeBlogBundle;
+
Controller Class: BlogController;
+
Method Name: showAction.
+
+
Notice that Symfony adds the string Controller to the class name (Blog =>
+BlogController) and Action to the method name (show => showAction).
Symfony comes with a base Controller class that assists with some of the most
+common controller tasks and gives your controller class access to any resource
+it might need:
If you need to get the content of a block from the parent template, you can
+use the {{ parent() }} function.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Template Naming and Locations (1/2)
+
+
+
By default, templates can live in two different locations:
+
+
app/Resources/views/: The applications views directory can contain
+ application-wide base templates (i.e. your application's layouts),
+ templates specific to your app as well as templates that override bundle
+ templates;
+
path/to/bundle/Resources/views/: Each (public) bundle houses its templates in its
+ Resources/views directory (and subdirectories).
+
+
Symfony uses a bundle:controller:template string syntax for templates.
+
You can skip the controller string: bundle::template. The template
+file would live in Resources/views/.
+
You can also skip the bundle string. It refers to an application-wide base
+template or layout. This means that the template is not located in any bundle,
+but instead in the root app/Resources/views/ directory.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Template Naming and Locations (2/2)
+
+
+
Example
+
AcmeBlogBundle:Blog:index.html.twig
+
+
+
+
AcmeBlogBundle: (bundle) the template lives inside the AcmeBlogBundle (e.g.
+src/Acme/BlogBundle);
+
Blog: (controller) indicates that the template lives inside the Blog
+subdirectory of Resources/views;
+
index.html.twig: (template) the actual name of the file is index.html.twig.
+
+
Assuming that the AcmeBlogBundle lives at src/Acme/BlogBundle, the final
+path to the layout would be:
In order to override the bundle template, copy the index.html.twig template
+from the bundle to: app/Resources/FooBarBundle/views/Bar/index.html.twig.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Overriding Core Templates
+
+
+
The core TwigBundle contains a number of different templates that can be
+overridden by copying each from the Resources/views/ directory of the
+TwigBundle to the app/Resources/TwigBundle/views/ directory.
The asset_version parameter is used to bust the cache on assets by globally
+adding a query parameter to all rendered asset paths:
+
/images/logo.png?v2
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Linking To Pages In JavaScript
+
+
+
The FOSJsRoutingBundle
+allows you to expose your routing in your JavaScript code. That means you'll
+be able to generate URL with given parameters like you can do with the Router
+component provided by Symfony.
app.environment: the current environment (dev, prod, etc);
+
app.debug: true if in debug mode. false otherwise.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Service Container
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What Is A Service?
+
+
+
A Service is a generic term for any PHP object that performs a specific task.
+
A service is usually used globally, such as a database connection object or an
+object that delivers email messages.
+
In Symfony, services are often configured and retrieved from the service
+container.
+
An application that has many decoupled services is said to follow a
+Service-Oriented Architecture (SOA).
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What Is A Service Container?
+
+
+
A Service Container, also known as a Dependency Injection Container
+(DIC), is a special object that manages the instantiation of services inside
+an application.
+
The service container takes care of lazily instantiating and injecting
+dependent services.
In the definition above, kernel.debug is a parameter defined by the framework
+itself. The foo service is now parametrized.
+
Also, it becomes easy to change the implementation of this service by simply
+overriding the my_bundle.foo.class parameter.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Injecting Services
+
+
+
As you may noticed, the Foo class takes an instance of Bar as first
+argument. You can inject this instance in your foo service by
+referencing the bar service:
A service container extension is a PHP class to accomplish two things:
+
+
import all service container resources needed to configure the services for
+ the bundle;
+
provide semantic, straightforward configuration so that the bundle can
+ be configured without interacting with the flat parameters of the bundle's
+ service container configuration.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Creating an Extension Class
+
+
+
An extension class should live in the DependencyInjection directory of your
+bundle and its name should be constructed by replacing the Bundle suffix of
+the Bundle class name with Extension.
The processConfiguration() method uses the configuration tree you've defined
+in the Configuration class to validate, normalize and merge all of
+the configuration arrays together.
assets
+ assets:install Installs bundles web assets under a public
+ web directory
+cache
+ cache:clear Clears the cache
+ cache:warmup Warms up an empty cache
+config
+ config:dump-reference Dumps default configuration for an extension
+container
+ container:debug Displays current services for an application
+debug
+ debug:container Displays current services for an application
+ debug:router Displays current routes for an application
+router
+ router:match Helps debug routes by simulating a path info
+ match
+server
+ server:run Runs PHP built-in web server
+translation
+ translation:update Updates the translation file
+lint
+ lint:twig Lints a template and outputs encountered
+ errors
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Creating Commands
+
+
+
Create a Command directory inside your bundle and create a php file suffixed
+with Command.php for each command that you want to provide:
When initially loading the page in a browser, the request method is GET
+and the form is simply created and rendered;
+
+
+
When the user submits the form (i.e. the method is POST) with invalid
+data, the form is bound and then rendered, this time displaying all
+validation errors;
+
+
+
When the user submits the form with valid data, the form is bound and you have
+the opportunity to perform some actions before redirecting the user to some
+other page (e.g. a "success" page).
+
+
+
Redirecting a user after a successful form submission prevents the user from
+being able to hit "refresh" and re-post the data.
Placing the form logic into its own class means that the form can be easily
+reused elsewhere in your project.
+
This is the best way to create forms, but the choice is up to you!
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The processForm() Method (1/2)
+
+
+
Saving or updating an object is pretty much the same thing. In order to avoid
+code duplication, you can use a processForm() method that can be used in both
+the newAction() and the updateAction():
+
/**
+ * Create a new Person
+ */
+publicfunctionnewAction(Request$request)
+{
+ return$this->processForm($request,newPerson());
+}
+
+/**
+ * Update an existing Person
+ */
+publicfunctionupdateAction(Request$request,$id)
+{
+ $person=...;// get a `Person` by its $id
+
+ return$this->processForm($request,$person);
+}
+
CSRF is a method by which a malicious user attempts to make your legitimate
+users unknowingly submit data that they don't intend to submit. Fortunately,
+CSRF attacks can be prevented by using a CSRF token inside your forms.
+
CSRF protection works by adding a hidden field to your form, called _token
+by default that contains a value that only you and your user knows.
+
This ensures that the user is submitting the given data. Symfony automatically
+validates the presence and accuracy of this token.
+
The _token field is a hidden field and will be automatically rendered if you
+include the form_rest() function in your template, which ensures that all
+un-rendered fields are output.
form_enctype(form): if at least one field is a file upload field, this renders
+ the obligatory enctype="multipart/form-data";
+
+
+
form_errors(form): renders any errors global to the whole form (field-specific
+ errors are displayed next to each field);
+
+
+
form_row(form.name): renders the label, any errors, and the HTML form widget
+ for the given field inside, by default, a div element;
+
+
+
form_rest(form): renders any fields that have not yet been rendered. It's
+ usually a good idea to place a call to this helper at the bottom of each form.
+ This helper is also useful for taking advantage of the automatic CSRF Protection.
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Validation
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
About Form Validation
+
+
+
In the previous section, you learned how a form can be submitted with valid or
+invalid data. In Symfony, validation is applied to the underlying object.
+
In other words, the question isn't whether the "form" is valid, but whether the
+object is valid after the form has applied the submitted data to it.
+
Calling $form->isValid() is a shortcut that asks the object whether it has
+valid data using a Validation layer.
+
Validation is done by adding a set of rules (called constraints) to a class.
$author=newAuthor();
+// ... do something to the $author object
+
+$validator=$this->get('validator');
+$errors=$validator->validate($author);
+
+if(count($errors)>0){
+ // Ooops, errors!
+}else{
+ // Everything is ok :-)
+}
+
+
+
If the $name property is empty, you will see the following error message:
+
AppBundle\Author.name:
+ This value should not be blank
+
+
+
Most of the time, you won't interact directly with the validator service or need
+to worry about printing out the errors. You will rather use validation
+indirectly when handling submitted form data.
Constraints can be applied to a class property or a public getter method
+(e.g. getFullName()). The first is the most common and easy to use, but the
+second allows you to specify more complex validation rules.
+
Properties
+
Validating class properties is the most basic validation technique. Symfony
+allows you to validate private, protected or public properties.
Some constraints apply to the entire class being validated. For example, the
+Callback constraint is a generic constraint that's applied to the class
+itself.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Constraint Targets (2/2)
+
+
+
Getters
+
Constraints can also be applied to the return value of a method. Symfony
+allows you to add a constraint to any public method whose name starts with
+get or is.
useSymfony\Component\Validator\Constraints\Email;
+
+$emailConstraint=newEmail();
+$emailConstraint->message='Invalid email address';
+
+$errorList=$this->get('validator')->validateValue(
+ $email,$emailConstraint
+);
+
+if(0!==count($errorList)){
+ // this is *not* a valid email address
+ $errorMessage=$errorList[0]->getMessage();
+}
+
+
+
By calling validateValue() on the validator, you can pass in a raw value and
+the constraint object that you want to validate that value against.
+
The validateValue() method returns a ConstraintViolationList object, which
+acts just like an array of errors.
+
Each error in the collection is a ConstraintViolation object, which holds the
+error message on its getMessage() method.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Translations
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Definitions
+
+
+
Internationalization
+
The term internationalization (often abbreviated i18n) refers to the
+process of abstracting strings and other locale-specific pieces out of your
+application and into a layer where they can be translated and converted
+based on the user's locale (i.e. language and country).
+
Localization
+
The act of creating translation files is an important part of localization
+(often abbreviated l10n). It is the process of adapting a product or
+service to a particular language, culture, and desired local
+look-and-feel.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Using the translator Service
+
+
+
# messages.fr.yml
+Symfony is great:J'aime Symfony
+'Hello%name%':Bonjour %name%
+
+
+
When the following code is executed, Symfony will attempt to translate the
+message Symfony is great based on the locale of the user:
+
echo$this->get('translator')->trans('Symfony is great');
+
+
+
Now, if the language of the user's locale is French (e.g. fr_FR or fr_BE),
+the message will be translated into J'aime Symfony.
+
Message Placeholders
+
echo$this->get('translator')->trans('Hello %name%',[
+ '%name%'=>'Will'
+]);
+
+// French: Bonjour Will
+// Default: Hello Will
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Translation Process
+
+
+
To translate the message, Symfony uses a simple process:
+
+
+
The locale of the current user, which is stored on the request (or stored as
+_locale on the session), is determined;
+
+
+
A catalog of translated messages is loaded from translation resources defined
+for the locale (e.g. fr_FR). Messages from the fallback locale are also loaded
+and added to the catalog if they don't already exist. The end result is a large
+"dictionary" of translations;
+
+
+
If the message is located in the catalog, the translation is returned. If not,
+the translator returns the original message.
+
+
+
When using the trans() method, Symfony looks for the exact string inside the
+appropriate message catalog and returns it (if it exists).
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Locations and Naming Conventions
+
+
+
Symfony looks for message files (i.e. translations) in the following locations:
+
+
the <kernel root directory>/Resources/translations directory;
+
the <kernel root directory>/Resources/<bundle name>/translations directory;
+
the Resources/translations/ directory of the bundle.
+
+
The filename of the translations is also important as Symfony uses a convention
+to determine details about the translations. Each message file must be named
+according to the following path: domain.locale.loader:
+
+
domain: an optional way to organize messages into groups;
+
locale: the locale that the translations are for (en_GB, en, etc);
+
loader: how Symfony should load and parse the file (xliff, php or yml).
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Pluralization
+
+
+
When a translation has different forms due to pluralization, you can provide all
+the forms as a string separated by a pipe (|):
+
'There is one apple|There are %count% apples'
+
+
+
To translate pluralized messages, use the transChoice() method:
+
$t=$this->get('translator')->transChoice(
+ 'There is one apple|There are %count% apples',
+ 10,
+ array('%count%'=>10)
+);
+
+
+
The second argument (10 in this example), is the number of objects being
+described and is used to determine which translation to use and also to
+populate the %count% placeholder.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Explicit Interval Pluralization
+
+
+
Sometimes, you'll need more control or want a different translation for specific
+cases (for 0, or when the count is negative, for example). For such cases, you
+can use explicit math intervals:
+
'{0} There are no apples|{1} There is one apple|]1,19] There are
+%count% apples|[20,Inf[ There are many apples'
+
A Translator object is now available in your JavaScript:
+
Translator.trans('ba.bar',{},'Hello','fr');
+// "Bonjour."
+
+Translator.trans('place.holder',{"username":"Will"},'Hello');
+// "Bonjour Will!"
+
+Translator.transChoice('plural',1,{"count":1},'Hello');
+// "Il y a 1 pomme"
+
+Translator.transChoice('plural',10,{"count":10},'Hello');
+// "Il y a 10 pommes"
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
HTTP Cache
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
HTTP Cache
+
+
+
The nature of rich web applications means that they're dynamic. No matter how
+efficient your application, each request will always contain more overhead
+than serving a static file.
+
But as your site grows, that overhead can become a problem. The processing
+that's normally performed on every request should be done only once.
+
This is exactly what caching aims to accomplish!
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Terminology (1/2)
+
+
+
Gateway Cache
+
A gateway cache, or reverse proxy, is an independent layer that sits in
+front of your application.
+
The reverse proxy caches responses as they are returned from your application
+and answers requests with cached responses before they hit your application.
+
Symfony provides its own reverse proxy, but any reverse proxy can be used.
+
HTTP Cache
+
HTTP cache headers are used to communicate with the gateway cache and any
+other caches between your application and the client.
+
Symfony provides sensible defaults and a powerful interface for interacting
+with the cache headers.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Terminology (2/2)
+
+
+
HTTP Expiration
+
HTTP expiration and validation are the two models used for determining
+whether cached content is fresh (can be reused from the cache) or stale (should
+be regenerated by the application).
+
Edge Side Includes
+
Edge Side Includes (ESI) allow HTTP cache to be used to cache page
+fragments (even nested fragments) independently. With ESI, you can even cache
+an entire page for 60 minutes, but an embedded sidebar for only 5 minutes.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Caching with a Gateway Cache
+
+
+
When caching with HTTP, the cache is separated from your application entirely
+and sits between your application and the client making the request.
+
The job of the cache is to accept requests from the client and pass them back
+to your application.
+
The cache will also receive responses back from your application and forward
+them on to the client. The cache is the middle-man of the request-response
+communication between the client and your application.
+
Along the way, the cache will store each response that is deemed cacheable.
+If the same resource is requested again, the cache sends the cached response to
+the client, ignoring your application entirely.
+
This type of cache is known as a HTTP gateway cache and many exist such as
+Varnish, Squid in reverse proxy mode, and the Symfony reverse
+proxy.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Types of Caches
+
+
+
The HTTP cache headers sent by your application are consumed and interpreted by
+up to three different types of caches:
+
+
Browser Caches: every browser comes with its own local cache that is mainly
+useful for when you hit "back" or for images and other assets. The browser cache
+is a private cache as cached resources aren't shared with anyone else;
+
Proxy Caches: a proxy is a shared cache as many people can be behind a
+single one. It's usually installed by large corporations and ISPs to reduce
+latency and network traffic;
+
Gateway Caches: like a proxy, it's also a shared cache but on the
+server side. Installed by network administrators, it makes websites more
+scalable, reliable and performant.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
HTTP Caching
+
+
+
HTTP specifies four response cache headers that are looked at here:
+
+
Cache-Control
+
Expires
+
ETag
+
Last-Modified
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Public vs Private Responses
+
+
+
Both gateway and proxy caches are considered shared caches as the cached
+content is shared by more than one user.
+
If a user-specific response were ever mistakenly stored by a shared cache, it
+might be returned later to any number of different users. Imagine if your
+account information were cached and then returned to every subsequent user who
+asked for their account page!
+
To handle this situation, every response may be set to be public or private:
+
+
public: indicates that the response may be cached by both private and
+shared caches;
+
private: indicates that all or part of the response message is intended for
+a single user and must not be cached by a shared cache.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Safe Methods
+
+
+
HTTP caching only works for safe HTTP methods (like GET and HEAD). Being
+safe means that you never change the application's state on the server when
+serving the request.
+
This has two very reasonable consequences:
+
+
You should never change the state of your application when responding to a
+GET or HEAD request. Even if you don't use a gateway cache, the presence
+of proxy caches mean that any GET or HEAD request may or may not actually
+hit your server;
+
Don't expect PUT, POST or DELETE methods to cache. These methods are
+meant to be used when mutating the state of your application. Caching them
+would prevent certain requests from hitting and mutating your application.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Expiration
+
+
+
The expiration model is the more efficient and straightforward of the two
+caching models and should be used whenever possible.
+
When a response is cached with an expiration, the cache will store the response
+and return it directly without hitting the application until it expires.
+
The expiration model can be accomplished using one of two HTTP headers:
+
+
Cache-Control
+
Expires
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Cache-Control Header (1/2)
+
+
+
The Cache-Control header is unique in that it contains not one, but
+various pieces of information about the cacheability of a response.
+
Each piece of information is separated by a comma:
Symfony provides an abstraction around the Cache-Control header:
+
useSymfony\Component\HttpFoundation\Response;
+
+$response=newResponse();
+
+// mark the response as either public or private
+$response->setPublic();
+$response->setPrivate();
+
+// set the private or shared max age
+$response->setMaxAge(600);
+$response->setSharedMaxAge(600);
+
+// set a custom Cache-Control directive
+$response->headers->addCacheControlDirective('must-revalidate',true);
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Expires Header
+
+
+
The Expires header can be set with the setExpires() Response method. It takes
+a DateTime instance as an argument:
The setExpires() method automatically converts the date to the GMT timezone as
+required by the specification.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Validation
+
+
+
With the expiration model, the application won't be asked to return the updated
+response until the cache finally becomes stale. It is not good!
+
The validation model addresses this issue.
+
Under this model, the cache continues to store responses. The difference is
+that, for each request, the cache asks the application whether or not the
+cached response is still valid.
+
If the cache is still valid, your application should return a 304 status code
+and no content. This tells the cache that it's ok to return the cached response.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The ETag Header
+
+
+
The ETag header is a string header called entity-tag that uniquely
+identifies one representation of the target resource. It's entirely
+generated and set by your application.
+
ETags are similar to fingerprints and they can be quickly compared to
+determine if two versions of a resource are the same or not.
+
publicfunctionindexAction()
+{
+ $response=$this->render('main/index.html.twig');
+ $response->setETag(md5($response->getContent()));
+ $response->setPublic();// make sure the response is public/cacheable
+ $response->isNotModified($this->getRequest());
+
+ return$response;
+}
+
+
+
The isNotModified() method compares the ETag sent with the Request with
+the one set on the Response. If the two match, the method automatically sets
+the Response status code to 304 Not Modified.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Last-Modified Header (1/2)
+
+
+
According to the HTTP specification, the Last-Modified header field indicates
+the date and time at which the origin server believes the representation was
+last modified.
+
In other words, the application decides whether or not the cached content has
+been updated based on whether or not it's been updated since the response was
+cached.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Last-Modified Header (2/2)
+
+
+
publicfunctionshowAction($articleSlug)
+{
+ // ...
+
+ $articleDate=new\DateTime($article->getUpdatedAt());
+ $authorDate=new\DateTime($author->getUpdatedAt());
+
+ $date=$authorDate>$articleDate?$authorDate:$articleDate;
+
+ $response->setLastModified($date);
+ // Set response as public. Otherwise it will be private by default
+ $response->setPublic();
+
+ if($response->isNotModified($this->getRequest())){
+ return$response;
+ }
+
+ // ... do more work to populate the response
+ // with the full content
+
+ return$response;
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Edge Side Includes (ESI)
+
+
+
Edge Side Includes or ESI is a small markup language for dynamic
+web content assembly at the reverse proxy level. The reverse proxy analyses the
+HTML code, parses ESI specific markup and assembles the final result before
+flushing it to the client.
+
+
<esi:includesrc="user.php"/>
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Stack PHP
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
HttpKernel
+
+
+
+
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What Is Stack?
+
+
+
A convention for composing HttpKernelInterface middlewares:
2010 - Minor changes to the PHP language were introduced by the HPHP team to
+ improve development time and provide basic type safety. The changes were XHP
+ and parameter type constraints;
+
2012 - Facebook engineering teams started exploring the idea of annotating
+ return types. And the Hack language was born...
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What Is Hack?
+
+
+
Hack is a programming language for HHVM that interoperates seamlessly
+with PHP. It has been created by Facebook. In general, anything you can write in
+PHP, you can also write in Hack.
+
Hack reconciles the fast development cycle of PHP with the discipline provided by
+static typing, while adding many features commonly found in other modern
+programming languages such as
+generics,
+collections, and
+nullable.
Use <?hh at the top of your file; you can also change <?php to <?hh in
+existing PHP files, and your code will run just as before in HHVM.
+
Optionally name your file with the .hh extension to distinguish your Hack files
+from your PHP files. Of course, you can keep the name of the file .php (or any
+other extension that you use).
// Fetches the value of $_GET['user'] and returns 'nobody'
+// if it does not exist.
+$username=$_GET['user']??'nobody';
+
+// This is equivalent to:
+$username=isset($_GET['user'])?$_GET['user']:'nobody';
+
+
+
Spaceship Operator: <=>
+
Returns -1, 0 or 1 when $a is respectively less than, equal to, or
+greater than $b:
The -> operator is used to call methods on objects.
+
Usage
+
$foo=newFoo();
+$foo->doSomething();
+
+// >= PHP 5.4
+(newFoo())->doSomething();
+
+// can also be done with a variable
+$method='doSomething';
+$foo->$method();
+
+$foo->{$method.'Else'}();
+// will call 'doSomethingElse()'; curly braces are required.
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Methods (4/4)
+
+
+
publicfunctiondoSomething()
+{
+ // method call
+ $this->doSomethingElse();
+
+ // parent method call (inheritance)
+ parent::doSomething();
+
+ // accessing a constant
+ self::VALUE;
+
+ // accessing a constant from another class
+ Bar::ANOTHER_VALUE;
+
+ // accessing an attribute
+ return$this->attribute;
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Static Keyword
+
+
+
Attributes/Methods can be defined as static:
+
classFoo
+{
+ publicstatic$value;
+
+ publicstaticfunctiondoThings()
+ {
+ // accessing a static attribute
+ // don't forget the dollar sign!
+ self::$value;
+ }
+}
+
$foo=newFoo();
+
+// accessing the attribute from an instance
+$foo::$value=123;
+
+// accessing the attribute directly from the class
+echoFoo::$value;
+=>123
+
// Interface may extend several other interfaces.
+// This is not possible with class though!
+interfaceMyTraversableextendsTraversable,Countable
+{
+}
+
+
+
Usage
+
// a class may implement several interfaces, but may extend only one class!
+classFooimplementsFooable,MyTraversable{}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Namespaces (1/2)
+
+
+
Namespaces prevent naming collisions with identifiers such as function, class,
+and interface names:
+
namespaceVendor\Model;
+// ...
+
+
+
Or:
+
namespaceMyNamespace{
+ // ...
+}
+
+
+
PSR-0
+
PSR-0 describes a set of rules related to
+namespaces for autoloader interoperability:
Many fatal and recoverable fatal errors have been converted to exceptions
+inheriting from the new Error class, which itself implements the Throwable
+interface, i.e. the new base interface all exceptions inherit.
REST is the underlying architectural principle of the web, formalized as a set
+of constraints, described in Roy Fielding's dissertation.
+
An API (i.e. a web service) that adheres to the principles of REST does not
+require the client to know anything about the structure of this API.
+Rather, the server needs to provide whatever information the client needs to
+interact with the service.
+
The key abstraction of information in REST is a resource. Any information
+that can be named can be a resource, and is identified by a Unified Resource
+Identifier (URI).
Hyper Media Types are MIME media types that contain native hyper-linking
+semantics that induce application flow: application/hal+json,
+application/collection+json, etc.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Content Type Negotiation
+
+
+
Content Type Negotiation is the principle of finding appropriate response
+formats based on client requirements.
+
No standardized algorithm available, even if the Apache
+mod_negotiation
+algorithm is documented. This also covers encoding (Accept-Encoding) and
+language (Accept-Language) negotiation.
HATEOAS stands for Hypermedia As The Engine Of
+Application State. It means that hypertext should be used to find your
+way through the API.
+
It is all about state transitions. Your application is just a big state
+machine.
+There should be a single endpoint for the resource, and all of the other
+actions you would need to undertake should be able to be discovered by
+inspecting that resource.
1. Does the 'Foo' class exist?
+ => Yes
+ Go on
+
+ => No
+ Do you have registered autoload functions?
+ => Yes
+ Call each function with 'Foo' as parameter
+ until the class gets included
+
+ => No
+ Is there a `__autoload()` method?
+ => Yes
+ Call `__autoload('Foo')`
+
+2. Does the 'Foo' class exist?
+ => Yes
+ Continue
+ => No
+ Fatal Error
+
Allow the use of serialize() and unserialize().
+Objects implementing JsonSerializable can customize their JSON representation
+when encoded with json_encode().
try-catch blocks also supports a finally block for code that should be run
+regardless of whether an exception has been thrown or not:
+
try{
+ // ..
+}catch(Exception$e){
+ // do something
+}finally{
+ // the code here will always be executed
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Password Hashing
+
+
+
The password hashing API provides an easy to use wrapper around crypt() to
+make it easy to create and manage passwords in a secure manner, since PHP 5.5.0.
+
password_hash() and password_verify() are your new friends!
A userland implementation exists for PHP >= 5.3.7:
+password_compat.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PHP Archive (PHAR)
+
+
+
The phar extension provides a way to put entire PHP applications into
+a single file called a "phar" (PHP Archive) for easy distribution and
+installation.
+
But, the API is hard to use.
+
Solution? Box, a command line for simplifying
+the PHAR creation process.
There are a ton of PHP libraries, frameworks, and components to choose from.
+Most of them have different versions, and don't always work well together.
+
Composer is a tool for dependency management in PHP. It allows you to declare
+the dependent libraries your project needs and it will install them in your
+project for you.
+
A lot of awesome PHP libraries are
+compatible with Composer and listed on Packagist, the
+official repository for Composer-compatible PHP libraries.
Composer automatically generates a PSR-4
+compliant and optimized autoloader for your entire application. Thanks to
+Composer, you don't have to take care about how to autoload classes/functions
+anymore.
+
Require the generated autoloader in your project as follows:
+
<?php
+
+require'vendor/autoload.php';
+
+// your PHP code
+
Routing is the process of binding URIs to controllers.
+
Folder organization
+
The simplest kind of routing, but also the hardest one to maintain:
+
web/
+├ trees/
+│ └ pineapple.php
+└ tree.php
+
+
+
Centralized Declaration
+
Modern frameworks provide a routing component such as the Symfony2 Routing
+component allowing to define routes in a centralized place, and easing URI
+generation.
+
This require a single entry point: the Front Controller.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Front Controller Pattern
+
+
+
A controller that handles all requests for a web application:
+
+
This controller dispatches the request to the specialized controllers.
+
It is usually tied to URL rewriting.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Databases
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Agenda
+
+
+
+
Database Design Patterns
+
Data Access Layer
+
Object Relational Mapping
+
Existing Components
+
A Note About Domain-Driven Design
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Quick note
+
+
+
In our context, a database is seen as a server hosting:
$con=newConnection('...');
+
+$banana=newBanana();
+$banana->setName('Super Banana');
+
+// Save the banana
+$banana->insert($con);
+
+// Update it
+$banana->setName('New name for my banana');
+$banana->update($con);
+
+// Delete it
+$banana->delete($con);
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Row Data Gateway
+
+
+
Under the hood
+
publicfunctioninsert(Connection$con)
+{
+ // Prepared statement
+ $stmt=$this->con->prepare('INSERT INTO bananas VALUES (:name)');
+
+ $stmt->bindValue(':name',$name);
+
+ $stmt->execute();
+
+ // Set the id for this banana
+ //
+ // It becomes easy to know whether the banana is new or not,
+ // you just need to check if id is defined.
+ $this->id=$this->con->lastInsertId();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
An object that acts as a Gateway to a database table.
+One instance handles all the rows in the table.
+
It's a Data Access Object.
+
$table=newBananaGateway(newConnection('...'));
+
+// Insert a new record
+$id=$table->insert('My favorite banana');
+
+// Update it
+$table->update($id,'THE banana');
+
+// Delete it
+$table->delete($id);
+
+
+
CRUD
+
A DAO implements the well-known Create Read Update
+Delete methods.
+
Read should not be a single method: Finders to the rescue!
/**
+ * @param string $name The name of the banana you want to create
+ *
+ * @return int The id of the banana
+ */
+publicfunctioninsert($name)
+{
+ // Prepared statement
+ $stmt=$this->con->prepare('INSERT INTO bananas VALUES (:name)');
+
+ $stmt->bindValue(':name',$name);
+
+ $stmt->execute();
+
+ return$this->con->lastInsertId();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
The update method
+
/**
+ * @param int $id The id of the banana to update
+ * @param string $name The new name of the banana
+ *
+ * @return bool Returns `true` on success, `false` otherwise
+ */
+publicfunctionupdate($id,$name)
+{
+ $stmt=$this->con->prepare(<<<SQL
+UPDATE bananas
+SET name = :name
+WHERE id = :id
+SQL
+ );
+
+ $stmt->bindValue(':id',$id);
+ $stmt->bindValue(':name',$name);
+
+ return$stmt->execute();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
The delete method
+
/**
+ * @param int $id The id of the banana to delete
+ *
+ * @return bool Returns `true` on success, `false` otherwise
+ */
+publicfunctiondelete($id)
+{
+ $stmt=$this->con->prepare('DELETE FROM bananas WHERE id = :id');
+
+ $stmt->bindValue(':id',$id);
+
+ return$stmt->execute();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
Finders
+
// Retrieve all bananas
+$bananas=$table->findAll();
+
+// Find bananas by name matching 'THE %'
+$bananas=$table->findByName('THE %');
+
+// Retrieve a given banana using its id
+$banana=$table->find(123);
+
+// Find one banana by name matching 'THE %'
+$banana=$table->findOneByName('THE %');
+
An object that wraps a row in a database table, encapsulates
+the database access, and adds domain logic on that data.
+
Active Record = Row Data Gateway + Domain Logic
+
$con=newConnection('...');
+
+$banana=newBanana();
+$banana->setName('Another banana');
+$banana->save($con);
+
+// Call a method that is part of the domain logic
+// What can a banana do anyway?
+$banana->grow();
+
+// Smart `save()` method
+// use `isNew()` under the hood
+$banana->save($con);
+
Ensures that each object gets loaded only once by keeping every loaded object in
+a map. Looks up objects using the map when referring to them.
+
classFinder
+{
+ private$identityMap=[];
+
+ publicfunctionfind($id)
+ {
+ if(!isset($this->identityMap[$id])){
+ // fetch the object for the given id
+ $this->identityMap[$id]=...;
+ }
+
+ return$this->identityMap[$id];
+ }
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data Access Layer
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data Access Layer / Data Source Name
+
+
+
A Data Access Layer (DAL) is a standard API to manipulate data,
+no matter which database server is used.
+A Data Source Name (DSN) can be used to determine which database
+vendor you are using.
+
PHP Data Object (PDO)
+
A DSN in PHP looks like: <database>:host=<host>;dbname=<dbname> where:
+
+
<database> can be: mysql, sqlite, pgsql, etc;
+
<host> is the IP address of the database server (e.g. localhost);
Looks like the Connection class you used before, right?
+
classConnectionextendsPDO
+{
+}
+
+
+
Usage
+
$con=newConnection($dsn,$user,$password);
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data Access Layer
+
+
+
Refactoring
+
Refactoring is a disciplined technique for restructuring an existing
+body of code, altering its internal structure without changing its
+external behavior.
/**
+ * @param int $id The id of the banana to update
+ * @param string $name The new name of the banana
+ *
+ * @return bool Returns `true` on success, `false` otherwise
+ */
+publicfunctionupdate($id,$name)
+{
+ $query='UPDATE bananas SET name = :name WHERE id = :id';
+
+ return$this->con->executeQuery($query,[
+ 'id'=>$id,
+ 'name'=>$name,
+ ]);
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Object Relational Mapping
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Object Relational Mapping (1/4)
+
+
+
Introduces the notion of relations between objects:
+
+
One-To-One;
+
One-To-Many;
+
Many-To-Many.
+
+
An ORM is often considered as a tool that implements some design patterns
+seen above, and that eases relationships between objects.
Client objects construct query specifications declaratively and submit them
+to Repository for satisfaction.
+
Objects can be added to and removed from the Repository, as they can from
+a simple collection of objects, and the mapping code encapsulated by the
+Repository will carry out the appropriate operations behind the scenes.
+
Conceptually, a Repository encapsulates the set of objects persisted in a data
+store and the operations performed over them, providing a more object-oriented
+view of the persistence layer.
+
Repository also supports the objective of achieving a clean separation and
+one-way dependency between the domain and data mapping layers.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Specification Pattern
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Specification Pattern
+
+
+
The Specification pattern is a way to model business rules as individual
+objects. The idea is that a question about an object, is answered by a
+isSatisfiedBy() method:
// Find customers who have ordered exactly three times,
+// but who are not premium customers (yet?)
+$specification=newAndSpecification(
+ newCustomerHasOrderedThreeTimes(),
+ newNotSpecification(
+ newCustomerIsPremium()
+ )
+);
+
+$customers=$repository->findSatisfying($specification);
+
Man in the Middle (capturing a valid session identifier);
+
Session Fixation (attacker chooses the session identifier);
+
Session Hijacking (all attacks that attempt to gain access to another user's
+ session).
+
+
Workarounds
+
+
Regenerate ids when authentication changes;
+
Bind sessions to IP addresses;
+
Define expiration/timeout;
+
Don't rely on the default settings;
+
Use HTTPS.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Session Configuration
+
+
+
An example of PHP session configuration that is more secure:
+
; Helps mitigate XSS by telling the browser not to expose the cookie to
+; client side scripting such as JavaScript
+session.cookie_httponly=1
+
+; Prevents session fixation by making sure that PHP only uses cookies for
+; sessions and disallow session ID passing as a GET parameter
+session.use_only_cookies=1
+
+; Better entropy source
+; Evades insufficient entropy vulnerabilities
+session.entropy_file="/dev/urandom"
+; `session.entropy_length` might help too!
+
+; Smaller exploitation window for XSS/CSRF/Clickjacking...
+session.cookie_lifetime=0
+
+; Ensures session cookies are only sent over secure connections (it requires
+; a valid SSL certificate)
+; Related to OWASP 2013-A6-Sensitive Data Exposure
+session.cookie_secure=1
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Authentication
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What You Have Right Now
+
+
+
No Authentication/Security Layer, anyone can access everything.
+
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Big Picture
+
+
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Interceptor Pattern
+
+
+
The Security Layer, as seen before, has to intercept the process of
+converting a request into a response in order to perform some checks.
+
You need a way to hook into this process before invoking the controller:
+Interceptor Pattern to the rescue!
+
The Interceptor Pattern allows you to execute some code during the default
+application's lifecyle.
+
A way to implement this pattern is to use events. It is more or less like
+the Observer/Observable pattern.
+
Event Dispatcher
+
The application notifies a set of listeners to an event.
+The listeners can register themselves to a particular event.
+An Event Dispatcher manages both the listeners, and the events.
In order to intercept the process described before, you have to notify some
+listeners once you enter in the process() method by dispatching the event:
$app->addListener('process.before',function(Request$request){
+ // code to execute
+});
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Firewall (1/2)
+
+
+
Now that you can hook into the application's lifecycle, you can write a basic
+but powerful Firewall.
+
This firewall needs a whitelist of unsecured routes (i.e. routes that
+don't require the user to be authenticated) associated with their allowed HTTP
+methods:
Read more about coding standards with
+PSR-1
+and
+PSR-2.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PHP Coding Standards Fixer
+
+
+
The PHP Coding Standards Fixer tool fixes most issues in your code when
+you want to follow the PHP coding standards as defined in the PSR-1 and
+PSR-2 documents.
Inversion of Control is about who initiates the call. If your code
+initiates a call, it is not IoC, if the container/system/library calls back
+into code that you provided it, it is IoC.
+
Hollywood Principle: Don't call us, we'll call you.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dependency Injection Container (DIC)
+
+
+
A framework or library for building graphs of objects by passing in (injecting)
+each object's dependencies. Object lifetimes are handled by the container
+instead of by the consuming object.
+
Most of the time, you rely on configuration files to describe your classes
+and their dependencies. A class in this context is also known as a
+service.
+
You ask this container to retrieve a service, and it is lazy loaded and
+dynamically built:
+
// It's an instance of `TemplateEngine`, but you don't know
+// anything about its internal implementation.
+// Is it the raw PHP implementation or Twig?
+$engine=$container->get('template_engine');
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PHP Implementations
+
+
+
Twittee
+
Twittee is the smallest Dependency Injection
+Container written in PHP.
+It fits in a tweet (less than 140 characters):
Dummy objects are passed around but never actually used. Usually they are
+ just used to fill parameter lists;
+
Fake objects actually have working implementations, but usually take some
+ shortcut which makes them not suitable for production (an in memory database
+ is a good example);
+
Stubs provide canned answers to calls made during the test, usually not
+ responding at all to anything outside what's programmed in for the test. Stubs
+ may also record information about calls, such as an email gateway stub that
+ remembers the messages it 'sent', or maybe only how many messages it 'sent'
+ → State verification;
+
Mocks are objects pre-programmed with expectations which form a
+ specification of the calls they are expected to receive → Behavior
+ verification.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Functional Testing
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Functional Testing
+
+
+
Also known as acceptance testing.
+
Use tools to create automated tests that actually use your application rather
+than only verifying that individual units of code behave correctly.
functionit($m,$p){echo($p?'✔︎':'✘')." It $m\n";if(!$p){$GLOBALS['f']=1;}}
+functiondone(){if(@$GLOBALS['f'])die(1);}
+
+it("should sum two numbers",1+1==2);
+it("should display an X for a failing test",1+1==3);
+done();
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Awesome Projects
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Assert
+
+
+
Assertions and guard methods for input validation (not filtering!) in
+business-model, libraries and application low-level code.
+
useAssert\Assertion;
+useAssert\AssertionFailedException;
+
+try{
+ \Assert\that($identifier)->notEmpty()->integer();
+ Assertion::notEmpty($message,'Message is not specified');
+}catch(AssertionFailedException$e){
+ $e->getValue();// the value that caused the failure
+ $e->getConstraints();// the additional constraints of the assertion
+}
+
// use the factory to create a `Faker\Generator` instance
+$faker=Faker\Factory::create();
+
+// generate data by accessing properties
+echo$faker->name;
+// 'Lucy Cechtelar';
+echo$faker->address;
+// "426 Jordy Lodge
+// Cartwrightshire, SC 88120-6700"
+echo$faker->text;
+// Sint velit eveniet. Rerum atque repellat voluptatem quia rerum. Numquam
+// beatae sint laudantium consequatur. Magni occaecati itaque sint et sit
+// tempore. Nesciunt amet quidem. Iusto deleniti cum autem ad quia aperiam.
+echo$faker->email;
+// 'tkshlerin@collins.com'
+echo$faker->ipv4;
+// '109.133.32.252'
+echo$faker->creditCardNumber;
+// '4485480221084675'
+
// Create the message
+$message=Swift_Message::newInstance()
+ // Give the message a subject
+ ->setSubject('Your subject')
+ // Set the From address with an associative array
+ ->setFrom(['john@doe.com'=>'John Doe'])
+ // Set the To addresses with an associative array
+ ->setTo(['receiver@domain.org','other@domain.org'=>'A name'])
+ // Give it a body
+ ->setBody('Here is the message itself')
+ ;
+
+// Create the Transport
+$transport=Swift_SmtpTransport::newInstance('smtp.example.org',25);
+
+// Create the Mailer using your created Transport
+$mailer=Swift_Mailer::newInstance($transport);
+
+// Send the message
+$result=$mailer->send($message);
+
// Fetches the value of $_GET['user'] and returns 'nobody'
+// if it does not exist.
+$username=$_GET['user']??'nobody';
+
+// This is equivalent to:
+$username=isset($_GET['user'])?$_GET['user']:'nobody';
+
+
+
Spaceship Operator: <=>
+
Returns -1, 0 or 1 when $a is respectively less than, equal to, or
+greater than $b:
The -> operator is used to call methods on objects.
+
Usage
+
$foo=newFoo();
+$foo->doSomething();
+
+// >= PHP 5.4
+(newFoo())->doSomething();
+
+// can also be done with a variable
+$method='doSomething';
+$foo->$method();
+
+$foo->{$method.'Else'}();
+// will call 'doSomethingElse()'; curly braces are required.
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Methods (4/4)
+
+
+
publicfunctiondoSomething()
+{
+ // method call
+ $this->doSomethingElse();
+
+ // parent method call (inheritance)
+ parent::doSomething();
+
+ // accessing a constant
+ self::VALUE;
+
+ // accessing a constant from another class
+ Bar::ANOTHER_VALUE;
+
+ // accessing an attribute
+ return$this->attribute;
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Static Keyword
+
+
+
Attributes/Methods can be defined as static:
+
classFoo
+{
+ publicstatic$value;
+
+ publicstaticfunctiondoThings()
+ {
+ // accessing a static attribute
+ // don't forget the dollar sign!
+ self::$value;
+ }
+}
+
$foo=newFoo();
+
+// accessing the attribute from an instance
+$foo::$value=123;
+
+// accessing the attribute directly from the class
+echoFoo::$value;
+=>123
+
// Interface may extend several other interfaces.
+// This is not possible with class though!
+interfaceMyTraversableextendsTraversable,Countable
+{
+}
+
+
+
Usage
+
// a class may implement several interfaces, but may extend only one class!
+classFooimplementsFooable,MyTraversable{}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Namespaces (1/2)
+
+
+
Namespaces prevent naming collisions with identifiers such as function, class,
+and interface names:
+
namespaceVendor\Model;
+// ...
+
+
+
Or:
+
namespaceMyNamespace{
+ // ...
+}
+
+
+
PSR-0
+
PSR-0 describes a set of rules related to
+namespaces for autoloader interoperability:
Many fatal and recoverable fatal errors have been converted to exceptions
+inheriting from the new Error class, which itself implements the Throwable
+interface, i.e. the new base interface all exceptions inherit.
REST is the underlying architectural principle of the web, formalized as a set
+of constraints, described in Roy Fielding's dissertation.
+
An API (i.e. a web service) that adheres to the principles of REST does not
+require the client to know anything about the structure of this API.
+Rather, the server needs to provide whatever information the client needs to
+interact with the service.
+
The key abstraction of information in REST is a resource. Any information
+that can be named can be a resource, and is identified by a Unified Resource
+Identifier (URI).
Hyper Media Types are MIME media types that contain native hyper-linking
+semantics that induce application flow: application/hal+json,
+application/collection+json, etc.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Content Type Negotiation
+
+
+
Content Type Negotiation is the principle of finding appropriate response
+formats based on client requirements.
+
No standardized algorithm available, even if the Apache
+mod_negotiation
+algorithm is documented. This also covers encoding (Accept-Encoding) and
+language (Accept-Language) negotiation.
HATEOAS stands for Hypermedia As The Engine Of
+Application State. It means that hypertext should be used to find your
+way through the API.
+
It is all about state transitions. Your application is just a big state
+machine.
+There should be a single endpoint for the resource, and all of the other
+actions you would need to undertake should be able to be discovered by
+inspecting that resource.
1. Does the 'Foo' class exist?
+ => Yes
+ Go on
+
+ => No
+ Do you have registered autoload functions?
+ => Yes
+ Call each function with 'Foo' as parameter
+ until the class gets included
+
+ => No
+ Is there a `__autoload()` method?
+ => Yes
+ Call `__autoload('Foo')`
+
+2. Does the 'Foo' class exist?
+ => Yes
+ Continue
+ => No
+ Fatal Error
+
Allow the use of serialize() and unserialize().
+Objects implementing JsonSerializable can customize their JSON representation
+when encoded with json_encode().
try-catch blocks also supports a finally block for code that should be run
+regardless of whether an exception has been thrown or not:
+
try{
+ // ..
+}catch(Exception$e){
+ // do something
+}finally{
+ // the code here will always be executed
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Password Hashing
+
+
+
The password hashing API provides an easy to use wrapper around crypt() to
+make it easy to create and manage passwords in a secure manner, since PHP 5.5.0.
+
password_hash() and password_verify() are your new friends!
A userland implementation exists for PHP >= 5.3.7:
+password_compat.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
PHP Archive (PHAR)
+
+
+
The phar extension provides a way to put entire PHP applications into
+a single file called a "phar" (PHP Archive) for easy distribution and
+installation.
+
But, the API is hard to use.
+
Solution? Box, a command line for simplifying
+the PHAR creation process.
There are a ton of PHP libraries, frameworks, and components to choose from.
+Most of them have different versions, and don't always work well together.
+
Composer is a tool for dependency management in PHP. It allows you to declare
+the dependent libraries your project needs and it will install them in your
+project for you.
+
A lot of awesome PHP libraries are
+compatible with Composer and listed on Packagist, the
+official repository for Composer-compatible PHP libraries.
Composer automatically generates a PSR-4
+compliant and optimized autoloader for your entire application. Thanks to
+Composer, you don't have to take care about how to autoload classes/functions
+anymore.
+
Require the generated autoloader in your project as follows:
+
<?php
+
+require'vendor/autoload.php';
+
+// your PHP code
+
Routing is the process of binding URIs to controllers.
+
Folder organization
+
The simplest kind of routing, but also the hardest one to maintain:
+
web/
+├ trees/
+│ └ pineapple.php
+└ tree.php
+
+
+
Centralized Declaration
+
Modern frameworks provide a routing component such as the Symfony2 Routing
+component allowing to define routes in a centralized place, and easing URI
+generation.
+
This require a single entry point: the Front Controller.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Front Controller Pattern
+
+
+
A controller that handles all requests for a web application:
+
+
This controller dispatches the request to the specialized controllers.
+
It is usually tied to URL rewriting.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Databases
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Agenda
+
+
+
+
Database Design Patterns
+
Data Access Layer
+
Object Relational Mapping
+
Existing Components
+
A Note About Domain-Driven Design
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Quick note
+
+
+
In our context, a database is seen as a server hosting:
$con=newConnection('...');
+
+$banana=newBanana();
+$banana->setName('Super Banana');
+
+// Save the banana
+$banana->insert($con);
+
+// Update it
+$banana->setName('New name for my banana');
+$banana->update($con);
+
+// Delete it
+$banana->delete($con);
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Row Data Gateway
+
+
+
Under the hood
+
publicfunctioninsert(Connection$con)
+{
+ // Prepared statement
+ $stmt=$this->con->prepare('INSERT INTO bananas VALUES (:name)');
+
+ $stmt->bindValue(':name',$name);
+
+ $stmt->execute();
+
+ // Set the id for this banana
+ //
+ // It becomes easy to know whether the banana is new or not,
+ // you just need to check if id is defined.
+ $this->id=$this->con->lastInsertId();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
An object that acts as a Gateway to a database table.
+One instance handles all the rows in the table.
+
It's a Data Access Object.
+
$table=newBananaGateway(newConnection('...'));
+
+// Insert a new record
+$id=$table->insert('My favorite banana');
+
+// Update it
+$table->update($id,'THE banana');
+
+// Delete it
+$table->delete($id);
+
+
+
CRUD
+
A DAO implements the well-known Create Read Update
+Delete methods.
+
Read should not be a single method: Finders to the rescue!
/**
+ * @param string $name The name of the banana you want to create
+ *
+ * @return int The id of the banana
+ */
+publicfunctioninsert($name)
+{
+ // Prepared statement
+ $stmt=$this->con->prepare('INSERT INTO bananas VALUES (:name)');
+
+ $stmt->bindValue(':name',$name);
+
+ $stmt->execute();
+
+ return$this->con->lastInsertId();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
The update method
+
/**
+ * @param int $id The id of the banana to update
+ * @param string $name The new name of the banana
+ *
+ * @return bool Returns `true` on success, `false` otherwise
+ */
+publicfunctionupdate($id,$name)
+{
+ $stmt=$this->con->prepare(<<<SQL
+UPDATE bananas
+SET name = :name
+WHERE id = :id
+SQL
+ );
+
+ $stmt->bindValue(':id',$id);
+ $stmt->bindValue(':name',$name);
+
+ return$stmt->execute();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
The delete method
+
/**
+ * @param int $id The id of the banana to delete
+ *
+ * @return bool Returns `true` on success, `false` otherwise
+ */
+publicfunctiondelete($id)
+{
+ $stmt=$this->con->prepare('DELETE FROM bananas WHERE id = :id');
+
+ $stmt->bindValue(':id',$id);
+
+ return$stmt->execute();
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Table Data Gateway
+
+
+
Finders
+
// Retrieve all bananas
+$bananas=$table->findAll();
+
+// Find bananas by name matching 'THE %'
+$bananas=$table->findByName('THE %');
+
+// Retrieve a given banana using its id
+$banana=$table->find(123);
+
+// Find one banana by name matching 'THE %'
+$banana=$table->findOneByName('THE %');
+
An object that wraps a row in a database table, encapsulates
+the database access, and adds domain logic on that data.
+
Active Record = Row Data Gateway + Domain Logic
+
$con=newConnection('...');
+
+$banana=newBanana();
+$banana->setName('Another banana');
+$banana->save($con);
+
+// Call a method that is part of the domain logic
+// What can a banana do anyway?
+$banana->grow();
+
+// Smart `save()` method
+// use `isNew()` under the hood
+$banana->save($con);
+
Ensures that each object gets loaded only once by keeping every loaded object in
+a map. Looks up objects using the map when referring to them.
+
classFinder
+{
+ private$identityMap=[];
+
+ publicfunctionfind($id)
+ {
+ if(!isset($this->identityMap[$id])){
+ // fetch the object for the given id
+ $this->identityMap[$id]=...;
+ }
+
+ return$this->identityMap[$id];
+ }
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data Access Layer
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data Access Layer / Data Source Name
+
+
+
A Data Access Layer (DAL) is a standard API to manipulate data,
+no matter which database server is used.
+A Data Source Name (DSN) can be used to determine which database
+vendor you are using.
+
PHP Data Object (PDO)
+
A DSN in PHP looks like: <database>:host=<host>;dbname=<dbname> where:
+
+
<database> can be: mysql, sqlite, pgsql, etc;
+
<host> is the IP address of the database server (e.g. localhost);
Looks like the Connection class you used before, right?
+
classConnectionextendsPDO
+{
+}
+
+
+
Usage
+
$con=newConnection($dsn,$user,$password);
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Data Access Layer
+
+
+
Refactoring
+
Refactoring is a disciplined technique for restructuring an existing
+body of code, altering its internal structure without changing its
+external behavior.
/**
+ * @param int $id The id of the banana to update
+ * @param string $name The new name of the banana
+ *
+ * @return bool Returns `true` on success, `false` otherwise
+ */
+publicfunctionupdate($id,$name)
+{
+ $query='UPDATE bananas SET name = :name WHERE id = :id';
+
+ return$this->con->executeQuery($query,[
+ 'id'=>$id,
+ 'name'=>$name,
+ ]);
+}
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Object Relational Mapping
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Object Relational Mapping (1/4)
+
+
+
Introduces the notion of relations between objects:
+
+
One-To-One;
+
One-To-Many;
+
Many-To-Many.
+
+
An ORM is often considered as a tool that implements some design patterns
+seen above, and that eases relationships between objects.
Client objects construct query specifications declaratively and submit them
+to Repository for satisfaction.
+
Objects can be added to and removed from the Repository, as they can from
+a simple collection of objects, and the mapping code encapsulated by the
+Repository will carry out the appropriate operations behind the scenes.
+
Conceptually, a Repository encapsulates the set of objects persisted in a data
+store and the operations performed over them, providing a more object-oriented
+view of the persistence layer.
+
Repository also supports the objective of achieving a clean separation and
+one-way dependency between the domain and data mapping layers.
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Specification Pattern
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Specification Pattern
+
+
+
The Specification pattern is a way to model business rules as individual
+objects. The idea is that a question about an object, is answered by a
+isSatisfiedBy() method:
// Find customers who have ordered exactly three times,
+// but who are not premium customers (yet?)
+$specification=newAndSpecification(
+ newCustomerHasOrderedThreeTimes(),
+ newNotSpecification(
+ newCustomerIsPremium()
+ )
+);
+
+$customers=$repository->findSatisfying($specification);
+
Man in the Middle (capturing a valid session identifier);
+
Session Fixation (attacker chooses the session identifier);
+
Session Hijacking (all attacks that attempt to gain access to another user's
+ session).
+
+
Workarounds
+
+
Regenerate ids when authentication changes;
+
Bind sessions to IP addresses;
+
Define expiration/timeout;
+
Don't rely on the default settings;
+
Use HTTPS.
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Session Configuration
+
+
+
An example of PHP session configuration that is more secure:
+
; Helps mitigate XSS by telling the browser not to expose the cookie to
+; client side scripting such as JavaScript
+session.cookie_httponly=1
+
+; Prevents session fixation by making sure that PHP only uses cookies for
+; sessions and disallow session ID passing as a GET parameter
+session.use_only_cookies=1
+
+; Better entropy source
+; Evades insufficient entropy vulnerabilities
+session.entropy_file="/dev/urandom"
+; `session.entropy_length` might help too!
+
+; Smaller exploitation window for XSS/CSRF/Clickjacking...
+session.cookie_lifetime=0
+
+; Ensures session cookies are only sent over secure connections (it requires
+; a valid SSL certificate)
+; Related to OWASP 2013-A6-Sensitive Data Exposure
+session.cookie_secure=1
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Authentication
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What You Have Right Now
+
+
+
No Authentication/Security Layer, anyone can access everything.
+
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Big Picture
+
+
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Interceptor Pattern
+
+
+
The Security Layer, as seen before, has to intercept the process of
+converting a request into a response in order to perform some checks.
+
You need a way to hook into this process before invoking the controller:
+Interceptor Pattern to the rescue!
+
The Interceptor Pattern allows you to execute some code during the default
+application's lifecyle.
+
A way to implement this pattern is to use events. It is more or less like
+the Observer/Observable pattern.
+
Event Dispatcher
+
The application notifies a set of listeners to an event.
+The listeners can register themselves to a particular event.
+An Event Dispatcher manages both the listeners, and the events.
In order to intercept the process described before, you have to notify some
+listeners once you enter in the process() method by dispatching the event:
$app->addListener('process.before',function(Request$request){
+ // code to execute
+});
+
+
+
+
+
+
Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The Firewall (1/2)
+
+
+
Now that you can hook into the application's lifecycle, you can write a basic
+but powerful Firewall.
+
This firewall needs a whitelist of unsecured routes (i.e. routes that
+don't require the user to be authenticated) associated with their allowed HTTP
+methods:
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/iut-extended.cfg b/iut-extended.cfg
deleted file mode 100644
index 79cf7ea..0000000
--- a/iut-extended.cfg
+++ /dev/null
@@ -1,24 +0,0 @@
-[landslide]
-source = src/first/iut-extended.md
- src/me.md
- src/agenda/iut-extended.md
- src/extended/00_intro.md
- src/extended/01_symfony2.md
- src/extended/02_symfony2_controllers.md
- src/extended/03_symfony2_templating.md
- src/extended/04_symfony2_dependency_injection.md
- src/extended/05_symfony2_command_line.md
- src/extended/06_symfony2_forms.md
- src/extended/07_symfony2_validation.md
- src/extended/08_symfony2_translation.md
- src/extended/09_http_cache.md
- src/extended/10_stackphp.md
- src/hack/
- src/extended/11_end.md
-destination = iut-extended.html
-theme = themes/avalanche/avalanche
-css = css/solarized.css
- css/custom.css
-embed = true
-relative = true
-linenos = no
diff --git a/iut-php.cfg b/iut-php.cfg
deleted file mode 100644
index ca26343..0000000
--- a/iut-php.cfg
+++ /dev/null
@@ -1,28 +0,0 @@
-[landslide]
-source = src/first/iut-php.md
- src/me.md
- src/agenda/iut-php.md
- src/common/01_php_hypertext_preprocessor.md
- src/common/02_the_php_syntax.md
- src/common/03_the_php_command_line.md
- src/common/04_client_server.md
- src/common/05_autoloading.md
- src/common/06_leveraging_php_apis.md
- src/common/07_dependency_management_in_php.md
- src/iut/07_dependency_management_example.md
- src/common/08_model_view_controller.md
- src/common/09_databases.md
- src/common/10_sessions.md
- src/common/11_authentication.md
- src/iut/12_writing_better_code.md
- src/iut/13_testing.md
- src/iut/14_awesome_projects.md
- src/iut/15_embracing_open_source.md
- src/iut/16_end.md
-destination = iut-php.html
-theme = themes/avalanche/avalanche
-css = css/solarized.css
- css/custom.css
-embed = true
-relative = true
-linenos = no
diff --git a/publish b/publish
deleted file mode 100755
index 3bb485f..0000000
--- a/publish
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-
-TMP_DIR=/tmp/licpro-php-slides
-
-git stash
-
-landslide iut-php.cfg
-landslide iut-extended.cfg
-landslide isima-php.cfg
-
-rm -rf $TMP_DIR
-mkdir $TMP_DIR
-mv iut-php.html $TMP_DIR/index.html
-mv iut-extended.html $TMP_DIR/extended.html
-mv isima-php.html $TMP_DIR/isima.html
-cp -R themes/ $TMP_DIR
-
-git checkout gh-pages
-
-mv $TMP_DIR/* .
-git add index.html extended.html isima.html themes/avalanche/avalanche/
-git commit -m "Publish slides (`date`)"
-
-git push origin gh-pages -f
-
-git clean -fdx
-
-git checkout master
-
-git stash apply
diff --git a/src/agenda/isima-php.md b/src/agenda/isima-php.md
deleted file mode 100644
index ff696db..0000000
--- a/src/agenda/isima-php.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Agenda
-
-### [Week #1](#slide10)
-
-PHP: Hypertext Preprocessor, The PHP Syntax, The PHP Command Line,
-Client/Server, REST
-
-### [Week #2](#slide65)
-
-Autoloading, Leveraging PHP APIs, Dependency Management, Model View Controller
-
-### [Week #3](#slide98)
-
-Databases, Sessions, Authentication
-
-### [Week #4](#slide162)
-
-Security 101
diff --git a/src/agenda/iut-extended.md b/src/agenda/iut-extended.md
deleted file mode 100644
index c7aa360..0000000
--- a/src/agenda/iut-extended.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Agenda
-
-* Symfony
- * Controllers
- * Templating
- * Dependency Injection
- * Command Line
- * Forms
- * Validation
- * Translation
- * HTTP Cache
-* Stack PHP
-* Hack
diff --git a/src/agenda/iut-php.md b/src/agenda/iut-php.md
deleted file mode 100644
index 7e50942..0000000
--- a/src/agenda/iut-php.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Agenda
-
-### Week #1
-
-PHP: Hypertext Preprocessor, The PHP Syntax, The PHP Command Line,
-Client/Server, REST
-
-### Week #2
-
-Autoloading, Leveraging PHP APIs, Dependency Management, Model View Controller
-
-### Week #3
-
-Databases
-
-### Week #4
-
-Sessions, Authentication, Writing Better Code, Testing, Awesome Projects,
-Embracing Open Source
diff --git a/src/common/01_php_hypertext_preprocessor.md b/src/common/01_php_hypertext_preprocessor.md
deleted file mode 100644
index b521753..0000000
--- a/src/common/01_php_hypertext_preprocessor.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# PHP: Hypertext Preprocessor
-
----
-
-# History & Numbers
-
-* Created by Rasmus Lerdorf
-* 4th language on GitHub
-([August 2015](https://github.com/blog/2047-language-trends-on-github))
-* 6th language in the world
-([TIOBE](http://tiobe.com/index.php/content/paperinfo/tpci/index.html) January 2016)
-* 1st language for web development
-* Running on +75% of all web servers
-* 20 years old in 2015!
-
----
-
-# 
-
----
-
-# Getting Started
-
-## Linux
-
- !bash
- $ sudo apt-get install php5-common libapache2-mod-php5 php5-cli
-
-> [http://php.net/manual/en/install.unix.debian.php](http://php.net/manual/en/install.unix.debian.php)
-
----
-
-# Getting Started
-
-## Mac OS X
-
- !bash
- $ curl -s http://php-osx.liip.ch/install.sh | bash -s 7.0
-
-
-
- !bash
- $ curl -s http://php-osx.liip.ch/install.sh | bash -s 5.6
-
-> [http://php-osx.liip.ch/](http://php-osx.liip.ch/)
-
----
-
-# Getting Started
-
-## Windows
-
-
-
-> [http://php.net/manual/en/install.windows.installer.msi.php](http://php.net/manual/en/install.windows.installer.msi.php)
-
----
-
-# HHVM
-
-HipHop Virtual Machine for PHP, created by Facebook.
-
-HHVM uses a just-in-time compilation approach to achieve superior performance.
-
-
-
-> [http://hhvm.com](http://hhvm.com)
-
----
-
-# RTFM: [http://php.net](http://php.net)
diff --git a/src/common/02_the_php_syntax.md b/src/common/02_the_php_syntax.md
deleted file mode 100644
index 01a8c37..0000000
--- a/src/common/02_the_php_syntax.md
+++ /dev/null
@@ -1,653 +0,0 @@
-# The PHP Syntax
-
----
-
-# Primitive Types
-
-4 **scalar** types: `boolean`, `integer`, `float`, `string`;
-
-2 **compound** types: `array`, `object`;
-
-2 **special** types: `resource`, `null`;
-
-And 3 **pseudo** types: `mixed`, `number`, `callback`.
-
-**Note:** most of these types have aliases. E.g. `double` for `float`.
-
-> Read more about the **PHP primitive types**:
-[http://php.net/manual/en/language.types.intro.php](http://php.net/manual/en/language.types.intro.php).
-
----
-
-# Comparison Operators
-
- !php
- // so, this is a PHP variable
- $a = 5;
-
- // compare value; return true
- var_dump($a == 5);
-
- // compare value (ignore type); return true
- var_dump($a == '5');
-
- // compare type/value (integer vs. integer); return true
- var_dump($a === 5);
-
- // compare type/value (integer vs. string); return false
- var_dump($a === '5');
-
-> Read more about **comparison operators**:
-[http://php.net/manual/en/language.operators.comparison.php](http://php.net/manual/en/language.operators.comparison.php).
-
-### Timing Attack Safe String Comparison
-
-The [`hash_equals()`](http://php.net/manual/en/function.hash-equals.php)
-function has been added in PHP 5.6 to compare two strings in constant time.
-
----
-
-# Operators
-
- !php
- $a++; // or: ++$a;
-
- $b--; // or: --$b;
-
- $a && $b; // AND
- $a || $b; // OR
-
- ! $a; // `true` if $a is not `true`
-
- $a . 'foo'; // concatenation
-
- 2 ** 3 = 8 // exponentiation (PHP 5.6+)
-
-But also:
-
- !php
- $a = 'foo';
- $a .= 'bar';
- // $a => 'foobar'
-
- $b = 0;
- $b += 1; // $b = 1
- $b -= 1; // $b = 0
-
- $c = 2;
- $c **= 3; // $c = 8
-
----
-
-# New Operators in PHP 7.0
-
-### Null Coalescing Operator: `??`
-
- !php
- // Fetches the value of $_GET['user'] and returns 'nobody'
- // if it does not exist.
- $username = $_GET['user'] ?? 'nobody';
-
- // This is equivalent to:
- $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
-
-### Spaceship Operator: `<=>`
-
-Returns `-1`, `0` or `1` when `$a` is respectively less than, equal to, or
-greater than `$b`:
-
- !php
- echo 1 <=> 1; // 0
- echo 1 <=> 2; // -1
- echo 2 <=> 1; // 1
-
----
-
-# Classes (1/3)
-
-### Simple class definition
-
- !php
- class Foo
- {
- }
-
-**Important:** No class-level visibility in PHP.
-
-### Abstract class definition
-
- !php
- abstract class AbstractFoo
- {
- abstract public function doSomething();
- }
-
----
-
-# Classes (2/3)
-
-Creating an instance:
-
- !php
- $foo = new Foo();
- // $foo = new Foo;
-
- // can also be done with a variable
- $class = 'Foo';
- $foo = new $class();
-
-Getting the class name of an instance:
-
- !php
- echo get_class($foo);
- => Foo
-
-Useful keyword: `instanceof`
-
- !php
- if ($foo instanceof Foo) {
- // do something
- }
-
-> [http://php.net/manual/en/language.oop5.basic.php](http://php.net/manual/en/language.oop5.basic.php)
-
----
-
-# Classes (3/3)
-
-### Anonymous Classes (PHP >= 7.0)
-
- !php
- new class {
- public function foo() {
- return 'foo';
- }
- }
-
-Anonymous classes behave as traditional classes:
-
- !php
- interface Logger {
- public function log($msg);
- }
-
- $logger = new class implements Logger {
- public function log($msg) {
- // ...
- }
- };
-
- $logger->log('Hello, Anonymous Class');
-
----
-
-# Visibility
-
-### Keywords
-
-* `public`
-* `protected`
-* `private`
-
-
-### The Rules
-
-Attribute visibility MUST be set.
-
-Method visibility SHOULD be set.
-
-Methods without any explicit visibility keyword are defined as `public`.
-
----
-
-# Properties
-
- !php
- class Foo
- {
- const VALUE = 123;
- // PHP 5.6+
- const SENTENCE = 'The value of VALUE is ' . self::VALUE;
- const ARRAY_OF_VALUES = ['a', 'b'];
-
- /**
- * @var int
- */
- public static $count = 0;
-
- /**
- * @var Iterator
- */
- public $iterator;
-
- /**
- * @var array
- */
- protected $values = array();
-
- /**
- * @var string|null
- */
- private $language = null;
- }
-
----
-
-# Methods (1/4)
-
- !php
- class Foo
- {
- public function doSomething()
- {
- }
- }
-
-### Type Hinting
-
-Works with classes, interfaces, arrays, `callable`, and `Closure`. You cannot
-use scalar types such as `int` or `string` with PHP < 7.0:
-
- !php
- public function doSomething(Foo $foo);
-
- public function doSomething(Traversable $iterator);
-
- public function doSomething(array $values);
-
- public function doSomething(callable $callback);
-
- public function doSomething(Closure $closure);
-
----
-
-# Methods (2/4)
-
-[PHP 7](https://secure.php.net/manual/en/migration70.new-features.php) \o/
-
-### Scalar Type Declarations
-
-Works with `int`, `float`, `string`, and `bool`:
-
- !php
- function sumOfInts(int ...$ints) {
- return array_sum($ints);
- }
-
-### Return Type Declarations
-
- !php
- function sumOfInts(int ...$ints) : int {
- return array_sum($ints);
- }
-
----
-
-# Methods (3/4)
-
-The `->` operator is used to call methods on objects.
-
-### Usage
-
- !php
- $foo = new Foo();
- $foo->doSomething();
-
- // >= PHP 5.4
- (new Foo())->doSomething();
-
- // can also be done with a variable
- $method = 'doSomething';
- $foo->$method();
-
- $foo->{$method . 'Else'}();
- // will call 'doSomethingElse()'; curly braces are required.
-
----
-
-# Methods (4/4)
-
- !php
- public function doSomething()
- {
- // method call
- $this->doSomethingElse();
-
- // parent method call (inheritance)
- parent::doSomething();
-
- // accessing a constant
- self::VALUE;
-
- // accessing a constant from another class
- Bar::ANOTHER_VALUE;
-
- // accessing an attribute
- return $this->attribute;
- }
-
----
-
-# Static Keyword
-
-Attributes/Methods can be defined as `static`:
-
- !php
- class Foo
- {
- public static $value;
-
- public static function doThings()
- {
- // accessing a static attribute
- // don't forget the dollar sign!
- self::$value;
- }
- }
-
-**Warning:** the `static` keyword can also be used to [define static
-variables](http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static)
-and for [late static
-bindings](http://php.net/manual/en/language.oop5.late-static-bindings.php).
-This is different!
-
----
-
-# Late Static Bindings
-
- !php
- class A
- {
- public static function who() { echo __CLASS__; }
-
- public static function testSelf()
- {
- self::who();
- }
-
- public static function testStatic()
- {
- static::who();
- }
- }
-
- class B extends A
- {
- public static function who() { echo __CLASS__; }
- }
-
-
-
- !php
- B::testSelf();
- // A
-
- B::testStatic();
- // B
-
----
-
-# Static Keyword
-
-### Usage
-
- !php
- $foo = new Foo();
-
- // accessing the attribute from an instance
- $foo::$value = 123;
-
- // accessing the attribute directly from the class
- echo Foo::$value;
- => 123
-
-> Read more:
-[http://php.net/manual/en/language.oop5.static.php](http://php.net/manual/en/language.oop5.static.php).
-
----
-
-# Variadic Functions
-
-New operator `...` as of PHP 5.6:
-
- !php
- function sum(...$numbers)
- {
- return array_sum($numbers);
- }
-
- echo sum(1, 2);
- // 3
-
-### Argument Unpacking
-
- !php
- $numbers = [ 2, 3 ];
- echo sum(1, ...$numbers);
- // 6
-
----
-
-# Interfaces
-
-### Simple interface definition
-
- !php
- interface Fooable
- {
- const VALUE = 123;
-
- // it's always public anyway
- public function doSomething();
- }
-
-### Inheritance
-
- !php
- // Interface may extend several other interfaces.
- // This is not possible with class though!
- interface MyTraversable extends Traversable, Countable
- {
- }
-
-### Usage
-
- !php
- // a class may implement several interfaces, but may extend only one class!
- class Foo implements Fooable, MyTraversable {}
-
----
-
-# Namespaces (1/2)
-
-Namespaces prevent naming collisions with identifiers such as function, class,
-and interface names:
-
- !php
- namespace Vendor\Model;
- // ...
-
-Or:
-
- !php
- namespace MyNamespace {
- // ...
- }
-
-### PSR-0
-
-[PSR-0](http://php-fig.org/psr/psr-0/) describes a set of rules related to
-namespaces for autoloader interoperability:
-
- !php
- \ns\package\Class_Name => vendor/ns/package/Class/Name.php
- \ns\package_name\Class_Name => vendor/ns/package_name/Class/Name.php
-
----
-
-# Namespaces (2/2)
-
-Classes, functions, and constants have to be **imported** with the `use`
-statement:
-
- !php
- namespace My\Namespace;
-
- // Pre PHP 7 code
- use some\namespace\ClassA;
- use some\namespace\ClassB;
-
- use function some\namespace\fn_a;
- use function some\namespace\fn_b;
-
- // PHP 7+ code
- use some\namespace\{ClassA, ClassB};
-
- use function some\namespace\{fn_a, fn_b};
-
- class MyClass
- {
- public function __construct(ClassA $a, ClassB $b) {
- // ...
- }
- }
-
----
-
-# The `class` Keyword
-
-Since PHP 5.5.0, class name resolution is possible via `::class`.
-
- !php
- namespace My\Namespace;
-
- class ClassName
- {
- }
-
-Assuming the class definition above, you can get the **F**ully **Q**ualified
-**C**lass **N**ame (FQCN) by doing:
-
- !php
- echo ClassName::class;
- // My\namespace\ClassName
-
-> Read more about the `class` keyword:
-[http://php.net/manual/en/language.oop5.basic.php](http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class.class).
-
----
-
-# Traits
-
-Horizontal Inheritance FTW!
-
- !php
- trait Hello trait World
- { {
- public function sayHello() public function sayWorld()
- { {
- echo 'Hello '; echo 'World';
- } }
- } }
-
- class MyHelloWorld
- {
- use Hello, World;
- }
-
- $obj = new MyHelloWorld();
- $obj->sayHello();
- $obj->sayWorld();
-
-
-> Read more about **traits**:
-[http://php.net/manual/en/language.oop5.traits.php](http://php.net/manual/en/language.oop5.traits.php).
-
----
-
-# Anonymous Functions
-
-An **anonymous function**, also known as **lambda** function, is a function
-defined, and possibly called, without being bound to an identifier.
-
- !php
- $greet = function ($name) {
- printf("Hello %s\n", $name);
- };
-
- $greet('World');
- => Hello World
-
-> Read more about **anonymous functions**:
-[http://php.net/manual/en/functions.anonymous.php](http://php.net/manual/en/functions.anonymous.php).
-
----
-
-# Closures
-
-A **closure** is an anonymous function that owns a context.
-
- !php
- $fibonacci = function ($n) use (&$fibonacci) {
- if (0 === $n || 1 === $n) {
- return $n;
- }
-
- return $fibonacci($n - 1) + $fibonacci($n - 2);
- };
-
- echo (int) $fibonacci(6);
- => 8
-
-> Read more about **closures**:
-[http://php.net/manual/en/class.closure.php](http://php.net/manual/en/class.closure.php).
-
----
-
-# Magic Methods
-
-Starts with `__`.
-
-Two useful methods:
-
- !php
- __construct() { /* ... */ }
-
-and:
-
- !php
- __toString() { /* ... */ }
-
-Other methods are not really useful but it's worth knowing them (`__get()`, `__set()`).
-
-> Read more about **magic methods**:
-[http://php.net/manual/en/language.oop5.magic.php](http://php.net/manual/en/language.oop5.magic.php).
-
----
-
-# Generators
-
-A **generator function** looks just like a normal function, except that instead
-of returning a value, a generator **yields** as many values as it needs to.
-
-The heart of a generator function is the `yield` keyword.
-
-> Read more about **generators**:
->
-> * [http://php.net/manual/en/language.generators.php](http://php.net/manual/en/language.generators.php);
-> * [What Generators Can Do For
-You](http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html);
-> * [https://github.com/nikic/iter](https://github.com/nikic/iter) (examples).
-
----
-
-# Errors in PHP 7
-
-No more **Fatal Errors** \o/
-
-Many fatal and recoverable fatal errors have been converted to exceptions
-inheriting from the new `Error` class, which itself implements the `Throwable`
-interface, i.e. the new base interface all exceptions inherit.
-
->
-[https://secure.php.net/manual/en/language.errors.php7.php](https://secure.php.net/manual/en/language.errors.php7.php)
diff --git a/src/common/03_the_php_command_line.md b/src/common/03_the_php_command_line.md
deleted file mode 100644
index db11a53..0000000
--- a/src/common/03_the_php_command_line.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# The PHP Command Line
-
----
-
-# The PHP Command Line (1/2)
-
-PHP is an interpreted language, no need for a compiler.
-
-You can try PHP using the command line:
-
- !bash
- $ php -r 'echo "Hello, World\n"'
- Hello, World
-
-
-
-
Help available by running: php -h
-
-
-PHP also provides an interactive shell:
-
- !bash
- $ php -a
- Interactive Shell
-
- php > echo "Hello, World\n";
- Hello, World
-
-> The command line is really useful, read more about command line options:
-[http://php.net/manual/en/features.commandline.options.php](http://php.net/manual/en/features.commandline.options.php).
-
----
-
-# The PHP Command Line (2/2)
-
-Your new best friend is the _linter_:
-
- !bash
- $ php -l my/script.php
- No syntax errors detected in my/script.php
-
-
-
-
A linter is a program that looks for problems in your code
- (syntax errors for instance).
-
-
-Embedded web server:
-
- !bash
- $ php -S localhost:8000
-
-> Learn more about the built-in, command line web server:
-[http://php.net/manual/en/features.commandline.webserver.php](http://php.net/manual/en/features.commandline.webserver.php).
-
----
-
-# Writing a CLI program
-
- !php
- #!/usr/bin/env php
-
-
-
-
-
-
Hello world !
-
-
-
----
-
-# Status Codes (1/2)
-
-### `1xx` Informational
-
-### `2xx` Successful
-
-* `200` OK
-* `201` Created
-* `204` No Content
-
-### `3xx` Redirections
-
-* `301` Moved Permanently
-* `302` Found
-* `304` Not Modified
-
-> [httpstatus.es](http://httpstatus.es/)
-
----
-
-# Status Codes (2/2)
-
-### `4xx` Client Error
-
-* `400` Bad Request
-* `401` Unauthorized
-* `403` Forbidden
-* `404` Not Found
-* `405` Method Not Allowed
-* `406` Not Acceptable
-* `409` Conflict
-* `415` Unsupported Media Type
-* `451` Unavailable For Legal Reasons
-
-### `5xx` Server Error
-
-* `500` Internal Server Error
-
----
-
-# HTTP Parameters (1/2)
-
-There are two types of parameters, **query string** and **request body**.
-
-If the request follows the **URL Form Encoded** format, you can access
-parameters through global variables:
-
-* **query string**: `$_GET`;
-* **request body**: `$_POST`;
-* All parameters are available in the `$_REQUEST` global variable.
-
-You can always use the following, but you need to parse them by yourself:
-
-* **query string**: `$_SERVER['QUERY_STRING']`;
-* **request body**: `$HTTP_RAW_POST_DATA`
- ([deprecated](https://github.com/php/php-src/blob/8648f76bac2f78391a1539253f21d62f53d83022/NEWS#L19-L22),
- do not use).
-
-**Note**: Don't use `$_REQUEST`, as there is a collision risk!
-
----
-
-# HTTP Parameters (2/2)
-
- !http
- GET /my/simple/uri?a=1&id=2 HTTP/1.1
- Host: example.org
- Content-Type: text/plain; charset=utf-8
- Content-Length: 14
-
- b=3&city=paris
-
-Will result in:
-
- !php
- $_GET = [ "a" => 1, "id" => 2 ];
-
- $_POST = [ "b" => 3, "city" => 'paris' ];
-
- $_REQUEST = [ "a" => 1, "id" => 2, "b" => 3, "city" => 'paris' ];
-
- $_SERVER['QUERY_STRING'] = "a=1&id=2";
-
- $HTTP_RAW_POST_DATA = "b=3&city=paris";
-
-
-**Important:** **never trust user input**, **never!**
-
----
-
-# REST
-
----
-
-# REpresentational State Transfer
-
-REST is the underlying architectural principle of the web, formalized as a set
-of **constraints**, described in Roy Fielding's dissertation.
-
-An API (i.e. a web service) that adheres to the principles of REST does not
-require the client to know anything about the structure of this API.
-Rather, the server needs to provide whatever information the client needs to
-interact with the service.
-
-The key abstraction of information in REST is a **resource**. Any information
-that can be named can be a resource, and is identified by a **Unified Resource
-Identifier** (URI).
-
-
-
----
-
-# Richardson Maturity Model
-
-
-
-
-
----
-
-# Level 0 - The Swamp of POX
-
-### In A Nutshell
-
-* HTTP as a tunneling mechanism;
-* RPC style system (SOAP, XML-RPC).
-
-
-
-
-
----
-
-# Level 1 - Resources
-
-### In A Nutshell
-
-* Individual resources (URIs);
-* Notion of object identity.
-
-
-
-
-
----
-
-# Level 2 - HTTP Verbs
-
-### In A Nutshell
-
-* Client uses specific HTTP verbs;
-* Server uses HTTP status codes.
-
-
-
-
-
----
-
-# Level 3 - Hypermedia Controls
-
-### In A Nutshell
-
-* Service discovery via link relations
-* Hypermedia formats
-
-
-
-
----
-
-# Level 3 = Content Negotiation + HATEOAS
-
----
-
-# Media Types
-
-### In A Nutshell
-
-* Identifies a representation format;
-* Custom types use `application/vnd.[XYZ]`;
-* Used inside the `Accept` / `Content-Type` headers.
-
-
-
-
-
Header
-
Description
-
-
-
-
`Content-Type`
HTTP message format
-
`Accept`
HTTP response format preference
-
-
-
-### Hyper Media Types
-
-Hyper Media Types are MIME media types that contain **native hyper-linking
-semantics** that induce application flow: `application/hal+json`,
-`application/collection+json`, etc.
-
----
-
-# Content Type Negotiation
-
-Content Type Negotiation is the principle of finding appropriate response
-formats based on client requirements.
-
-No standardized algorithm available, even if the Apache
-[mod_negotiation](http://httpd.apache.org/docs/2.4/content-negotiation.html)
-algorithm is documented. This also covers encoding (`Accept-Encoding`) and
-language (`Accept-Language`) negotiation.
-
- !text
- Accept: application/json, application/xml;q=0.9, text/html;q=0.8,
- text/*;q=0.7, */*;q=0.5
-
-
-
-
Priority
-
Mime Type
-
-
-
`q=1.0`
`application/json`
-
`q=0.9`
`application/xml`
-
`q=0.8`
`text/html`
-
`q=0.7`
`text/*` (ie. any text)
-
`q=0.5`
`*/*` (ie. any media type)
-
-
-
----
-
-# HATEOAS
-
-**HATEOAS** stands for **H**ypermedia **A**s **T**he **E**ngine **O**f
-**A**pplication **S**tate. It means that hypertext should be used to find your
-way through the API.
-
-It is all about **state transitions**. Your application is just a big **state
-machine**.
-There should be a single endpoint for the resource, and **all of the other
-actions** you would need to undertake **should be able to be discovered by
-inspecting that resource**.
-
- !xml
-
-
-
-
-
-
-
-
-
-
-> Must read: [Haters gonna
-HATEOAS](http://timelessrepo.com/haters-gonna-hateoas).
diff --git a/src/common/05_autoloading.md b/src/common/05_autoloading.md
deleted file mode 100644
index ed3d81c..0000000
--- a/src/common/05_autoloading.md
+++ /dev/null
@@ -1,215 +0,0 @@
-# PHP Autoloading
-
----
-
-# Why Is It Necessary?
-
-PHP won't magically load your classes by itself.
-
-You have to manually include the class declaration:
-
- !php
- class Octopus
- {
- public function scream()
- {
- echo "o o O O ° °";
- }
- }
-
-If you want to create a new `Octopus`, you will write the following code:
-
- !php
- $paul = new Octopus();
- $paul->scream();
-
-As the class declaration isn't included, PHP raises a `Fatal Error`:
-
- !bash
- Fatal error: Class 'Octopus' not found in /path/to/file.php
-
----
-
-# The `require()` Way
-
-Include the class definition before instantiating it:
-
- !php
- require __DIR__ . '../Model/Octopus.php';
-
- $paul = new Octopus();
- $paul->scream();
-
-It works!
-
-But, what happens when the class is included again, somewhere else?
-
- !php
- // somewhere further in your application
- require __DIR__ . '../Model/Octopus.php';
-
- class Squid extends Octopus
- {
- }
-
-PHP raises a `Fatal Error`:
-
- !bash
- Fatal error: Cannot redeclare class Octopus in /path/to/file.php
-
----
-
-# The `require_once()` Way
-
-The `require_once()` function is identical to `require()` except that PHP will
-check whether the file has already been included:
-
- !php
- require_once __DIR__ . '../Model/Octopus.php';
-
- $paul = new Octopus();
- $paul->scream();
-
-And somewhere else:
-
- !php
- // somewhere further in your application
- require_once __DIR__ . '../Model/Octopus.php';
-
- class Squid extends Octopus
- {
- }
-
-It just works!
-
----
-
-# Working With Multiple Files
-
-Multiple `require_once()` can turn into a nightmare when you deal with more than
-a few files:
-
- !php
- Yes
- Go on
-
- => No
- Do you have registered autoload functions?
- => Yes
- Call each function with 'Foo' as parameter
- until the class gets included
-
- => No
- Is there a `__autoload()` method?
- => Yes
- Call `__autoload('Foo')`
-
- 2. Does the 'Foo' class exist?
- => Yes
- Continue
- => No
- Fatal Error
-
----
-
-# PSR-0 vs PSR-4
-
-
-### PSR-0
-
- !php
- \Zend\Mail\Message
- // => /path/to/project/lib/vendor/Zend/Mail/Message.php
-
-
-
- !php
- Zend_Mail_Message
- // => /path/to/project/lib/vendor/Zend/Mail/Message.php
-
-**Important:** as of 2014-10-21 PSR-0 has been marked as deprecated.
-
-### PSR-4
-
-Like [PSR-0](http://www.php-fig.org/psr/psr-0/), but **better**:
-
-* more concise folder structure;
-* remove the remnants of PSR-0 (e.g. PEAR support).
diff --git a/src/common/06_leveraging_php_apis.md b/src/common/06_leveraging_php_apis.md
deleted file mode 100644
index 6c1533c..0000000
--- a/src/common/06_leveraging_php_apis.md
+++ /dev/null
@@ -1,246 +0,0 @@
-# Leveraging PHP APIs
-
----
-
-# Built-in Interfaces
-
-### ArrayAccess
-
-Access properties as an array:
-
- !php
- $tom = new MyObject();
- $tom['name'] = 'Tom';
-
-### Serializable, JsonSerializable
-
-Allow the use of `serialize()` and `unserialize()`.
-Objects implementing `JsonSerializable` can customize their JSON representation
-when encoded with `json_encode()`.
-
-### Traversable
-
-Allow the use of `foreach`.
-
-> Read more:
-[http://php.net/manual/en/reserved.interfaces.php](http://php.net/manual/en/reserved.interfaces.php).
-
----
-
-# The Reflection API (1/2)
-
-Enable code introspection:
-
- !php
- /** A comment */
- class MyClass
- {
- public function hello() { printf("Hello %s", $this->getName()); }
-
- protected function getName() { return 'foo'; }
- }
-
- $reflClass = new ReflectionClass('MyClass');
-
- // access comments
- var_dump($reflClass->getDocComment());
- // string(16) "/** A comment */"
-
- // get all methods
- $reflClass->getMethods();
-
- // get all public methods
- $reflClass->getMethods(ReflectionMethod::IS_PUBLIC);
-
-
----
-
-# The Reflection API (2/2)
-
-It is even possible to invoke private methods!
-
- !php
- class MyClass
- {
- public function hello() { printf("Hello %s", $this->getName()); }
-
- private function getName() { return 'foo'; }
- }
-
- $reflClass = new ReflectionClass('MyClass');
-
- // access private method
- $method = $reflClass->getMethod('getName');
- $method->setAccessible(true);
-
- $method->invoke(new MyClass());
- // 'foo'
-
-
-> Read more:
-[http://php.net/manual/en/book.reflection.php](http://php.net/manual/en/book.reflection.php).
-
----
-
-# The Standard PHP Library (SPL)
-
-Provides a collection of classes and interfaces:
-
-### Datastructures
-
-`SplStack`, `SplQueue`,
-[`SplObjectStorage`](http://php.net/manual/en/class.splobjectstorage.php), etc.
-
-### Named Exceptions
-
-`LogicException`, `InvalidArgumentException`, `OutOfRangeException`,
-`RuntimeException`, etc.
-
-### SPL Functions
-
-`class_parents()`, `spl_autoload_register()`, `spl_autoload_unregister()`, etc.
-
-> Read more about the **SPL**:
-[http://php.net/manual/en/book.spl.php](http://php.net/manual/en/book.spl.php).
-
----
-
-# Observer Pattern (1/2)
-
-The `SplObserver` interface is used alongside `SplSubject` to implement the
-**Observer** Design Pattern.
-
- !php
- class Subject implements SplSubject
- {
- /* ... */
- }
-
- class Observer implements SplObserver
- {
- /* ... */
- }
-
- $subject = new Subject();
-
- $observer1 = new Observer();
-
- $subject->attach($observer1);
-
- $subject->notify();
-
-> Read more: [http://php.net/manual/en/class.splobserver.php](http://php.net/manual/en/class.splobserver.php).
-
----
-
-# Observer Pattern (2/2)
-
-Those interfaces are **never** used as there is only one default **channel** for
-the `notify()` method.
-
-Symfony2 [EventDispatcher](https://github.com/symfony/EventDispatcher)
-component to the rescue!
-
- !php
- use Symfony\Component\EventDispatcher\EventDispatcher;
- use Symfony\Component\EventDispatcher\Event;
-
- $dispatcher = new EventDispatcher();
-
- $dispatcher->addListener('event_name', function (Event $event) {
- // ...
- });
-
- $dispatcher->dispatch('event_name');
-
-> Read more:
-[http://symfony.com/doc/master/components/event_dispatcher/](http://symfony.com/doc/master/components/event_dispatcher/index.html).
-
----
-
-# Exceptions (1/2)
-
-`try`-`catch` block with multiple `catch` statements:
-
- !php
- try {
- // ...
- } catch (RuntimeException $e) {
- // do something
- } catch (Exception $e) {
- // do something else
- }
-
-Create your own exceptions:
-
- !php
- class SomethingWentWrong extends RuntimeException
- {
- }
-
- class ErrorWhileDoingSomething extends Exception implements ExceptionInterface
- {
- }
-
-**Name** your named exceptions!
-
----
-
-# Exceptions (2/2)
-
-`try`-`catch` blocks also supports a `finally` block for code that should be run
-regardless of whether an exception has been thrown or not:
-
- !php
- try {
- // ..
- } catch (Exception $e) {
- // do something
- } finally {
- // the code here will always be executed
- }
-
----
-
-# Password Hashing
-
-The password hashing API provides an easy to use wrapper around `crypt()` to
-make it easy to create and manage passwords in a secure manner, since PHP 5.5.0.
-
-`password_hash()` and `password_verify()` are your new friends!
-
- !php
- $passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);
-
- if (password_verify('bad-password', $passwordHash)) {
- // Correct Password
- } else {
- // Wrong password
- }
-
-> Read more about the **Password Hashing API**:
-[http://php.net/manual/en/book.password.php](http://php.net/manual/en/book.password.php).
-
-
-
-> A **userland implementation** exists for PHP >= 5.3.7:
-[password_compat](https://github.com/ircmaxell/password_compat).
-
----
-
-# PHP Archive (PHAR)
-
-The phar extension provides a way to put entire PHP applications into
-a single file called a "phar" (PHP Archive) for easy distribution and
-installation.
-
-But, the API is **hard** to use.
-
-Solution? [Box](http://box-project.org/), a command line for simplifying
-the PHAR creation process.
-
-> Read more about **PHAR**:
->
-> * [http://php.net/manual/en/intro.phar.php](http://php.net/manual/en/intro.phar.php);
-> * [http://blog.pascal-martin.fr/post/php-5.3-phar-php-archive](http://blog.pascal-martin.fr/post/php-5.3-phar-php-archive)
-> * [https://mwop.net/blog/2015-12-14-secure-phar-automation.html](https://mwop.net/blog/2015-12-14-secure-phar-automation.html).
diff --git a/src/common/07_dependency_management_in_php.md b/src/common/07_dependency_management_in_php.md
deleted file mode 100644
index 092885f..0000000
--- a/src/common/07_dependency_management_in_php.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Dependency Management
-
----
-
-# Composer
-
-There are a ton of PHP libraries, frameworks, and components to choose from.
-Most of them have different versions, and don't always work well together.
-
-**Composer** is a tool for dependency management in PHP. It allows you to declare
-the dependent libraries your project needs and it will install them in your
-project for you.
-
-A lot of [awesome PHP libraries](https://github.com/ziadoz/awesome-php) are
-compatible with Composer and listed on [Packagist](http://packagist.org/), the
-official repository for Composer-compatible PHP libraries.
-
- !bash
- $ curl -sS https://getcomposer.org/installer | php
-
-This will download `composer.phar` (a PHP binary archive).
-
-> [http://getcomposer.org/doc/00-intro.md](http://getcomposer.org/doc/00-intro.md)
-
----
-
-# `composer install`
-
-Create a `composer.json` file in your project's root directory:
-
- !javascript
- {
- "require": {
- "willdurand/geocoder": "~2.0"
- }
- }
-
-You can also require a library by using the `require` command:
-
- !bash
- $ php composer.phar require willdurand/geocoder
-
-Run the following command to download and install the project dependencies into
-a `vendor` directory:
-
- !bash
- $ php composer.phar install
-
-> [Composer Version
-Constraints](https://igor.io/2013/01/07/composer-versioning.html)
-
----
-
-# Composer Autoloader
-
-Composer automatically generates a [PSR-4](http://www.php-fig.org/psr/psr-4/)
-compliant and optimized autoloader for your entire application. Thanks to
-Composer, you don't have to take care about how to autoload classes/functions
-anymore.
-
-Require the generated autoloader in your project as follows:
-
- !php
- Must read: [Composer Primer](http://daylerees.com/composer-primer).
diff --git a/src/common/08_model_view_controller.md b/src/common/08_model_view_controller.md
deleted file mode 100644
index 548652f..0000000
--- a/src/common/08_model_view_controller.md
+++ /dev/null
@@ -1,183 +0,0 @@
-# Model View Controller
----
-
-# MVC Overview
-
-Typical client request process in MVC architecture:
-
-
-
----
-
-# The Model
-
-**Model** is the layer in charge of data interaction.
-
-All **data related business logic** is embedded here.
-Using it should not require to understand internals.
-
-Examples:
-
-* Manipulate **database** records;
-* Communicate with **search engine**;
-* **API** calls;
-* etc.
-
-
-
-
More on this next week!
-
-
-
-
-
More on this in a few minutes!
-
-
----
-
-# The View
-
-PHP is a templating language per se.
-
-**Never**, **ever**, **ever** mix HTML and PHP codes or kittens
-will die: you have to separate the presentation from the business logic.
-
- !php
- class PhpTemplateEngine implements TemplateEngine
- {
- private $templateDir;
-
- public function __construct($templateDir)
- {
- $this->templateDir = $templateDir;
- }
-
- public function render($template, array $parameters = [])
- {
- extract($parameters);
-
- ob_start();
- include $this->templateDir . DIRECTORY_SEPARATOR . $template;
-
- return ob_get_clean();
- }
- }
-
----
-
-# The View
-
-### Template
-
- !html
-
-
-
----
-
-# The View
-
-**Twig** is a modern template engine for PHP. It takes care of escaping for
-you and much much more! Read more:
-[http://twig.sensiolabs.org/](http://twig.sensiolabs.org/).
-
-### Template
-
- !html+django
- {# my_template.html #}
-
-
----
-
-# The Controller
-
-**Glue** between the **Model** and the **View** layers.
-
-It **should not** contain any business logic.
-
- !php
- class BananaController
- {
- public function __construct(
- BananaRepository $repository,
- TemplateEngine $engine
- ) {
- $this->repository = $repository;
- $this->engine = $engine;
- }
-
- public function listAction()
- {
- $bananas = $this->repository->findAll();
-
- return $this->engine->render('list.html', [
- 'bananas' => $bananas,
- ]);
- }
- }
-
----
-
-# Routing
-
-Routing is the process of binding `URI`s to controllers.
-
-## Folder organization
-
-The simplest kind of routing, but also the hardest one to maintain:
-
- !text
- web/
- ├ trees/
- │ └ pineapple.php
- └ tree.php
-
-## Centralized Declaration
-
-Modern frameworks provide a routing component such as the **Symfony2 Routing**
-component allowing to define routes in a centralized place, and easing `URI`
-generation.
-
-This require a single entry point: the **Front Controller**.
-
----
-
-# Front Controller Pattern
-
-A controller that handles all requests for a web application:
-
-
-
-This controller dispatches the request to the **specialized controllers**.
-
-It is usually tied to URL rewriting.
diff --git a/src/common/09_databases.md b/src/common/09_databases.md
deleted file mode 100644
index f72d16d..0000000
--- a/src/common/09_databases.md
+++ /dev/null
@@ -1,865 +0,0 @@
-# Databases
-
----
-
-# Agenda
-
-* Database Design Patterns
-* Data Access Layer
-* Object Relational Mapping
-* Existing Components
-* A Note About Domain-Driven Design
-
----
-
-# Quick note
-
-In our context, a **database** is seen as a server hosting:
-
-* a set of `records`;
-* organised through `tables` or `collections`;
-* grouped by `databases`.
-
----
-
-# Database Design Patterns
-
-* Row Data Gateway
-* Table Data Gateway
-* Active Record
-* Data Mapper
-* Identity Map
-* etc.
-
-Definitions and figures are part of the [Catalog of Patterns of Enterprise
-Application Architecture](http://martinfowler.com/eaaCatalog/index.html)
-created by **Martin Fowler**.
-
-Don't forget his name! Read his books!
-
----
-
-# Row Data Gateway 
-
----
-
-# Row Data Gateway
-
-An object that acts as a Gateway to a single record (row) in a database.
-There is one instance per row.
-
- !php
- // This is the implementation of `BananaGateway`
- class Banana
- {
- private $id;
-
- private $name;
-
- public function getId()
- {
- return $this->id;
- }
-
- public function getName()
- {
- return $this->name;
- }
-
- public function setName($name)
- {
- $this->name = $name;
- }
- }
-
----
-
-# Row Data Gateway
-
-### Usage
-
- !php
- $con = new Connection('...');
-
- $banana = new Banana();
- $banana->setName('Super Banana');
-
- // Save the banana
- $banana->insert($con);
-
- // Update it
- $banana->setName('New name for my banana');
- $banana->update($con);
-
- // Delete it
- $banana->delete($con);
-
----
-
-# Row Data Gateway
-
-### Under the hood
-
- !php
- public function insert(Connection $con)
- {
- // Prepared statement
- $stmt = $this->con->prepare('INSERT INTO bananas VALUES (:name)');
-
- $stmt->bindValue(':name', $name);
-
- $stmt->execute();
-
- // Set the id for this banana
- //
- // It becomes easy to know whether the banana is new or not,
- // you just need to check if id is defined.
- $this->id = $this->con->lastInsertId();
- }
-
----
-
-# Table Data Gateway 
-
----
-
-# Table Data Gateway
-
-An object that acts as a _Gateway_ to a database table.
-One instance handles all the rows in the table.
-
-It's a **D**ata **A**ccess **O**bject.
-
- !php
- $table = new BananaGateway(new Connection('...'));
-
- // Insert a new record
- $id = $table->insert('My favorite banana');
-
- // Update it
- $table->update($id, 'THE banana');
-
- // Delete it
- $table->delete($id);
-
-
-### CRUD
-
-A DAO implements the well-known **C**reate **R**ead **U**pdate
-**D**elete methods.
-
-**R**ead should not be a single method: **Finders** to the rescue!
-
----
-
-# Table Data Gateway
-
-### Implementation
-
- !php
- class BananaGateway
- {
- private $con;
-
- public function __construct(Connection $con)
- {
- $this->con = $con;
- }
-
- public function insert($name) {}
-
- public function update($id, $name) {}
-
- public function delete($id);
- }
-
----
-
-# Table Data Gateway
-
-### The insert method
-
- !php
- /**
- * @param string $name The name of the banana you want to create
- *
- * @return int The id of the banana
- */
- public function insert($name)
- {
- // Prepared statement
- $stmt = $this->con->prepare('INSERT INTO bananas VALUES (:name)');
-
- $stmt->bindValue(':name', $name);
-
- $stmt->execute();
-
- return $this->con->lastInsertId();
- }
-
----
-
-# Table Data Gateway
-
-### The update method
-
- !php
- /**
- * @param int $id The id of the banana to update
- * @param string $name The new name of the banana
- *
- * @return bool Returns `true` on success, `false` otherwise
- */
- public function update($id, $name)
- {
- $stmt = $this->con->prepare(<<bindValue(':id', $id);
- $stmt->bindValue(':name', $name);
-
- return $stmt->execute();
- }
-
----
-
-# Table Data Gateway
-
-### The delete method
-
- !php
- /**
- * @param int $id The id of the banana to delete
- *
- * @return bool Returns `true` on success, `false` otherwise
- */
- public function delete($id)
- {
- $stmt = $this->con->prepare('DELETE FROM bananas WHERE id = :id');
-
- $stmt->bindValue(':id', $id);
-
- return $stmt->execute();
- }
-
----
-
-# Table Data Gateway
-
-### Finders
-
- !php
- // Retrieve all bananas
- $bananas = $table->findAll();
-
- // Find bananas by name matching 'THE %'
- $bananas = $table->findByName('THE %');
-
- // Retrieve a given banana using its id
- $banana = $table->find(123);
-
- // Find one banana by name matching 'THE %'
- $banana = $table->findOneByName('THE %');
-
-> Use the `__call()` magic method to create magic finders:
-[http://www.php.net/manual/en/language.oop5.overloading.php#object.call](http://www.php.net/manual/en/language.oop5.overloading.php#object.call).
-
----
-
-# Active Record 
-
----
-
-# Active Record
-
-An object that wraps a row in a database table, encapsulates
-the database access, and adds domain logic on that data.
-
-## Active Record = Row Data Gateway + Domain Logic
-
- !php
- $con = new Connection('...');
-
- $banana = new Banana();
- $banana->setName('Another banana');
- $banana->save($con);
-
- // Call a method that is part of the domain logic
- // What can a banana do anyway?
- $banana->grow();
-
- // Smart `save()` method
- // use `isNew()` under the hood
- $banana->save($con);
-
----
-
-# Active Record
-
- !php
- class Banana
- {
- private $height = 1;
-
- public function grow()
- {
- $this->height++;
- }
-
- public function save(Connection $con)
- {
- if ($this->isNew()) {
- // issue an INSERT query
- } else {
- // issue an UPDATE query
- }
- }
-
- public function isNew()
- {
- // Yoda style
- return null === $this->id;
- }
- }
-
----
-
-# Data Mapper 
-
----
-
-# Data Mapper
-
-A layer of Mappers that moves data between objects and a database
-while keeping them independent of each other and the mapper itself.
-
-Sort of _"Man in the Middle"_.
-
- !php
- class BananaMapper
- {
- private $con;
-
- public function __construct(Connection $con)
- {
- $this->con = $con;
- }
-
- public function persist(Banana $banana)
- {
- // code to save the banana
- }
-
- public function remove(Banana $banana)
- {
- // code to delete the banana
- }
- }
-
----
-
-# Data Mapper
-
-### Usage
-
- !php
- $banana = new Banana();
- $banana->setName('Fantastic Banana');
-
- $con = new Connection('...');
- $mapper = new BananaMapper($con);
-
-### Persist = Save or Update
-
- !php
- $mapper->persist($banana);
-
-### Remove
-
- !php
- $mapper->remove($banana);
-
----
-
-# Identity Map 
-
----
-
-# Identity Map
-
-Ensures that each object gets loaded only once by keeping every loaded object in
-a map. Looks up objects using the map when referring to them.
-
- !php
- class Finder
- {
- private $identityMap = [];
-
- public function find($id)
- {
- if (!isset($this->identityMap[$id])) {
- // fetch the object for the given id
- $this->identityMap[$id] = ...;
- }
-
- return $this->identityMap[$id];
- }
- }
-
----
-
-# Data Access Layer
-
----
-
-# Data Access Layer / Data Source Name
-
-A **D**ata **A**ccess **L**ayer (DAL) is a standard API to manipulate data,
-no matter which database server is used.
-A **D**ata **S**ource **N**ame (DSN) can be used to determine which database
-vendor you are using.
-
-### PHP Data Object (PDO)
-
-A DSN in PHP looks like: `:host=;dbname=` where:
-
-* `` can be: `mysql`, `sqlite`, `pgsql`, etc;
-* `` is the IP address of the database server (e.g. `localhost`);
-* `` is your database name.
-
-> [http://www.php.net/manual/en/intro.pdo.php](http://www.php.net/manual/en/intro.pdo.php)
-
----
-
-# Data Access Layer
-
-### PDO usage
-
- !php
- $dsn = 'mysql:host=localhost;dbname=test';
-
- $con = new PDO($dsn, $user, $password);
-
- // Prepared statement
- $stmt = $con->prepare($query);
- $stmt->execute();
-
-Looks like the `Connection` class you used before, right?
-
- !php
- class Connection extends PDO
- {
- }
-
-### Usage
-
- !php
- $con = new Connection($dsn, $user, $password);
-
----
-
-# Data Access Layer
-
-### Refactoring
-
-Refactoring is a disciplined technique for restructuring an existing
-body of code, altering its internal structure without changing its
-external behavior.
-
- !php
- class Connection extends PDO
- {
- /**
- * @param string $query
- * @param array $parameters
- *
- * @return bool Returns `true` on success, `false` otherwise
- */
- public function executeQuery($query, array $parameters = [])
- {
- $stmt = $this->prepare($query);
-
- foreach ($parameters as $name => $value) {
- $stmt->bindValue(':' . $name, $value);
- }
-
- return $stmt->execute();
- }
- }
-
----
-
-# Data Access Layer
-
-### Usage
-
- !php
- /**
- * @param int $id The id of the banana to update
- * @param string $name The new name of the banana
- *
- * @return bool Returns `true` on success, `false` otherwise
- */
- public function update($id, $name)
- {
- $query = 'UPDATE bananas SET name = :name WHERE id = :id';
-
- return $this->con->executeQuery($query, [
- 'id' => $id,
- 'name' => $name,
- ]);
- }
-
----
-
-# Object Relational Mapping
-
----
-
-# Object Relational Mapping (1/4)
-
-Introduces the notion of **relations** between objects:
-
-* One-To-One;
-* One-To-Many;
-* Many-To-Many.
-
-An **ORM** is often considered as a _tool_ that implements some design patterns
-seen above, and that eases relationships between objects.
-
----
-
-# Object Relational Mapping (2/4)
-
-### One-To-One (1-1)
-
-
-
-### Code Snippet
-
- !php
- $profile = $banana->getProfile();
-
-.fx: no-border
-
----
-
-# Object Relational Mapping (3/4)
-
-### One-To-Many (1-N)
-
-
-
-### Code Snippet
-
- !php
- $bananas = $bananaTree->getBananas();
-
-.fx: no-border
-
----
-
-# Object Relational Mapping (4/4)
-
-### Many-To-Many (N-N)
-
-
-
-### Code Snippet
-
- !php
- $roles = [];
- foreach ($banana->getBananaRoles() as $bananaRole) {
- $roles[] = $bananaRole->getRole();
- }
-
- // Or, better:
- $roles = $banana->getRoles();
-
-.fx: no-border
-
----
-
-# Existing Components
-
-### Propel ORM
-
-An ORM that implements the **Table Data Gateway** and **Row Data Gateway**
-patterns, often seen as an **Active Record** approach.
-
-> Documentation: [www.propelorm.org](http://www.propelorm.org).
-
-### Doctrine2 ORM
-
-An ORM that implements the **Data Mapper** pattern.
-
-> Documentation: [www.doctrine-project.org](http://www.doctrine-project.org/).
-
----
-
-# A Note About Domain-Driven Design
-
----
-
-# Entities
-
-An object defined primarily by its identity is called an **entity**:
-
- !php
- class Customer
- {
- private $id;
-
- private $name;
-
- public function __construct($id, Name $name)
- {
- $this->id = $id;
- $this->name = $name;
- }
-
- public function getId()
- {
- return $this->id;
- }
-
- public function getName()
- {
- return $this->name;
- }
- }
-
----
-
-# Value Objects
-
-An object that represents a descriptive aspect of the domain with no conceptual
-identity is called a **Value Object**:
-
- !php
- class Name
- {
- private $firstName;
-
- private $lastName;
-
- public function __construct($firstName, $lastName)
- {
- $this->firstName = $firstName;
- $this->lastName = $lastName;
- }
-
- public function getFirstName()
- {
- return $this->firstName;
- }
-
- public function getLastName()
- {
- return $this->lastName;
- }
- }
-
----
-
-# The Repository Pattern 
-
----
-
-# The Repository Pattern
-
-A Repository **mediates between the domain and data mapping layers**, acting
-like an **in-memory domain object collection**.
-
- !php
- interface CustomerRepository
- {
- /**
- * @return Customer
- */
- public function find($customerId);
-
- /**
- * @return Customer[]
- */
- public function findAll();
-
- public function add(Customer $user);
-
- public function remove(Customer $user);
- }
-
----
-
-# The Repository Pattern
-
-Client objects construct **query specifications** declaratively and submit them
-to Repository for satisfaction.
-
-**Objects can be added to and removed** from the Repository, **as they can from
-a simple collection of objects**, and the mapping code encapsulated by the
-Repository will carry out the appropriate operations behind the scenes.
-
-Conceptually, a Repository encapsulates the set of objects persisted in a data
-store and the operations performed over them, providing a more object-oriented
-view of the persistence layer.
-
-Repository also supports the objective of achieving a **clean separation and
-one-way dependency between the domain and data mapping layers**.
-
----
-
-# The Specification Pattern 
-
----
-
-# The Specification Pattern
-
-The Specification pattern is a way to model business rules as individual
-objects. The idea is that a question about an object, is answered by a
-`isSatisfiedBy()` method:
-
- !php
- interface CustomerSpecification
- {
- /**
- * @return boolean
- */
- public function isSatisfiedBy(Customer $customer);
- }
-
-
-
- !php
- class CustomerIsPremium implements CustomerSpecification
- {
- /**
- * {@inheritDoc}
- */
- public function isSatisfiedBy(Customer $customer)
- {
- // figure out if the customer is indeed premium,
- // and return true or false.
- }
- }
-
----
-
-# Repository ♥ Specification
-
-A `findSatisfying()` method can be added to the `CustomerRepository`:
-
- !php
- interface CustomerRepository
- {
- ...
-
- /**
- * @return Customer[]
- */
- public function findSatisfying(CustomerSpecification $specification);
- }
-
-### Usage
-
- !php
- $specification = new CustomerIsPremium();
- $customers = $repository->findSatisfying($specification);
-
----
-
-# Combine Them!
-
- !php
- class OrSpecification implements CustomerSpecification
- {
- public function __construct(
- CustomerSpecification $s1,
- CustomerSpecification $s2
- ) {
- $this->s1 = $s1;
- $this->s2 = $s2;
- }
-
- public function isSatisfiedBy(Customer $c)
- {
- return $this->s1->isSatisfiedBy($c) || $this->s2->isSatisfiedBy($c);
- }
- }
-
-
-
- !php
- class AndSpecification implements CustomerSpecification
- {
- ...
-
- public function isSatisfiedBy(Customer $c)
- {
- return $this->s1->isSatisfiedBy($c) && $this->s2->isSatisfiedBy($c);
- }
- }
-
----
-
-# Combine Them!
-
- !php
- class NotSpecification implements CustomerSpecification
- {
- public function __construct(CustomerSpecification $s)
- {
- $this->s = $s;
- }
-
- public function isSatisfiedBy(Customer $c)
- {
- return !$this->s->isSatisfiedBy($c);
- }
- }
-
-### Usage
-
- !php
- // Find customers who have ordered exactly three times,
- // but who are not premium customers (yet?)
- $specification = new AndSpecification(
- new CustomerHasOrderedThreeTimes(),
- new NotSpecification(
- new CustomerIsPremium()
- )
- );
-
- $customers = $repository->findSatisfying($specification);
-
----
-
-# Specification For Business Rules
-
-Reuse your specifications in your business layer:
-
- !php
- class AwesomeOfferSender
- {
- private $specification;
-
- public function __construct(CustomerIsPremium $specification)
- {
- $this->specification = $specification;
- }
-
- public function sendOffersTo(Customer $customer)
- {
- if ($this->specification->isSatisfiedBy($customer)) {
- // send offers
- }
- }
- }
-
----
-
-# Checkout: [RulerZ](https://github.com/K-Phoen/rulerz)
diff --git a/src/common/10_sessions.md b/src/common/10_sessions.md
deleted file mode 100644
index c13a82d..0000000
--- a/src/common/10_sessions.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Sessions
-
----
-
-# Overview
-
-Sessions are a way to preserve certain data across subsequent accesses.
-
-### In A Nutshell
-
-* Unique identifier (session id);
-* Server side;
-* Easy to use;
-* Built-in.
-
-### A few use cases:
-
-* Keeping user authentication and roles;
-* Storing items into a cart;
-* Storing a flash message between redirections.
-
----
-
-# Code Please
-
- !php
- // Initalize session
- session_start();
-
- // Regenerate identifier
- session_regenerate_id();
-
- // Assign "key" to `$value`
- $_SESSION['key'] = $value;
-
- // Retrieve "key"
- $_SESSION['key'];
-
- // Terminate session
- session_destroy();
-
-
-> Session should be started before headers are sent!
-> [http://php.net/manual/en/book.session.php](http://php.net/manual/en/book.session.php).
-
----
-
-# Security Concerns
-
-* Prediction (guessing a valid session identifier);
-* Man in the Middle (capturing a valid session identifier);
-* Session Fixation (attacker chooses the session identifier);
-* Session Hijacking (all attacks that attempt to gain access to another user's
- session).
-
-### Workarounds
-
-* Regenerate ids when authentication changes;
-* Bind sessions to IP addresses;
-* Define expiration/timeout;
-* Don't rely on the default settings;
-* Use HTTPS.
-
----
-
-# Session Configuration
-
-An example of PHP session configuration that is more secure:
-
- !ini
- ; Helps mitigate XSS by telling the browser not to expose the cookie to
- ; client side scripting such as JavaScript
- session.cookie_httponly = 1
-
- ; Prevents session fixation by making sure that PHP only uses cookies for
- ; sessions and disallow session ID passing as a GET parameter
- session.use_only_cookies = 1
-
- ; Better entropy source
- ; Evades insufficient entropy vulnerabilities
- session.entropy_file = "/dev/urandom"
- ; `session.entropy_length` might help too!
-
- ; Smaller exploitation window for XSS/CSRF/Clickjacking...
- session.cookie_lifetime = 0
-
- ; Ensures session cookies are only sent over secure connections (it requires
- ; a valid SSL certificate)
- ; Related to OWASP 2013-A6-Sensitive Data Exposure
- session.cookie_secure = 1
diff --git a/src/common/11_authentication.md b/src/common/11_authentication.md
deleted file mode 100644
index 492dda1..0000000
--- a/src/common/11_authentication.md
+++ /dev/null
@@ -1,235 +0,0 @@
-# Authentication
-
----
-
-# What You Have Right Now
-
-No **Authentication**/**Security Layer**, anyone can access everything.
-
-
-
-
-
----
-
-# The Big Picture
-
-
-
-
----
-
-# The Interceptor Pattern
-
-The **Security Layer**, as seen before, has to **intercept** the process of
-converting a request into a response in order to perform some checks.
-
-You need a way to hook into this process before invoking the controller:
-**Interceptor Pattern** to the rescue!
-
-The **Interceptor Pattern** allows you to execute some code during the default
-application's lifecyle.
-
-A way to implement this pattern is to use **events**. It is more or less like
-the **Observer**/**Observable** pattern.
-
-### Event Dispatcher
-
-The application notifies a set of listeners to an event.
-The listeners can register themselves to a particular event.
-An **Event Dispatcher** manages both the listeners, and the events.
-
----
-
-# Introducing the Event Dispatcher
-
-Simple event dispatcher using a **trait**:
-
- !php
- trait EventDispatcherTrait
- {
- private $events = [];
-
- public function addListener($name, $callable)
- {
- $this->events[$name][] = $callable;
- }
-
- public function dispatch($name, array $arguments = [])
- {
- foreach ($this->events[$name] as $callable) {
- call_user_func_array($callable, $arguments);
- }
- }
- }
-
----
-
-# Using the EventDispatcherTrait
-
-In order to intercept the process described before, you have to **notify** some
-listeners once you enter in the `process()` method by **dispatching** the event:
-
- !php
- class App
- {
- use EventDispatcherTrait;
-
- ...
-
- private function process(Request $request, Route $route)
- {
- $this->dispatch('process.before', [ $request ]);
-
- ...
- }
- }
-
-The **listeners** have to listen to this event:
-
- !php
- $app->addListener('process.before', function (Request $request) {
- // code to execute
- });
-
----
-
-# The Firewall (1/2)
-
-Now that you can hook into the application's lifecycle, you can write a basic
-but powerful **Firewall**.
-
-This firewall needs a **whitelist** of unsecured routes (i.e. routes that
-don't require the user to be authenticated) associated with their allowed HTTP
-methods:
-
- !php
- $allowed = [
- '/login' => [ Request::GET, Request::POST ],
- '/locations' => [ Request::GET ],
- ];
-
-> [Never Blacklist; Only
-Whitelist](http://phpsecurity.readthedocs.org/en/latest/Input-Validation.html#never-blacklist-only-whitelist)
-
----
-
-# The Firewall (2/2)
-
-The **Firewall** leverages the **session** to determine whether the user is
-authenticated or not:
-
- !php
- session_start();
-
- if (isset($_SESSION['is_authenticated'])
- && true === $_SESSION['is_authenticated']) {
- return;
- }
-
-If authentication fails, the server should return a `401` status code.
-
----
-
-# Implementing The Firewall
-
- !php
- $app->addListener('process.before', function(Request $req) use ($app) {
- session_start();
-
- $allowed = [
- '/login' => [ Request::GET, Request::POST ],
- ];
-
- if (isset($_SESSION['is_authenticated'])
- && true === $_SESSION['is_authenticated']) {
- return;
- }
-
- foreach ($allowed as $pattern => $methods) {
- if (preg_match(sprintf('#^%s$#', $pattern), $req->getUri())
- && in_array($req->getMethod(), $methods)) {
- return;
- }
- }
-
- switch ($req->guessBestFormat()) {
- case 'json':
- throw new HttpException(401);
- }
-
- return $app->redirect('/login');
- });
-
----
-
-# Authentication Mechanism
-
-
-
-
----
-
-# Adding New Routes
-
- !php
- $app->get('/login', function () use ($app) {
- return $app->render('login.php');
- });
-
- $app->post('/login', function (Request $request) use ($app) {
- $user = $request->getParameter('user');
- $pass = $request->getParameter('password');
-
- if ('will' === $user && 'will' === $pass) {
- $_SESSION['is_authenticated'] = true;
-
- return $app->redirect('/');
- }
-
- return $app->render('login.php', [ 'user' => $user ]);
- });
-
- $app->get('/logout', function (Request $request) use ($app) {
- session_destroy();
-
- return $app->redirect('/');
- });
-
-> [UI Terminology: Logon vs
-Login](http://stackoverflow.com/questions/406016/ui-terminology-logon-vs-login).
-
----
-
-# Stateless Authentication
-
-Useful for API authentication.
-
-### OpenID (in stateless mode)
-
-[http://openid.net/](http://openid.net/)
-
-### Basic and Digest Access Authentication
-
-[http://pretty-rfc.herokuapp.com/RFC2617](http://pretty-rfc.herokuapp.com/RFC2617)
-
-### WSSE Username Token
-
-[http://www.xml.com/pub/a/2003/12/17/dive.html](http://www.xml.com/pub/a/2003/12/17/dive.html)
-
----
-
-# Basic Security Thinking
-
-1. Trust nobody and nothing;
-2. Assume a worse-case scenario;
-3. Apply Defense-In-Depth;
-4. Keep It Simple Stupid (KISS);
-5. Principle of Least Privilege;
-6. Attackers can smell obscurity;
-7. RTFM but never trust it;
-8. If it is not tested, it does not work;
-9. It is always your fault!
-
-> [Survive The Deep End: PHP
-Security](http://phpsecurity.readthedocs.org/en/latest/index.html)
diff --git a/src/extended/00_intro.md b/src/extended/00_intro.md
deleted file mode 100644
index c94fa73..0000000
--- a/src/extended/00_intro.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# A Framework To Simplify Developments
-
-A **framework helps you work better** by structuring developments,
-and **faster** by reusing generic modules.
-
-A framework **facilitates long-term maintenance** and **scalability** by
-complying with standard development rules.
-
-Compliance with development standards also simplifies integrating and
-interfacing the application with the rest of the information system.
-
-In other words, it works as a tool to make the development process
-easier and more productive.
-
-Most of the time, a framework implements many kinds of **design patterns**.
-
-> Read more: [Symfony explained to a
-> Developer](https://symfony.com/symfony-explained-to-a-developer).
diff --git a/src/extended/01_symfony2.md b/src/extended/01_symfony2.md
deleted file mode 100644
index a2bb0c9..0000000
--- a/src/extended/01_symfony2.md
+++ /dev/null
@@ -1,655 +0,0 @@
-# 
-
-.fx: no-border
-
----
-
-# What Is Symfony?
-
-First of all:
-
-
-
Symfony is a reusable set of standalone, decoupled, and cohesive PHP
- components that solve common web development problems.
-
-
-Then, based on these components:
-
-
-
Symfony is also a full-stack web framework.
-
-
-_Fabien Potencier,
-[http://fabien.potencier.org/article/49/what-is-symfony2](http://fabien.potencier.org/article/49/what-is-symfony2)._
-
----
-
-# Is Symfony A MVC Framework?
-
----
-
-# NO!
-
-.fx: color-red
-
----
-
-# Why You Should Use Symfony
-
-Symfony is built on powerful concepts:
-
-* **Separation of Concerns**;
-* **Pragmatism**;
-* **Best Practices**.
-
-
-
-It has been written by [~1502 developers](http://symfony.com/contributors/code).
-
-Open Source, **MIT** licensed.
-
----
-
-# The Symfony Components
-
-The Components implement **common features** needed to develop websites.
-
-They are the **foundation of the Symfony full-stack framework**, but they can
-also be used **standalone** even if you don't use the framework as they don't
-have any mandatory dependencies.
-
-There are ~30 components, including:
-
- !text
- BrowserKit EventDispatcher OptionsResolver Templating
- ClassLoader ExpressionLanguage Process Translation
- Config Filesystem PropertyAccess VarDumper
- Console Finder PropertyInfo Yaml
- CssSelector Form Routing
- Debug HttpFoundation Security
- DependencyInjection HttpKernel Serializer
- DomCrawler Intl Stopwatch
-
----
-
-# Getting Ready With Components
-
-Say you want to play with YAML files, start by requiring the `symfony/yaml`
-component into your `composer.json` file:
-
- !yaml
- {
- "require": {
- "symfony/yaml": "~3.0"
- }
- }
-
-Install it by running `php composer.phar install`, and use it:
-
- !php
- require __DIR__ . '/vendor/autoload.php';
-
- use Symfony\Component\Yaml\Yaml;
-
- $yaml = Yaml::parse('/path/to/file.yml');
-
-> [http://symfony.com/doc/current/components/yaml/introduction.html](http://symfony.com/doc/current/components/yaml/introduction.html)
-
----
-
-# Full-Stack Framework
-
-The **Symfony Framework** accomplishes two distinct tasks:
-
-* Provides a selection of components;
-* Provides sensible configuration and a "glue" library that ties all of these
- pieces together.
-
-The goal of the framework is **to integrate many independent tools** in order to
-provide a consistent experience for the developer. Even **the framework itself is
-a Symfony bundle** (i.e. a plugin) that can be configured or replaced entirely.
-
-Symfony **provides a powerful set of tools for rapidly developing web
-applications** without imposing on your application.
-
-> [http://symfony.com/doc/current/book/index.html](http://symfony.com/doc/current/book/index.html)
-
----
-
-# Overall Architecture
-
----
-
-# The Symfony Request
-
- !php
- use Symfony\Component\HttpFoundation\Request;
-
- $request = Request::createFromGlobals();
-
- // the URI being requested (e.g. /about) minus any query parameters
- $request->getPathInfo();
-
- // the HTTP verb
- $request->getMethod();
-
- // GET variables
- $request->query->get('foo');
-
- // POST variables
- $request->request->get('bar');
-
- // SERVER variables
- $request->server->get('HTTP_HOST');
-
- // retrieve an HTTP request header, with normalized, lowercase keys
- $request->headers->get('host');
-
----
-
-# The Symfony Response
-
- !php
- use Symfony\Component\HttpFoundation\Response;
-
- $response = new Response();
-
- $response->setContent(<<
-
-
Hello world!
-
-
- HTML
- );
-
- $response->setStatusCode(200);
-
- $response->headers->set('Content-Type', 'text/html');
-
- // prints the HTTP headers followed by the content
- $response->send();
-
----
-
-# The Simplest Front Controller Ever
-
- !php
- // index.php
- use Symfony\Component\HttpFoundation\Request;
- use Symfony\Component\HttpFoundation\Response;
-
- $request = Request::createFromGlobals();
- $path = $request->getPathInfo();
-
- if (in_array($path, ['', '/'])) {
- $response = new Response('Welcome to the homepage.');
- } elseif ('/hello' === $path) {
- $response = new Response('hello, World!');
- } else {
- $response = new Response('Page not found.', 404);
- }
-
- $response->send();
-
----
-
-# The Symfony Application Flow
-
-It's all about transforming a **Request** into a **Response**:
-
-
-
-
----
-
-# Routing Definition
-
-The routing system determines which PHP function should be executed based on
-information from the request and routing configuration you've created.
-
- !yaml
- # app/config/routing.yml
- hello:
- path: /hello
- defaults: { _controller: AppBundle:Main:hello }
-
-The `AppBundle:Main:hello` string is a short syntax that points to a
-specific PHP method named `helloAction()` inside a class called
-`MainController`.
-
-
-
-
- This example uses YAML to define the routing configuration.
- Routing configuration can also be written in other formats such as XML or PHP.
-
-
-
----
-
-# Your First Controller
-
-In Symfony, a method in a controller is called an **action**. The convention is
-to suffix each method with `Action`.
-
-Also, each controller should be suffixed with `Controller`.
-
- !php
- // src/AppBundle/Controller/MainController.php
- namespace AppBundle\Controller;
-
- use Symfony\Component\HttpFoundation\Response;
-
- class MainController
- {
- public function helloAction()
- {
- return new Response('
Hello, World!
');
- }
- }
-
----
-
-# A Symfony Project (1/2)
-
-**Recommended** structure of a Symfony (3.x) project:
-
- !text
- path/to/project/
- app/
- config/
- Resources/
- views/
- bin/
- console
- src/
- ...
- tests/
- ...
- var/
- cache/
- logs/
- sessions/
- vendor/
- ...
- web/
- app.php
- ...
-
----
-
-# A Symfony Project (2/2)
-
-Each directory has its own purpose (and set of files):
-
-* `app/` contains the application kernel, views, and the configuration;
-* `src/` contains your **bundles**;
-* `tests/` contains your tests;
-* `var/` contains files that change often (like in Unix systems);
-* `vendor/` contains your dependencies;
-* `web/` contains your front controllers and your assets.
-
----
-
-# Application Kernel
-
-This is the **central part** of your application:
-
- !php
- // app/AppKernel.php
- use Symfony\Component\HttpKernel\Kernel;
-
- class AppKernel extends Kernel
- {
- public function registerBundles()
- {
- $bundles = [
- new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
- // ...
- ];
-
- if (in_array($this->getEnvironment(), ['dev', 'test'])) {
- $bundles[] = // dev bundle;
- }
-
- return $bundles;
- }
-
- // ...
- }
-
----
-
-# Application Configuration
-
-An application consists of a collection of "bundles" representing all of the
-features and capabilities of your application.
-
-Each "bundle" can be customized via configuration files written in `YAML`, `XML`
-or `PHP`.
-
-By default, the main configuration file lives in the `app/config/`
-directory and is called either `config.yml`, `config.xml` or `config.php`
-depending on which format you prefer.
-
-Symfony is all about configuring everything, and you can do pretty much
-everything you want. That's why people agreed on some conventions, but then
-again, a convention is just **A** way to do things, not **THE** way to do them.
-
----
-
-# YAML Configuration
-
-### Example:
-
- !yaml
- # app/config/config.yml
- imports:
- - { resource: parameters.yml }
- - { resource: security.yml }
-
- framework:
- secret: '%secret%'
- router: { resource: '%kernel.root_dir%/config/routing.yml' }
- # ...
-
- # Twig Configuration
- twig:
- debug: '%kernel.debug%'
- strict_variables: '%kernel.debug%'
-
- # ...
-
----
-
-# XML Configuration
-
-### Example:
-
- !xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----
-
-# PHP Configuration
-
-### Example:
-
- !php
- $this->import('parameters.yml');
- $this->import('security.yml');
-
- $container->loadFromExtension('framework', [
- 'secret' => '%secret%',
- 'router' => [
- 'resource' => '%kernel.root_dir%/config/routing.php'
- ],
- // ...
- ]);
-
- // Twig Configuration
- $container->loadFromExtension('twig', [
- 'debug' => '%kernel.debug%',
- 'strict_variables' => '%kernel.debug%',
- ]);
-
- // ...
-
----
-
-# The Rules (Well... My Rules)
-
-The **main configuration** MUST be written in `YAML`:
-
- !yaml
- # app/config/config.yml
- # ...
- twig:
- debug: '%kernel.debug%'
- strict_variables: '%kernel.debug%'
-
-The **routing definition** MUST be written in `YAML`:
-
- !yaml
- # app/config/routing.yml
- hello:
- path: /hello
- defaults: { _controller: AppBundle:Main:hello }
-
-The **DI Container configuration** MUST be written in `XML`:
-
- !xml
-
-
-
-
----
-
-# Environments
-
-An application can run in various environments. The different environments
-**share the same PHP code**, but use different configuration.
-
-A Symfony project generally uses three environments: `dev`, `test` and `prod`.
-
- !php
- // web/app.php
-
- // ...
- $kernel = new AppKernel('prod', false);
-
-The `AppKernel` class is responsible for actually loading the configuration file
-of your choice:
-
- !php
- // app/AppKernel.php
- public function registerContainerConfiguration(LoaderInterface $loader)
- {
- $loader->load(
- __DIR__ . '/config/config_' . $this->getEnvironment() . '.yml'
- );
- }
-
----
-
-# What Is A Bundle?
-
-_A **Bundle** is a directory containing a set of files (PHP files, stylesheets,
-JavaScripts, images, ...) that implement a **single feature** (a blog, a forum,
-etc)._
-
-It should be **reusable**, so that you don't reinvent the wheel each time you
-need a common feature. In Symfony, (almost) everything lives inside a bundle.
-
-In order to use a bundle in your application, you need to register it in the
-`AppKernel`, using the `registerBundles()` method:
-
- !php
- public function registerBundles()
- {
- $bundles = array(
- // ...
-
- new My\AwesomeBundle\MyAwesomeBundle(),
- );
-
- // ...
- }
-
----
-
-# Bundle: Directory Structure
-
-Recommended structure for a bundle:
-
- !text
- XXX/...
- DemoBundle/
- DemoBundle.php
- Controller/
- Resources/
- config/
- doc/
- index.rst
- translations/
- views/
- public/
- Tests/
- LICENSE
-
-The `DemoBundle` class is mandatory, and both `LICENSE` and
-`Resources/doc/index.rst` files should be present.
-
-The `XXX` directory(ies) reflects the namespace structure of the bundle.
-
----
-
-# Bundle: Where To Put Your Classes?
-
-
-
-
-
-
-
-
-
Type
-
Directory
-
-
-
-
-
Commands
-
Command/
-
-
-
Controllers
-
Controller/
-
-
-
Service Container Extensions
-
DependencyInjection/
-
-
-
Event Listeners
-
EventListener/
-
-
-
Configuration
-
Resources/config/
-
-
-
Web Resources
-
Resources/public/
-
-
-
Translation files
-
Resources/translations/
-
-
-
Templates
-
Resources/views/
-
-
-
Unit and Functional Tests
-
Tests/
-
-
-
-
----
-
-# Creating a Bundle
-
-A **bundle** has to extend the `Symfony\Component\HttpKernel\Bundle\Bundle`
-class:
-
- !php
- // src/Acme/MyFirstBundle/AcmeMyFirstBundle.php
- namespace Acme\MyFirstBundle;
-
- use Symfony\Component\HttpKernel\Bundle\Bundle;
-
- class AcmeMyFirstBundle extends Bundle
- {
- }
-
-Then, you can register your bundle:
-
- !php
- // app/AppKernel.php
- public function registerBundles()
- {
- $bundles = array(
- new Acme\MyFirstBundle\AcmeMyFirstBundle(),
- );
-
- return $bundles;
- }
-
----
-
-# The Web Directory
-
-The web root directory is the **home of all public and static files** including
-images, stylesheets, and JavaScript files. It is also where each front
-controller lives:
-
- !php
- // web/app.php
- require_once __DIR__.'/../app/bootstrap.php.cache';
- require_once __DIR__.'/../app/AppKernel.php';
-
- use Symfony\Component\HttpFoundation\Request;
-
- $kernel = new AppKernel('prod', false);
- $request = Request::createFromGlobals();
- $response = $kernel->handle($request);
- $response->send();
-
-The front controller file (`app.php` in this example) is the actual PHP file
-that's executed when using a Symfony application and its job is to **use a
-Kernel** class, `AppKernel`, to **bootstrap the application**, for a given
-**environment**.
-
----
-
-# Summary
-
-Creating a page is a three-step process involving a _route_, a _controller_, and
-(optionally) a _template_.
-
-Each project contains just a few main directories: `web/` (web assets and the
-front controllers), `app/` (configuration), `src/` (your bundles), and `vendor/`
-(third-party code).
-
-Each feature in Symfony (including the Symfony framework core) is organized
-into a **bundle**, which is a structured set of files for that feature.
-
-The configuration for each bundle lives in the `Resources/config` directory of the
-bundle and can be specified in `YAML`, `XML` or `PHP`.
-
-The global application configuration lives in the `app/config/` directory.
-
-Each environment is accessible via a different front controller (e.g. `app.php`
-and `app_dev.php`) and loads a different configuration file.
-
----
-
-# Read The [Best Practices](https://symfony.com/doc/current/best_practices/index.html)!
diff --git a/src/extended/02_symfony2_controllers.md b/src/extended/02_symfony2_controllers.md
deleted file mode 100644
index e7dadd3..0000000
--- a/src/extended/02_symfony2_controllers.md
+++ /dev/null
@@ -1,313 +0,0 @@
-# Controllers
-
----
-
-# Request, Controller, Response
-
-A controller is a **PHP function** you create that takes information from the
-**HTTP request** and constructs and returns an **HTTP response**.
-
-Every request handled by a Symfony project goes through the same lifecycle:
-
-1. Each request is handled by a single front controller file (e.g. `app.php` or
-`app_dev.php`) that bootstraps the application;
-2. The **Router** reads information from the request (e.g. the URI), finds a route
-that matches that information, and reads the `_controller` parameter from the
-route;
-3. The **controller** from the matched route is executed and the code inside the
-controller creates and returns a `Response` object;
-4. The HTTP headers and content of the `Response` object are sent back to the
-client.
-
----
-
-# The Simplest Page Ever
-
-### Routing Definition
-
- !yaml
- # app/config/routing.yml
- homepage:
- path: /
- defaults: { _controller: AppBundle:Hello:index }
-
-### Controller Implementation
-
- !php
- // src/AppBundle/Controller/HelloController.php
- namespace AppBundle\Controller;
-
- use Symfony\Component\HttpFoundation\Response;
-
- class HelloController
- {
- public function indexAction()
- {
- return new Response('Home, Sweet Home!');
- }
- }
-
----
-
-# Controller Naming Pattern
-
-Every route must have a `_controller` parameter, which dictates **which controller
-should be executed when that route is matched**.
-
-This parameter uses a simple string pattern called the **logical controller name**.
-The pattern has three parts, each separated by a colon: `bundle:controller:action`.
-
-For example, a `_controller` value of `AcmeBlogBundle:Blog:show` means:
-
-* Bundle: `AcmeBlogBundle`;
-* Controller Class: `BlogController`;
-* Method Name: `showAction`.
-
-Notice that Symfony adds the string `Controller` to the class name (`Blog` =>
-`BlogController`) and `Action` to the method name (`show` => `showAction`).
-
----
-
-# Route Params as Controller Args
-
-### Routing Definition
-
- !yaml
- # src/AppBundle/Resources/config/routing.yml
- app.hello_hello:
- path: /hello/{name}
- defaults: { _controller: AppBundle:Hello:hello }
- requirements:
- _method: GET
-
-
-### Controller Implementation
-
- !php
- // src/AppBundle/Controller/HelloController.php
-
- class HelloController
- {
- // ...
-
- public function helloAction($name)
- {
- return new Response(sprintf('Home, Sweet %s!', $name));
- }
- }
-
----
-
-# The Request as a Controller Argument
-
-For convenience, you can also have Symfony pass you the Request object as an
-argument to your controller:
-
- !php
- use Symfony\Component\HttpFoundation\Request;
-
- class HelloController
- {
- // ...
-
- public function updateAction(Request $request)
- {
- // do something useful with $request
- }
- }
-
-This is useful when you are working with forms.
-
----
-
-# The Base Controller Class
-
-Symfony comes with a base `Controller` class that assists with some of the most
-common controller tasks and gives your controller class access to any resource
-it might need:
-
- !php
- use Symfony\Bundle\FrameworkBundle\Controller\Controller
-
- class HelloController extends Controller
- {
- // ...
- }
-
-### Redirecting
-
- !php
- $this->redirect($this->generateUrl('homepage'));
-
-
-### Rendering Templates
-
- !php
- return $this->render(
- 'hello/hello.html.twig', array('name' => $name)
- );
-
----
-
-# The Response
-
-The only **requirement** for a controller is to return a `Response` object.
-
-Create a simple `Response` with a `200` status code:
-
- !php
- use Symfony\Component\HttpFoundation\Response;
-
- $response = new Response('Hello, ' . $name, 200);
-
-Create a JSON response with a `200` status code:
-
- !php
- $response = new Response(json_encode(array('name' => $name)));
- $response->headers->set('Content-Type', 'application/json');
-
-Or:
-
- !php
- use Symfony\Component\HttpFoundation\JsonResponse;
-
- $response = new JsonResponse(array('name' => $name));
-
----
-
-# Routing
-
----
-
-# Basic Route Configuration
-
-The Symfony router lets you define URLs that you map to different areas of
-your application.
-
-A _route_ is a map from a URL path to a controller. Each route is named, and
-maps a `path` to a `_controller`:
-
- !yaml
- # app/config/routing.yml
- homepage:
- path: /
- defaults: { _controller: AppBundle:Hello:index }
-
-This route matches the homepage (`/`) and maps it to the
-`AppBundle:Hello:index` controller.
-
-> [http://symfony.com/doc/master/book/routing.html](http://symfony.com/doc/master/book/routing.html)
-
----
-
-# Routing with Placeholders (1/2)
-
-### Required Placeholders
-
- !yaml
- blog:
- path: /blog/{page}
- defaults: { _controller: AcmeBlogBundle:Blog:index }
-
-The path will match anything that looks like `/blog/*`.
-
-Even better, the value matching the `{page}` **placeholder** will be available
-inside your controller.
-
-`/blog` will **not** match.
-
----
-
-# Routing with Placeholders (2/2)
-
-### Optional Placeholders
-
- !yaml
- blog:
- path: /blog/{page}
- defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
-
-By adding `page` to the **defaults** key, `{page}` is **no longer required**.
-
-`/blog` will match this route and the value of the `page` parameter will be
-set to `1`. `/blog/2` will also match, giving the `page` parameter a value of `2`.
-
----
-
-# Requirements
-
- !yaml
- blog:
- path: /blog/{page}
- defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
- requirements:
- page: \d+
-
-The `\d+` requirement is a **regular expression** that says that the value of
-the `{page}` parameter must be a digit (i.e. a number).
-
-### HTTP Method Requirements
-
- !yaml
- # src/AppBundle/Resources/config/routing.yml
- app.hello_hello:
- path: /hello/{name}
- defaults: { _controller: AppBundle:Hello:hello }
- methods: [ GET ]
- # methods: [ GET, POST ]
-
----
-
-# Including External Routing Resources
-
-All routes are loaded via a single configuration file, most of the time it will
-be `app/config/routing.yml`.
-
-In order to respect the "bundle" principle, the routing configuration should be
-located in the bundle itself, and you should just require it:
-
- !yaml
- # app/config/routing.yml
- appa:
- resource: '@AppBundle/Resources/config/routing.yml'
-
-
-### Prefixing Imported Routes
-
- !yaml
- # app/config/routing.yml
- app:
- resource: '@AppBundle/Resources/config/routing.yml'
- prefix: /demo
-
-The string `/demo` now be prepended to the path of each route loaded from
-the new routing resource.
-
----
-
-# Generating URLs
-
-The `Router` is able to generate both relative and absolute URLs.
-
- !php
- $router = $this->get('router');
-
-### Relative URLs
-
- !php
- $router->generate('app.hello_hello', [ 'name' => 'will' ]);
- // /hello/will
-
-### Absolute URLs
-
- !php
- $router->generate('app.hello_hello', [ 'name' => 'will' ], true);
- // http://example.com/hello/will
-
-### Query String
-
- !php
- $router->generate('app.hello_hello', [
- 'name' => 'will', 'some' => 'thing'
- ]);
- // /hello/will?some=thing
diff --git a/src/extended/03_symfony2_templating.md b/src/extended/03_symfony2_templating.md
deleted file mode 100644
index 4df8423..0000000
--- a/src/extended/03_symfony2_templating.md
+++ /dev/null
@@ -1,401 +0,0 @@
-# Templating
-
----
-
-# 
-
-.fx: no-border
-
----
-
-# Why Twig?
-
-**Fast**, **Secure**, **Flexible**.
-
-#### Before
-
- !php
-
-
----
-
-# Getting Familiar With Twig
-
-### Delimiters
-
-* `{{ ... }}`: prints a **variable** or the result of an expression;
-* `{% ... %}`: controls the **logic** of the template; it is used to execute for
- loops and if statements, for example;
-* `{# ... #}`: **comments**.
-
-> [http://twig.sensiolabs.org/](http://twig.sensiolabs.org/)
-
----
-
-# Accessing Variables
-
- !jinja
- {# array('name' => 'Fabien') #}
- {{ name }}
-
- {# array('user' => array('name' => 'Fabien')) #}
- {{ user.name }}
-
- {# force array lookup #}
- {{ user['name'] }}
-
- {# array('user' => new User('Fabien')) #}
- {{ user.name }}
- {{ user.getName }}
-
- {# force method name lookup #}
- {{ user.name() }}
- {{ user.getName() }}
-
- {# pass arguments to a method #}
- {{ user.date('Y-m-d') }}
-
----
-
-# Control Structure
-
-### Conditions
-
- !jinja
- {% if user.isSuperAdmin() %}
- ...
- {% elseif user.isMember() %}
- ...
- {% else %}
- ...
- {% endif %}
-
-### Loops
-
- !jinja
-
- {% for user in users if user.active %}
-
{{ user.username }}
- {% else %}
-
No users found
- {% endfor %}
-
-
----
-
-# Filters
-
-Filters are used to modify Twig variables.
-
-You can use **inline filters** by using the `|` symbol:
-
- !jinja
- {{ 'hello'|upper }}
-
-But you can also use the **block syntax**:
-
- !jinja
- {% filter upper %}
- hello
- {% endfilter %}
-
-Filters can be parametrized:
-
- !jinja
- {{ post.createdAt|date('Y-m-d') }}
-
-> [http://twig.sensiolabs.org/doc/filters/index.html](http://twig.sensiolabs.org/doc/filters/index.html)
-
-
----
-
-# Including Other Templates
-
-The `include` tag is useful to include a template and return the rendered
-content of that template into the current one:
-
- !jinja
- {% include 'sidebar.html' %}
-
-### Example
-
-Given the following template:
-
- !jinja
- {% for user in users %}
- {% include "render_user.html" %}
- {% endfor %}
-
-with `render_user.html`:
-
- !jinja
-
{{ user.username }}
-
-
-
- !html
-
William D.
-
Julien M.
-
----
-
-# Template Inheritance (1/2)
-
-Let's define a base template, `base.html`, which defines a simple HTML skeleton:
-
- !jinja
- {# app/Resources/views/base.html.twig #}
-
-
-
- {% block title %}Test Application{% endblock %}
-
-
-
-
----
-
-# Agenda
-
-* Unit Testing
-* Functional Testing
-* Behavior Driven Development
-* Testing Tweet Frameworks
-
----
-
-# Unit Testing
-
----
-
-# Unit Testing
-
-Unit testing is a Method by which individual units of source code are tested to
-determine if they are fit for use.
-
-[PHPUnit](http://www.phpunit.de/manual/current/en/) is the de-facto standard for
-unit testing in PHP projects.
-
-Install it using Composer:
-
- !javascript
- {
- "require-dev": {
- "phpunit/phpunit": "~3.7"
- }
- }
-
-Or as a PHAR:
-
- !bash
- $ wget http://pear.phpunit.de/get/phpunit.phar
- $ chmod +x phpunit.phar
-
----
-
-# PHPUnit — The Rules
-
-The tests for a class `Class` go into a class `ClassTest`.
-
-`ClassTest` should inherit from `PHPUnit_Framework_TestCase`, but
-a common practice is to create a `TestCase` class for a project, and to inherit
-from it:
-
- !php
- class TestCase extends PHPUnit_Framework_TestCase {}
-
-The tests are public methods that are named `test*`, but you can also use the
-`@test` annotation:
-
- !php
- class ClassTest extends TestCase
- {
- public function testFoo()
- {
- $object = new MyClass();
- $this->assertEquals('foo', $object->foo(), 'optional comment');
- }
- }
-
----
-
-# PHPUnit — Assertions
-
-* `assertEquals()`
-* `assertTrue()`
-* `assertFalse()`
-* etc.
-
-> See all assertion methods:
-[http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html](http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.assertions).
-
----
-
-# Running PHPUnit
-
-Running the test suite:
-
- !bash
- $ phpunit
- PHPUnit 3.7.0 by Sebastian Bergmann.
-
- .
-
- Time: 1 seconds, Memory: 5.25Mb
-
- OK (1 test, 1 assertion)
-
-
-Getting the code coverage:
-
- !bash
- $ phpunit --coverage-text
-
-
-See also:
-
- !bash
- $ phpunit --log-junit junit_output.xml
-
- $ phpunit --testdox
-
----
-
-# Test Double
-
-* **Dummy** objects are passed around but never actually used. Usually they are
- just used to fill parameter lists;
-* **Fake** objects actually have working implementations, but usually take some
- shortcut which makes them not suitable for production (an in memory database
- is a good example);
-* **Stubs** provide canned answers to calls made during the test, usually not
- responding at all to anything outside what's programmed in for the test. Stubs
- may also record information about calls, such as an email gateway stub that
- remembers the messages it 'sent', or maybe only how many messages it 'sent'
- → **State verification**;
-* **Mocks** are objects pre-programmed with expectations which form a
- specification of the calls they are expected to receive → **Behavior
- verification**.
-
----
-
-# Functional Testing
-
----
-
-# Functional Testing
-
-Also known as **acceptance testing**.
-
-Use tools to create automated tests that actually use your application rather
-than only verifying that individual units of code behave correctly.
-
-* [Selenium](http://seleniumhq.org/)
-* [Mink](http://mink.behat.org/)
-* [CasperJS](http://casperjs.org/)
-
----
-
-# Behavior Driven Development
-
----
-
-# Behavior Driven Development
-
-## SpecBDD
-
-* You write specifications that describe how your actual code should
- behave;
-* Focused on technical behavior;
-* [PHPSpec](http://www.phpspec.net/).
-
-## StoryBDD
-
-* You write human-readable stories that describe the behavior of your
- application;
-* Business oriented;
-* [Behat](http://behat.org/).
-
----
-
-# Behat
-
-Install **Behat** via Composer:
-
- !javascript
- {
- "require-dev": {
- "behat/behat": "2.4.*@stable"
- },
- "config": {
- "bin-dir": "bin/"
- }
- }
-
-The `bin/behat` command is now installed!
-
-Or as a PHAR:
-
- !bash
- $ wget https://github.com/downloads/Behat/Behat/behat.phar
- $ chmod +x behat.phar
-
----
-
-# Using Behat (1/2)
-
-Initialize your project:
-
- !bash
- $ bin/behat --init
-
-Define a **Feature**:
-
- !gherkin
- # features/your_first_feature.feature
- Feature: Your first feature
- In order to start using Behat
- As a manager or developer
- I need to try
-
-Define a **Scenario**:
-
- !gherkin
- Scenario: Successfully describing scenario
- Given there is something
- When I do something
- Then I should see something
-
----
-
-# Using Behat (2/2)
-
-Executing Behat:
-
- !bash
- $ behat
-
-Writing your Step definitions:
-
- !php
- /**
- * @Given /^there is something$/
- */
- public function thereIsSomething()
- {
- throw new PendingException();
- }
-
-> **Must Read**:
-[https://speakerdeck.com/everzet/behat-by-example](https://speakerdeck.com/everzet/behat-by-example).
-
-
-
-> Read more about Behat:
-[http://docs.behat.org/](http://docs.behat.org/).
-
----
-
-# Testing Tweet Frameworks
-
-### [Tweetest](http://adambrett.github.io/tweetest/)
-
- !php
- function tweetest($c,$m) {$c=is_callable($c)?$c():$c;echo($c)?'.':"F[{$m}]";}
-
- tweetest($testValue !== 'bar', '$testValue should never equal bar');
-
-
-### [TestFrameworkInATweet.php](https://gist.github.com//mathiasverraes/9046427)
-
- !php
- function it($m,$p){echo ($p?'✔︎':'✘')." It $m\n";if(!$p){$GLOBALS['f']=1;}}
- function done(){if(@$GLOBALS['f'])die(1);}
-
- it("should sum two numbers", 1 + 1 == 2);
- it("should display an X for a failing test", 1 + 1 == 3);
- done();
diff --git a/src/iut/14_awesome_projects.md b/src/iut/14_awesome_projects.md
deleted file mode 100644
index c8ce6e0..0000000
--- a/src/iut/14_awesome_projects.md
+++ /dev/null
@@ -1,279 +0,0 @@
-# Awesome Projects
-
----
-
-# Assert
-
-Assertions and guard methods for input validation (not filtering!) in
-business-model, libraries and application low-level code.
-
- !php
- use Assert\Assertion;
- use Assert\AssertionFailedException;
-
- try {
- \Assert\that($identifier)->notEmpty()->integer();
- Assertion::notEmpty($message, 'Message is not specified');
- } catch(AssertionFailedException $e) {
- $e->getValue(); // the value that caused the failure
- $e->getConstraints(); // the additional constraints of the assertion
- }
-
-
-
----
-
-# 
-
-Event-driven, non-blocking I/O with PHP:
-[http://reactphp.org/](http://reactphp.org/).
-
-.fx: center
-
----
-
-# Swiftmailer
-
-Comprehensive mailing tools for PHP.
-
- !php
- // Create the message
- $message = Swift_Message::newInstance()
- // Give the message a subject
- ->setSubject('Your subject')
- // Set the From address with an associative array
- ->setFrom([ 'john@doe.com' => 'John Doe' ])
- // Set the To addresses with an associative array
- ->setTo([ 'receiver@domain.org', 'other@domain.org' => 'A name' ])
- // Give it a body
- ->setBody('Here is the message itself')
- ;
-
- // Create the Transport
- $transport = Swift_SmtpTransport::newInstance('smtp.example.org', 25);
-
- // Create the Mailer using your created Transport
- $mailer = Swift_Mailer::newInstance($transport);
-
- // Send the message
- $result = $mailer->send($message);
-
-