StackStarter.io/spin
Spin Code: PHPOO
StackStarter.io/spin
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.
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#
Abstraction
Encapsulation
Inheritance
Polymorphism
/**
* Our first Class definition
*/
class House {
}
/**
* Our first Class definition
*/
class House {
}
// Lets contruct!
$myHouse = new House();
At this point $myHouse is an object of type House.
An object fundamentally consists of data and behavior
/**
* Our first Class definition
*/
class House {
// A property decribing the number of rooms in this house.
public $num_rooms;
}
/**
* 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;
}
}
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();
Very simply it is a reference to itself.
Class names are CamelCased.
Properties and Methods are lowerCamelCased
You only should have 1 class per file.
We do not include an closing ?> tag.
Let's build on these ideas
Automatically including the classes we need when we need them.
Using Objects at any scale will require us to organize our code in manageable ways.
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
composer require --dev phpunit/phpunit ^6
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);
}
}
./vendor/bin/phpunit --bootstrap ./vendor/autoload.php tests/CarTest
"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."
namespace Vehicles;
interface CarInterface {
public function getMake();
}
What are we saying here?
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
// ...
The idea of
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.
Lets spin up a Drupal instance
/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!
Without hook_menu!
/**
* 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'
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 %}
/**
* 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',
);
}
}
Small (as possible) stateless objects that perform some testable task.
This opens up an interesting and powerful world for us...
A service container (or dependency injection container) is a PHP object that manages the instantiation of services.
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
/**
* 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,
);
}
}
Leveraging the Symfony Framework Events.
List of core events: http://bit.ly/core_events
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!
Email: nick@segosolutions.com
Twitter: @direct
Slides Available:
http://nickgs.com/php-oo-slides