Объектно-ориентированное программирование на PHP. Сортировка объектов средствами SPL.

Владимир | | PHP, Web разработка.

php spl

К сожалению, использование объектно-ориентированных возможностей PHP встречается не так часто, как хотелось бы. Конечно, тут есть объективные причины, например, не все хостеры предоставляют PHP5 (именно с этой версии появилась нормальная поддержка ООП).

К тому же, огромное количество PHP скриптов написано без использования ООП. И во многих случаях нет необходимости их переписывать.

Но ООП – это не «модная фишка». Эта парадигма программирования при правильном использовании позволяет сделать php скрипты понятнее, сократить количество ошибок, и, самое главное, увеличить скорость разработки (за счет повторного использования кода).

В этой заметке я хочу рассказать о нескольких таких возможностях и показать пример работы с SPL (StandardPHPLibrary).

Предположим, у нас есть задача, нужно отсортировать массив. Ничего сложного, можно просто использовать функцию sort.

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

В таком случае можно написать собственную функцию сравнения двух объетов (назовем ее cmpObjects), а затем использовать функцию uasort. Ей в первом параметре передается массив, а во втором – имя функции, которая сравнивает объекты.

В принципе, задача решена. Но функция cmpObjects, с точки зрения PHP, никак не связана с объектами, которые она сравнивает. Хотя очевидно, что использовать ее с другими объектами будет бессмысленно. ООП позволяет устранить этот недосток.

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

Примечание. Если следовать терминологии ООП, то функции, объявленные внутри класса, нужно называть методами, а переменные — свойствами.

Рассмотрим конкретный пример.

<?php
class MyClass {
	
	private $name;
	private $age;
	
	public function MyClass($name, $age) {
		$this->name = $name;
		$this->age = $age;
	}
	
	public function getName() {
		return $this->name;
	}
	
	public function getAge() {
		return $this->age;
	}
	
	public function toString() {
		return 'Имя: '.$this->name.'; Возраст: '.$this->age;
	}
	
	public static function ageCmp($val1, $val2) {
		if ($val1->getAge() === $val2->getAge()) {
			return 0;
		}
		return ($val1->getAge() >= $val2->getAge()) ? 1 : -1;
	}
	
	public static function nameCmp($val1, $val2) {
		return strcmp($val1->getName(), $val2->getName());
	}
}
?>

Как видите, этот класс имеет два свойства (name и age), два метода для чтения этих свойств (getName, getAge), конструктор, метод toString (возвращает значения свойств в виде строки) и два метода сравнения.

Первый метод (ageCmp) в качестве основы сравнения использует свойство age, а второй (nameCmp) — name. Принцип их работы прост. Если первый объект (val1) больше второго (val2), возвращаем «1», если меньше – «-1», а если они равны – «0».

Обратите внимание на ключевое слово static в объявлении этих методов. Оно позволяет их использовать, не создавая экземпляр класса. Т.е. не нужно выполнять new MyClass(...);.

Теперь рассмотрим, как использовать этот класс. Напишем небольшой скрипт.

include "myclass.php";

$v1 = new MyClass('Ваня', 15);
$v2 = new MyClass('Петя', 17);
$v3 = new MyClass('Коля', 13);

$data = array($v1, $v2, $v3);

$dataIterator = new ArrayIterator($data);
foreach ($dataIterator as $val) {
	echo $val->toString().'<br />';
}

echo "Сортировка по возрасту<br />";

$dataIterator->uasort('MyClass::ageCmp');

$dataIterator->rewind();
foreach ($dataIterator as $val) {
	echo $val->toString().'<br />';
}

echo "Сортировка по имени<br />";

$dataIterator->uasort('MyClass::nameCmp');

$dataIterator->rewind();
foreach ($dataIterator as $val) {
	echo $val->toString().'<br />';
}

Прежде всего, мы подключаем файл с объявлением класса, создаем три объекта ($v1, $v2, $v3) и формируем из них массив.

Затем создаем объект типа ArrayIterator и передаем его конструктору наш массив.

Примечание. ArrayIterator входит в библиотеку SPL, а документация с его описанием находится здесь.

Теперь мы можем использовать $dataIterator для работы с массивом.

Как видите, $dataIterator можно использовать в циклах foreach, т.е. работать как с обычным массивом.

Но в тоже время, его возможности гораздо шире.

Во-первых, обратите внимание на метод rewind(). Он перемещает указатель на первый элемент массива.

Во-вторых, ArrayIterator имеет свой метод uasort, который мы используем для сортировки. Первый раз (строка 16) мы выполняем сортировку с помощью метода ageCmp, а после этого (строка 25) – используя nameCmp.

В результат работы скрипта будет выглядеть так:

Имя: Ваня; Возраст: 15
Имя: Петя; Возраст: 17
Имя: Коля; Возраст: 13
Сортировка по возрасту
Имя: Коля; Возраст: 13
Имя: Ваня; Возраст: 15
Имя: Петя; Возраст: 17
Сортировка по имени
Имя: Ваня; Возраст: 15
Имя: Коля; Возраст: 13
Имя: Петя; Возраст: 17

Как видите, массив мы отсортировали 😉

Самое главное, что основной скрипт не содержит кода сортировки и практически ничего не знает о классе. Т.е. мы можем вместо MyClass использовать другой класс. И это потребует минимальных изменений кода.

До встречи!

Интересно почитать:

Новые стандарты хостинга — yeshost.ru.

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

    • «не к месту» можно использовать практически что-угодно.
      Например, бездумное использование шаблонов (patterns) проектирования или библиотек только усложнит систему вместо того чтобы сделать ее проще и понятнее.

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

    • «не к месту» можно использовать практически что-угодно.
      Например, бездумное использование шаблонов (patterns) проектирования или библиотек только усложнит систему вместо того чтобы сделать ее проще и понятнее.

  • ООП жиждется на трех китах: инкапсуляция, полиморфизм, наследование. И PHP до такой реализации как в том же Delphi еще далеко. Но: PHP и был создан совсем для другого.

    • Тут много спорных вопросов. Первоначально PHP ООП можно считать не поддерживал, но начиная с 5 версии ситуация изменилась.
      Просто сказываются особенности самих web приложений. Если движок сделан без использования ООП и нормально работает, то никто не будет его переделывать.
      К тому же хостеры очень медленно переходят на новые версии PHP. В принципе, это правильно, для них важнее стабильность, а не новые возможности.
      Тем не менее ООП в PHP на сегодняшний день очень широко используется в новых разработках.

  • ООП жиждется на трех китах: инкапсуляция, полиморфизм, наследование. И PHP до такой реализации как в том же Delphi еще далеко. Но: PHP и был создан совсем для другого.

    • Тут много спорных вопросов. Первоначально PHP ООП можно считать не поддерживал, но начиная с 5 версии ситуация изменилась.
      Просто сказываются особенности самих web приложений. Если движок сделан без использования ООП и нормально работает, то никто не будет его переделывать.
      К тому же хостеры очень медленно переходят на новые версии PHP. В принципе, это правильно, для них важнее стабильность, а не новые возможности.
      Тем не менее ООП в PHP на сегодняшний день очень широко используется в новых разработках.

  • Можно еще привести в пример класс «База данных», что зачастую и делают в жизни: создают класс class BD. И для класса пишут все функции: _query, _result, _fetch_array, _num_rows, и т.д. Скажем изначально проект реализуется на MySQL, а затем оказывается что нужны иерархические запросы, вложенные запросы в секции FROM и еще пара фишек которых нет в MySQL. Нужно срочняк перевести на Оракл. И чтобы перевести на Оракл — нужно всего переписать класс BD.

    • В такой ситуации лучше использовать готовую библиотеку, например, ADOdb. Сразу получите поддержку самых распространенных БД.
      Разработкой своих классов лучше если нет готовых решений, либо они почему-то не устраивают, например, производительность нужно увеличить и т.п.

  • Можно еще привести в пример класс «База данных», что зачастую и делают в жизни: создают класс class BD. И для класса пишут все функции: _query, _result, _fetch_array, _num_rows, и т.д. Скажем изначально проект реализуется на MySQL, а затем оказывается что нужны иерархические запросы, вложенные запросы в секции FROM и еще пара фишек которых нет в MySQL. Нужно срочняк перевести на Оракл. И чтобы перевести на Оракл — нужно всего переписать класс BD.

    • В такой ситуации лучше использовать готовую библиотеку, например, ADOdb. Сразу получите поддержку самых распространенных БД.
      Разработкой своих классов лучше если нет готовых решений, либо они почему-то не устраивают, например, производительность нужно увеличить и т.п.

  • хм… у меня в php5.1 ругается на $dataIterator->uasort('MyClass::ageCmp'); а в php5.2 всё нормально… скажите как можно отсортировать этот же набор объектов другим способом, не используя SPL?…

  • хм… у меня в php5.1 ругается на $dataIterator->uasort('MyClass::ageCmp'); а в php5.2 всё нормально… скажите как можно отсортировать этот же набор объектов другим способом, не используя SPL?…

  • сделал так:


    usort($data, array(«MyClass»,»nameCmp»));

    чем так хуже?

    • Так не хуже и не лучше. Все зависит от того, что вы хотите получить. SPL библиотека расширяет возможности стандартных функций PHP.
      Если вы можете решить задачу встроенными функциями, то смысла использовать SPL нет, если встроенных функций не хватает есть смысл попробовать SPL или другую библиотеку. Можно и собственную функцию написать. Алгоритмов сортировки существует много и их описание вы легко найдете в интернете.

  • сделал так:


    usort($data, array(«MyClass»,»nameCmp»));

    чем так хуже?

    • Так не хуже и не лучше. Все зависит от того, что вы хотите получить. SPL библиотека расширяет возможности стандартных функций PHP.
      Если вы можете решить задачу встроенными функциями, то смысла использовать SPL нет, если встроенных функций не хватает есть смысл попробовать SPL или другую библиотеку. Можно и собственную функцию написать. Алгоритмов сортировки существует много и их описание вы легко найдете в интернете.