Пишем на php… статично

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

Я как и многие php программисты думал что статическая типизация это «усложнение». Она ограничивает гибкость и вообще: как люди с ней работают? И искренне не понимал, почему многие опытные программисты отдают предпочтение языкам со статической типизацией и строгой проверкой типов.

Дебаты о типизации

Я относился к правой половине людей, которые мало что знают о типах, но при этом искренне верят, что это не удобно. И так было до тех пор пока я не познакомился с одним из строго типизированных языков (c#) вплотную. С тех пор мое отношение к php да и вообще к программированию в целом изменилось.

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



Для начала определимся вообще что такое статическая типизация:

Статическая типизация определяется тем, что конечные типы переменных и функций устанавливаются на этапе компиляции. Т.е. уже компилятор на 100% уверен, какой тип где находится.

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

Для чего все это нужно в PHP?


Изучая c# и параллельно осваивая продукт, по которому ставились задачи, не имея достаточного опыта и документации (точнее документации не было вообще), меня выручало то, что благодаря строгой типизации большинство ошибок отсеивалось сразу, а еще большим плюсом были подсказки IDE, выдаваемые на основе заданных типов. Мне не нужна была документация, что бы понять что и куда передавать, я не мог сделать что то не так, оно просто не компилировалось!

Это заметно сокращало время, потому что ты сразу видишь, что этот метод, принимает только такие параметры, а создавая объекты для данных параметров, узнаешь, каких звеньев не хватает для их конструирования и так далее.

“Хорошо, это и вправду удобно” — скажете вы — “Но ты не рассказал нам ничего нового, в PHP есть PHPDoc, который понимает большинство современных IDE, и помогает так же “на лету” писать код!”

Все верно, но что бы это работало так же как в статично типизированных языках необходимо не просто написать комментарий у каждого метода и свойства класса, но важно что бы вся архитектура была заточена под работу с заранее известными объектами. Что бы построить такую архитектуру, нужно придерживаться определенных правил. Именно о них я бы хотел поговорить далее.

Используйте объекты вместо массивов


Да, именно так. Во общем эта истина не нова и о ней написано много: объекты проще расширить, к ним проще добавить методы, и вообще, дорогу ООП.

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

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

$eventData = array(
	'sender' => $controller, //экземпляр некоего контроллера
	'name' => 'onDelete',
	'group' => 'global',
	'arguments' => array(
		'id' => 15
	),
);

$eventDispatcher->triggerEvent($eventData);

...

/**
 * Обработчик события удаления
 * @param array $eventData
 */
protected function onDelete($eventData) {
	//Здесь к нам в $eventData попадает некий массив, 
        //структуру которого мы сможем узнать только вызвав var_dump() 
        //или же найдя код где это событие вызывается
	$model = $eventData['sender']->model->deleteChilds();
}


Простейший и довольно частый случай, ничего сложного, составляем массив с данными и отправляем его в функцию.

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

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

Давайте теперь рассмотрим что будет, если вместо массива мы описали бы класс и создали его объект. Изменим код:

class EventData {
	/**
	 * Экземпляр контроллера, инициировавший событие
	 * @var BaseController
	 */
	public $sender;
	
	/**
	 * Имя события
	 * @var string 
	 */
	public $name;
	/**
	 * Группа к которой относится событие (нэймспейс)
	 * @var string 
	 */
	public $group;
	/**
	 * Массив с произвольными аргументами
	 * @var array 
	 */
	public $arguments;
	
}

$eventData = new EventData();

$eventData->sender = $controller;
$eventData->name = 'onDelete';
$eventData->group = 'global';
$eventData->arguments = array('id' => 15);

$eventDispatcher->triggerEvent($eventData);

...

/**
 * Обработчик события удаления
 * @param EventData $eventData
 */
protected function onDelete($eventData) {
	$eventData->sender->model->deleteChilds();
}	

Что мы получили в итоге? Теперь наша любимая IDE подсказывает нам, что же содержит полученный объект.



Кроме того, благодаря тому что мы подробно расписали тип для каждого атрибута в переданном объекте, мы можем тут же получить подсказки и по ним. Т.е. набрав $eventData->controller-> — мы можем увидеть, что находится внутри атрибута с именем controller.

Теперь вам не придется лезть куда-то в дебри кода, для того что бы понять, что же передается в этом объекте и какого оно типа. Представьте, как вам будут благодарны другие программисты, которые будут работать с вашим кодом!

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

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

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

Не бойтесь создавать классы. Вам не обязательно создавать для каждого отдельный файл, если они взаимосвязаны друг с другом.
Например, я бы создал файл EventArguments.php, и в нём бы описал все возможные варианты аргументов событий.

UPD: Я не призываю понимать мой призыв заменить ВСЕ массивы на объекты буквально, это не правильно.

Цитата из вики:
Массив — упорядоченный набор данных, для хранения данных одного типа, идентифицируемых с помощью одного или нескольких индексов.

Именно в таком ключе и должны использоваться массивы. т.е. хранить некие упорядоченные однотипные данные.

Бездумное изменение массивов на объекты может принести больше вреда чем пользы. Поэтому вы должны сами для себя (и для своего проекта) определить грань между удобством использования объектов и производительностью системы.

Инициализируйте переменные с правильным типом.


Сразу к примеру:

	/**
	 * Получить детишек
	 * @return array
	 */
public function getChildren(){
	
	if(!$this->isLeaf) {
		return $this->find('where id = :id', array(':id', $this->id));
	}
	
}
...
		foreach($model->getChildren() as $child) {
			//делаем что либо с потомками
		}

Знакомо? Вся проблема кроется в том, что если условие !$this->isLeaf не выполнится, то результатом функции будет совсем не array, и нам придется писать кучу шаблонного кода, что бы foreach не падал, из-за неправильных аргументов.

Как должно быть:

	/**
	 * Получить детишек
	 * @return array
	 */
public function getChildren(){
	//Во первых необходимо ВСЕГДА определять переменную, прежде чем её использовать - это хороший тон. 
	//Но в нашем случае мы её определяем не абы как, а сразу с тем типом, которым предполагаем использовать. 
	$children = array(); 
	if(!$this->isLeaf) {
		$children = $this->find('where id = :id', array(':id', $this->id));
	}
	return $children;
}

Теперь если условие !$this->isLeaf не выполнится, то результатом функции будет пустой массив который мы определили в начале, и foreach в который попадет значение из этой функции не упадет если что то пойдет не так.

Рассмотренный пример — это лишь вершина айсберга. Если вы всегда будете сразу определять переменную с нужным, заранее известным типом, то вы сможете сэкономить кучу времени. А так же писать более красивый код без дополнительных ухудшающих восприятие кода проверок.

Кстати, это касается не только php. К примеру, в JavaScript в движке V8, определение типа переменной на этапе её создания ускоряет исполнение скрипта, так как не заставляет движок делать лишних преобразований (подробнее в этой статье)

Инструменты строгой типизации.


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

Например, при определении функции вы можете явно указать, какого типа должны быть её параметры:

	public function triggerEvent(EventData $event) { //указываем что в эту функцию можно передавать только объекты типа EventData или его потомки
	}
	...
$dispatcher->triggerEvent(new EventData);  //Функция вызовется
$dispatcher->triggerEvent(new EventDataChild);  //EventDataChild - потомок EventData, функция вызовется
$dispatcher->triggerEvent(new AnotherClass()); //Catchable fatal error: Argument 1 passed to EventDispatcher::triggerEvent() must be an instance of EventData, instance of new AnotherClass given

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

Цитата со страниц мануала о контроле типов php.net/oop5.typehinting:

На данный момент функции имеют возможность заставлять параметры быть либо объектами (путем указания имени класса в прототипе функции), либо интерфейсами, либо массивами (начиная с PHP 5.1), или колбеком с типом callable (начиная с PHP 5.4).

Однако, если NULL использовался как значение параметра по умолчанию, то это будет также допустимо в качестве аргумента для последующего вызова.

Если класс или интерфейс указан для контроля типа, то все его потомки или реализации также допустимы.
Контроль типа не может быть использован со скалярными типами, такими как int или string. Трейты также недопустимы.

Делая вывод из вышесказанного, мы можем указывать почти все, кроме скалярных типов, но и на этот счет PHP имеет решение. В стандартной библиотеке SPL, содержится несколько занимательных классов призванных решить эту проблему:
  • SplInt
  • SplFloat
  • SplEnum
  • SplBool
  • SplString

Поскольку эти типы являются классами (масло масляное, но в контексте php где int является типом, но не является классом — позволительно) их можно указать в определении функции, тем самым предотвратив передачу в функцию, к примеру, строк там где требуется boolean. Соответственно эти классы вам придется использовать по всему коду, взамен стандартных скаляров.

Честно признаться, я этим не пользовался, поскольку на мой взгляд это перебор. Кроме того эти классы не входят в стандартную поставку PHP, и распространяются PECL пакетом, что затрудняет их использование.

Используйте PHPDoc везде где это возможно


PHPDoc и его поддержка в IDE — это отличный инструмент, экономящий время PHP программисту.

Поскольку синтаксис языка и его идеология не позволяет установить тип переменной при ее создании, вашей IDE очень сложно понять какая переменная чем является. Так помогите ей — укажите тип в комментарии, и ваша IDE отблагодарит вас подсказками во время работы.

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

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

Небольшие подсказки для тех, кто не знаком достаточно хорошо с PHPDoc (все это корректно для среды NetBeans)

	//Если метод возвращает объект некоего класса, 
        // то можно указать имя этого типа в инструкции @return
	//И тогда IDE при получении результата этой функции будет трактовать его как объект этого класса
	
	/**
	 * Этот метод возвращает объект класса EventData
	 * @return EventData
	 */
	public function getEventData(){
		return new EventData();
	}
	
	//Если метод возвращает массив, содержащий объекты некоего класса, то это можно указать так:
	//@return EventHandler[]
	//Ключевой нюанс - это квадратные скобки после имени класса. 
	//Эта конструкция говорит парсеру, что будет возвращен массив, состоящий из объектов этого класса
	//
	//Пример:
	/** Возвращаем массив элементов EventHandler
	 * @return EventHandler[]
	 */
	public function getEventHandlers(){
		return $this->handlers;
	}
	
	public function triggerEvent(){
		foreach ($this->getEventHandlers() as $handler) {
			$handler; //на этом этапе IDE будет знать, что $handler является объектом класса EventHandler, и будет предлагать подсказки 
		}
	}
	
	public function getModel(){
		//некая фабрика возвращает объект неведомого типа 
		//(она может возвращать разные объекты. Какой именно, будет известно исходя из переданных параметров)
		//но вы точно знаете, что в этом месте метод вернет вам некий класс. Пропишите его вот так: 
		
		$model = NodeFactory::byId($id);
		/* @var $model Folder */
		$model; // здесь IDE будет подсказывать вам атрибуты и методы из класса Folder
	}


Заключение


Что бы кто не говорил, но PHP вырос до достаточно мощного инструмента. Динамическая типизация — это что позволяет быть ему максимально гибким и простым. Но вместе с ней приходят и сложности.



Статическая типизация, позволяет улучшить производительность труда и сократить количество ошибок в коде.

Из комментариев на хабре к статье Ликбез по типизации:
В задачах, сложность которых заставляет испытывать дикий восторг от возможности автоматической проверки хоть чего-либо, языки с сильной статической типизацией обгоняют всё остальное. Сложность задач решаемых при сайтостроении плавно приближается к такому уровню.

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

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

Полезные ссылки:


Ликбез по типизации в языках программирования
Контроль типов в PHP
PHP SPL Типы
Статический анализ кода (Wikipedia)
Поделиться публикацией
Комментарии 101
    –9
    Однажды наткнулся на другую сторону строгой типизации — системы, которые позволяют вместо одного класса-обработчика подставить другой, как например Magento. Написал функции которые принимали параметры заданного типа (к примеру func(Mage_Catalog_Model_Product $product)), но вот я не учел, что класс подменили на другой стороннего модуля, и лог сразу засорился предупреждениями о несоответствии типов. Пришлось всюду убрать любую типизацию входных параметров и проверку на тип переменных.
      +3
      Этот стороний модуль должен наследовать Ваш класс, и всё будет ок, иначе получилось нарушение наследования, у вас 2 разных класа с одинаковым целевым применением.
        –3
        Да нет, я просто принимал объекты класса «Продукт», я не наследовал его.
          +5
          А зря. Даже если не получалось наследовать базовый класс, можно было бы хотя имплементировать одинаковый интерфейс.
            –2
            Наследовать для чего? Если мне нужно обработать продукты, а не изменить логику их работы, мне незачем наследовать базовый класс. Если наследовать его внутри модуля, не перезаписывая на уровне системы, только чтобы иметь возможность посылаться на этот тип, а потом установить другой модуль который перезаписывает модель, я буду наследоваться от класса, который не стоит первым в цепочке. Вариант же, когда я перепишу класс Продукт на уровне системы, только ради типизации, бредовый — можно запросто перетереть уже установленный сторонний. С какой стороны не посмотри, куча минусов от людей, которые просто не в теме.
              –1
              Не недо читать php для начинающих.:((((
                0
                Я бы порекомендовал читать еще что-то кроме нее.
      +1
      $eventData->arguments = array('id' => 15);

      В конечном итоге, остался массив, структура которого неведома. Должно было быть что то типа
      $eventData->arguments = new EventData_Arguments(15);
        +1
        Продолжая улучшать наш пример, я бы не остановился на создании одного класса. Обычно в современных веб-приложениях происходит много различных событий, и в них передаются разные аргументы, их можно сгруппировать, и для каждой группы событий создать свой — подходящий класс аргументов.


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

        Т.е. предполагается что есть события в которых нужен $id, а есть события в которых нужен, скажем, какой ни будь $templateKey.

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

        Соответственно в функциях-хендлерах в тайп хинтинге мы указываем эти классы, и соответсвенно при работе с ними у нас не возникает вопросов, что там приходит в параметрах.
        +3
        Это конечно очень и очень круто и я стремлюсь к этому, но уж очень напрягает плодить отдельный класс для каждого массива. Всё это порождает другие сложности. Я бы назвал их «бюрократическими».

        //Эта конструкция говорит парсеру, что будет возвращен массив, состоящий из объектов этого класса

        И если уж говорить о совсем строгой типизации, то здесь нужно делать коллекцию. Таким образом, для таблицы `users`, потребуется создать минимум два класса User + UsersCollection. Ну и ещё в класс User добавить геттеры+сеттеры для всех 22 полей. А при добавлении нового поля в таблицу, нужно не забыть добавить новое поле+геттеры+сеттеры в класс User. Это напоминает бюрократические походы в 100500 энстанций, вместо того что бы просто сделать один ALTER TABLE.
          0
          Зачем, же? например в том же самом Magento, геттеры и сеттеры реализованы через __call,
          если нам нужно добавить параметр в какойнибудь обьект, просто делаем

          $obj->setSampleKey(«SampleVar»);
          $obj->getSamleVar();

          __call же, берёт первые три буквы метода, и соответственно отправляет данные на __get или __set для подальшей обработки.
            +2
            магические методы тоже имеют свои минусы. Не зря их называют «магическими».
              0
              Конкретно в Мадженте это костыль вокруг getData/setData, и они сами планировали отказаться от магии.
                0
                … и, в конце концов, Magento — не истина в последней инстанции.
              –3
              Все верно, такая структура позволит вам быть чуть более уверенным в том что нигде ничего не поломается со временем, и уверенным в том, что в любой момент вы сможете добавить некую бизнес логику в сеттер поля Name.

              Часто «быстрые» решения не всегда полезные. Иногда приходится тратить несколько часов на рефакторинг определенного кода, хотя это можно за 2 минуты решить с помощью копипаста одной функции в несколько мест. Цена этого решения всплывет потом, и смею вас уверить будет гораздо дороже чем изначально придерживаться принципов заложенных архитектурой.
                0
                Я не имею ввиду что это сложно. Я имею ввиду что это хрупко. Если не мне а другому программисту потребуется добавить новое поле, он сделает ALTER TABLE, но вспомнит ли он про новое поле в User?

                Сдаётся мне, что если у вас есть не самый большой проект состоящий из 50 таблиц, в каждой из которых, в среднем, по 10 полей (всего 500 полей), то поддерживать в актуальном состоянии 100 классов и 1000 методов (сеттеры+геттеры) будет не то что бы накладно, а очень хрупко. Особенно если над проектом работаете не только Вы один.
                0
                Для этого есть IDE или генераторы кода. Возьмем Doctrine 2 и описание маппинга в yml/xml:
                • Добавили описание поля
                • Запустили генерацию кода сущностей (автоматически добавилось поле, геттер, сеттер, инициализация коллекции в конструктор, если нужно) — doctrine:generate:entities
                • Запустили обновление схемы БД — doctrine:schema:update
                  0
                  Если вы собираетесь модифицировать классы (добавлять новые методы или модифицировать геттеры/сетторы), то перегенерацию кода лучше не использовать. А если вы не собираетесь модифицировать классы, то в чём профит? Только в автодополнении?
                    0
                    Doctrine к сторонним изменениям относится бережно — добавление геттеров-сеттеров инкрементальное (имеющиеся геттеры и сеттеры не изменяются, сторонние методы тоже, новые пишутся в конец класса), бэкап старого файла с классом делается автоматически. IDE подствечивает отличия от текущей ревизии VCS, всегда можно в этом убедиться после генерации. Проблем с модификацией классов сущностей и параллельным добавлением новых полей с помощью генерации кода нет.
                      0
                      Это интересно. Но не все пользуются Doctrine.
                        0
                        Безусловно, я привел ее именно как пример удобного инструмента, из которого можно почерпнуть наработки по автоматизации рутины.
                0
                $eventData = array(
                'sender' => $controller, //экземпляр некоего контроллера
                'name' => 'onDelete',
                'group' => 'global',
                'arguments' => array(
                'id' => 15
                ),
                );

                По моему достаточно для комфортной работы использовать геттеры и сеттеры. И дополнять PHPdoc @property. Например: @property BaseController $sender
                Насчет строгой типизации — поддерживаю, сам в последнее время стал использовать.
                  +1
                  Посыл статьи правильный, но, ИМХО, не те вопросы, не тем инстурментом, есть тесты, которые выполняют две очень важные в разработке вещи — проверяют правильно ли вы вычисляете значения и одновременно документируют ваш код. Если код и тесты написаны криво, что ж это скорее беда программиста, которую к сожалению ни один язык не исправит — ни строго типизированный ни динамически.
                    +1
                    И к слову сказать, если вам так нравится ООП и нравится, что везде вместо обычных типов — классы, которые можно расширять — обратите свое внимание на Ruby, как минимум вам не придется изобретать велосипед, как максимум вы откроете для себя новый мир.
                      +2
                      Лучше Python, там такого безумного количества синтаксиса нет.
                        –1
                        Дело вкуса же.

                        Для меня — в пейтоне безумное количество синтаксиса, избыточное.
                          +2
                          Причём тут дело вкуса? Есть объективная реальность. В Руби его действительно намного больше.
                            +1
                            Синтаксис один. DSL это всего-лишь бонус, если вы используете гем с извращенным DSL, то это не косяк языка :)
                              +1
                              Мы сейчас стандарты сравниваем?
                          +1
                          Автор и так осваивает C# )) За исключением ограничения платформы, он ничем не уступит Ruby, Python и тд… (ASP.NET MVC)… Так же постепенно ухожу из РНР в дотНет — ASP.NET MVC безумно радует, шарп + EF + LINQ + WebApi безумно доставляют )

                          ПыСы: НЕ холивара ради
                          –1
                          Хочу заметить что при таком решении количество кода возрастает и также возрастает зависимость от IDE
                            +2
                            А ещё возрастут тормоза. Объекты работают медленнее примитивных типов.
                              +1
                              Уже надоело видеть это замечание в любом предложении об использовании ООП или большого количества обьектов. В всех проектах тормоза вызваны не ООП и не обьектами, а кривыми руками программистов. Да, использование обьектов дает небольшой перерасход ресурсов, но это не сравнится с какой нибудь сортировкой пузырьком или запросом на 3 таблицы в БД чтобы извлечь значение которое потом нигде не используется.
                                –2
                                По поводу «всех проектов» я вам следующее скажу. Мне проходилось далеко не один высоконагруженный проект делать. И в некоторых случаях нужно было прекрасные фантазии с абстрактными базовыми классами и прочим переделывать на обычные массивы. Иногда становилось значительно быстрее.

                                Добро пожаловать в реальный мир.
                                  +2
                                  В вашем же ответе есть подтверждение моим словам :). Ключевые слова: «некоторых случаях» — «Иногда становилось».

                                  Я не спорю что массивы действительно могут помочь в определенных случаях (например ресурсоемкий алгоритм обрабатывающий десятки тысяч обьектов за раз), но в большинстве случаев разница не принципиальна. Зато правильное использование ООП позволяет сделать поддержку и развите проекта легче и это как правило гораздо важнее 10% выигрыша в производительности.

                                  Резюмируя хочу сказать что массивы или обьекты не являются серебрянной пулей. В каждой конкретной ситуации надо думать и принимать решение в зависимости от условий.
                                    +3
                                    Согласен с вами.
                                      –3
                                      Сударь, у Вас ООП головного мозга.

                                      Инструментарием нужно пользоваться с умом и отказываться от совсем уж извращённых конструкций там, где это совершенно не нужно. Например, скрипт-парсер на 5 процедур значительно выиграет в производительности у скрипта с одним классом на 35 методов и в 3 раза раздутым кодом, а для парсера это очень критично.

                                      Поэтому далеко не во всех проектах «тормоза вызваны не ООП и не обьектами, а кривыми руками программистов». Во многих проектах эти же тормоза вызваны использованием неоптимальных подходов к проектированию архитектуры.
                                        0
                                        Производительность и методикой написания никак не связаны.

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

                                      Не надо быть ежом, чтобы понять, что в определённом узком месте лучше сделать тупо или написать «говно»-код (гОвно имеется в виду не изящный, а оптимизированный добезумия), или вааще переписать на C.
                                    +1
                                    Статические HTML странички отдаются еще быстрее, но ведь никто не стремится делать сайты на одном только HTML?

                                    Нам всегда нужно выбирать между удобством разработки и эффективностью наших программ.

                                    К примеру, тот же ActiveRecord: все знают что он медленнее чем просто запрос-ответ, но так же никто не собирается с него уходить, все понимают какие плюсы он несет. Скорость и удобство разработки, не сопоставимо с теми тормозами которые могу возникнуть.
                                      0
                                      Про «никто не собирается уходить» я с вами не согласен. Я согласен, что баланс нужно соблюдать. Иногда я выберу ActiveRecords, иногда откажусь от него.

                                      На статичных HTML сайты тоже делают. И много сайтов, которые кешируют себя в статичных HTML.
                                  0
                                  Делая вывод из вышесказанного, мы можем указывать почти все, кроме скалярных типов, но и на этот счет PHP имеет решение. В стандартной библиотеке SPL, содержится несколько занимательных классов призванных решить эту проблему:
                                  SplInt
                                  SplFloat
                                  SplEnum
                                  SplBool
                                  SplString

                                  Скорее это задел на будущее. Посмотрите на ArrayObject, например. PHP медленно движется к тому, чтобы все типы были объектами. Когда-нибудь PHP совершит переключение. А пока эти объекты будут потихоньку появляться и покрываться нужными методами.
                                    +2
                                    Понятия «правильной архитектуры» и «эффективного программирования» ортогональны обоим упомянутым вам понятиям типизации. Напрасно вы всё это в кучу собрали.
                                    Эффективность обуславливается опытом и инструментами (как на уровня языка, так и уровня сред программирования), а правильность архитектуры вообще эфимерна.

                                    Остается только расценивать вашу статью как обзор phpDoc и typehinting.
                                      –1
                                      ООП головного мозга
                                        0
                                        А чего минусанули-то? Предлагая заменять повсеместно массивы объектами, автор на самом деле диагностировал у себя ООПухоль лобной доли.
                                          0
                                          Вы видимо прочитали только заголовки в статье.
                                          Если бы в рассмотрели пример, и вникнули в суть, то поняли, я не предлагаю заменить ВСЕ массивы.

                                          Массивы — это удобный инструмент, и должен использоваться по своему прямому назначению, а именно:

                                          Массив — упорядоченный набор данных, для хранения данных одного типа, идентифицируемых с помощью одного или нескольких индексов.

                                          (вики)

                                          И в этих случаях они и должны быть использованы.

                                          Я говорил о тех случаях когда массивы используются как основную структуры данных, когда массив становится центром всей системы, и вся бизнес логику начинает вращаться вокруг него.
                                            –3
                                            Почему же. Я не имею привычки критиковать не прочитав. Я внимательно прочитал вашу статью и не увидел там ни одного предупреждения о превратном понимании вашего призыва использовать массивы. Вы не говорите ни слова о том, что замена массивов на объекты применима далеко не везде, а иногда может привести больше вреда чем пользы.

                                            Может быть, я все-таки проглядел, вы тогда процитируйте себя пожалуйста.
                                              0
                                              Добавил предупреждение. Теперь должна быть понятнее суть предложения.
                                                0
                                                Отлично! Спасибо.
                                                0
                                                Не указание чего-либо !== Призыву к чему-либо противному
                                                  0
                                                  Инструкция, в которой должным образом не указано как пользоваться инструментом по назначению – плохая инструкция. Автор согласился, а вы поспорить хотите?
                                                    0
                                                    Неа.

                                                    Просто мы таким образом путаем понятия.

                                                    Инструкция плоха — окей. Но вывод из отсутствия некоего утверждения на противоположное — вот что меня покоробило. Да и хрен с ним.
                                                –1
                                                А можно пример, когда массив в php может являться центром всей системы?
                                                  0
                                                  В плохо спроектированных ООП системах — сплошь и рядом.

                                                  К примеру есть некий контроллер NewsController, есть вроде как даже модель NewsModel (правда в не совсем верном её понимании).

                                                  У модели есть метод: NewsModel->getAllNews(), при вызове которого, а возвращается какой то массив, с этим массивом дальше начинают работать в контроллере, всячески его обрабатывая, и в конце концов выводя в браузер.

                                                  Таким образом вся архитектура построенная на массивах, которые гоняют туда-сюда.

                                                    0
                                                    Это cake во всей своей «красе».

                                                    Yii в этом отношении лучше, Zend — ещё лучше, а симфони вааще всех за пояс затыкает полнейшим DI.
                                                      0
                                                      А может проще тогда заставить записывать метод NewsModel->getAllNews() в переменные класса, вместо возврата массива?

                                                      Или сделать возврат вместо массива ссылки на класс, например используя объектный стиль работы с mysqli, вместо процедурного/
                                                    0
                                                    Хочу только напомнить, что стандартный массив в php — это сортированный map. Поэтому массивами все часто и пользуются в качестве Value Object-ов.
                                                –3
                                                Так-то оно так, да только это как посмотреть (с) неизвестный рыбак с Ливенки

                                                Представим дядю Васю. Подхожу я к дяде Васе и говорю: подержи, дядя Вася, молоток и забей-ка им пару гвоздей в заборе. Дядя Вася берет молоток, идет и забивает пару гвоздей. Функция работает. Но вот, беру я микроскоп, подхожу к дяде Васе и говорю: дядь Вась, а давай-ка ты вот это возьмешь и забьешь пару гвоздиков вот этим. Ошибка типа, — говорит мне дядя Вася. И приходится мне учить дядю Васю забивать гвозди микроскопом, т.е. писать новую функцию в его сознании. А это — дополнительные затраты на разработку.

                                                Или вот… Подхожу я к дяде Васе и говорю: дядь Вась, отправь почту. И отдаю ему массив: три посылки. Берет и отправляет. Подхожу опять: дядь Вась, вот тут другой массив: две посылки и одно письмо. Отправь, пожалуйста. А он мне: иди ты, мальчик, у тебя массив не такой как я умею. И облом.

                                                Вывод: подсказки и описание назначения и схемы работы функций, входных и выходных данных, конечно, хорошо. Но не следует это путать с типизацией. Хороший правильный дядя Вася должен уметь гвозди забивать всем тяжелым инструментом, что мы ему дадим. И отправлять любую корреспонденцию, какую мы ему вручим, если на ней стоит адрес и ФИО получателя. И не материться, что она не того типа.
                                                  +1
                                                  Ага, говорите «забей гвоздь дядя Вася». Он берет микроскоп, и хреначит. Не потому что надо, а потому, что все время он доставал из коробки молотки, а тут как-то там появился микроскоп (кто-то в стороне накосячил). И что бы вы думали, дядя Вася не думая расхреначит к хренам микроскопом, или еще лучше, промычит что-то типа «а я не нашел рукоятки и не стал ничего делать», гвоздь останется не забитым, и мы потом будем еще веселиться по поводу того почему каждый 17-ый гвоздь не забит, и микроскопы все разбиты.
                                                    0
                                                    Это уже беда другого характера — это отсутствие проверки входных данных. Тоже черта плохого дяди Васи.

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

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

                                                    Или даже то — что сделало из обезьяны человека.

                                                    ps: Хотя как это оно так сделало?
                                                      0
                                                      Это вы к чему?
                                                        0
                                                        К тому, что надо в случае конфликта конкретики переходить на абстракцию.
                                                          0
                                                          А где вы нашли «конфликт конкретики»? Кстати, что это такое?
                                                            0
                                                            Молоток — конкретика1.
                                                            Микроскоп — конкретика2.

                                                            Конфликт конкретик — ошибка типа.

                                                            Что такое интерфейсы мы типа не в курсе. Да в курсе мы! Просто подлая лень нас вечно побеждает. Да или не да? :)
                                                              0
                                                              А-а-а. Ви в ентом смисле (с) не вспомню под вечер и на больную голову, из какого фильма

                                                              Абстракция — наше все.

                                                              Кстати, вспомнил еще кое-что. В PHP еще с 4-ой его версии была хорошая рекомендация вот в этой книжке.

                                                              Суть рекомендации сводилась к следующему:
                                                              1. документировать
                                                              2. проверять входные данные на соответствие задаче
                                                              3. выдавать адекватные сообщения при их несоответствии
                                                              4. наслаждаться прелестями отсутствия типизации
                                                                0
                                                                Эта рекомендация — то же самое, что типизация, только средствами ПХП4.

                                                                Цель та же самая, но типизация — это не велосипед.
                                                                  0
                                                                  Не сказал бы, что то же самое.
                                                                  Отличия:
                                                                  1. на входе все еще слабо типизированные данные, которые могут нести дополнительную информацию, может, этой самой функции и не нужные, но нужные где-то еще.
                                                                  2. входные данные, если они имеют дополнительную информацию, могут задействовать дополнительные операции внутри функции.
                                                                  3. программист, руководствующийся этой рекомендацией, не забывает о дополнительных проверках, которые компилятор просто не в силах осуществить.
                                                                    0
                                                                    Для того, чтобы что-то умело нести что-то нужное где-то еще придумали интерфейсы.

                                                                    interface bla {
                                                                        method foo()
                                                                    }
                                                                    
                                                                    class bar impl bla {
                                                                       metod foo() {...} // and million other info
                                                                    }
                                                                    
                                                    0
                                                    Массив — упорядоченный набор данных, для хранения данных одного типа, идентифицируемых с помощью одного или нескольких индексов. вики

                                                    Фраза «заменить массив объектом» для не-пэхапэшника звучит несколько дико
                                                      –1
                                                      А зачем использовать PHP, если для ваших нужд необходима статическая типизация? Динамическая типизация — это гибкость и скорость разработки. Превращая PHP в Java вы растеряете все преимущества скриптового языка, при этом не получив преимуществ компилируемых (как например отловка ошибок еще перед тем как вы запустили сам проект или во время компиляции).
                                                        +1
                                                        Со статической типизацией в IDE хорошо работается. Это скорость и восполнение забывчивости.
                                                        В остальном, да, блажь.
                                                          +1
                                                          Это суровая правда жизни. Желая иметь стабильный софт, но и находить разработчиков которые смогут его поддерживать, приходится писать на строго(иногда почти-строго) типизированном php.
                                                            –1
                                                            Стабильность софта можно обеспечить тестированием. Необязательно превращать PHP в строго типизированный язык. Ну и Java с .Net никто не отменял. А разработчиков, способных писать хороший код, искать нужно и для PHP.
                                                              0
                                                              Я просто говорю о реалиях, за год мы нашли 4 нормальных php разработчика, и только одного хорошего java разработчика (у нас есть и php и java).
                                                              .Net это дорогая инфраструктура, да и ide не дешевые.
                                                              Тесты это выход, но он куда более трудозатратен чем строгая типизация в коде.
                                                              Все таки аренда серверов сейчас уже дешевле чем неделя команды даже из 10 человек.
                                                                0
                                                                Тесты это выход, но он куда более трудозатратен чем строгая типизация в коде.

                                                                Это Вам ваши PHP'шники нашептали?
                                                                  0
                                                                  Это реалии рынка — «добро пожаловать в реальность» (с)

                                                                  ПХП — более свободная платформа в отличие от явы (оракл) и сишарпа (мс).

                                                                  Хотя и ява и оракл лучше пхп в плане того, что они на след. ступеньке развития (статичны, компиляемы и переносимы).
                                                                    0
                                                                    Прошу не трогать наших PHP-шников. Вы ничего не знаете о их способностях, и предвзятое отношение здесь не уместно.

                                                                    Но Вы правда считаете, что покрытие веб сайта с бизнес-логикой тестами менее трудо-затратно по часам чем использование классов в php для бизнес-логики?

                                                                    Даже для небольших и молодых проектов я сомневаюсь что можно такое утверждать.

                                                                    Ну и я говорю 300+ мегабайтах кода, трех тысячах тикетов в год (около 30 небольших релизов и 3 крупных).

                                                                    Backend покрыт тестами, но он не настолько часто меняет поведение.
                                                                      0
                                                                      FYI, это была добрая ирония.
                                                                      Не хочу вдаваться в холивар. Каждый разрабатывает так, как того требует архитектура и определения «правильно/неправильно» можно давать только относительно каждого проекта/задачи, но не в рамках всего языка.
                                                                        0
                                                                        FYI, это была добрая ирония.

                                                                        Тогда прошу прощения.

                                                                        На самом деле я люблю тесты. И на самом деле мы пытаемся их внедрять, потому как сложные части таки необходимо покрывать тестами.
                                                                        Но получается покрывать далеко не все.
                                                            0
                                                            Во второй симфони есть очень интересный компонент, который позволяет валидировать массивы параметров:
                                                            github.com/symfony/OptionsResolver
                                                              –1
                                                              Фишка не в валидации, а в IDE`шке.

                                                              + такой велосипед на то, что встроено не есть хорошо. Хотя возможно в какой-то ситуации хорош.
                                                                0
                                                                Нет, фишка как раз таки в валидации. Компонент позволяет не создавать на каждый чих value-object в параметрами + ещё имеет огромную кучу плюх. Посмотрите пример в документации хотя бы
                                                                github.com/symfony/OptionsResolver/blob/master/README.md
                                                                  –1
                                                                  Я имею в виду фишка призыва к объектам — больше пользы в code-completeion`е.

                                                                  Код пишется один раз, а читается, обновляется тыщщи.

                                                                  + и там и там нам надо будет код писать — что новый класс, что новый конфиг опций.

                                                                  Создать файл нового класса — это не всегда страшно :) Да и пусть они лежат где-нить в одном месте, чтобы зайти туда и сделать Ctrl+C / Ctrl+V.

                                                                  Я тоже иногда ленюсь классы делать, но тут уж не инструменты, а воля больше должна работать.
                                                                    0
                                                                    А ещё есть такие моменты как:
                                                                    1. Валидация значений (например поле gender — только 'male' или 'female')
                                                                    2. Динамические параметры по умолчанию — вот это самый главный плюс компонента. Это когда если один из параметров не передан явно, то на основе других параметров можно вычислить его значение.

                                                                    Эти штуки идут из коробки.
                                                                      +1
                                                                      Я не говорю, что данный компонент — не то, я говорю, что применение данного компонента для целей, упомянутых в статье — не то.

                                                                      <?php
                                                                      
                                                                      class Foo {
                                                                      
                                                                      /**
                                                                       * @var string Some thing
                                                                       */
                                                                      private $_someThing = 'bar';
                                                                      
                                                                      }
                                                                      


                                                                      ps: Простите, не удержался :)
                                                              0
                                                              Хочу подметить (как однажды подмечал уж), что все три языка, наиболее популярные в сайтостроении (JavaScript, PHP, а до PHP — Perl), имеют динамическую слабую неявную типизацию.

                                                              Стало быть, что-то в этом есть, не правда ли?
                                                                0
                                                                Если это есть — не значит, что это хорошо. Сигареты вон везде продаются, а от курения люди умирают.
                                                                  0
                                                                  Перефразируя: зимой выпадает снег, но это не значит, что это хорошо.
                                                                  Это просто есть. И раз уж это один из слонов, на которых стоит PHP, то зачем под него засовывать дополнительные костыли?
                                                                    0
                                                                    Снег — это прекрасно. Снежки, горные лыжи и снеговики за него. А уж дед мороз без него к нам не доедет.

                                                                    Однако вернёмся к нашим боратам — если что-то просто есть и это слон, то не значит, что стоя на слоне мы делаем хорошо. На слоне надо ездить и желательно в повозке с опахалом.

                                                                    Вот эта повозка и есть.

                                                                    Сравните просто лошадь и лошадь с седлом — какая лучше?

                                                                    Седло !== Костыли.
                                                                      0
                                                                      Седло от лошади не совместимо со слоном.

                                                                      P.S. Я не говорю, что статическая типизация — плохо. Просто возникают сомнения в адекватности подхода, описанного в статье.
                                                                      Мне почему-то для контроля типов достаточно PHPDoc и строгой проверки там, где это критично.
                                                                        +2
                                                                        Методу человек.СидетьНаСедле(АбстрактноеСедло $седло) это пофигу, т.к. используется такое понятие, как абстракция.

                                                                        Слон вааще с лошадью не совместимы и че?
                                                                  0
                                                                  Сам PHP развивается все больше в сторону OOP, предлагая все больше инструментов для этого. В этом тоже что-то есть. Да и в JavaScript прекрасно можно работать с объектами, что зачастую сильно упрощает работу с ним.

                                                                  С автором могу только согласиться, что видишь PHP немного по-другому, поработав со строго типизированными языками типа Java, C#, Obj-C.
                                                                    0
                                                                    >> Стало быть, что-то в этом есть, не правда ли?
                                                                    Думаю, что низкий порог вхождения и большая скорость разработки и делает привлекательным именно этот подход.

                                                                    Однако как многие профессионалы в своё время отказались от Windows и пересели на более надёжный и предсказуемый Linux. Так эти же люди со временем предпочтут более строгий язык, но чуть надёжный язык.
                                                                    –1
                                                                    Разрабатывая плагины под Livestreet столкнулся и взял на вооружение следующую практику. Имя каждой переменной начинается с буквы, которая обозначает тип данных, которые она содержит
                                                                    Примеры
                                                                    $sName = 'String data';
                                                                    $oUser = new User();
                                                                    $aSettings = array();
                                                                    $iNum = 5;
                                                                    $bErrors = false;
                                                                    


                                                                    Если в переменной могут хранится данные нескольких типов, то ставим m (mixed)
                                                                    $mData = "string";
                                                                    $mData = false;
                                                                    
                                                                      +1
                                                                      И имя ей венгерская нотация. Практически не имеет смысла в современных IDE.
                                                                        0
                                                                        а почему смысла не имеет? Даже передовик PHPstorm далеко не всегда знает что будет в переменной
                                                                        Например:
                                                                        $oHelper = HelperDecorator::getHelper('HelperName');
                                                                        

                                                                        Автокомплита для $oObject ожидать не стоит
                                                                          0
                                                                          Не знаю как PHPStorm, но если вы укажете в PHPDoc тип возвращаемого значения для getHelper, то автокомплит в Eclipse PDT у вас будет.
                                                                            +1
                                                                            Да. А если вдруг тип возвращаемого значения динамический, то можно непосредственно в перед вызовом написать докблок var ClassName
                                                                            0
                                                                            Потому, что потом вы решите поменять тип, и дальше сами понимаете.
                                                                            Тут как с передачей интерфейсов вместо классов.
                                                                        +2
                                                                        Мартин Фаулер, Рефакторинг, замена массива объектом.

                                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                        Самое читаемое