Пользователь
0,0
рейтинг
23 июля 2014 в 12:15

Разработка → Руководство по собеседованию на вакансию PHP-программиста из песочницы

PHP*
Вездесущий… это определённо то слово, которым можно описать язык PHP по отношению к вебу. Он действительно вездесущ. В настоящее время, среди всех серверных языков программирования, PHP используется наиболее широко. На нём написаны более 80% сайтов, при этом следующий по популярности ASP.NET со своими 17% остаётся далеко позади.



Почему? Что позволило PHP стать настолько популярным и широко используемым? На этот вопрос нет однозначного ответа, но простота использования языка, безусловно, существенный фактор, поспособствовавший его успеху. Новички в PHP могут быстро выйти на достаточный уровень и разместить динамический контент на своих веб-сайтах с минимум познаний в программировании.

Именно в этом и заключается основная проблема поиска высококвалифицированных PHP-разработчиков. Относительно низкий порог вхождения и 20 лет развития языка привели к тому, что PHP-программисты стали такими же вездесущими, как и сам PHP. Многие из них могут на законных основания утверждать, что «знают» язык. Однако разработчики, которые действительно являются экспертами по PHP, способны создавать куда более функциональное, надёжное, масштабируемое и простое в сопровождении программное обеспечение.

Как же отличить тех, кто обладает реальной компетентностью в PHP (не говоря уже о тех, кто входит в 1% наиболее опытных кандидатов) от тех, у кого только поверхностные знания?

От переводчика: данный текст представляет собой перевод и адаптацию с небольшими дополнениями статьи The Insider's Guide to PHP Interviewing, ссылка на которую была опубликована в последнем июньском дайджесте по PHP. Я постарался максимально передать смысл оригинала, добавил несколько пояснений, значительно увеличил количество ссылок на документацию, а также добавил ссылки на статьи с хабра и на другие источники. Важное отличие от оригинала: добавлена навигация по вопросам, часть кода и текста скрыта под спойлеры.

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

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

Список вопросов


  1. Расскажите о замыканиях в PHP. Приведите примеры, когда, почему и как они могут быть использованы?
  2. Объясните, с какой целью и как используется ключевое слово global. Приведите пример, когда его применение целесообразно и когда нет.
  3. Расскажите о пространствах имён в PHP и о том, почему они полезны.
  4. Что такое типажи (traits, трейты)? Опишите их основные характеристики и расскажите, чем они полезны. Приведите пример объявления типажа и класса, использующего несколько типажей.
  5. Расскажите, как связаны между собой php://input и $_POST и как получить доступ к потоку php://input?
  6. Назовите не менее пяти суперглобальных переменных, имена которых начинаются с $_, и дайте им определение. Расскажите об их связи с переменной $GLOBALS.
  7. Объясните назначение и применение магических методов __get, __set, __isset, __unset, __call, и __callStatic. Когда, как и почему их стоит использовать (или не использовать)?
  8. Опишите несколько структур данных из стандартной библиотеки PHP (SPL). Приведите примеры использования.
  9. Чему будет равен $x после выполнения выражения $x = 3 + "15%" + "$25"?
  10. Объясните назначение ключевого слова static при вызове метода или обращении к свойству. Расскажите, когда и зачем его нужно использовать, а также чем оно отличается от ключевого слова self. Приведите пример.
  11. Расскажите о внутреннем устройстве массивов в PHP?
  12. В чем различие между ArrayAccess и ArrayObject?
  13. Что такое генераторы? Когда их можно использовать вместо итераторов и обычных массивов? Для чего нужны ключевые слова yield и send?
  14. Перечислите ключевые отличия между версиями PHP 5.3, 5.4, 5.5.


Ключевые концепции и парадигмы


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

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

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

Первый строительный блок для замыкания – это анонимные (лямбда) функции, т.е. такие функции, у которых нет ассоциированных с ними имён. Например:

// 2-ой аргумент для array_walk - анонимная функция
array_walk($array, function($dog) {
    echo $dog->bark();
}); 

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

// объявляем анонимную функции и ассоциируем её
// с переменной $dogs_bark
$dogs_bark = function($dog) {  
    echo $dog->bark();
}
array_walk($array, $dogs_bark);

Внутреннее устройство замыканий в PHP представлено специальным классом замыканий – Closure.
Содержимое анонимной функции существует в своей области видимости независимо от области видимости, в которой эта функция была создана. Тем не менее, можно явно связать одну или несколько переменных из внешней области видимости, на которые можно будет сослаться в области видимости анонимной функции. Для этого нужно воспользоваться конструкцией use при определении анонимной функции.

Пример кода
class Dog {
    public function bark() { echo 'woof'; }
}

$dogs_bark = function($dog) use (&$collar) { // связываем по ссылке
    if ($collar == 'fitsWell'){
         echo $dog->bark(); // 'woof'
    } else {
         echo 'no bark';    // ошейник ($collar) слишком туго затянут
    }
};

$dog = new Dog;

$collar = 'fitsWell'; // внешняя переменная
$dogs_bark($dog); // 'woof'

$collar = 'tight';
$dogs_bark($dog); // 'no bark'


Способность получить доступ ко внешним переменным в пределах замыкания особенно полезна при использовании функций высшего порядка. Возьмём, например, функцию array_walk($array, $calback), которая, как и другие подобные функции, позволяет обойти переданный ей набор переменных и обработать их специфическим способом. Эта функция обходит массив $array и на каждой итерации вызывает анонимную функцию ($callback), передавая ей только значение текущего элемента и его ключ. Поэтому использовать переменную $collar без замыкания и конструкции use не получится. Конечно, мы можем воспользоваться ключевым словом global, но это приведёт к бессмысленному засорению глобального пространства имён переменной, которая нужна только в данном конкретном контексте.
Замыкания обладают дополнительными объектно-ориентированными возможностями. Начиная с версии PHP 5.4, в интерфейсе класса Closure появились новые методы: bind() и bindTo(), которые можно использоваться для привязки новых объектов к замыканию. Например:

Closure::bindTo($newthis, $newscope); 

Этот метод дублирует замыкание и связывает его область видимости с новым объектом таким образом, что внутри замыкания переменная $this станет ссылкой на $newthis в объектном контексте. Давайте изменим функцию $dogs_bark так, чтобы она использовала переменную $this, а затем привяжем её к объекту $dog.

// объявляем замыкание, но не связываем его с объектом
$dogs_bark = function() {
    echo $this->sound;  // где sound - свойство объекта $this
};
$new_dog = new Dog();
// создаем новое замыкание и привязываем его к объекту $new_dog
$new_closure = $dogs_bark->bindTo($new_dog);
$new_closure();   // выводит значение свойства $sound

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

$dog = new Dog();
$dog->closure = $dogs_bark;
$dog->closure();

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

Вопрос: Объясните, с какой целью и как используется ключевое слово global. Приведите пример, когда его применение целесообразно и когда нет.

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

Рассмотрим простой пример:
class Dog {
    function bark() {
        global $sounds;
        return $sounds->bark();
    }
} 


Код демонстрирует появление скрытой зависимости класса Dog от глобальной переменной $sounds. Конечно, бывают случаи, когда это оправданно (допустим, в системе существует единственный набор звуков, который вообще никогда не изменяется), но гораздо лучше явно передать $sounds объекту класса Dog в конструкторе, и хранить и использовать его в пределах этого экземпляра:
Пример кода
class Dog {
    protected $sounds; 
    function __construct($sounds) {
        $this->sounds = $sounds;
    }

    public function bark() {
        return $this->getSounds()->bark();
    }

    public function getSounds() {
        return $this->sounds;
    }
}


Но, как часто бывает в программировании, никогда не говори никогда. Существует ряд надёжных и стабильных продуктов, написанных на PHP, которые активно пользуются глобальными переменными: Wordpress, на котором работает около 20% всех сайтов (и это число растёт из года в год), Joomla! 1.5 (да, она до сих пор еще широко распространена) и iLance Enterprise Auction system – это все примеры успешных проектов, в течение многих лет использующих глобальные переменные. Точно так же и у вас могут возникнуть ситуации, в которых глобальные переменные будут уместны, однако к их применению нужно подходить осторожно.
К списку вопросов

Вопрос: Расскажите о пространствах имён в PHP и о том, почему они полезны.

Одно из объектно-ориентированных нововведений в PHP 5.3 – пространства имён. Как следует из названия, пространство имён определяет такую область видимости в программе, внутри которой имена классов, интерфейсов, функций, переменных и констант не будут вызывать конфликтов с элементами, обладающими такими же именами в другом пространстве имён.

До PHP 5.3, классам часто давали длинные имена, чтобы отразить в их названии структуру пакетов и избежать конфликтов имён. Предположим, что у нас есть класс Dog, определенный в модели приложения Dog_Pound. Без пространства имён полное имя класса выглядело бы следующим образом:

class Dog_Pound_Model_Dogs { // ух, попробуй выговори
    function getDogs();
}

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

namespace dog_pound\model;  // указываем текущее пространство имён
class Dogs {   // объявляем только "Dogs" в dog_pound\model
    function getDogs();
}
$dogs = new Dogs; // класс создаваемого объекта определён однозначно,
// т.к. в текущем пространстве имён он один имеет такое имя

Также поддерживаются относительные ссылки на пространства имён. Например, когда вы находитесь в пространстве dog_pound, вы сможете создать экземпляр класса Dogs вот таким образом:

$dogs = new model\Dogs  // только одна "model" определена в dog_pound

Кроме того, элементы из одного пространства имён могут быть импортированы в другое и использоваться в нём непосредственно (т.е. без ссылок на исходное пространство имён). Этого можно добиться с помощью ключевого слова use:

namespace this\new\namespace;  // текущее пространство имён

// импортируем Dogs из пространства dog_pound\model
use dog_pound\model\Dogs;

$dogs = new Dogs;

К списку вопросов

Вопрос: Что такое типажи (traits, трейты)? Опишите их основные характеристики и расскажите, чем они полезны. Приведите пример объявления типажа и класса, использующего несколько типажей.

Типажи – это великолепное дополнение, появившееся в PHP 5.4, позволяющее добавлять классу поведение без необходимости расширения родительского класса через наследование (до версии 5.4 это можно было реализовать с помощью паттерна примесь). Важно отметить, что в контексте одного класса можно использовать несколько типажей. Их применение способствует улучшению организации кода и разделению обязанностей, а также соответствует известному принципу проектирования, гласящему: «предпочитайте композицию наследованию».

Пара примеров объявления типажей:
trait Movement {
    public function topSpeed() {
        $this->speed = 100;
        echo "Running at 100 %!" . PHP_EOL;
    }
    public function stop() {
        $this->speed = 0;
        echo "Stopped moving!" . PHP_EOL;
    }
}

trait Speak {
    public function makeSound(){
        echo $this->sound . PHP_EOL; 
    }
}


Теперь воспользуемся ключевым словом use, но не для импорта пространства имён, а для включение типажей в определение класса:
Пример кода
class Dog {
    use Movement, Speak;  // теперь у класса Dog есть функционал из этих типажей
    protected $sound;
    
    function __construct() {
        $this->sound = 'bark';
    }
}

$dog = new Dog();
$dog->topSpeed();  // из типажа Movement
$dog->stop();      // из типажа Movement
$dog->makeSound(); // из типажа Speak


К списку вопросов

PHP на пальцах


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

Вопрос: Расскажите, как связаны между собой php://input и $_POST и как получить доступ к потоку php://input?

Говоря простым языком, $_POST – это суперглобальный массив, представляющий проанализированное и отформатированное тело запроса, отправленного на сервер методом post.
Пример текста запроса
POST /php-hiring-guide/php-post.php HTTP/1.1
Host: toptal.com
Referer: http:///toptal.php/php-hiring-guide/php.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 63
article_name=PHP Hiring Guide&tag_line=You are hired!&action=Submit


Доступ к телу запроса можно получить через входной поток таким же образом, как читаются любые файлы:
$input = file_get_contents("php://input"); 

К списку вопросов

Вопрос: Назовите не менее пяти суперглобальных переменных, имена которых начинаются с $_, и дайте им определение. Расскажите об их связи с переменной $GLOBALS.

Список суперглобальных переменных
$_POST – ассоциативный массив, содержащий пары ключ-значение, отправленные на сервер методом post.
$_GET – ассоциативный массив, содержащий пары ключ-значение, отправленные на сервер методом get.
$_REQUEST – объединение пар ключ-значение из $_POST и $_GET.
$_SERVER – представляет параметры веб-сервера, имеющие отношение к выполнению программы.
$_ENV – содержит значения, связанные с сервером (хостом) и его конфигурацией.
$_SESSION – ассоциативный массив, хранящий значения переменных сессии между переходами по страницам и запусками приложения.
$_COOKIE – предоставляет доступ к переменным, сохраненным в кукисах на клиенте.
$_FILES – специальный массив для входных данных, полученных при отправке на сервер файлов методом post.

Суперглобальная переменная $GLOBALS – родственник ключевого слова global. В массиве $GLOBALS хранятся все переменные, доступные в глобальной области видимости, в том числе и суперглобальные массивы. Например, доступ к $_ENV можно получить вот таким образом: $GLOBALS['_ENV'];
К списку вопросов

Вопрос: Объясните назначение и применение магических методов __get, __set, __isset, __unset, __call, и __callStatic. Когда, как и почему их стоит использовать (или не использовать)?

Первые четыре метода в нашем списке используются для перегрузки свойств объекта. Они позволяют определить, каким образом будет взаимодействовать внешний мир со свойствами, объявленными с модификатором видимости private или protected, либо вообще отсутствующими у объекта.

Пример кода
// свойство 'whiskers' не объявлено в классе Dog,
// но обрабатывается методом __get:
function __get($name) {
    if ($name == 'whiskers') {
        // создаем единственный whiskersService для экземпляра
        if (! isset($this->whiskersService)) {
            $this->whiskersService = new Whiskers($this);          
        }
        // при обращении к 'whiskers' будет выполнена загрузка whiskersService
        return  $this->whiskersService->load();
    }
}


Теперь можно просто обратиться к свойству 'whiskers':

$hairs = $dog->whiskers;


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

Пример кода
function __set($name, $value) {
    if ($name == 'whiskers') {
        if ($value instanceOf Whisker) {
            $this->whiskersService->setData($value);
            return $this->whiskersService->getData();
        } else {
            throw new WhiskerException("That's not a whisker");
            return false;
        }
    }       
}


Изменение значения свойства 'whiskers' будет выглядеть так:

$dog->whiskers = $hairs; 

Это выражение приведет к автоматическому вызову метода __set() с передачей ему в качестве первого параметра имени свойства ('whiskers') и правой части оператора присвоения в качестве второго.

Наконец, __isset и __unset завершают квартет методов для перегрузки свойств. Каждый из них принимает только один параметр – имя свойства, и вызываются при выполнении операций isset() и unset() над этим свойством.
Пример кода
function __isset($name) {
    if ($name == 'whiskers') {
        return (bool) is_object($this->whiskersService) &&
                                !empty($this->whiskersService->getData());
    }
}
function __unset($name) {
    if ($name == 'whiskers' && is_object($this->whiskersService)) {
        // мы не хотим полностью останавливать сервис
        $this->whiskersService->reset();
    }
}


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

Прим. переводчика: __call вызывается при обращении к члену класса в контексте объекта, а __callStatic при обращении к статическому члену класса. В качестве аргументов, они оба принимают имя $name вызываемого метода и массив $args с переданными методу $name параметрами.

Например, вот такая реализация __call приведет к тому, что при обращении к любому «невидимому» методу будет выполнена полезная нагрузка на whiskersService:

public function __call($method, $args) {
    return $this->whiskersService->load();
}

Метод __callStatic работает таким же образом и принимает те же аргументы, однако он вызывается при обращении к «невидимому» методу не в контексте объекта, а в статическом контексте. Если мы определим этот метод, то получим возможность обрабатывать вызовы, подобные ClassName::notVisibleMethod().

public function __callStatic($method, $args) {
    if (!is_object(static::$whiskersService)) {
        static::$whiskersService = new Whiskers(__CLASS__);
    }
    return static::$whiskersService->load(); 
}

$hairs = Dog::whiskers();

В приведённых выше примерах мы инкапсулировали реализацию whiskers от внешнего мира, сделав его единственным объектом, доступным таким способом. Клиенты определенных нами методов и свойств ничего не знают о базовом классе whiskersService и о том, как вообще Dog хранит свои данные.

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

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

Вопрос: Опишите несколько структур данных из стандартной библиотеки PHP (SPL). Приведите примеры использования.

Процесс разработки на PHP по большей части связан с получением и обработкой данных из разных источников, таких как базы данных, локальные файлы, удаленные API и т.д. Разработчики тратят очень много времени на организацию данных, их получение, перемещение и обработку. Наиболее используемая структура для представления данных на PHP – это массив. Однако в некоторых случаях массивы не годятся для решения задач из-за недостаточной производительности и избыточного потребления памяти, и поэтому требуются более подходящие структуры данных.

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

Применение стандартной библиотеки PHP (Standard PHP Library, SPL) и знание её состава – та область, владение которой может подтвердить компетентность PHP-разработчика. Если у кандидата имеется приличный опыт работы с SPL, то, скорее всего, он сможет успешно работать над вашим приложением, вне зависимости от используемых в вашем окружении фреймворков.

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

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

SplQueue и SplStack очень похожи на SplDoublyLinkedList. Обе эти структуры, по сути, представляют собой двусвязные списки с разными флагами итераторов(IT_MODE_LIFO – Last In First Out – последним пришёл, первым ушёл; и IT_MODE_FIFO – First In First Out – первым пришёл, первым ушёл), которые регулируют порядок обработки узлов и что делать с этими элементами после того, как они будут обработаны. Ещё одно отличие между этими структурами заключается в том, что интерфейс SplQueue содержит более интуитивно понятные методы enqueue() и dequeue() в отличие от методов push() и pop() у SplStack.

SplHeap – куча, представленная в виде бинарного дерева, каждый узел которого имеет не более двух дочерних узлов. Это абстрактный класс, требующий расширения с определением метода compare(), позволяющего выполнять сортировку в реальном времени при вставке новых узлов в дерево.

SplMaxHeap и SplMinHeap — конкретные реализации абстрактного класса SplHeap. SplMaxHeap реализует метод compare() таким образом, чтобы дерево было отсортировано в порядке убывания значений узлов, а SplMinHeap – в порядке возрастания значений.

SplPriorityQueue – очередь, похожая на SplHeap, но в отличие от SplHeap сортировка осуществляется на основании значения свойства priority (приоритет), заданного для каждого узла.

SplFixedArray – массив фиксированной длины, индексами которого могут быть только целые числа. Эти ограничению обеспечивают более высокую скорость обработки массива, которая достигается, в том числе, благодаря тому, что в SplFixedArray нет хеширования ключей элементов при их добавлении (в отличие от обычных массивов).

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

Также о структурах данных из SPL можно почитать на хабре.
К списку вопросов

Вопрос: Чему будет равен $x после выполнения выражения $x = 3 + "15%" + "$25"?

Правильный ответ – 18. Давайте разберёмся почему.

PHP поддерживает автоматическое приведение типов, основанное на контексте, в котором используется переменная.

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

Зная это, мы можем увидеть, что в выражении $x = 3 + "15%" + "$25" строка "15%" будет соответствовать числу 15, а строка "$25" — нулю. Поэтому результат выражения будет равен 18 (3 + 15 + 0).

Одни программисты считают автоматическое приведение типов ценной особенностью, другие – отвратительным недостатком PHP. С одной стороны, осознанное и правильное его применение добавляет удобства разработчику (нет необходимости писать код для конвертации «15» в 15 перед применением арифметической операции). С другой стороны, оно может легко привести к трудноулавливаемым ошибкам, если будет задействовано случайно (т.к. не будет выдано никаких предупреждений и ошибок).
К списку вопросов

Вопрос: Объясните назначение ключевого слова static при вызове метода или обращении к свойству. Расскажите, когда и зачем его нужно использовать, а также чем оно отличается от ключевого слова self. Приведите пример.

В PHP, начиная с версии 5.3, реализовано позднее статическое связывание. Термин «позднее связывание» означает, что ключевое слово static связывает метод или свойство (к которому происходит обращение через static::) не с тем классом, в котором был определён использующий его метод, а с тем, в котором этот метод был вызван во время выполнения. Кстати, название «статическое связывание» не совсем корректно, т.к. static допустимо использовать не только для обращения к статическим членам класса.

Пример кода
class Dog {
    static $whoami = 'собака';
    static $sound = 'лает';

    function makeSounds() {
        echo self::makeSound() . ', ';
        echo static::makeSound() . PHP_EOL;
    }

    function makeSound() {
        echo static::$whoami . ' ' . static::$sound;
    }
}
Dog::makeSounds();


Результат выполнения этого кода не удивляет: «собака лает, собака лает».
Расширим пример:

class Puppy extends Dog {
    static $whoami = 'щенок';
    static $sound = 'гавкает';

    function makeSound(){
        echo static::$whoami . ' скулит';
    }
}

Puppy::makeSounds();

Вы можете удивиться, но результат выполнения этого кода: «щенок гавкает, щенок скулит». Чтобы понять, почему это происходит, давайте снова посмотрим на метод makeSounds() в классе Dog.

В методе makeSounds() сначала вызывается self::makeSound(). self:: всегда указывает на контекст того класса, в котором происходит обращение через него (в данном случае это класс Dog). Поэтому self::makeSound() всегда будет приводить к версии метода makeSound() из класса Dog. Это справедливо и в том случае, когда происходит вызов Puppy::makeSounds(), т.к. в классе Puppy нет собственного метода makeSounds(), и поэтому вызывается метод makeSounds() из класса Dog. В результате, при вызове Puppy::makeSounds() выполняется self::makeSound(), что приводит к появлению текста «щенок гавкает».

Затем в методе makeSounds() класса Dog вызывается static::makeSound(). static:: обрабатывается иначе, чем self::. При обращении через static:: используется версия метода, определённая в контексте вызвавшего этот метод класса во время выполнения (в данном случае, это класс Puppy). Следовательно, вызов static::makeSound() приводит к выполнению версии метода makeSound() из класса Puppy. Именно по этой причине при вызове из Puppy::makeSounds() метода static::makeSound() выводится текст «щенок скулит».

К списку вопросов

Под капотом PHP


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

Вопрос: Расскажите о внутреннем устройстве массивов в PHP?

С массивами связана значительная часть процесса разработки на PHP. Они хорошо подходят для тех случаев, когда вам необходима итерируемая структура данных. Изнутри массивы представлены, как и многие другие структуры данных, в виде хэш-таблиц. PHP написан на C, в котором нет ассоциативных массивов – массивы в C могут иметь только целочисленные индексы. Для трансляции индексов PHP-массива в целочисленные индексы массива на C применяется хэш-функция, преобразующая индексы PHP-массива (и целочисленные, и строковые) в целые числа. Значения элементов массива располагаются в получившейся хеш-таблице.
Сложность процесс поиска элементов по хэш-ключам составляет O(1) (в нотации большое О) благодаря тому, что для нахождения элемента не требуется итерация по хешу (хеш-функция возвращает значение, точно указывающее на положении элемента в таблице).

Прим. переводчика: Здесь я сознательно исключил из текста понятие бакета (bucket, корзина), т.к. автор оригинального текста не даёт никаких пояснений на его счет. Если вам действительно интересно устройство хеш-таблиц, то рекомендую к прочтению вот этот материал.

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

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

Вопрос: В чем различие между ArrayAccess и ArrayObject?

ArrayAccess – это просто интерфейс, требующий определения следующих методов: offsetGet, offsetSet, offsetExists, и offsetUnset.

ArrayObject – класс, реализующий интерфейс ArrayAccess. Удобство ArrayObject заключается в том, что к его свойствам можно получать доступ таким же образом, как к обычному массиву через оператор [], например: $dogs['sound'], или через метод offsetGet('имя свойства'), например: $dogs->offsetGet('sound').
Подробнее об использовании объектов как массивов.
К списку вопросов

Вопрос: Что такое генераторы? Когда их можно использовать вместо итераторов и обычных массивов? Для чего нужны ключевые слова yield и send?

Генератор внешне похож на обычную функцию, и обладает некоторым сходством с замыканиями. При первом вызове в контексте итератора (например, в цикле foreach), генератор возвращает объекта класса Generator, который, в свою очередь, реализует интерфейс Iterator. Значения для итерации генерируются в реальном времени, что избавляет от необходимости загружать весь набор данных в память, получая значения по мере необходимости. Из этого следует, что итерация по результату осуществляется так же, как по массиву, не считая некоторых отличий:

  1. итерация направлена только вперед, нельзя вернуться к предыдущему значению или к началу. Вызов метода rewind() выбросит исключение;
  2. скорость выполнения итерации ниже, чем в случае с предварительно заполненным массивом, т.к. для получения следующего значения генератор выполняет какие-то действия;
  3. крайне эффективное использование памяти.


Невозможность возврата значения через оператор return – это одно из основных отличий генератора от других функций или замыканий. Вместо него для передачи значения из генератора применяется ключевое слово yield (например, yield $dog). Указание значения после return приведет к синтаксической ошибке, а применение пустого оператора – простой способ остановить работу генератора.

Оператор yield приостанавливает выполнение и возвращает текущее значение в контекст структуры, использующей генератор. Эта структура может отправить информацию обратно в генератор во время его выполнения с помощью метода send (например, $generator->send(-1);).

Пример кода с использованием генератора
Сначала объявим сам генератор:

function dog_generator() {
    foreach (range(0, 2) as $value) {
        // некоторый ложный источник данных
        $dog = DogHelper::getDogFromDataSource($value);
        // перехватываем input от send или используем $dog
        $input = (yield $dog);    
        if ($input == -1) {
            return; // останавливаем генератор
        }
    }
}

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

А теперь напишем код, использующий объявленный генератор:

// получаем экземпляр генератора и присваиваем его переменной
$generator = dog_generator();
foreach ($generator as $dog) { // $dog - это результат вызова yield
    echo $dog . PHP_EOL;
    // мы хотим найти терьера
    if ($dog == 'Terrier') {
        // отправляем входные данные генератору через yield
        $generator->send(-1);
        echo 'Мы нашли терьера, останавливаем просмотр';
    }
}

Если запустить этот код, то он прекратит своё выполнение после обнаружения терьера:

Далматинец
Терьер
Мы нашли терьера, останавливаем просмотр


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

Общая картина


Вопрос: Перечислите ключевые отличия между версиями PHP 5.3, 5.4, 5.5.

Версия PHP 5.3 была выпущена в июне 2009 года, но до сих пор широко распространена. Основные новшества: замыкания, лямбда-функции, пространства имён и позднее статическое связывание.

Релиз PHP 5.4 состоялся в марте 2012 года. В нём были представлены типажи (трейты), возможность объявления массива с коротким синтаксисом [] (например, ['шит-цу', 'ротвейлер']). Ко всему прочему, в этой версии была убрана опция register_globals.

PHP 5.5 был выпущен в июне 2013 года, и это текущий релиз. В этой версии появились генераторы (и связанное с ними ключевое слово yield), в инструкцию try/catch добавлен блок finally, на замену APC Cache пришел включённый по умолчанию OpCache (базируется на Zend Optimizer). Также был расширен синтаксис литералов массивов (['шит-цу', 'ротвейлер'][1] вернёт 'ротвейлер'), и добавлена удобная возможность обращения к символам в строке как к элементам массива (например, '6e5d4'[0] вернёт «6»).
К списку вопросов

Подведём итоги


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

Ссылка на оригинал
Юрий Горин @CoolWolf
карма
9,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (101)

  • +23
    Вопросы и ответы на них очень хорошие, вот только применять их к собеседованиям я бы не стал, слишком большой акцент на различия в версиях и фичи появившиеся в последних версиях. Опытный разработчик, у которого многие наработки уже сушествуют десяток лет может быть достаточно консервативен и сторонится использования недавно введенных функций, учитывая задержку обновления версий на популярных хостингах и т.п., при этом несмотря на игнорирование этих новшеств он может быть на порядок эффективнее в решении задач с точки зрения бизнеса.
    • +2
      Само-собой, можно не применять на практике многие возможности из последних версий, но знать о том, что происходит с языком, как он развивается, я думаю, полезно.
      Я отношусь к этим вопросам как к списку хороших тем для обсуждения.
    • +5
      Опытный разработчик, у которого многие наработки уже сушествуют десяток лет


      Мне кажется, эта фраза потеряла актуальность лет 5 назад. Сейчас во-первых, опытный разработчик скорее всего не будет работать с шаредами в принципе (коробочные продукты да — ориентированы как раз на шареды, но их относительно мало, и по большей части там сейчас не ведется активная разработка, да и не интересны они опытным). Во-вторых, если человек перестал развиваться, не изучает и не пользуется новыми фичами PHP (здесь особенно важно понимать, что после 5.2 каждая версия приносит пачку важных и нужных фич, которые обязательно надо использовать вместо тех костылей, что были до них), не интересен.
      • –1
        Да, сейчас есть движение в направлении всяких digitalocean, где проблемы типичных шаредов не столь актуальны, но по мне оно только начинается и уж точно говорить нельзя что оно успешно закончилось 5 лет назад.
        Что изучение новых фич должно интересовать тут вопросов нет, я абсолютно согласен с позицией автора, которую он обозначил в комменте выше — в качестве тем для обсуждения между разработчиками это отличные вопросы. Вот только «Руководство по собеседованию» это несколько другое, если дать такое руководство человеку слабо знаюшему PHP и послать его нанимать програмистов ничего хорошего не выйдет… или вы правда считаете, что человек услышав вопрос «Объясните назначение ключевого слова static» должен начать свой ответ с того, что «В PHP, начиная с версии 5.3, реализовано позднее статическое связывание. »?
        • 0
          Да по мойму как раз таки последние лет 5 все перебираются на VDS. Последние года 3-4 так точно. Я лично не знаю никого кто бы все еще сидел на шаредах.

          Что до static, в ответе определенно должно фигурировать «позднее статическое связывание». Ну и да, судя по характеру статьи, ориентирована она все же больше на тех кто хочет пройти собеседование, а не вести его.
  • +17
    Тема поиска PHP-программиста раскрыта. Тема поиска хорошего программиста — ­нет.
  • +2
    Не умаляя ценности статьи, хочу заметить, что бОльшая часть людей, называющих себя разработчиками на PHP, отсеивается на гораздо более простых вопросах, не доходя до трейтов, SPL и LSB.

    Вот мой топ вопросов-детекторов понимания духа и буквы PHP:

    1. Что такое «передача по ссылке» и «передача по значению», как эти термины связаны с понятием «область видимости», как в PHP передаются разные типы аргументов?
    2. Что такое «ссылка» в контексте «переменная-ссылка» в отличие от вопроса №1? Как вообще в PHP работают ссылки?
    3. Зачем в PHP отдельный оператор конкатенации строк, если JS прекрасно обходится оператором "+" и почему для разделения пространств имен был выбран "\", а не уже имевшийся символ "::"?
    4. Продемонстрируйте несколько (как можно больше) способов умножить все значения в массиве на 2, с рассказом об обоснованности применения каждого способа
    5. Зачем в PHP нужны интерфейсы, если есть абстрактные классы?

    Но за статью все равно спасибо, забрал в избранное.
    • –1
      А можно увидеть ответ на пятый вопрос именно в контексте php? Когда интерфейсы и уж тем более абстрактные классы реально необходимы (учитывая динамическую типизацию)?
      • +2
        Интерфейсы необходимы в нескольких случаях.

        1. Интерфейс — это контракт, который заключается с разработчиком класса. И этот контракт гарантирует, что разные классы из разных библиотек будут выполнять этот контракт и иметь одинаковый интерфейс (вот такая тавтология). Самый хороший для этого пример — github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md

        2. Для обхода отсутствия множественного наследования. Нельзя отнаследоваться от двух классов, но можно реализовать два интерфейса. Собственно в этом и есть принципиальная разница.

        Если на собеседовании будет отвечено примерно так, пусть даже своими словами — это зачёт.
        • –1
          Это понятно для статических языков, но вроде бы в PHP можно просто добавить классу нужные методы и все и так взлетит. Тот же JavaScript вроде бы с интерфейсами не заморачивается вообще.
          • +2
            JS порой с банальной логикой не заморачивается, а Вы про интерфейсы…

            На самом деле настоящее понимание того, зачем нужны интерфейсы, приходит при проектировании системы, чуть более сложной, чем личный бложег. И я допускаю, что программист, не занимающийся проектированием, вполне может до конца и не понимать. Но вот знать — обязан. Хотя бы потому, что ему придется часто разбираться с чужим кодом.
            • +1
              Еще раз поясню. Я не спорю с тем, что надо в принципе знать, что такое интерфейс. Я просто пока не понял, зачем он нужен как синтаксическая конструкция в динамическом PHP. Если объявить класс, реализующий некий интерфейс, а потом интерфейс убрать (а методы не трогать), то ничего не рассыпается. Либо есть случаи, когда не так, либо интерфейс как конструкция языка не нужен.
              • 0
                Как пример можно привести унификацию при разработки каких-либо драйверов. Тот же класс для кеширования. Можно использовать MemCache, APC, file based and etc. К примеру нужно реализовать еще один драйвер. Подключаем интерфейс с «правилами» и пишем драйвер «ничего не забыв»…
              • +5
                В PHP интерфейсы, как и классы, можно использовать для тайпхинтинга.
              • +1
                Я Вам выше привел пример с LoggerInterface. Чем не устраивает?

                Интерфейс, имхо, это не синтаксическое, но архитектурное понятие, воплощенное в синтаксисе языка.
              • 0
                как не рассыпется?
                если в коде подключается какой-либо драйвер или мейлер (мы точно не знаем, с чем именно будет работать код, а только знаем, что это нечто должно реализовывать некоторую логику, методы), то в нормальном коде обязательно проверяется if ($object instanceof Interface).
                если потом интерфейс убрать, то всё рассыпется
                • 0
                  Ну или проверяется через class hinting.
                  О чем, впрочем, уже писали выше.
              • 0
                Просто PHP пока не понял, в каком направлении он движется. Вроде бы язык динамической типизации, а есть тайпхинтинг для объектов и массивов. Плюс постоянно поднимается вопрос о тайпхинтинге для скаляров. Вся эта кухня только добавляет рассогласованности в и так довольно сумбурный язык (highstask, needle VS needle, highstask и strpos VS str_replace etc...).
              • 0
                Скорее интерфейс как конструкция языка обязывает разработчика реализовать все имеющиеся в нем методы. Необходимость в этом возникает, когда критична верная работа приложения. (Пусть лучше совсем не поднимется, чем отработает на половину).

                Пример
                Тест код:
                interface Animals
                {
                	public function tease();
                
                	public function play();
                }
                
                class Dog
                {
                	public function tease()
                	{
                		echo 'tease';
                	}
                }
                
                class Cat implements Animals
                {
                	public function tease()
                	{
                		echo 'tease';
                	}
                
                	public function play()
                	{
                		echo 'play';
                	}
                }
                
                class AnimalsController
                {
                	private $animal;
                
                	public function __construct(Animals $animal)
                	{
                		$this->animal = $animal;
                	}
                
                	public function Run()
                	{
                		$this->animal->tease();
                		$this->animal->play();
                	}
                }
                


                Все просто, в данном примере реализация класса AnimalsController обязывает любого разработчика работающего с кодом, в качестве животного передавать класс реализующий одноименный интерфейс (Animals).

                Таким образом написанный на коленке код класса собаки (не реализующий интерфейс) вызовет ошибку:
                PHP Catchable fatal error: Argument 1 passed to AnimalsController::__construct() must implement interface Animals, instance of Dog given

                А если разработчик пишет код к np++ и забыл описать реализацию одного из методов, но привязал интерфейс:
                class Dog extends Animals
                


                то все равно работать не будет:
                Fatal error: Class Dog cannot extend from interface Animals


                В примере конечно использование интерфейса излишне, но в реальных системах, с большой кодовой базой интерфейсы практически незаменимы
                • 0
                  Вот как раз не стоит для AnimalS использовать интерфейс. Не для того они придуманы были. Плюс именовать классы существительными во множественном числе не стоит.
      • +1
        Первый практический опыт интерфейсов можно почерпнуть из фреймворков.
        Автор пишет интерфейс для работы с кешом. Сообщество наследует этот интерфейс и пишет свои классы для сохранения кеша в файликах/мемкеше итд.
        Если очень грубо, то это документация, которая кинет эксепшн, если ты с ней не согласишься.
        Очень круто интерфейсы пригождаются при массовой разработке, чтобы уже работающие методы продолжили работать.
    • +7
      3. Вопрос для языковых гиков, не имеющий ни малейшего отношения к реалиям прикладной разработки. Или вы набираете людей в команду для разработки Zend Engine?

      5. Интерфейс зачастую определяет только один аспект поведения реализующего его класса, в то время как абстрактный класс определяет саму суть (природу) объектов его подклассов. По информации, что какой-то объект instanceof MyInterface ничего конкретного об этом объекте сказать нельзя (кроме того, что у него точно есть пара методов, определенных в MyInterface). Например, интерфейс может называться Comparable, а реализовывать его могут классы Money, User и Rating. Каждый из этих классов — обособленная сущность предметной области, все они разной природы. Если же есть информация, что какой-то объект instanceof MyAbstractClass, это всегда должно означать что мы имеем дело с иерархией объектов одной природы. Например, абстрактный класс может называться Car, тогда все его наследники 100% должны быть также машинами. Рассматривать абстрактные классы как замену интерфейсам (и наоборот) в корне некорректно в силу их абсолютно разного назначения.
      • 0
        Ну привет, приехали, вопрос про то, что в PHP тип результата операции определяется оператором, а не операндами — для гиков…
        Без понимания сути этого вопроса не будет понимания системы типов и типизации в языке. То есть не будет понимания языка в принципе.
        А прикладная разработка в стиле (как говорил мой дед) «тяп-ляп-насрал-и-стартап-гречневая-каша» мне не нужна.

        Рассматривать абстрактные классы как замену интерфейсам (и наоборот) в корне некорректно в силу их абсолютно разного назначения.

        А вот здесь Вы совершенно правы. Поэтому и нужен такой вопрос.
        • +1
          Видимо тогда для меня проблема оказалась в понимании формулировки вопросов. Почему-то что 3, что 5 заставили мой мозг думать абсолютно не в том направлении, в котором видимо Вы хотели бы получить ответ.
    • 0
      Странный у вас топ вопросов. Пункт 3 — сугубо философский. Всем вообще наплевать, каким оператором используется конкатенация и почему именно таким. Потому, что конвеншн. Если вы пишете на нескольких языках, вы просто используете регламентированный синтаксис. Почему JS использует +? Так исторически сложилось.

      4й вопрос вообще относится к наиболее неудачным вопросам на собеседовании. Потому что вы его тщательно придумали и ожидаете какой-то неведомый обществу ответ, сложившийся именно в вашей голове. Если кандидат не нащупает интуитивно какой-то из вариантов, с которыми вам почему-то довелось столкнуться, он проиграл. Статья в этом плане предлагает куда более удобные и чистые для понимания вопросы.
      • 0
        >> Пункт 3 — сугубо философский.
        Неверно. Он имеет самое прямое отношение к архитектуре языка. В данном случае — к системе типов. Не понимаете почему точка — не понимаете язык.

        >>4й вопрос вообще относится к наиболее неудачным вопросам на собеседовании. Потому что вы его тщательно придумали и ожидаете какой-то неведомый обществу ответ
        Неверно. Я ожидаю умение пользоваться базовыми конструкциями языка. Как минимум — циклом foreach, ссылками, базовой функциональщиной на примере банальной функции array_map и анонимными функциями. Одного даже упоминания вслух об этих возможностях уже достаточно для «зачёта» по этому вопросу. Будет что-то еще — отлично, но необязательно.
        • 0
          Всё что ниже, сугубо моё мнение.

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

          В вопросе 3 уберите сравнение с JS, так на самом деле далеко не все бекенд разработчики программировали на JS хоть что-нибудь без использования Jquery и подобных фреймворков и об основах этого языка знать в целом не обязаны (если вы, конечно, не ищите full stack или разработчика ещё и под nodejs). Ну и метод «не понимаете точку — не понимаете язык» — не лучший способ искать программистов.

          • 0
            Услышал Вас и Ваши рекомендации. Но остался при своём сугубом мнении. Мне не нужны битрикс-boys, мне нужны программисты в лучшем значении этого слова. С инженерным мышлением, со стремлением знать, почему и зачем, а не просто пользоваться готовыми инструментами.

            То, что вы ошибочно считаете «двусмысленностью» — это просто неявный тест на понимание базовых вещей, без которых программиста нельзя назвать таковым.
  • +7
    Мне кажется, что если человек ответит на все вопросы, то это уже не php программист в обычном понимании. Если человек знает как устроен массив, что такое список, знает про сложность алгоритмов, позднее связывание и т.п., то скорее всего он универсал, которому в целом пофигу на чем писать, был бы stackoverflow под рукой. И у таких людей php обычно совсем не на первом месте в списке любимых языков.
    • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    Чему будет равен $x после выполнения выражения $x = 3 + «15%» + "$25"?

    У вас в вопросе "«15%»" а в ответе разбирается пример c ''15%''
    $x = 3 + ''15%'' + "$25"

    Это сбивает.
    • 0
      Совершенно верно! Написал автору напрямую.
    • 0
      Поправил, спасибо. Не сразу понял, где именно вы увидели типографские кавычки :)
  • 0
    Спасибо за перевод — так и не дочитал в оригинале, и не перевел сюда из-за объема.
  • 0
    «перезагрузка свойств», странное понятие, возможно имелась в виду «перегрузка свойств»?
    • +1
      Конечно. Поправил, спасибо.
  • +7
    Много знакомых вопросов, которые попадаются во всяких предварительных тестах на собеседования.
    Самое большое недоумения вызывают вопросы, которые затрагивают отвратительные практики программирования, вроде «как используется global» или "$x = 3 + ''15%'' + "$25".
    Зачем это нужно знать, если в трезвом уме никогда такую фигню писать не станешь?
    • 0
      А как же поддержка legacy-кода, в котором много что использоваться может?
      Знать и понимать фичи языка нужно, но следовать «отвратительным практикам» никто не заставляет.
      • 0
        Но все-таки вопросы странные, заставляют задуматься об адекватности работодателя.
        Я бы из принципа ответил что-то вроде: «не помню, ибо не использую/никогда так не делаю/var_dump() „
        и, если после этого этот работодатель мной не заинтересуется — я точно не буду расстроен.
        • +3
          Расстроены вы будете, когда ответите на все вопросы, получите работу, а там вам дадут сервер с PHP 5.2, на котором ничего из этого не заработает.
          • +3
            По-моему нужно стараться избегать работ, где нужно всего лишь поддерживать некий старый код, который не будет развиваться.
            А так непонятная ситуация, зачем работодателю думать какой там я PHP буду использовать для проекта, обычно работодателя интересует лишь сколько денег необходимо на сервера.
            • 0
              С тредом согласен =). Я тут знаю как рабтают сервис локаторы и IoC, а меня спрашивают про global. Кстати на вопрос про глобал моя первая фраза была такой.

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

              И еще я про HTTP1.0 и HTTP1.1 не смог отличая назвать, хоть знаю как устроен протокол вдоль и поерек, а так же весь стек OSI наверх вплоть до того как кодируется самовосстанавливающиеся коды.

              Короче я не понимаю работодателя. Считаю что к такому идти не надо.
        • 0
          Буквально вчера эти вопросы видел. Понял что работодатель неадекватен, либо придется возиться с жудким легаси.

          Я кстати удивлен почему в этих списках вопросов нет var_dump() — её я последний раз использовал чертикогда и только на API.
          • +2
            Воу-воу! В чем проблема с var_dump'ом?
      • 0
        Дело в том, что если данные практики не применяются уже долгое время, то либо о них уже все забывают, либо даже никогда и не успевают столкнуться. Я например активно на php разрабатываю с 2011го года и с global познакомился только тогда, когда в руки передали очень старый проект, не имевший отношения к работе. А с 13го года я занимался разработкой исключительно на php 5.4 и 5.5. Ни за какие ковришки не пойду на проект, который меньше 5.4.
        То есть тонкости знания древнего кода мне совершенно не интересны и если меня начинут спрашивать подобные вещи на собеседовании, то я не постесняюсь поинтересоваться образцами кода проекта данной компании, потому что один раз так я уже попал на быдлокодовый проект с 5.1 и сбежал очень быстро.
        • 0
          А если бы вам сказали «проект старый, нужен рефакторинг, время на это дело выделим в должном объеме», вы бы согласились?
          • 0
            При прочих нормальных условиях я думаю, что мог бы взяться.
            Если это вопрос с тем, чтобы меня подвести к осознанию необходимости знания тонкостей старого кода, то всё равно я не считаю важным выносить это на собеседование. К примеру, в случае упомянутого краткого синтаксиса в echo, достаточно выполнить трассировку и получить промежуточный результат, в крайнем случае сделать декомпозицию, тем более что осуществляться будет рефакторинг.
    • +6
      Разве что для того, чтобы понять, что хотел сказать автор своим кодом, который, возможно, придется поддерживать. Большая часть вопросов на практике не особо применима.

      Ответ на вопрос про замыкания, например, перечитал несколько раз, но так толком ничего из него и не понял. Когда увидел этот вопрос в списке, заинтересовался: понадеялся, что узнаю, какова их область применения непосредственно в PHP. А в итоге одно разочарование. Не увидел вообще ни намека на привычные мне замыкания. Какая-то каша про анонимные функции, которые вдруг почему-то стали замыканиями. Впрочем, в документации тоже «The Closure class — Class used to represent anonymous functions».

      Как человек, практикующий написание кода на PHP, я понимаю, что тут своя атмосфера. Но конкретно это уже абсолютно вне моего понимания.
    • 0
      ну например от пользователя может придти что-то подобное при очень кривой фильтрации данных.
    • 0
      Спорное утверждение. Приведение типов — очень важная часть РНР, и если человек знает, как оно работает, это позволяет писать намного более простой и чистый код. Знающий человек не станет без лишней надобности проверять, число у нас или строка в переменной, например, и уж тем паче писать код для перевода строки «15%» в число.
      • 0
        Знающий человек не станет без лишней надобности проверять, число у нас или строка в переменной


        Знающий проверять не будет, потому что он будет знать заранее, какой тип там в переменной. И конструкции в коде, где так как в этом примере лепятся разные типы не допустит в принципе. Это плохой код как минимум потому, что сходу он непонятный (я про неявное приведение, когда сроки с числами перемешиваться через плюс).
      • +1
        Именно что использование неявного приведения типов делает код более сложным и грязным. Простота и чистота кода обычно плохо соотносится со стремлением написать всё покороче.
  • +3
    после 5 лет в PHP я не мог найти ничего более 40-50 кк руб

    судьба привела меня к javascript
    теперь за мной гоняются хедхантеры с предложениями 120 — 150 кк

    PS тесты хорошие
    • +8
      кк?
    • +1
      догнали?)
    • +3
      Весьма странно. Знакомые в Спб работают за 100к+
      Сам работаю удаленно в районе сумм, с которыми за Вами гоняются хедхантеры.
      • 0
        я же не говорю, что никто не рабоатет за 100+

        но вакансий я вижу 12 штук

        из них половина от 50к. так что я думаю 100 к они не преполагают. а типа от 50 но супер спецу 55
        • 0
          Не сезон пока. Посмотрите с сентябре/октябре по тому же запросу.
    • +1
      Хм не знаю как в СпБ но в Москве найти приличного PHP программиста даже за 60-70 не очень просто
      И за приличного в этом случае я бы считал такого кто ответит хотя бы на 3 вопроса из 14 в статье. Серьезно.

      Хотя сами вопросы мне не понравились (потому что на половину я не знаю ответа хотя 10 лет в программировании хаха) — но все очень зависит от задач. Такие вещи которые нужно знать для ответа на эти вопросы — случайно не учат. Либо это очень углубленные познания либо прошлый опыт. Я же в первую очередь смотрю количество практики на разных проектах — ближе к реальности.

      Вообще из-за низкого порога входа поиск пхп-шника тот еще ад — очень много самоучек которые считают что вполне уже могут работать на 100к а сами только-только коснулись SQL и классов
      • +1
        Черт возьми, Вы очень правы :(
      • +1
        А вы перестаньте искать программистов ДАЖЕ за 60-70 к и найдете нормальных.
        • 0
          Так я же на джуниора искал, никаких фантастических задач — поддержка уже работающих проектов, внесение изменений по уже отработанным методам, поиск ошибок. Но человек приходит на 60 и не может ответить на вопросы уровня лабораторных работ второго курса, о чем вы.
          • 0
            Например?
            • 0
              Например не может ответить как работать с куками через php, чем отличается $element от &$element, чем отличается LEFT и RIGHT JOIN в SQL, как в SQL производится выборка с 10 по 30 записи из запроса. Как обрабатывать глобальные перменные get/post запросов и файлов, каков принцип работы шаблонизаторов типа smarty, как в общих чертах сделать на сайте ЧПУ.
              У меня в общем есть список вопросов которые я на джуниора веб-программиста таскаю — он очень простой и устный, охватывает сразу php/js/css/html/sql/jquery/bitrix и на многие вопросы есть несколько правильных ответов, но по тому как соискатели отвечают уровень виден сразу.
              И оказалось что получить адекватные ответы хотя бы на 80% элементарных вопросов это уже неплохо.
          • 0
            Из контекста же не было понятно, что речь о джуниорах. Джуниор + PHP — гремучая смесь. :)
      • 0
        Вообще из-за низкого порога входа поиск пхп-шника тот еще ад — очень много самоучек которые считают что вполне уже могут работать на 100к а сами только-только коснулись SQL и классов

        Далеко ходить не надо. В этом же посте ниже спрашивают, зачем вообще нужно ООП, и это не сарказм.
    • 0
      То ли ещё будет, когда опыт перевалит за 10 лет :)
      Потребности растут, а рынок иногда неожиданно просаживается.
  • 0
    «APC Cache был заменён на OpCache» — разве это корректно? APC всегда был внешним модулем, а OpCache сейчас включили «по-умолчанию». А вообще мне показалось, что данный тест предполагает найти специалиста по конкретному языку, а не программиста, что обычно требуется. Странно, что global в купе с новшествами PHP 5.5 рассматриваются.
    • 0
      «APC Cache был заменён на OpCache» — разве это корректно? APC всегда был внешним модулем, а OpCache сейчас включили «по-умолчанию».

      Вы правы, перефразировал. Спасибо.
  • +4
    И придет такой человек, выучивший мануал или тупо подготовившейся к собеседованию, к вам, и будет писать жуткий говнокод, от которого волосы дыбом будут стоять.

    Я сейчас на знания PHP, как языка/стандартной библиотеки, вообще ни одного вопроса не задаю (да, ищем не junior'ов). Слишком простой язык, чтобы там было на что спрашивать.
  • +18
    Если честно, вопросы ни о чём, т.к. сугубо академичны. Я уже лет 15 как ведущий PHP разработчик и даже не стал бы на них отвечать. Не потому что я не знаю, а потому что это глупо. Некоторые вещи используются настолько редко, что быстро вылетают из головы, либо уже давно обёрнуты «высокоуровневыми прокладками». Достаточно провести один вечер над мануалом и это собеседование будет легко пройдено. Сколько я уже повидал зубрил, которые на собеседовании блещут теорией, а на практике не могут связать а и б… Дай мне сейчас листик бумаги и я не смогу написать там простой цикл. Я не знаю наизусть всех функций, как и порядка их аргументов, спасибо автокомплиту в IDE. Быть мне теперь Junior'ом? :)

    ArrayObjects — это что вообще такое? Пишите как есть: ArrayObject
    • +1
      В точку, в целом. Писал что-то подобное, но Вы опередили :).
      Всем рулит Mr. Experience и культура программирования (как способность программиста придерживаться одного стиля и заданных правил внутри проекта вне зависимости от его личных убеждений :)).
    • 0
      ArrayObjects — это что вообще такое? Пишите как есть: ArrayObject

      Опечатка. Поправили. Спасибо.
    • 0
      +100500, главное понимание, знание базовых алгоритмов, оценка сложности алгоритма и практические навыки проектирование. А как сделать $$var = 'name'; без одного вопроса как работать с базоы данных не даст ровным счетом ничего.
  • –4
    Вообще ничего нет про OOP как основу приложения, хотя бы на базовом уровне — чем отличается абстрактный класс от интерфейса, что такое IoC и тому подобное. Это тема другого собеседования или Вы считаете, что это не нужно?

    Зато есть про суперглобальные переменные с одной стороны, и про трейты/генераторы — с другой. Имхо, как-то неконсистентно.
    • +4
      объясните. зачем нужно ООП?
      • +1
        Мммм… Не сочтите за труд, объясните, как современному программисту PHP могут быть не нужны знания ООП. Это не сарказм. Я, наверно, чего-то не понимаю.
        • –3
          Я вот лично ООП изучаю и использую(некоторые API), но пока свои не писал за ненадобностью.
          И да, я прекрасно обхожусь функциями.
      • 0
        объясните. зачем нужно ООП?

        Кажется, я смог определить, что ночью во сне меня убили и подменили на клона.
  • +3
    А вот бы кто-нибудь написал такие же вопросы про Python — я был бы рад, как начинающий питонист:)
  • +1
    А для кого собственно гайд? Кто «должен» задавать такие вопросы? Технический специалист, рекрутер, кто-то еще?
    И что им проверяется? Уровень программиста, знание деталей языка, давно ли он «зубрил мануал», читал ли он этот пост или десятки ему подобных?
  • 0
    Насколько я понимаю, все вопросы предполагают не проверку знаний php, или адекватности, или зубрения мануала — а возможность ответа на вопрос насколько собеседуемый зашел в php internals. Другими словами тест предполагает — не отсеивание говнокодеров-жумлистов, а способность С-программистов изучивших движок максимально быстро извлечь из накопленной за изучением библиотеки знаний — быстро притворить ее в жизнь на php. Ну и те, кому из вышеуказанных, это покажется интересным — конечно же будут писать хороший и качественный код не задумываясь.
  • +2
    Мало знать мат.часть (ее тоже необходимо знать, не спорю), надо уметь применять эти знания в нужном месте в нужное время.
    НО, если проект прост как 3 копейки, зачем «заморачиваться» и писать сложные функции.

    Я считаю, что опыт — вот главный критерий при приеме на работу в данное время. Не раз убеждаюсь уже (не только на своем примере) — теория теорией, а опыт «не пропьешь» и не получишь читая просто книжки. Причем опыт должен быть именно не на своих любительских программах (хотя и там тоже есть свои плюсы), а работы в команде. Начинаешь лучше понимать принципы построения.

    Статья хорошая — +автору.
  • 0
    Мне всегда казалось, что идеальные вопросы по php это сильно утрированные, но реальные задачи из веба. Человек должен знать, как работает HTTP, GET/POST запросы, формы, сессии, куки.

    Например скрипт, который выводит какую-то информацию и форму, он же отвечает за сабмит этой формы.

    Пример
    <?php

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $post_id = $_POST['post_id'];

    // save comment
    //…

    header('Location: /?post_id='. $post_id);
    }
    elseif (!empty($_GET['post_id'])) {
    // print post
    //…
    }

    ?>

    />
    />
    />


    Немного запутаем
    <?php

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $post_id = !empty($_POST['post_id'])? $_POST['post_id']: 30;

    // save comment
    //…

    header('Location: /?post_id='. $post_id);
    }
    elseif (!empty($_GET['post_id'])) {
    // print post
    //…
    }

    ?>

    />
    />
    />


    И просим человека рассказать что будет при сабмите формы. Формы в веб-приложениях есть почти всегда, и подобный вопрос по-моему обязателен.
    • 0
      Я вам больше скажу, чуть ли не половина кандидатов на вакансию PHP-джуниора отсеивается заданием «напишите функцию расчета факториала». Даже если рассказать, что такое факториал и как его считают.
      Поэтому без выполнения тестового задания (пусть даже достаточно простого) на собеседование можно никого не приглашать :)
      • 0
        При наличии алгоритма все равно не могут? Дополнительных требований, вроде необходимости использовании рекурсии, никаких нет? Что конкретно вызывает затруднения: цикл или умножение двух чисел?
  • 0
    А где вопрос про оператор три равно (===)? =)
    Я, например, про него узнал не сразу, т.к. прийдя в РНР из C# даже не думал что такой оператор существует!
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Вот честно, я сходу не смогу рассказать всех нюансов работы == (во всяком случае не уверен), === то простое. Сравнение по значению и типу. А == коварно. Это как проверять наличие ключа через isset, ключи вроде есть, а вроде и нет. Вообще массивы в PHP то еще развлечение…
  • +3
    Как то раз один профессор по двигателям сдавал экзамен в ГАИ, ему попался вопрос «Принцип функционирования ДВС» (дословно не помню). Тот обрадовался, начал с формулами и выкладками рассказывать. И что Вы думаете, не сдал. Гаишник его просто не понял и сказал «что Вы мне тут голову морочаете».
    Где гарантия того, что экзаменатор разбирается в задаваемыхвопросах лучше чем экзаменуемый? Их отличие, зачастую в том, что первый проштудировал эти вопросы заранее. Сколько раз уже тут писал, человека надо проверять не на то сколько тонкостей он знает а на то как он умеет быстро в них разобраться при необходимости. Примерно как Капица принимал экзамены в библиотеке, бери, читай что хочешь но покажи что ты ориентируешься где что искать и имеешь базу в этом разобраться. Ну и конечно глянуть на то что он уже делал (в код) не стесняясь спросить почему так а не иначе, не корча из себя всезнайку потому как может оказаться что человек разбирается в чем то лучше тебя и в итоге будешь выглядить глупо со своими проверками. Талантливый, приветливый, способный быстро учиться человек гораздо интереснее
    сухаря спеца с которым никто не может работать в команде. Бывают конечно исключения, но Вы ж не гугл чтоб к Вам спецы толпой валили а Вы только отсеивали. Потому подходите к вопросу творчески а не формально.
    • +1
      Те задачи, которые должен решать PHP, требуют его развития всего лишь в 3 направлениях
      1) SPL, а именно его дополнение такими замечательными вещами как FixedArray
      2) OpCache
      3) Рождение процесса, смерть и их регулировщики, в частности развитие php-fpm, который начал заметно проседать, ёклмн.

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

      Поэтому вся эта (прости господи) срань господня в виде трэйтов, интерфейсов и прочего прелестного, становиться похожа на кучку беспомощных котят, когда весь input-output начинает безжалостно сегфолтиться, мемликать, просить set_time_limit(0), max-input-time over9000, memory_limit(+100500).
      И вот такими статьями воспитывается поколение программистов PHP ради PHP.

      Ни в коем случае нельзя оформлять такие статьи, как то, что вы должны знать на собеседовании.
      Это задаст ложное направление свежей пачке выпускников от мира web разработки.
      Это всегда должно преподноситься, как «это интересно, это поможет тебе в этом и вот в этом, избавит от этого и от этого.» И конкретные примеры плохих, именно ужасно (а не допустимо-) плохих практик.

      А если в бизнесе вас начинает волновать, в курсе ли человек в первую очередь про то, когда умер 5,2 и что такое разница abstract и interface, а не то, что его зарабатывающую деньги страничку или терминал видят только 10 человек из 100, по причине «facially yours, nginx», то у меня для вас плохие новости.
  • 0
    Очень спорно применимая статья, но интересная зато :) порадовало что хоть что-то я все-таки знаю. Спасибо
    • +2
      Прочитал все комментарии и удивлён тем, что есть очень много критикующих этот список вопросов с позиции «Эти вопросы не выявят хорошего программиста» и при этом всего лишь 4-5 комментариев с конкретными альтернативными вопросами к собеседованию. При этом основная позиция: видели мы тех, кто это знает, но ничего не умеет — они не хорошие программисты.

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

      P.S. Я из всех вопросов топика не смог ответить только на последний, потому что PHP 5.4 вот только недавно установил на виртуалку, и то не по своей воле, а потому что один из PHPшных фреймворков это потребовал. На практике ещё не приходилось сталкиваться с PHP 5.5 а про 5.6 я только краем уха слышал.

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

      1. В каких случая индексы на таблицах — это хорошо, а в каких — плохо?
      2. Аналогично про внешние ключи таблиц.
      3. Каким образом практически может быть идентифицирован каждый уникальный пользователь веб-приложения?
      4. Что и как потребуется настроить, чтобы AJAX запросы смогли проходить между разными доменами? Будет ли проще, если это домены или субдомены одного веб-приложения?
      5. Что такое XSS инъекция и как с ней бороться? Есть ли универсальное решение?
      6. Что такое SQL инъекция и, тоже, как с ней бороться? Есть ли универсальное решение?
      7. Шаблоны проектирования. Но не все, а конкретно: MVC, Service Locator, Lazy initialization. И не нарисовать, а дать чужой или свой пример.
      8. Что делать, если один HTTP запрос отрабатывает на сервере 15 минут, а надо 15 секунд или меньше?
      9. Что будет, если не бороться за эти секунды? Когда это допустимо?
      10. Что делать, если веб-сервер не справляется с 1 запросом в секунду (RPS). C 10 RPS? C 100 RPS? C 1000 RPS?
      11. Сравни между собой по нескольким критериям следующие форматы данных: JSON, XML, CSV. Есть ли по каждому критерию ещё более эффективный формат данных?
      12. Хорошо, давай про Legacy Code: напиши пример кода на PHP4, который не будет выполняться в PHP5. Хотя бы вспомни пару функций, по наличию которых в коде можно сказать, что этот проект на PHP 5 работать не сможет.
      13. И вот тебе дали задачу переписать код небольшого проекта из двух десятков скриптов или классов с PHP4 на PHP5. Твой порядок действий?
      14. Тебе дали задачу переписать код одной единственной гадкой функции, которая работает странно. Как выявить и устранить странность?
      15. Ты выявил и готов исправить странность одной единственной гадкой функции, но помимо твоего веб-приложения этой функцией пользуются несколько неких сторонних веб-приложений посредством RPC, SOAP или иначе, причём именно недокументированными странностями. Как удовлетворить всех? ;)
      16. Я могу утверждать, что у нас в коллективе все простой цикл и условие записывают вот так:
      foreach($someElements as $element)
      {
          if ($element->isGood())
          {
              $this->doSometing($element);
          }
      }
      

      Как ты думаешь, почему это вдруг так?
      17. В проекте приняты стандарты кодирования. Как бы ты поступил, если бы через 2 года нашёл в каком-нибудь месте проекта расхождение с принятыми стандартами?
      18. Тебе дали задание: сделать новый проект на новом фреймворке и быстро. Причём с новым фреймворком ты и коллеги практически не сталкивались, а выбрали по принципу — «на хабре писали, что это самый новый/быстрый/маленький/большой/крутой/популярный»? Взялся бы за такое задание?

      Не хватил ли я лишка в своих вопросах? :)
      Может я пытаюсь найти умного, но ленивого гуру, а вашего проекта нужен всего лишь исполнительный программист на твердую «четвёрочку»?
      • 0
        Хорошие вопросы, точнее, темы для обсуждения с потенциальным кандидатом чтобы посмотреть его ход мыслей. Про PHP4 только вы загнули, 10 лет уже прошло как PHP5 вышел. На такой legacy code ни один вменяемый разработчик не пойдет. И в целом от вопросов веет «у нас проект на самописном движке десятилетней давности, сможешь ли ты его поддерживать и умудряться добавлять туда новые фичи?»
        • +1
          Нет, не 10ти летней давности. Ведь немногие в 2004 году бросились переписывать стабильно работающий говнокод с PHP4 на доселе неведомую почти-ООП 5.0. Я сам лично ещё долго писал совместимый с двумя версиями код — да мало ли кому понадобятся мои библиотеки, мало ли какой shared hosting у них будет.

          Давайте переформулирую под современные реалии. В PHP 5.3, PHP 5.4, PHP 5.5 есть deprecated functions, при наличии которых код работать не будет.

          12. Хорошо, давай про Legacy Code: напиши пример кода на PHP 5.3, который не будет выполняться в PHP 5.5. Хотя бы вспомни пару функций, по наличию которых в коде можно сказать, что этот проект на PHP 5.5 работать не сможет.
          13. И вот тебе дали задачу переписать код небольшого проекта из двух десятков скриптов или классов с PHP 5.3 на PHP 5.5. Твой порядок действий?
  • 0
    Спасибо! Полезная статья. Узнал много нового со времен 4-ого PHP. Время летит...)

    ЗЫ 6 вопрос:
    $_REQUEST – объединение пар ключ-значение из $_POST и $_getDog.

    По-моему, что-то не так…
  • 0
    PHP 5.5… и добавлена удобная возможность обращения к символам в строке как к элементам массива (например, '6e5d4'[0] вернёт «6»).

    тыщу лет был этот сахарок, кажется еще в пхп 4.3 уже было, пусть синтаксис такой красивый не был, но суть та же:

    $array = "PHP";
    echo $array[1];// выводит H
    
    • 0
      но суть та же

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

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