PHP (PHP: Hypertext Preprocessor - «PHP: препроцессор гипертекста»; первоначально Personal Home Page Tools - «Инструменты для создания персональных веб-страниц») - скриптовый язык общего назначения, интенсивно применяемый для разработки веб-приложений. В настоящее время поддерживается подавляющим большинством хостинг-провайдеров и является одним из лидеров среди языков, применяющихся для создания динамических веб-сайтов. Язык и его интерпретатор (Zend Engine) разрабатываются группой энтузиастов в рамках проекта с открытым кодом. Проект распространяется под собственной лицензией, несовместимой с GNU GPL.



Парадигма программирования - это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию).

Основные парадигмы программирования:

Императивное программирование - это парадигма программирования, для которой характерно следующее:

  • в исходном коде программы записываются инструкции (команды);
  • инструкции должны выполняться последовательно;
  • при выполнении инструкции данные, полученные при выполнении предыдущих инструкций, могут читаться из памяти;
  • данные, полученные при выполнении инструкции, могут записываться в память.

Императивная программа похожа на приказы (imperative - приказ, повелительное наклонение), выражаемые повелительным наклонением в естественных языках, то есть представляют собой последовательность команд, которые должен выполнить компьютер.

a := 5;
b := 10;
c := a * b;
print c;

Декларативное программирование (declare - объявлять) - это парадигма программирования, в которой задаётся спецификация решения задачи, то есть описывается, что представляет собой проблема и ожидаемый результат. Противоположностью декларативного является императивное программирование, описывающее на том или ином уровне детализации, как решить задачу и представить результат. В общем и целом, декларативное программирование идёт от человека к машине, тогда как императивное - от машины к человеку. Как следствие, декларативные программы не используют понятия состояния, то есть не содержат переменных и операторов присваивания.

Структурное программирование - это парадигма программирования, в основе которой лежит представление программы в виде иерархической структуры блоков. Методология структурного программирования появилась как следствие возрастания сложности решаемых на компьютерах задач, и соответственно, усложнения программного обеспечения.

Принципы:

  • Следует отказаться от использования оператора безусловного перехода goto;
  • Любая программа строится из трёх базовых управляющих конструкций: последовательность, ветвление, цикл;
  • В программе базовые управляющие конструкции могут быть вложены друг в друга произвольным образом;
  • Каждую логически законченную группу инструкций следует оформить как блок (block);
  • Все перечисленные конструкции должны иметь один вход и один выход;
  • Разработка программы ведётся пошагово, методом "сверху вниз";

Процедурное программирование (подход) - это программирование на императивном языке, при котором последовательно выполняемые операторы можно собрать в подпрограммы, то есть более крупные целостные единицы кода, с помощью механизмов самого языка. Процедурное программирование является отражением архитектуры традиционных ЭВМ, которая была предложена Фон Нейманом в 1940-х годах. Теоретической моделью процедурного программирования служит абстрактная вычислительная система под названием машина Тьюринга. Выполнение программы сводится к последовательному выполнению операторов с целью преобразования исходного состояния памяти, то есть значений исходных данных, в заключительное, то есть в результаты. Таким образом, с точки зрения программиста имеются программа и память, причем первая последовательно обновляет содержимое последней.

Функциональное программирование - это парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании). Функциональное программирование предполагает обходиться вычислением результатов функций от исходных данных и результатов других функций, и не предполагает явного хранения состояния программы. Соответственно, не предполагает оно и изменяемость этого состояния (в отличие от императивного, где одной из базовых концепций является переменная, хранящая своё значение и позволяющая менять его по мере выполнения алгоритма).

Объектно-ориентированное программирование - это парадигма программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определённого класса, а классы образуют иерархию наследования.


class Student {
	public $firstName;
	public $lastName;
	public $birthDate;

	public function getFullName() {
		return $this->lastName . ' ' . $this->firstName;
	}

	public function getFullInfo() {
		return $this->getFullName() . ' ' . $this->birthDate;
	}
}

$student1 = new Student();
$student2 = new Student();

$student1->firstName = 'Orkhan';
$student1->lastName = 'Alyshov';
$student1->birthDate = '1992.09.09';

$student2->firstName = 'Vusal';
$student2->lastName = 'Alyshov';
$student2->birthDate = '1991.01.01';

$student3 = $student2;
$student3->firstName = 'Vasya';

$student4 = clone $student1;
$student4->firstName = 'John';

echo $student1->getFullInfo();  // Alyshov Orkhan 1992.09.09
echo $student2->getFullInfo();  // Alyshov Vasya 1991.01.01
echo $student3->getFullInfo();  // Alyshov Vasya 1991.01.01
echo $student4->getFullInfo();  // Alyshov John 1992.09.09

Объект живет в памяти до тех пор, пока на него есть указатели с разных переменных. Чтобы избавиться от объекта:

$student = null;    // вариант 1
unset($student);    // вариант 2

DTO (Data Transfer Object)

class Name {
    public $first;
    public $last;
}

class Phone {
    public $code;
    public $number;
}

class Address {
    public $country;
    public $region;
    public $city;
    public $street;
    public $house;
}

function pintReport(Name $name, Phone $phone, Address $address) {
    echo $name->first . ' ' . $phone->code . ' ' . $address->country;
}

$name = new Name();
$name->first = 'Orkhan';

$phone = new Phone();
$phone->code = '999';

$address = new Address();
$address->country = 'Azerbaijan';

pintReport($name, $phone, $address);  // Orkhan 999 Azerbaijan

Методы могут быть модифицирующими:

class Student {
    public $firstName;
    public $lastName;

    public function getFullName() {
        return $this->lastName . ' ' . $this->firstName;
    }

    public function rename($firstName, $lastName) {
        if (empty($firstName)) {
            throw new InvalidArgumentException('Введите имя');
        }

        if (empty($lastName)) {
            throw new InvalidArgumentException('Введите фамилию');
        }

        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
}

$student = new Student();

$student->firstName = 'Vasya';
$student->lastName = 'Pupkin';

echo $student->getFullName();  // Pupkin Vasya

$student->rename('Petya', 'Ivanov');

echo $student->getFullName();  // Ivanov Petya

Constructor

class Student {
    public function __construct() {
        echo 'Constructor';
    }
}

$student = new Student(); // Constructor
class Student {
    private $firstName;
    private $lastName;
    private $birthDate;

    public function __construct($firstName, $lastName, $birthDate) {
        if (empty($firstName) || empty($lastName) || empty($birthDate)) {
            throw new InvalidArgumentException('Некорректные данные');
        }

        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->birthDate = $birthDate;
    }

    public function rename($firstName, $lastName) {
        if (empty($firstName) || empty($lastName)) {
            throw new InvalidArgumentException('Введите имя и фамилию');
        }

        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function getFullName() { return $this->lastName . ' ' . $this->firstName; }
    public function getFirstName() { return $this->firstName; }
    public function getLastName() { return $this->lastName; }
    public function getBirthDate() { return $this->birthDate; }
}

$student = new Student('Petya', 'Ivanov', '1995-08-26');
echo $student->getFullName();  // Ivanov Petya

$student->rename('Vasya', 'Pupkin');
echo $student->getFullName();  // Pupkin Vasya

Destructor

class Student {
    public function __construct() {
        echo 'Constructor';
    }

    public function __destruct() {
        echo 'Destructor';
    }
}

$student = new Student();  // Constructor Destructor
class Reader {
    private $file;

    public function __construct($fileName) {
        $this->file = fopen($fileName, 'r');
    }

    public function __destruct() {
        fclose($this->file);
    }
}

$read = new Reader('test.txt');

PHP позволяет в объекте заполнить поле, которого не существует:

class Student {
    public $number;
}

$student = new Student();

$student->undefinedProperty = 'Hello';
echo $student->undefinedProperty;  // Hello

Вспомогательные методы __get(), __set(), __isset(), __unset() обрабатывают не существующие свойства или private свойства:

class Student {
    public $number;

    public function __get($name) {
        echo 'Get ' . $name . PHP_EOL;
    }

    public function __set($name, $value) {
        echo 'Set ' . $name . ' ' . $value . PHP_EOL;
    }

    public function __isset($name) {
        echo 'Isset ' . $name . PHP_EOL;
    }

    public function __unset($name) {
        echo 'Unset ' . $name . PHP_EOL;
    }
}

$student = new Student();

$student->number = 1;
if (isset($student->number)) {
    // do something
}
echo $student->number;
unset($student->number);
/*
1
*/
class Student {
    public function __get($name) {
        echo 'Get ' . $name . PHP_EOL;
    }

    public function __set($name, $value) {
        echo 'Set ' . $name . ' ' . $value . PHP_EOL;
    }

    public function __isset($name) {
        echo 'Isset ' . $name . PHP_EOL;
    }

    public function __unset($name) {
        echo 'Unset ' . $name . PHP_EOL;
    }
}

$student = new Student();

$student->undefinedProperty = 'Hello';
if (isset($student->undefinedProperty)) {
    // do something
}
echo $student->undefinedProperty;
unset($student->undefinedProperty);

/*
Set undefinedProperty Hello
Isset undefinedProperty
Get undefinedProperty
Unset undefinedProperty
*/
class Student {
    private $number;

    public function __get($name) {
        echo 'Get ' . $name . PHP_EOL;
    }

    public function __set($name, $value) {
        echo 'Set ' . $name . ' ' . $value . PHP_EOL;
    }

    public function __isset($name) {
        echo 'Isset ' . $name . PHP_EOL;
    }

    public function __unset($name) {
        echo 'Unset ' . $name . PHP_EOL;
    }
}

$student = new Student();

$student->number = 1;
if (isset($student->number)) {
    // do something
}
echo $student->number;
unset($student->number);

/*
Set number 1
Isset number
Get number
Unset number
*/

Если вызвать метод, который не существует в классе, то вызовется магический метод __call():

class Student {
    public function __call($name, $params) {
        return 'Call ' . $name . ' ' . print_r($params, true);
    }
}

$student = new Student();
echo $student->move(123, 12) . PHP_EOL;

/*
Call move Array
(
    [0] => 123
    [1] => 12
)
*/

__toString():

class Student {
    public function __toString() {
        return 'Cool object';
    }
}

$student = new Student();

echo 'I print ' . $student . PHP_EOL;

/*
I print Cool object
*/

__invoke() срабатывает тогда, когда мы вызываем сам объект как функцию:

class Student {
    public function __invoke($params) {
        return 'Invoke ' . print_r($params, true);
    }
}

$student = new Student();

echo $student(123) . PHP_EOL;

$this - этот объект, а self - сам класс.
К любому полю данного класса можно обратиться через $this, а к константе как через Student::TYPE_1, так и self::TYPE_1:

class Student {
    const TYPE_1 = 1;
    const TYPE_2 = 2;

    private $firstName;
    private $lastName;
    private $type;

    public function __construct($firstName, $lastName, $type = self::TYPE_1) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->type = $type;
    }

    public function getFullName() {
        return $this->lastName . ' ' . $this->firstName;
    }

    public function getType() {
        return $this->type;
    }

    public function isFirst() {
        return $this->type === self::TYPE_1;
    }

    public function isSecond() {
        return $this->type === self::TYPE_2;
    }
}

$student = new Student('Petya', 'Ivanov');
$student = new Student('Vasya', 'Petrov', Student::TYPE_1);
$student = new Student('Kolya', 'Sifdorov', Student::TYPE_2);

echo $student->getFullName() . PHP_EOL;  // Sifdorov Kolya

Статический метод:

class Student {
    public static function sayHello() {
        return 'Hello, World!';
    }
}

echo Student::sayHello();  // Hello, World!

class Student {
    const TYPE_1 = 1;
    const TYPE_2 = 2;

    private $name;
    private $type;

    private function __construct($name, $type) {
        $this->name = $name;
        $this->type = $type;
    }

    public static function createFirst($name) {
        return new self($name, self::TYPE_1);
    }

    public static function createSecond($name) {
        return new self($name, self::TYPE_2);
    }

    public function getName() {
        return $this->name;
    }
}

$student1 = Student::createFirst('Vasya Ivanov');
$student2 = Student::createSecond('Petya Sidorov');

echo $student1->getName();  // Vasya Ivanov
echo $student2->getName();  // Petya Sidorov

Чтение .txt файла в стиле ООП.

list.txt:

Pupkin;Vasya;1990-01-12
Ivanov;Petya;1992-05-10
Petrov;Ilya;1980-10-01
Sidorov;Andrey;1981-05-01

index.php:

class Student {
    public $firstName;
    public $lastName;
    public $birthDate;

    public function __construct($firstName, $lastName, $birthDate) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->birthDate = $birthDate;
    }

    public function getFullName() {
        return $this->lastName . ' ' . $this->firstName;
    }
}

class StudentRepository {
    private $file;

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

    public function findAll() {
        $rows = file($this->file);
        $students = [];

        foreach ($rows as $row) {
            $values = array_map('trim', explode(';', $row));
            $students[] = new Student($values[0], $values[1], $values[2]);
        }

        return $students;
    }
}

$studentRepository = new StudentRepository('list.txt');
$students = $studentRepository->findAll();

foreach ($students as $student) {
    echo $student->getFullName() . ' ' . $student->birthDate . '<br/>';
}

/*
Vasya Pupkin 1990-01-12
Petya Ivanov 1992-05-10
Ilya Petrov 1980-10-01
Andrey Sidorov 1981-05-01
*/

Наследование

class Base {
    public $name = 'Vasya';

    public function first() {
        return 'first';
    }
}

class Sub extends Base {
    public $date = '2000-01-12';
}

$base = new Base();
echo $base->first();  // first

$sub = new Sub();
echo $sub->first();  // first

Переопределение метода:

class Base {
    public $name = 'Vasya';

    public function first() {
        return 'first';
    }
}

class Sub extends Base {
    public $date = '2000-01-12';

    public function first() {
        return 'first_2';
    }

    public function second() {
        return 'second';
    }
}

$base = new Base();
echo $base->first();  // first

$sub = new Sub();
echo $sub->first();  // first_2
class Base {
    public function first() {
        return 'first';
    }
}

class Sub extends Base {
    public function second() {
        return 'second';
    }
}

class Third extends Sub {
    public function third() {
        return 'third';
    }
}

$base = new Base();
echo $base->first();  // first

$sub = new Sub();
echo $sub->first();  // first
echo $sub->second();  // second

$third = new Third();
echo $third->first();  // first
echo $third->second();  // second
echo $third->third();  // third

class Base {
    public $name;
    public $email;

    public function __construct($name, $email) {
        $this->name = $name;
        $this->email = $email;
    }
}

class Sub extends Base {
    public $status;

    public function __construct($name, $email, $status) {
        parent::__construct($name, $email);
        $this->status = $status;
    }

    public function getInfo() {
        return "Name: $this->name; Email: $this->email; Status: $this->status";
    }
}

$base = new Base('Vasya', '[email protected]');
$base = new Sub('Vasya', '[email protected]', 1);

echo $base->getInfo();  // Name: Vasya; Email: [email protected]; Status: 1
class Base {
    public function first() {
        return 'first';
    }
}

class Sub extends Base {
    public function first() {
        return 'extended ' . parent::first();
    }

    public function second() {
        return 'second';
    }
}

$base = new Base();
echo $base->first();  // first

$sub = new Sub();
echo $sub->first();  // extended first
echo $sub->second();  // second

Модификаторы доступа:

class Base {
    public function first() {
        return 'first + ' . $this->second() . ' + ' . $this->third();
    }

    protected function second() {
        return 'second';
    }

    private function third() {
        return 'third';
    }
}

class Sub extends Base {
    public function first() {
        return 'first_2 + ' . $this->second();
    }
}

$base = new Base();
echo $base->first();  // first + second + third
echo $base->second();  // Fatal error:  Call to protected method
echo $base->third();  // Fatal error:  Call to private method

$base = new Sub();
echo $base->first();  // first_2 + second
class Base {
    public function first() {
        return 'first + ' . $this->second();
    }

    protected function second() {
        return 'second_1';
    }
}

class Sub extends Base {
    protected function second() {
        return 'second_2';
    }
}

$base = new Base();
echo $base->first();  // first + second_1

$base = new Sub();
echo $base->first();  // first + second_2
class Base {
    private $third = 'third_1';

    public function first() {
        return 'first + ' . $this->second();
    }

    protected function second() {
        return $this->third;
    }
}

class Sub extends Base {
    private $third = 'third_2';
}

$base = new Base();
echo $base->first();  // first + third_1

$base = new Sub();
echo $base->first();  // first + third_1

Статические методы:

class Base {
    public static function first() {
        return 'first + ' . self::second();
    }

    public static function second() {
        return 'second_1';
    }
}

class Sub extends Base {
    public static function second() {
        return 'second_2';
    }
}

echo Base::first();  // first + second_1
echo Sub::first();  // first + second_1

self - текущий класс.


class Base {
    public static function first() {
        return 'first + ' . static::second();
    }

    public static function second() {
        return 'second_1';
    }
}

class Sub extends Base {
    public static function second() {
        return 'second_2';
    }
}

echo Base::first();  // first + second_1
echo Sub::first();  // first + second_2

static - позднее статическое связывание.


abstract class Base {
    public function first() {
        return 'first + ' . $this->second();
    }

    protected function second() {
        return 'second';
    }
}

class Sub1 extends Base {}

class Sub2 extends Base {
    protected function second() {
        return 'second_2';
    }
}

$base = new Sub1();
echo $base->first();  // first + second

$base = new Sub2();
echo $base->first();  // first + second_2

abstract class. Нельзя создавать экземпляры абстрактных классов. Можно унаследовать.


final class Base {
    public function first() {
        return 'first + ' . $this->second();
    }

    protected function second() {
        return 'second';
    }
}

$base = new Base();
echo $base->first();  // first + second

final class. Можно создавать экземпляры финальных классов. Нельзя унаследовать.


Josh Lockhart - PHP. The right way

Встроенный веб-сервер

Этот веб-сервер был разработан для помощи в разработке. Он также может быть полезным в тестовых целях или для демонстрации приложения, запускаемого в полностью контролируемом окружении. Он не выполняет функции полноценного веб-сервера и не должен использоваться в общедоступных сетях.

Начиная с PHP 5.4.0, модуль CLI SAPI содержит встроенный веб-сервер.

Веб-сервер выполняет только один однопоточный процесс, поэтому приложения PHP будут останавливаться, если запрос заблокирован.

URI запросы обслуживаются из текущей директории, в которой был запущен PHP, если не используется опция -t для явного указания корневого документа. Если URI запроса не указывает на определенный файл, то будет возвращен index.php или index.html в указанной директории. Если ни один из файлов не существует, то поиск этих файлов будет продолжен в родительской директории и так далее до тех пор, пока они не будут найдены или был достигнут корень документа. Если найден index.php или index.html, он возвращается, а в $_SERVER['PATH_INFO'] будет находится последняя часть URL. В противном случае возвращается 404 код ответа.

Если PHP-файл указывается в командной строке, когда запускается веб-сервер, то он рассматривается как скрипт "маршрутизации" (router). Скрипт выполняется в самом начале каждого HTTP-запроса. Если этот скрипт возвращает FALSE, то запрашиваемый ресурс возвращается как есть. В противном случае браузеру будет возвращен вывод этого скрипта.

Запуск веб-сервера:

$ cd ~/public_html
$ php -S localhost:8000

В консоли выведется:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit

После URI-запросов http://localhost:8000/ и http://localhost:8000/myscript.html в консоли выведется примерно следующее:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit.
[Thu Jul 21 10:48:48 2011] ::1:39144 GET /favicon.ico - Request read
[Thu Jul 21 10:48:50 2011] ::1:39146 GET / - Request read
[Thu Jul 21 10:48:50 2011] ::1:39147 GET /favicon.ico - Request read
[Thu Jul 21 10:48:52 2011] ::1:39148 GET /myscript.html - Request read
[Thu Jul 21 10:48:52 2011] ::1:39149 GET /favicon.ico - Request read

Стандарты написания кода

Группа Совместимости Фреймворков предложила и одобрила ряд стилевых рекомендаций, известных как PSR-0, PSR-1 и PSR-2. Не дайте веселым именам смутить вас, эти рекомендации представляют собой набор правил. В идеале, вы должны писать PHP код, придерживаясь известных стандартов. Это может быть любая комбинация PSR-ов, или один из стандартов кода, сделанных PEAR или Zend. Это позволит другим разработчикам легко читать и работать с вашим кодом, и приложения, которые используют компоненты, смогут сохранить структуру приложения, даже работая с огромным количеством стороннего кода.

Вы можете использовать PHP CodeSniffer чтобы проверить код на соответствие одной из этих рекомендаций.

Используйте PHP Coding Standards Fixer, созданный Фабиеном Потенсьером, для автоматического исправления синтаксиса вашего кода так, чтобы он соответствовал этим стандартам, что спасет вас от исправления каждой проблемы вручную.


Функциональное программирование в PHP

PHP поддерживает перво-классные функции, это значит, что функция может быть применена к переменной. И определенные пользователем, и встроенные функции могут быть применены к переменной и вызываться динамически. Функции могут быть переданы, как аргумент к другой функции (эта особенность называется функцией высшего порядка), а также функция может возвращать другую функцию.

Рекурсия, особенность, которая позволяет функции вызывать саму себя, это поддерживается языком, но большая часть кода PHP фокусируется на итерации.

Новые анонимные функции(с поддержкой для замыканий) присутствую с PHP 5.3 (2009).

В PHP 5.4 добавлена возможность связывать замыкание с областью видимости объекта, а также улучшена поддержка callables (всё что может быть вызвано), так что они могут быть использованы наравне с анонимными функциями практически во всех случаях.

Наиболее распространным использованием функций высшего порядка, является реализация паттерна стратегия. Встроенная функция array_filter спрашивает одинаково, как входной массив (данные), так и функцию (стратегия или callback), используемая, как фильтр для каждого элемента массива.

$input = array(1, 2, 3, 4, 5, 6);

// Создает новую анонимную функцию и присваевает её к переменной
$filter_even = function($item) {
    return ($item % 2) == 0;
};

// Встроенная функция принимает, как массив, так и функцию
$output = array_filter($input, $filter_even);

// Функции не обязательно нужно быть присвоенной к переменной. Это также работает:
$output = array_filter($input, function($item) {
    return ($item % 2) == 0;
});

print_r($output);

/*
Array
(
    [1] => 2
    [3] => 4
    [5] => 6
)
*/

Замыкания - это анонимные функции, которые могут получить доступ к переменным, импортированным извне области видимости, без использования любых глобальных переменных. Теоретически, замыкание - функция с закрытыми некоторыми аргументами (например, фиксированными) окружением, когда они объявлены. Замыкания могут обойти ограничения области видимости переменных, чистым способом.


Глобальное пространство имён

Когда вы используете пространство имён (namespaces), вы можете обнаружить, что некоторые функции вам скрыты, недоступны. Чтобы исправить это, указываете что это глобальная функция, использовав обратную косую черту '\' перед именем функции.

namespace phptherightway;

function fopen() {
    $file = \fopen();
    // Функция имеет имя такое же как и глобальная функция 'fopen'.
    // Чтобы их различать используйте в глобальных '\'.
}

function arrayIterator() {
    $iterator = new \ArrayIterator();
    // ArrayIterator внешний класс. Если использовать его без '\'
    // то интерпретатор PHP будет пытаться найти его в пространстве 'phptherightway'.
}

Время от времени разработчики пытаются сделать свой код "чище", используя предопределённые переменные. Обычно это только ведёт к увеличению используемой памяти. Для примера сообщите какой-нибудь переменной строку размером 1мб, в результате вы скопируете это дважды.

$about = 'Очень большой текст';    // будет использоваться 2MB памяти
echo $about;

// vs

echo 'Очень большой текст';        // а тут всего лишь 1MB

PDO

PDO - это абстрактная библиотека для подключения к базе данных, встроенная в PHP с версии 5.1.0, которая обеспечивает единый интерфейс для взаимодействия с большим количеством различных баз данных. PDO не будет переводить ваши SQL запросы или эмулировать отсутствующие возможности; он чист для подключения к нескольким типам баз данных с тем же API.

Более важно, что PDO позволяет вам безопасно вводить пользовательские данные (например, идентификатор) в ваши SQL запросы, без беспокойствия о SQL-инъекциях. Это возможно благодаря использованию PDO выражений и связывания (bound) параметров.

Предположим, что PHP скрипт получает числовой идентификатор в качестве параметра из запроса. Этот идентификатор должен быть использован для получения пользовательских записей из базы данных. Ниже приведён неправильный способ реализации этого:

$pdo = new PDO('sqlite:users.db');
$pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']);  // <-- Это неправильно!

Это ужасный код. Вы вставляете необработанные параметры в SQL запрос. Это приведёт к взлому. Просто представьте, что взломщик сделает запрос http://domain.com/?id=1%3BDELETE+FROM+users, который присвоит переменной $_GET['id'] значение 1;DELETE FROM users и приведёт к удалению всех ваших пользователей! Вместо этого, вы должны очистить ввод идентификатора с помощью связывания параметров PDO.

$pdo = new PDO('sqlite:users.db');
$stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT);  //<-- Автоматически очищено с помощью PDO
$stmt->execute();

Это правильный код. Он использует связанный параметр в выражении PDO. Это позволяет избежать ввода некоректного ID перед тем, как передать запрос в базу данных, тем самым предотвращая потенциальные SQL-инъекции.


Сообщения об ошибках

Логирование ошибок полезно при поиске проблемных мест вашего приложения, также логирование может выдать информацию о структуре вашего приложения. Для эффективной защиты вашего приложения от проблем, которые могут быть вызваны выводом этих сообщений, вам необходимы различные настройки сервера для разработки и продакшна.

Разработка

Для того, чтобы видеть все возможные ошибки во время разработки, настройте следующие параметры в вашем php.ini:

display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On

Продакшн

Чтобы спрятать все ошибки вашей среды во время продакшна, настройте ваш php.ini следующим образом:

display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On

С этими настройками в продакшне, ошибки всё также будут записываться в лог ошибок веб сервера, но не будут показаны пользователю.


Кэширование

Кэширование байткода

Во время исполнения PHP файла, на низком уровне он сперва компилируется в байткод (или опкод) и только потом исполняется байткод. Если PHP файл не изменён, то байткод будет всегда одинаков. Это значит, что шаг компиляции - пустая трата процессорных ресурсов.

И тут настает время кэширования байткода. Оно предотвращает избыточные компиляции, сохраняя в памяти байткод и переиспользуя его в последовательных вызовах. Установка подобного кэширования - вопрос пары минут, а скорость вашего приложения поднимется существенно. Поэтому нет причин не использовать кэширование.

Начиная с PHP 5.5 появилось встроенное расширение для кэширования байткод - OPcache. Оно также доступно в виде отдельного расширения для ранних версий.

Кэширование объектов

Бывают моменты, когда вам необходимо кэшировать определённые объекты в вашем коде, такие как данные, которые неразумно получать ещё раз из базы данных, когда результат вряд ли изменится. Вы можете использовать ПО для кэширования чтобы сохранить эти кусочки данных в памяти, что позволит чрезвычайно быстро обратиться к ним позже. Если вы сохраните эти объекты в хранилище данных и после получите их и выдадите напрямую из кэша для некоторых запросов, вы можете получить существенное улучшение производительности и уменьшение нагрузки на сервер базы данных.

Самыми популярными системами кэширования объектов являются APC и memcached. APC — идеальный выбор для кэширования объектов, он включает простой API для добавления данных в кэш память и при этом очень просто устанавливается и используется. Единственное существующее ограничение APC состоит в том, что он привязан к серверу на котором установлен. Memcached, напротив, устанавливается как отдельный сервис, и к нему можно получить доступ по сети, что позволяет хранить объекты в очень быстром централизованном хранилище данных и множество других систем могут получать эти данные из него.


Дэвид Скляр, Адам Трахтенберг - PHP. Рецепты программирования

Задача:

Требуется отформатировать данные в виде списка значений, разделенных запятыми (CSV, Comma-Separated Values), чтобы их можно было импортировать в электронную таблицу или базу данных.

Решение:

$sales = array(
    array('Northeast','2005-01-01','2005-02-01',12.54),
    array('Northwest','2005-01-01','2005-02-01',546.33),
    array('Southeast','2005-01-01','2005-02-01',93.26),
    array('Southwest','2005-01-01','2005-02-01',945.21),
    array('All Regions','--','--',1597.34)
);

$filename = './sales.csv';
$fh = fopen($filename, 'w') or die("Can't open $filename");

foreach ($sales as $sales_line) {
    if (fputcsv($fh, $sales_line) === false) {
        die("Can't write CSV line");
    }
}

fclose($fh) or die("Can't close $filename");

Задача:

Имеется набор данных, разделенных запятыми (формат CSV), например файл, экспортированный из Excel или базы данных. Требуется извлечь записи и поля в формат, с которым удобно работать в PHP.

Решение:

Если данные в формате CSV хранятся в файле (или доступны по URL-адресу), откройте файл функцией fopen() и прочитайте данные функцией fgetcsv().

$filename = 'sales.csv';
$fp = fopen($filename,'r') or die("can't open file");

print "<table>\n";
while($csv_line = fgetcsv($fp)) {
    print '<tr>';
    for ($i = 0, $j = count($csv_line); $i < $j; $i++) {
        print '<td>'.htmlentities($csv_line[$i]).'</td>';
    }
    print "</tr>\n";
}
print "</table>\n";

fclose($fp) or die("can't close file");

Задача:

Требуется организовать перенос текста в строке, например вывести текст в тегах <pre> и </pre>, но так, чтобы он помещался в окне браузера обычного размера.

Решение:

$s = "Four score and seven years ago our fathers brought forth on this continent
a new nation, conceived in liberty and dedicated to the proposition
that all men are created equal.";

print "<pre>\n".wordwrap($s)."\n</pre>";

По умолчанию функция wordwrap() переносит текст с длиной строки 75 символов. Необязательный второй аргумент задает другую длину строки: print wordwrap($s, 50);


Задача:

Требуется создать и определить функцию во время выполнения программы.

Решение:

$increment = 7;

$add = function($i, $j) use ($increment) {
    return $i + $j + $increment;
};

echo $add(1, 2);  // 10

В синтаксисе замыканий PHP сможет проверить анонимные функции на стадии компиляции точно так же, как это делается с остальным кодом. При этом используется тот же синтаксис, что и для написания функций, за одним исключением: в объявлении use() после списка аргументов могут перечисляться переменные из области видимости, содержащей определение замыкания, которые должны быть доступны внутри замыкания. В приведенном примере конструкция use($increment) означает, что внутри замыкания $increment имеет то же значение, что и в области видимости, в которой определяется замыкание.


Задача:

Некоторые методы должны использоваться разными классами, но при этом наследование всех классов от одного родительского класса выглядит нелогично.

Решение:

Определите интерфейс и объявите, что ваши классы реализуют этот интерфейс:

interface NameInterface {
	public function getName();
	public function setName($name);
}

class Book implements NameInterface {
	private $name;
	
	public function getName() {
		return $this->name;
	}
	
	public function setName($name) {
		return $this->name = $name;
	}
}

Интерфейс NameInterface определяет два метода для присваивания имени объекту. Класс Book объявляет о реализации интерфейса NameInterface и определяет два метода в теле класса. Механизм, заставляющий классы поддерживать определенный набор методов, называется интерфейсом. Внутри интерфейса определяются прототипы методов, но не предоставляются их реализации. Приведенный фрагмент создает интерфейс с именем NameInterface. Любой класс, реализующий NameInterface, должен реализовать два метода, указанных в интерфейсе: getName() и setName(). Если класс поддерживает все методы интерфейса, он реализует этот интерфейс. Если класс реализует не все методы, входящие в интерфейс, или реализует их с другими прототипами, PHP выдает фатальную ошибку. Класс может реализовать столько интерфейсов, сколько потребуется.

Если вы захотите включить код, реализующий интерфейс, в другое место, определите типаж (trait) и объявите об его использовании другими классами:

trait NameTrait {
	private $name;
	
	public function getName() {
		return $this->name;
	}
	
	public function setName($name) {
		return $this->name = $name;
	}
}

class Book {
	use NameTrait;
}

class Child {
	use NameTrait;
}

Типаж NameTrait определяет и реализует два метода, необходимых для присваивания имени объекту. Так, класс Book объявляет об использовании типажа NameTrait, после чего вы можете вызвать два метода из тела класса.


Задача:

Требуется создать абстрактный класс - иначе говоря, класс, который используется только как общая база для производных классов (без возможности создания объектов этого класса).

Решение:

Пометьте класс ключевым словом abstract:

abstract class Database {
	// ...
}

Ключевое слово abstract размещается перед определением class.

В классе также должен быть определен как минимум один абстрактный метод. Для этого ключевое слово abstract размещается в начале определения метода:

abstract class Database {
	abstract public function connect();
	abstract public function query();
	abstract public function fetch();
	abstract public function close();
}

Абстрактные классы лучше всего использовать для групп объектов, связанных отношением типа «является частным случаем». По этой причине логично заставить такие объекты наследовать от общего родителя — но при этом потомки «материальны», а родитель абстрактен.

Для примера возьмем класс Database. База данных — реальный объект, поэтому создание класса Database выглядит логично. Сдругой стороны, существуют Oracle, MySQL, Postgres, MSSQL и сотни других баз данных, но загрузить и установить «базу данных вообще» невозможно. Вы должны выбрать конкретную базу данных.

PHP позволяет определять классы, объекты которых не могут создаваться. Такие классы называются абстрактными. Для примера возьмем класс Database:

abstract class Database {
	abstract public function connect($server, $username, $password, $database);
	abstract public function query($sql);
	abstract public function fetch();
	abstract public function close();
}

Чтобы пометить класс как абстрактный, поместите ключевое слово abstract перед class.

Абстрактные классы должны содержать как минимум один метод с пометкой abstract. Эти методы называются абстрактными методами. Класс Database содержит четыре абстрактных метода: connect(), query(), fetch() и close(). Эти четыре метода образуют базовую функциональность, необходимую для работы с базой данных.

Если класс содержит абстрактный метод, сам класс тоже должен быть объявлен абстрактным. Тем не менее абстрактные классы могут содержать неабстрактные методы (хотя в классе Database таких нет).

Абстрактные методы, как иметоды интерфейсов, не реализуются внутри абстрактного класса. Вместо этого абстрактные методы реализуются в производном классе, расширяющем абстрактного родителя. Например, вы можете использовать класс MySQL:

class MySQL extends Database {
	protected $dbh;
	protected $query;
	
	public function connect($server, $username, $password, $database) {
		$this->dbh = mysqli_connect($server, $username, $password, $database);
	}
	
	public function query($sql) {
		$this->query = mysqli_query($this->dbh, $sql);
	}
	
	public function fetch() {
		return mysqli_fetch_row($this->dbh, $this->query);
	}
	
	public function close() {
		mysqli_close($this->dbh);
	}
}

Если производный класс не реализует все абстрактные методы родительского класса, он тоже является абстрактным, поэтому другой класс должен осуществить дальнейшее субклассирование. Например, такая иерархия может появиться, если вы хотите создать два класса MySQL: первый осуществляет выборку информации в виде объектов, а другой возвращает массивы.

К абстрактным методам предъявляются два требования:

  • Абстрактные методы не могут определяться как закрытые (private), потому что они должны использоваться при наследовании.
  • Абстрактные методы не могут определяться с ключевым словом final, потому что они должны переопределяться.

Абстрактные классы и интерфейсы похожи, и все же это разные концепции. Прежде всего, класс может реализовать несколько интерфейсов, но расширять он может только один абстрактный класс. Кроме того, в интерфейсе можно определять только прототипы методов — никаких реализаций. Напротив, в абстрактном классе должен присутствовать только один абстрактный метод, наряду с которым могут быть другие неабстрактные методы и даже свойства.


Задача:

Требуется проанализировать объект и узнать, какие методы и свойства он содержит; это позволит вам писать обобщенный код, работающий с произвольным объектом независимо от его типа.

Решение:

Используйте классы Reflection для получения информации об объекте.

Чтобы получить краткую сводку для класса, вызовите Reflection::export():

// Получение информации о car
Reflection::export(new ReflectionClass('car'));

Или проверьте существование конкретных компонентов класса:

$car = new ReflectionClass('car');
if ($car->hasMethod('retractTop')) {
	// Содержит метод retractTop
}

Ситуации, в которых у вас имеется объект, но вы не можете просмотреть его исходный код, встречаются нечасто. Тем не менее классы Reflection позволяют на программном уровне получить информацию как по объектно-ориентированным сущностям (классам, методам, свойствам), так и по обычным функциям, расширениям и т. д.