Share
Embed
Epic PHP #5
Слайды к пятому занятию курса Epic PHP (http://epixx.ru/epic-php/).
Темы: основы ООП, дизайн и паттерны, composer,
Roma
March 13, 2014
Transcript
131 slides
-
Объекты? Стратегию ООП лучше всего описать как смещение приоритетов в
процессе программирования от функциональности приложения к структурам данных. Это позволяет программисту моделировать в создаваемых приложениях реальные объекты и ситуации. -
Class class ComplicatedClass { ! public $someVar = 'a default
value'; ! public function displayVar() { echo $this->someVar; } } -
Class • Может содержать свои константы, переменные (свойства) и функции
(методы) • Название класса не может быть зарезервированным словом PHP • Название может начинаться с буквы или символа подчеркивания и состоять из букв, цифр и _. -
$this Псевдопеременная, доступная, когда метод вызывается в контексте объекта. Является
ссылкой на вызывающий объект. -
new $instance = new ComplicatedClass(); ! $className = 'Foo'; $instance
= new $className(); // Foo() -
new • Создает экземпляр класса • В контексте класса можно
создать объект, используя new self или new parent -
Свойства // invalid property declarations: public $var1 = 'hello '
. 'world'; public $var3 = 1+2; public $var4 = self::myStaticMethod(); public $var5 = $myVar; ! // valid property declarations: public $var6 = myConstant; public $var7 = array(true, false); -
extends class ExtendClass extends ComplicatedClass { function displayVar() { echo
"Extending class\n"; parent::displayVar(); } } ! $extended = new ExtendClass(); $extended->displayVar(); -
extends • Наследоваться можно только от одного класса • Свойства
и методы могут быть переопределены (если не заданы как final в родителе) • Сигнатура параметров переопределямых методов должна совпадать (кроме конструктора), иначе — E_STRICT -
Задание Создайте базовый класс Сharacter. Унаследуйте от него наш SuperHero
и новый класс SuperVillain. Вынесите в Character общие свойства для героев и злодеев, а в них оставьте только уникальные поля. -
Константы class MyClass { const constant = 'constant value'; !
function showConstant() { echo self::constant . "\n"; } } ! echo MyClass::constant . "\n"; ! $classname = "MyClass"; echo $classname::constant . "\n"; // As of PHP 5.3.0 ! $class = new MyClass(); $class->showConstant(); ! echo $class::constant."\n"; // As of PHP 5.3.0 -
Константы • доступны как часть класса • доступны во всех
областях видимости • могут содержать только скалярные значения • способствуют более чистому коду • работают быстрее констант, определенных через define() -
Constructor class BaseClass { function __construct() { print "In BaseClass
constructor\n"; } } ! class SubClass extends BaseClass { function __construct() { parent::__construct(); print "In SubClass constructor\n"; } } ! class OtherSubClass extends BaseClass { // inherits BaseClass's constructor } -
Constructor // In BaseClass constructor $obj = new BaseClass(); !
// In BaseClass constructor // In SubClass constructor $obj = new SubClass(); ! // In BaseClass constructor $obj = new OtherSubClass(); -
Constructor • вызывается при создании объекта • родительский конструктор надо
вызывать явно: parent::__construct() • если не переопределен, наследуется от класса родителя (если там он не private) • при переопределении параметры могут меняться -
Задание Добавьте нашим героям и злодеям конструкторы, чтобы при создании
объекта задавались основные свойства. Общие параметры должны задаваться только в конструкторе родительского класса. -
Destructor class MyDestructableClass { function __construct() { print "In constructor\n";
$this->name = "MyDestructableClass"; } ! function __destruct() { print "Destroying " . $this->name . "\n"; } } ! $obj = new MyDestructableClass(); -
Destructor • будет вызван, как только на объект не останется
ссылок • либо при завершении работы скрипта • родительский destructor надо вызывать явно: parent::__destruct() • будет вызван даже при остановке скрипта с помощью exit() -
PPP & F • Public • Private • Protected •
Final — может применять к методам и классу целиком -
PPP class MyClass { public $public = 'Public'; protected $protected
= 'Protected'; private $private = 'Private'; ! function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } -
PPP $obj = new MyClass(); echo $obj->public; // Works echo
$obj->protected; // Fatal Error echo $obj->private; // Fatal Error $obj->printHello(); // Shows Public, Protected and Private -
PPP class MyClass2 extends MyClass { // We can redeclare
the public and protected method, but not private protected $protected = 'Protected2'; ! function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } -
PPP $obj2 = new MyClass2(); echo $obj2->public; // Works echo
$obj2->private; // Undefined echo $obj2->protected; // Fatal Error $obj2->printHello(); // Shows Public, Protected2, Undefined -
Задание. Проверьте, правильно ли у вас расставлены уровни доступа у
свойств и методов в созданных ранее классах. Добавьте свойств с недостающими уровнями видимости. -
Задание. На удивление в PHP можно получить доступ к private
и protected свойствам и методам объкта из объекта такого же класса. ! Попробуйте сами написать код, подтверждающий это. Если не получится, то найдите в документации и напишите подобный. -
static <?php class Foo { public static $my_static = 'foo';
! public function staticValue() { return self::$my_static; } } ! class Bar extends Foo { public function fooStatic() { return parent::$my_static; } } -
static print Foo::$my_static . "\n"; ! $foo = new Foo();
print $foo->staticValue() . "\n"; print $foo->my_static . "\n"; // Undefined "Property" my_static ! print $foo::$my_static . "\n"; $classname = 'Foo'; print $classname::$my_static . "\n"; // As of PHP 5.3.0 ! print Bar::$my_static . "\n"; $bar = new Bar(); print $bar->fooStatic() . "\n"; -
static • доступны именно как часть класса • вызываются без
создания объекта • вызов статичного свойства с помощью объектной нотации — Notice • по умолчанию public • использовать $this внутри статичных методов нельзя -
abstract abstract class AbstractClass { // Force Extending class to
define this method abstract protected function getValue(); abstract protected function prefixValue($prefix); ! // Common method public function printOut() { print $this->getValue() . "\n"; } } -
abstract class ConcreteClass1 extends AbstractClass { protected function getValue() {
return "ConcreteClass1"; } ! public function prefixValue($prefix) { return "{$prefix}ConcreteClass1"; } } -
abstract • Класс должен быть объявлен абстрактным, если содержит или
наследует хотя бы один абстрактный метод без имплементации • класс может иметь только одного предка • реализует ограничения для дизайна группы классов -
Задание. Сделайте класс Character абстрактным и объявите в нем несколько
абстрактных методов методов и один-два уже реализованных (общих для героев и злодеев). Абстрактные методы реализуйте в классах- наследниках. Например: saveWorld(), fight(), makeSomethingBad(), muahahahaha(), think(), putOnMask(), useSuperPower(), sayCatchPhrase() и т.д. -
interface Интерфейсы позволяют создавать код, который определяет, какие методы должны
реализовывать классы без необходимости определять саму реализацию методов. -
interface class Template implements iTemplate { private $vars = array();
public function setVariable($name, $var) { $this->vars[$name] = $var; } public function getHtml($template) { foreach($this->vars as $name => $value) { $template = str_replace('{' . $name . '}', $value, $template); } return $template; } } -
interface • все метода интерфейса — public, иначе просто бессмысленно
:) • класс может имплементить больше одного интерфейса • но у этих интерфейсов не должно быть методов с одинаковыми именами -
interface • интерфейсы могут наследоваться • интерфейсы могут содержать константы
• сигнатуры методов интерфейса и класса должны совпадать -
Задание. Создайте два интерфейса: один супергеройский, другой суперзлодейский. Объявите в
них обязательные методы. Пусть наши классы будут реализовывать каждый свой интерфейс. ! Варианты методов: doEvil(), muahaha(), scaryFace(), saveTheWorldAgainBeforeBreakfast() -
Instanceof Позволяет определить является ли объект экземпляром класса или интерфейса
с учетом наследования. -
Instanceof class ParentClass { } ! class MyClass extends ParentClass
{ } ! $a = new MyClass(); ! var_dump($a instanceof MyClass); var_dump($a instanceof ParentClass); -
Instanceof interface MyInterface { } ! class MyClass implements MyInterface
{ } ! $a = new MyClass(); ! var_dump($a instanceof MyClass); var_dump($a instanceof MyInterface); -
Задание. У нас уже есть три класса и два интерфейса.
Создайте несколько объектов и проверьте являются ли они инстансами каждого класса и интерфейса. -
Object Iteration class MyClass { public $var1 = 'value 1';
public $var2 = 'value 2'; public $var3 = 'value 3'; ! protected $protected = 'protected var'; private $private = 'private var'; ! function iterateVisible() { echo "MyClass::iterateVisible:\n"; foreach($this as $key => $value) { print "$key => $value\n"; } } } -
Object Iteration $class = new MyClass(); ! foreach($class as $key
=> $value) { print "$key => $value\n"; } echo "\n"; ! ! $class->iterateVisible(); -
Задание. Задайте классам героев и злодеев магические методы. Пусть в
них просто выводится что-нибудь жизнеутверждающее и нетленное. -
Клонирование $class = new MyClass(); ! $copy = $class; !
do_some_stuff($copy); ! /* $class и $copy остались ссылками на один и тот же объект и будут изменены оба */ -
Клонирование $class = new MyClass(); ! $copy = clone $class;
! do_some_stuff($copy); ! /* $class и $copy будут разными объектами. Изменения в $copy не будут влиять на изменения в $class */ -
Клонирование public function __clone() { /* если при клонировании нужны
особые условия */ } -
Сравнение объектов • == — одинаковые атрибуты, значения и реализуют
один и тот же класс • === — ссылки на один и тот же инстанс класса -
Проблемы • Коллизии имен между пользовательским кодом и классами/функциями/ константами
самого PHP или стороннего используемого кода. • Слишком длинные имена, которыми решалась предыдущия проблема -
Namespaces <?php namespace my\name; ! class MyClass {} function myfunction()
{} const MYCONST = 1; ! $a = new MyClass; $c = new \my\name\MyClass; -
Namespaces $a = strlen('hi'); ! $d = namespace\MYCONST; ! $d
= __NAMESPACE__ . '\MYCONST'; echo constant($d); -
Namespaces • namespace — первое, что есть в php файле
после открывающего тэга • Один и тот же namespace может быть объявлен в нескольких файлах • Пространства имен могут формировать иерархию -
Namespaces • к глобальному пространству имен всегда можно обратиться добавив
перед вызовом \ • __NAMESPACE__ — константа содержащая имя текущего пространства имен -
Импортируем use My\Full\Classname as Another, My\Full\NSname; ! $obj = new
Another; // создает объект класса My\Full\Classname ! NSname\subns\func(); // вызывает функцию My\Full\NSname\subns\func -
Интерпретируем • имя указано без\ в начале — класс будет
искаться в текущем namespace • при его отсутствии — Fatal error • Для функций и констант без полного имени ненайденных в текущем namespace будет попытка найти их в глобальном пространстве имен (fallback). -
PSR-0 • \Zend\Acl => /vendor/Zend/Acl.php • \Doctrine\Common\IsolatedClassLoader => /vendor/Doctrine/Common/ IsolatedClassLoader.php
• https://github.com/php-fig/fig-standards/ blob/master/accepted/PSR-0.md -
Задание. Организуем наш код как взрослые. Для приложения — отдельный
каталог epicoop Весь код классов — в epicoop/src. Главный namespace — Epicphp Расположение классов соответствует PSR-0 index.php — epicoop/web В index.php подключаем все нужные классы -
Exceptions function inverse($x) { if (!$x) { throw new Exception('Division
by zero.'); } else return 1/$x; } ! try { echo inverse(5) . "\n"; echo inverse(0) . "\n"; } catch (Exception $e) { echo 'Caught exception: ', $e->getMessage(), "\n"; } -
Exceptions • Унифицированный метод обработки ошибок • По сути являются
объектами, создаваемыми (“выбрасываемыми”) в случае ошибки • Все исключения в throw должны быть инстансами класса Exception или его наследников -
Exceptions • Каждый try должен иметь хотя бы один catch
• блоков catch может быть несколько (для разных классов исключений) • После “выкинутого” код не выполняется до первого найденного catch • Исключение не поймано — Fatal error -
Exceptions • Внутренние функции PHP используют error reporting и только
новые ООП расширения исользуют exceptions, но это можно переопределить • http://www.php.net/manual/en/ spl.exceptions.php -
Проблемы • Много классов в ООП приложениях • Как правило,
один класс — один файл • До использования каждый класс надо подключить • Получались частоколы require и include • Много ошибок из-за забытых файлов -
__autoload() function __autoload($name) { echo "Want to load $name.\n"; throw
new Exception("Unable to load $name."); } ! try { $obj = new NonLoadableClass(); } catch (Exception $e) { echo $e->getMessage(), "\n"; } -
spl_autoload_register Позволяет сделать несколько автолоадеров на случай разных концепций загрузки
файлов. ! function my_autoloader($class) { include 'classes/' . $class . '.class.php'; } ! spl_autoload_register('my_autoloader'); -
Задание. Убираем из index.php все require и создаем функцию автолоадинга.
Проверяем, что при создании объектов классов ошибок нет. -
SPL • Iterators • Datastructures • Interfaces • Exceptions •
“и кое-что еще...” © • http://php.net/spl -
Проблемы • На PHP написано много кода • На самом
деле много • Вам столько даже представить сложно • И я не шучу • С чего бы мне шутить -
Проблемы • Посреди тон кода есть куча полезностей • Фреймворки
• Библиотеки • Компоненты • Да, большинство простых проблем уже решены за вас -
Проблемы • И вы будете использовать чужие фреймворки, библиотеки, компоненты...
• Много, это точно • Но их же надо как-то искать? • Устанавливать? • Обновлять? -
Composer Composer является блестящим менеджером зависимостей для PHP. Укажите список
зависимостей вашего проекта, в файле composer.json и с несколькими простыми командами, Composer автоматически скачает зависимости вашего проекта и установит автозагрузку для вас. -
Composer • Множество совместимых библиотек • Их можно искать: https://packagist.org/
• Взрывное развитие и популярность у разработчиков • Если вашей библиотеки нет в packagist и ее не поставить через composer, то вашей библиотеки просто нет -
Composer curl -s https://getcomposer.org/installer | php ! php composer.phar require
twig/twig:~1.8 ! php composer.phar install -
Composer <?php require 'vendor/autoload.php'; ! /* проблема автолоадинга только что
была решена за вас бесплатно и без СМС! */ -
composer.json { "require": { "silex/silex": "1.0.*", "swiftmailer/swiftmailer": ">=4.1.2,<4.2-dev", "twig/twig": ">=1.8,<2.0-dev",
"symfony/twig-bridge": "2.1.*", "symfony/form": "2.1.*", "symfony/validator": "2.1.*", "symfony/config": "2.1.*", "symfony/translation": "2.1.*", "rmzamora/google-api-php": "dev-master", "zendframework/zendgdata": "2.0.*", "symfony/security": "2.1.*" }, "minimum-stability": "dev", ! "autoload": { "psr-0": { "App": "src/" } } } -
Composer • composer.json • composer.lock • php composer.phar update •
http://getcomposer.org/doc/00-intro.md • Composer умеет ставить либы из PEAR -
Задание. Установите композер в наш epicoop. Найдите в packagist symfony/finder,
symfony/config, symfony/http-foundation. Установите их через composer. -
Code Reuse • Мы решаем одни и те же проблемы
каждый день • Мы решаем те же пробемы, что и наши “соседи” каждый день • Мы решаем одинаковые проблемы на разных проектах • И мы ленивые, но хотим делать хорошо! -
Дизайн и паттерны • Правильные и конкретные решения для общих
проблем • Общий язык для общения между разработчиками -
Singleton public static function getInstance() { static $instance = null;
if (null === $instance) { $instance = new static; } ! return $instance; } ! protected function __construct() { } -
Singleton • убедиться, что в системе только один инстанс определенного
класса • применения: обертки соединений (база данных, файлы, внешние источники данных) • Не рекомендуется к использованию в последнее время -
Registry • Расширение Singleton • По сути явлется хранилищем для
данных и инстансов других объектов • И обеспечивает, что это хранилище в системе одно • Zend_Registry -
Factory • Предоставляет собой общий интерфейс к набору классов со
схожей функциональностью, но разной начинкой • Создает инстанс нужного класса • например, фабрика для выбора хранилища данных, службы доставки, платежной системы -
Front controller • Одна (!) точка входа для всех запрсов
приложения • Совсем одна • Одна-одинешенька • Как Уилл Смит в «Я Легенда» • Пока не встретил женщину, из-за которой умер -
Front controller • index.php • + настройка сервера (например, .htaccess)
• код, ответственный за загрузку зависимостей, обработку запроса, отправку ответа, весь он в одном месте • способствует модульности -
MVC • во всех фреймворках страны • т.е. во всех
современных фреймворках на самом деле -
MVC • Model — данные и обработка • View —
шаблоны (html, xml, markup...) и никакой логики • Controller — обработка запроса, отправка данных из модели во вьюшку -
Задание. Переделайте ваше CRUD приложение с использованием объектов и паттернов.
! Код вне объектов может быть только в index.php. Html должен быть в отдельных файлах. Логики там содержаться не должно, максимум вывод переменных, циклы для списков и if необходимые для определения логики вывода. ! Не бойтесь ошибиться. Не попробовав, вы никогда не поймете, как оно работает на самом деле. -
Задание. Переделайте ваше CRUD приложение с использованием объектов и паттернов.
! Код вне объектов может быть только в index.php. Html должен быть в отдельных файлах. Логики там содержаться не должно. ! Не бойтесь ошибиться. Не попробовав, вы никогда не поймете, как оно.