StackStarter.io/spin
Spin Code: PHPOO

Object Oriented PHP

Building solid foundations

Nick Selvaggio / www.segosolutions.com

A little about me.

A little about you.

Play Along!

StackStarter.io/spin
Spin Code: PHPOO

Let's talk goals.

Build a solid foundation of core

Object Oriented concepts

Apply this foundation to the

PHP language

Leave here today feeling comfortable to dive into

larger PHP projects, like Drupal

Story time

Brief History of PHP

Open sourced by Rasmus Lerdorf in 1994

Flexible, dynamic language

Object Oriented support entered with PHP 5.0 in 2004

Supports many different programming paradigms.

Brief History of OO

First started to emerge in an AI group at MIT in the 1960s (LISP atoms)

Formally introducted with the Simula 67 language

Many languages and environments support the paradigm today.

Java, C++, C#

Core Concepts

Procedural Code

vs

Object Oriented Code

Lets Review our Object Oriented Concepts

The 4 Pillars of OOP

Abstraction

Encapsulation

Inheritance

Polymorphism

Classes and Objects

The Class Keyword

              
/**
 * Our first Class definition
 */
 class House {
 }
              

Build a house in OO?

                
/**
* Our first Class definition
*/
class House {
}

// Lets contruct!
$myHouse = new House();
                

At this point $myHouse is an object of type House.

How can we further build this?

An object fundamentally consists of data and behavior

We describe our data in

Properties

                    
/**
* Our first Class definition
*/
class House {
  // A property decribing the number of rooms in this house.
  public $num_rooms;
}

                

We describe our behavior in

Methods

                      
  /**
  * Our first Class definition
  */
  class House {
    // A property decribing the number of rooms in this house.
    public $num_rooms;

    // A method which returns the number of rooms in this house.
    public function getNumRooms(){
      return $this->num_rooms;
    }
  }
  
                  

The -> operator

We use this operator, called the 'arrow operator', to access the methods and properties of an object.

                        
    /**
    * Our first Class definition
    */
    class House {
      // A property decribing the number of rooms in this house.
      public $num_rooms;
  
      // A method which returns the number of rooms in this house.
      public function getNumRooms(){
        return $this->num_rooms;
      }
    }

    $myHouse = new House();
    $myHouse->num_rooms = 2;
    echo $myHouse->getNumRooms();
    
                    

What the heck is $this?

Very simply it is a reference to itself.

Public, Private, Protected

Oh my!

Let's talk about Encapsulation

Lets talk a bit about coding standards

Class names are CamelCased.

Properties and Methods are lowerCamelCased

You only should have 1 class per file.

We do not include an closing ?> tag.

Lets dive into an exercise!

Exercise #1

Awesome work!

Let's build on these ideas

Autoloading

Automatically including the classes we need when we need them.

Namespaces

Using Objects at any scale will require us to organize our code in manageable ways.

Enter Composer!

Lets play with these tools...

Some notes

Reusing code

Building on exsisting code via

Inheritance

Used to extend the implementation of a "parent" class

Our "child" class "inherits" all the public and protected properties/methods of its parent

PHP has a single inheritance model

This results in visualizing our program as a

directed graph

Lets work through an example.

Exercise #2

Look at us...

Abstracting, Encapsulating, and Inheriting!

Lets take a step back

and look at Testing!

PHPUnit

PHP's unit testing framework

Lets bring PHPUnit into our project


              composer require --dev phpunit/phpunit ^6
            

Writing our first test


use PHPUnit\Framework\TestCase;

// Bring in our Car class.
use Vehicles\Car;

final class CarTest extends TestCase {
    
    public function testCarCanBeBuilt() {
        // Instantiate a new car.
        $c = new Car();
        
        // Assert that the newly built car is in fact a Car.
        $this->assertInstanceOf(Car::class, $c);
    }
}
              

Running this bad boy..


                ./vendor/bin/phpunit --bootstrap ./vendor/autoload.php tests/CarTest
            
            

Lets go ahead and write some tests for our

SuperCar class

Our last Pillar

Polymorphism

"The occurrence of different forms among the members of a population or colony, or in the life cycle of an individual organism."

"Polymorphism allows the expression of some sort of contract, with potentially many types implementing that contract (whether through class inheritance or not) in different ways, each according to their own purpose. CodeĀ usingĀ that contract should not(*) have to care about which implementation is involved, only that the contract will be obeyed."

This is achieved in OOP with

Interfaces

A simple example:


namespace Vehicles;

interface CarInterface {
    
    public function getMake();
    
}

            
            

What are we saying here?

Lets go and use this interface


namespace Vehicles;

class Car implements CarInterface {
    private $make;  

    public function getMake() {
      return $this->make;
    }
    
}

            
            

This opens up some interesting flexibility for us.


// ...
  function getVehicleMake(CarInterface $c) {
    //this can now be ANY type of Car.
    return $c->getMake();
  }

  $c = new Car();
  $c->setMake("Honda");

  $s = new SuperCar();
  $s->setMake("Lambo");

  echo getVehicleMake($c); // Honda
  echo getVehicleMake($s); //Lambo
// ...
            
            

Exercise #3

The idea of

Dependency Injection

Lets analyze this code:


use Vehicles\Engine;

class Car implements VehicleInterface {
  private $e; //the cars engine.

  public function __construct() {
    $this->e = new Engine();
  }
}
            
            

How is this better?


use Vehicles\Engine;

class Car implements VehicleInterface {
  private $e; //the cars engine.

  public function __construct(Engine $e) {
    $this->e = $e;
  }
}
            
            

One more improvement


use Vehicles\EngineInterface;

class Car implements VehicleInterface {
  private $e; //the cars engine.

  public function __construct(EngineInterface $e) {
    $this->e = $e;
  }
}
            
            

We should rely on abstractions not concretions.

Exercise #4

Phew.. ok...

what about Drupal?

Lets spin up a Drupal instance

Spin Code: DRUPAL8

Structure of a module

/modules/custom/hello/hello.info.yml


name: Hello
description: A friendly Drupal 8 module
type: module
core: 8.x
            

Modules now live in the /modules directory

We don't need a .module file anymore!

Lets make a Page...

Without hook_menu!

/modules/custom/hello/src/Controller/HelloController.php

/**
* Our first Drupal 8 controller.
*/
namespace Drupal\hello\Controller;

use Drupal\Core\Controller\ControllerBase;

class HelloController extends ControllerBase {
  public function sayHi() {
    return array(
      '#markup'=>"Hello Drupal 8!",
    );
  }
}
          

/modules/custom/hello/hello.routing.yml

              
              hello.sayHi:
  path: '/hello'
  defaults:
    _controller: '\Drupal\hello\Controller\HelloController::sayHi'
  requirements:
    _permission: 'access content'

              
            

What about theming our content?

This will look procedural

/modules/custom/hello/hello.module

                
/**
* Implements hook_theme
*/
function hello_theme() {
  $theme['say_hello'] = array(
    'variables' => array(),
    'template' => 'say_hello',
  );
    
  return $theme;
}
                
              

/modules/custom/hello/templates/say_hello.html.twig

                  
{% trans %}
  

Hello!

{% endtrans %}
/modules/custom/hello/src/Controller/HelloController.php

      /**
       * Our first Drupal 8 controller.
       * Lets say HI
       */
      namespace Drupal\hello\Controller;
      
      use Drupal\Core\Controller\ControllerBase;
      
      class HelloController extends ControllerBase {
        public function sayHi() {
          return array(
              '#theme'=>'say_hello',
            );
        }
      }
                

Services

Small (as possible) stateless objects that perform some testable task.

This opens up an interesting and powerful world for us...

The Service Container

A service container (or dependency injection container) is a PHP object that manages the instantiation of services.

/modules/custom/hello/src/HelloService.php

namespace Drupal\hello;

class HelloService {
  //dead simple example.
  public function doSomeWork() {
    //some heavy lifting.
    drupal_set_message("Hello Services");
  }
}
          
          

/modules/custom/hello/hello.services.yml

              
              services:
  hello.hello_service:
    class: Drupal\hello\HelloService
              
            

Now lets use it!

/modules/custom/hello/src/Controller/HelloController.php

/**
 * Our first Drupal 8 controller with a custom service!
 * Lets say HI
 */
namespace Drupal\hello\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\hello\HelloService;
use Symfony\Component\DependencyInjection\ContainerInterface;

class HelloController extends ControllerBase {
  protected $helloServ;

  //our contructor is injected with our service.
  public function __construct(HelloService $s) {
    $this->helloServ = $s;
  }

  //This is the interesting method. ControllerBase will call this.
  public static function create(ContainerInterface $container) {
    //and create an instance of this class, injecting our service!
    return new static($container->get('hello.hello_service'));
  }

  public function sayHi($name) {
    //lets use that bad boy!
    $this->helloServ->doSomeWork();

    return array(
        '#theme'=>'say_hello',
        '#name' => $name,
      );
  }
}

          
          

Services can serve many purposes

Lets take a look at another example

Drupal 8 Events

Leveraging the Symfony Framework Events.

List of core events: http://bit.ly/core_events

/modules/custom/hello/src/EventSubscriber/HelloEventSubscriber.php

namespace Drupal\hello\EventSubscriber;

use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\Core\Session\AccountInterface;

class HelloEventSubscriber implements EventSubscriberInterface {
  protected $account;

  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }

  public function checkForSomething(GetResponseEvent $event) {
      drupal_set_message("Big brother is here!");
  }

  /**
   * {@inheritdoc}
   */
  static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = array('checkForSomething');
    return $events;
  }
}
          
          

/modules/custom/hello/hello.services.yml

              
  hello_event_subscriber:
    class: Drupal\hello\EventSubscriber\HelloEventSubscriber
    arguments: ['@current_user']
    tags: #Our Service Tags
      - {name: event_subscriber}
              
            

So much more to explore and learn...

Thank you!

Questions?

Email: nick@segosolutions.com

Twitter: @direct

Slides Available:
http://nickgs.com/php-oo-slides