пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ пїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅпїЅ
По вашему запросу ничего не найдено :(
Убедитесь, что запрос написан правильно, или посмотрите другие наши статьи:
img
В инфраструктуре любого предприятия есть очень много требующих внимания деталей. Например, места для серверов, среды разработки, безопасность, стеки программного обеспечения, обновления программного обеспечения, обслуживание оборудования, что все затраты на обслуживание платформы, как правило, огромны. Компаниям, которые разрабатывают и развертывают приложения, необходимо выделить много ресурсов для поддержания работы платформы - ресурсов, которые в противном случае можно было бы использовать для разработки программного обеспечения. Поэтому возникла необходимость в облачных платформах. Эти решения используют модель облачных вычислений, чтобы предоставить разработчикам все необходимое для выполнения их работы - от сред разработки на хостах и инструментов баз данных до полных возможностей управления приложениями. Разработчики, работающие в облачной платформе, имеют доступ ко всем ресурсам, необходимым для создания, развертывания и запуска программных приложений. Для больших компаний облачная платформа может обеспечить масштабируемую базу для новых приложений, которые необходимо предоставлять в короткие сроки. При использовании модели "плати по мере роста" нет необходимости в долгосрочных инвестициях в локальные платформы. Почему "опенсорс"? Теперь, когда мы заявили о преимуществах облачных вычислений перед традиционными платформами, следующий вопрос заключается в том, почему облачная платформа с открытым исходным кодом является лучшим вариантом, чем запатентованная облачные решения. Самый очевидный ответ - стоимость: лицензии на проприетарные решения всегда предполагают более затратные вложения. Еще одним важным преимуществом является гибкость и свобода выбора из самых разнообразных структур, облаков и услуг. Платные платформы, с другой стороны, могут привязать вас к инструментам и услугам, которыми они владеют. В обмен они предлагают определенные преимущества, такие как соблюдение соглашения об уровне обслуживания (SLA) и освобождение от препятствий, таких как тестирование и интеграция, но эти преимущества едва ли перевешивают преимущества открытости. Ниже представлен список облачных платформ с открытым исходным кодом для предприятий, которые сегодня пользуются популярностью на рынке. Cloud Foundry Созданный компанией VMWare затем приобретённый компанией Pivotal Software, Cloud Foundry отличается тем, что он доступен как автономное приложение с открытым исходным кодом, что делает его независимым от поставщика. Его можно развернуть в VMware vSphere или других облачных инфраструктурах, таких как HP Helion, Azure или AWS. Или даже можно самостоятельно разместить его на сервере OpenStack. Благодаря использованию пакетов сборки Cloud Foundry упрощает поддержку среды выполнения и инфраструктуры. При каждой компиляции приложения Cloud Foundry Application Runtime выбирает наиболее удобный для него пакет сборки. Затем buildpack занимается компиляцией приложения и подготовкой его к запуску. Cloud Foundry разработана для быстрой разработки и развертывания приложений с помощью высокомасштабируемой архитектуры и удобных для DevOps рабочих процессов. Эта технология наряду с другим языками так же, поддерживает языки, как Python, Ruby, PHP, Java и Go. Тем не менее, чтобы правильно вписаться в Cloud Foundry, рекомендуется, чтобы ваш проект соответствовал 12-факторному стандарту приложений - методологии, специально разработанной для разработки оптимальных приложений SaaS. WSO2 Если часто работаете над сервис-ориентированной архитектурой (SOA), то скорее всего у вас есть большое количество внутренних и внешних API. Это тот сценарий, когда WSO2 в большей степени проявляет себя благодаря своему API-менеджеру, способному обрабатывать весь цикл API от начала до конца. WSO2 обеспечивает соответствие большинству требований, которые могут быть выдвинуты клиентами, включая управление версиями, документацию API и разгрузку SSL. WSO2 использует концепцию магазина, в которой разработчики могут находить, пробовать и оценивать API. Развертывание является простым и простым, предоставляя множество опций для управления потоком API. Он также предоставляет функцию автоматического восстановления в случае приостановки работы конечной точки. Все эти качества направлены на сокращение времени вывода на рынок, упрощение управления затратами и, в целом, повышение гибкости бизнес-процессов. Большим плюсом WSO2 API Manager является его простая интеграция с WSO2 Identity Server - решением IAM (Identity and access manager), управляемым API. Эта интеграция предлагает удобную платформу для аутентификации в облачных средах. Cloudify Cloudify - это фреймворк оркестрации, предназначенная для моделирования приложений и услуг при автоматизации их жизненных циклов. Фреймворк включает в себя возможность развертывания в любой облачной среде или центре обработки данных. Он также предлагает инструменты для мониторинга всех аспектов развернутых приложений, определения условий отказа и их решения вручную или автоматически. Одной из наиболее заметных особенностей Cloudify является моделирование проекта на основе TOSCA. Это нововведение позволяет разработчикам использовать YAML для создания чертежей топологий приложения. YAML - считываемый человеком язык сериализации данных, используемый для написания определений на основе спецификации TOSCA, что даёт разработчикам стандартизированный способ описания взаимосвязей между приложениями, системами и компонентами облачной инфраструктуры. Облачная оркестрация Cloudify обеспечивает прочную базу для управления ИТ и обеспечения безопасности, позволяя пользователям применять ограничения доступа с различными ролями и уровнями разрешений. Для общения с внешними сервисами, такими как контейнеры Kubernetes, облачные сервисы (AWS, Azure, vSphere, OpenStack) и инструменты управления конфигурацией (Pucket, Anulable, Chef), Cloudify использует свой набор официальных плагинов, в то время как многие другие сервисы работают с существующими плагинами. OpenShift OpenShift - платформа на базе Kubernetes, с гибким и очень быстрым установщиком и поддержкой большого числа API, что позволяет разработчикам расширять платформу, исходя из своих потребностей. Он построен с учетом безопасности, что иллюстрируется примером: контейнеры должны запускаться от имени обычных пользователей, и когда это не так, OpenShift требует явного переопределения для запуска контейнера. Использование Kubernetes требует значительного количества серверов, и для его освоения требуется определенное обучение. Именно поэтому эта платформа не подходит для небольших проектов, если в ближайшем будущем она не превратится в более масштабный проект. Пользователи OpenShift подчеркивают возможность его быстрой установки и настройки, а также простоту обслуживания модулей и надстроек. Еще один плюс - факт наличия собственного Git репозитория. В противовес этому имеется некая сложность в чтении и интерпретации логов. В частности, когда происходит сбой при загрузке проекта, трудно понять, где проблема. Tsuru Rede Globo, вторая по величине коммерческая телесеть во всем мире, запустила Tsuru как продукт на базе Docker PaaS (платформа как сервис), способный организовывать и запускать приложения в производственной среде. Это платформа с открытым исходным кодом, поддерживающая сайты с миллионами пользователей, разработанная компанией Globo.com. Пользователи Tsuru утверждают, что это существенно улучшает время вывода на рынок, не отказываясь от простоты, высокой доступности, безопасности или стабильности. Его можно запускать на различных облачных инфраструктурах, будь то публичная или частная, при условии, что они поддерживаются Docker-машинами. Также он поддерживает практически все известные язык программирования, что даёт разработчикам свободу выбора в соответствии с их предпочтениями. С помощью Tsuru можно использовать различные хранилища данных, включая базы данных SQL или NoSQL, или альтернативы в памяти, такие как Memcached или Redis. Чтобы управлять приложением, вы можете выбрать один из своих предпочтений и подключить его к приложению. Чтобы управлять приложением, вы можете выбрать между использованием командной строки или веб-интерфейсом, а затем развернуть через Git. Инфраструктура Tsuru займется всеми рутинными делами. Stackato Stackato - это полиглотный продукт PaaS, основанный на Cloud Foundry и Docker, который работает поверх облачной инфраструктуры и служит платформой для запуска приложений. Пользователи Stackato говорят, что он предоставляет гибкую и надежную платформу приложений, которая помогает повысить производительность как администраторов облачных вычислений, так и разработчиков. Он хорошо подходит для развертывания корпоративных облачных сред, сочетая гибкость непосредственного доступа к виртуальной машине в облачной инфраструктуре с автоматизированной конфигурацией, обеспечиваемой полнофункциональной системой PaaS. Среди поддерживаемых облачных инфраструктур можно показать HP Cloud Services, Citrix XenServer, AWS, OpenStack, VMware. В Stackato у каждого приложения есть свой контейнер Linux (LXC), который гарантирует эффективное и безопасное совместное использование ресурсов. Его спектр услуг состоит из Helion Control Plane, который Stackato использует для связи с основным облаком и управления всем циклом услуг; Helion Service Manager - хранилище дополнительных служб, доступных для приложений; Helion Cloud Foundry - гибкая среда выполнения, предназначенная для упрощения хостинга и разработки приложений; Helion Code Engine, сервис непрерывной доставки, интегрированный с репозиториями Git, частными или публичными, и Helion Stackato Console, веб-интерфейс для управления всеми функциями Helion Cloud. Alibaba Хотя и сложно представить компанию Alibaba в числе облачных платформах с открытым исходным кодом и PaaS, бизнес Alibaba Cloud Computing растет быстрыми темпами. Она уже завоевала 50% китайского рынка облачных технологий, а также удачно обслуживает рынки за пределами Китая. Например, они начинают оказывать биллинговую поддержку в долларах США по 168 странам и разрабатывать услуги, специально предназначенные для зарубежных рынков. Сервисы облачных платформ, включенные в предложение Alibaba, включают множество бесплатных функций, включая контейнерные сервисы для Docker и Kubernetes, Container Registry, Auto Scaling и DataWorks, защищенную среду для разработки данных в автономном режиме. Его службы хорошо задокументированы и предоставляют все необходимое, чтобы сразу начать перенос приложений в облако, в том числе много обучающих видеороликов. Следуя нескольким простым шагам и не инвестируя ни цента, Alibaba обеспечивает развертывание приложения в кратчайшие сроки. Заключение К счастью для всех разработчиков, облачные технологии становятся более доступными. Пару лет назад, конкурируя за контейнерные технологии (Docker, Kubernetes, Mesos, Nomad, ECS, назовем несколько) угрожали разделить рынок на изолированные отсеки, создавая значительные риски всякий раз, когда нужно было выбрать платформу. Но, несмотря на то, что в наши дни на выбор предоставляются все больше платформ, различия между сегодняшними вариантами с открытым исходным кодом заключаются только в деталях: разных схемах затрат, разных инструментах управления, разных подходах к безопасности. Другими словами, если выбирали одну облачную платформу с открытым исходным кодом и вас она не устраивает, легко можете перейти к другой, не обременяя себя расходами. В зависимости от технической задачи вы можете выбрать платформу, которая лучше отвечает вашим потребностям и позволяет забыть о таких проблемах, как емкость сервера, промежуточное программное обеспечение, платформы, виртуальные машины, хранилища данных и т.д. После того, как вы освободитесь от всего этого, вы сможете вложить все свои ресурсы и все свое внимание в одно, что действительно важно для вас: как можно быстрее сделать доступным приложение пользователям.
img
В данной статье будет рассмотрена настройка Voicemail (голосовой почты) на FreePBX 13. Для начала необходимо создать или выбрать уже имеющийся экстеншен (Applications – Extensions – Quick Create Extensions). Далее в настройках экстеншена во второй вкладке (Voicemail) необходимо активировать ящик голосовой почты После того как включили голосовую почту для экстеншена (Enabled - Yes) и выбрали пароль – необходимо нажать Submit и затем Apply Config. Следующий шаг – активация ящика. Самый простой способ – зарегистрировать экстеншен на телефонесофтфоне и набрать на телефоне *97. Затем, для дальнейшей настройки VM нужно пройти по следующему пути: Settings – Voicemail Admin. Там выбирается экстеншен и открываются следующие опции: Здесь так же можно поменять пароль на ящике, указать адрес электронной почты и выбрать опции (данные настройки так же доступны в поле редактирования экстеншена): Email Attachment – сообщения голосовой почты будут вложены в письме на указанный адрес Play CID – проигрывание номера человека, оставившего сообщение. Play Envelope – проигрывание даты получения сообщения Delete Voicemail – удаление голосовых сообщений с сервера. Если опция Email Attachment будет выключена при включенной функции удаления, то сообщения не будут получены вообще. Во второй вкладке можно увидеть статистику по ящику голосовой почты: Далее будет рассмотрена возможность поменять базовое приветствие на свой записанный аудио-ролик: Записанный ролик необходимо переименовать в unavail.wav и поместить по следующему пути /var/spool/asterisk/voicemail/default/@extension_number@ После этого в случае недоступности экстеншена вызов будет уходить на ящик голосовой почты, и предварительно будет проигрываться записанное сообщение. Так же можно сразу направлять вызов на ящик голосовой почты, если это необходимо для каких-то специфических целей – например, для организации отдельного номера для жалоб и предложений.
img
С помощью PHP можно относительно легко создать веб-приложение, и это является одной из причин, по которой он так популярен. Однако, несмотря на простоту его использования, PHP стал довольно навороченным языком с большим количеством различных нюансов и тонкостей, которые могут усложнить процесс отладки для разработчиков так, что они волосы начнут на себе рвать. В этой статье я выделил десять самых распространенных ошибок, которых PHP-разработчикам следует избегать.  Ошибка №1. Оставлять висячие ссылки на массивы после циклов foreach Не знаете точно, как использовать циклы foreach в PHP? Если вы хотите работать с каждым элементом итерируемого массива в отдельности, использование ссылок в циклах foreach может оказаться полезным.  $arr = array(1, 2, 3, 4); foreach ($arr as &$value) {    $value = $value * 2; } // $arr is now array(2, 4, 6, 8) { // $arr – это теперь array(2, 4, 6, 8) } Проблема в том, что, если вы не будете достаточно осторожны, это может привести к некоторым нежелательным побочным эффектам и последствиям. В частности, если мы посмотрим на пример выше, то увидим, что после того, как код выполнится, $value останется в области видимости и будет хранить ссылку на последний элемент массива. Таким образом, если вы будете использовать $value и дальше, то можете случайно изменить последний элемент массива.  Главное запомните, что у foreach нет области видимости. А значит, $value в приведенном выше примере является ссылкой на верхнюю часть сценария. На каждой итерации foreach задает ссылку, которая указывает на следующий элемент массива $array. Так что, после того, как цикл закончит свою работу, $value так и будет указывать на последний элемент $array и останется в области видимости.  Ниже приведен пример ошибок, к которым это может привести. Такие ошибки сбивают с толку и их довольно трудно обнаружить. $array = [1, 2, 3]; echo implode(',', $array), "\n"; foreach ($array as &$value) {}  // by reference { // по ссылке } echo implode(',', $array), "\n"; foreach ($array as $value) {} // by value (i.e., copy) {  // по значению (то есть путем копирования) } echo implode(',', $array), "\n"; Приведенный выше код выдаст следующий результат: 1,2,3 1,2,3 1,2,2 Нет, это не опечатка. Последнее значение в последней строке и правда равно 2, а не 3.  Почему же? После того, как мы прошли первый цикл foreach, $array остается таким же, но, как мы уже говорили выше, $value остается в качестве висячей ссылки на последний элемент $array (так как цикл foreach обращался к $value по ссылке). В результате, когда мы проходим по второму циклу foreach, начинают происходить всякие «странные вещи». В частности, так как теперь мы осуществляем доступ к $value по значению (то есть путем копирования), foreach на каждом шаге цикла копирует каждый последующий элемент массива $array в $value. Таким образом, на каждом шаге второго цикла происходит следующее: Проход 1: $array[0] (т.е. «1») копируется в $value (которое является ссылкой на $array[2]), поэтому теперь $array[2] равен 1. Так что, теперь $array содержит [1, 2, 1].  Проход 2: $array[1] (т.е. «2») копируется в $value (которое является ссылкой на $array[2]), поэтому теперь $array[2] равен 2. Так что, теперь $array содержит [1, 2, 2]. Проход 3: $array[2] (которое теперь равно «2») копируется в $value (которое является ссылкой на $array[2]), так что $array[2] так и остается равным 2. Поэтому теперь $array содержит [1, 2, 2]. Для того, чтобы быть иметь возможность использовать преимущества ссылок в циклах foreach и при этом быть уверенным в том, что подобных проблем возникать не будет, вызовите функцию unset() для переменной сразу после цикла foreach, чтобы удалить ссылку. Например,  $arr = array(1, 2, 3, 4); foreach ($arr as &$value) {    $value = $value * 2; } unset($value); // $value no longer references $arr[3] { // $value уже не является ссылкой на $arr[3] } Ошибка №2. Непонимание того, как ведет себя функция isset() Несмотря на свое название, функция  isset() возвращает значение false не только для несуществующих элементов, но и для нулевых значений (null).  Такое поведение функции может привести к куда более серьезным проблемам, чем кажется на первый взгляд. Именно оно и является довольно-таки распространенным источником проблем.  Давайте посмотрим на следующий фрагмент кода: $data = fetchRecordFromStorage($storage, $identifier); if (!isset($data['keyShouldBeSet']) {    // do something here if 'keyShouldBeSet' is not set    { // если 'keyShouldBeSet' не существует, выполняются действия } } Тот, кто писал этот код, судя по всему, хотел проверить, существует ли keyShouldBeSet в $data. Но, как мы уже говорили ранее, функция isset($data['keyShouldBeSet']) вернет false, даже если $data['keyShouldBeSet'] существует, но имеет значение null. Так что, логика, приведенная выше, неверна. А вот другой пример: if ($_POST['active']) {    $postData = extractSomething($_POST); } // ... if (!isset($postData)) {    echo 'post not active'; } Код выше предполагает, что, если $_POST['active'] возвращает true, то postData обязательно существует, а, значит, isset($postData) вернет true. И наоборот, если isset($postData) возвращает false, то и $_POST['active'] возвращает false.  Но это не так.  Как мы уже говорили, isset($postData) может также вернуть false, если $postData имеет значение null. Таким образом, функция isset($postData) может вернуть false, даже если $_POST['active'] вернул true. И опять получается, что логика, приведенная выше, неверна. К слову, если цель приведенного выше кода на самом деле заключалась в том, чтобы еще раз проверить, возвращает ли $_POST['active'] значение true, прибегнуть к помощи функции isset() было не самым хорошим решением. Вместо этого было бы гораздо лучше просто перепроверить $_POST['active'], то есть: if ($_POST['active']) {    $postData = extractSomething($_POST); } // ... if ($_POST['active']) {    echo 'post not active'; } Правда, говоря о случаях, когда нам нужно проверить, существует ли переменная на самом деле (то есть отличить переменную, которая не существует, от переменной, значение которой равно null), гораздо более надежным решением будет использование метода array_key_exists(). Например, вы могли бы переписать первый пример следующим образом: $data = fetchRecordFromStorage($storage, $identifier); if (! array_key_exists('keyShouldBeSet', $data)) {    // do this if 'keyShouldBeSet' isn't set    { // если ‘keyShouldBeSet’ не существует, то выполняются действия, указанные здесь } } Более того, вы можете проверить, существует ли переменная в текущей области видимости. Для этого вам нужно объединить методы array_key_exists() и get_defined_vars(): if (array_key_exists('varShouldBeSet', get_defined_vars())) {    // variable $varShouldBeSet exists in current scope    { // переменная $varShouldBeSet существует в текущей области видимости } } Ошибка №3. Путаница с возвратом по ссылке и по значению Давайте посмотрим на следующий фрагмент кода: class Config {    private $values = [];    public function getValues() {        return $this->values;    } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test']; Если вы запустите этот код, то получите следующий результат: PHP Notice:  Undefined index: test in /path/to/my/script.php on line 21 Что же не так? Проблема в том, что в коде, который мы привели выше, произошла путаница с возвратом массива по ссылке и по значению. Если вы явно не укажите PHP, чтобы он вернул массив по ссылке (то есть с помощью &), то он по умолчанию вернет массив «по значению». Это значит, что будет возвращена копия массива, и, как следствие, вызываемая функция и вызывающая сторона будут обращаться к разным экземплярам массива.  Так что, вызываемая выше функция getValues() возвращает копию массива $values, а не ссылку на него. Помня об этом, вернемся к двум ключевым строкам из примера выше: // getValues() returns a COPY of the $values array, so this adds a 'test' element // to a COPY of the $values array, but not to the $values array itself. { // функция getValues() возвращает КОПИЮ массива $values, а значит, что элемент test добавляется в КОПИЮ массива $values, в не в сам массив. } $config->getValues()['test'] = 'test'; // getValues() again returns ANOTHER COPY of the $values array, and THIS copy doesn't // contain a 'test' element (which is why we get the "undefined index" message). { // функция getValues() снова возвращает ДРУГУЮ КОПИЮ массива $values, и в ЭТОЙ копии нет элемента test (собственно говоря, почему мы и получаем сообщение о неопределенном индексе) } echo $config->getValues()['test']; Одно из возможных решений этой проблемы заключается в следующем: вы можете сохранить первую копию массива $values, которую возвращает функция getValues(), и дальше работать с этой копией. Например,  $vals = $config->getValues(); $vals['test'] = 'test'; echo $vals['test']; Этот код сработает отлично, то есть он выведет элемент test, не выдавая сообщения о неопределенном индексе. Однако такой подход может оказаться непригодным; это зависит от ваших целей. В частности, с помощью этого кода вы не сможете изменить исходный массив $values. Так что, если вы хотите, чтобы изменения (например, добавление элемента test) касались также исходного массива, вам нужно изменить функцию getValues() так, чтобы она возвращала ссылку на сам массив $values. Вы можете это сделать, добавив & перед именем функции, показывая таким образом, что она должна вернуть ссылку: class Config {    private $values = [];    // return a REFERENCE to the actual $values array    { // возвращает ССЫЛКУ на фактический массив $values }    public function &getValues() {        return $this->values;    } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test']; Как мы и хотели, результатом будет test.  Но давайте запутаем ситуацию еще больше. Рассмотрим следующий код: class Config {    private $values;    // using ArrayObject rather than array    { // используем ArrayObject вместо массива }    public function __construct() {        $this->values = new ArrayObject();    }    public function getValues() {        return $this->values;    } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test']; Если вы считаете, что здесь мы получим ту же ошибку с неопределенным индексом (что и в предыдущем примере), то вы ошибаетесь. На самом деле, этот код будет отлично работать. Это связано с тем, что PHP всегда передает объекты по ссылке (в отличие от массивов). (ArrayObject – это объект SPL, который полностью имитирует использование массивов, но при этом работает как объект.)  Как вы могли заметить, в PHP не всегда можно сразу понять, работаете вы с копией или с ссылкой. Поэтому важно знать, как ведут себя те или иные объекты по умолчанию (т.е. то, что переменные и массивы передаются по значению, а объекты – по ссылке). Кроме того, стоит хорошо изучить документацию API для функции, которую вы вызываете, чтобы понимать, что она возвращает (значение, копию массива, ссылку на массив или ссылку на объект). При этом стоит отметить, что возвращение ссылок на массив или ArrayObject, как правило, считается не самой лучшей практикой, так как вызывающая сторона может изменить частные данные экземпляра. Это противоречит понятию инкапсуляции. Вместо этого лучше использовать старомодные геттеры и сеттеры. Например, class Config {    private $values = [];       public function setValue($key, $value) {        $this->values[$key] = $value;    }       public function getValue($key) {        return $this->values[$key];    } } $config = new Config(); $config->setValue('testKey', 'testValue'); echo $config->getValue('testKey');  // echos 'testValue' При таком подходе вызывающая сторона может задать или получить любое значение массива, не предоставляя общий доступ к самому массиву $values, который скрыт для других.  Ошибка №4. Выполнение запросов внутри цикла Нередко можно столкнуться с чем-то подобным, что приводит к тому, что ваш PHP-код не работает: $models = []; foreach ($inputValues as $inputValue) {    $models[] = $valueRepository->findByValue($inputValue); } По сути, здесь все верно, но если вы проследите за логикой кода, то можете обнаружить, что безвредный на первый взгляд вызов $valueRepository->findByValue() в итоге приводит к появлению некоторого запроса: $result = $connection->query("SELECT `x`,`y` FROM `values` WHERE `value`=" . $inputValue); В результате, с каждой итерацией будет выполняться запрос к базе данных. Поэтому, если вы передадите в цикл массив из 1000 элементов, то он создаст 1000 отдельных запросов к базе данных! А если этот сценарий будет вызываться в нескольких потоках, то в теории это может привести к полной остановке системы.   Именно поэтому очень важно уметь распознавать, когда ваш код создает запросы, и по возможности собирать эти значения, чтобы выполнить один запрос, который вернет все результаты.  Существуют довольно распространенные ситуации, когда запросы выполняются неэффективно (т.е. в цикле). Например, когда отправляется форма со списком значений (например, идентификаторов). После чего код будет перебирать массив в цикле и выполнять отдельный SQL-запрос для каждого идентификатора, чтобы получить полные данные записи для каждого из них. В большинстве случаев это будет выглядеть следующим образом: $data = []; foreach ($ids as $id) {    $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` = " . $id);    $data[] = $result->fetch_row(); } Но то же самое можно выполнить гораздо более эффективным способом. Вам понадобиться всего один SQL-запрос: $data = []; if (count($ids)) {    $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` IN (" . implode(',', $ids));    while ($row = $result->fetch_row()) {        $data[] = $row;    } } Таким образом, очень важно уметь распознавать, выполняет ваш код запросы напрямую или нет. По возможности соберите значения, а потом, чтобы получить все результаты, выполните единый запрос. И все же даже здесь следует быть осторожным. И здесь мы плавно переходим к следующей распространенной ошибке… Ошибка №5. Ложное и нерациональное использование памяти Несмотря на то, что извлечение множества записей за раз куда как более эффективно, чем выполнение запросов для каждой строки, которую вы хотите извлечь, если вы используете расширение PHP mysql и библиотеку libmysqlclient, то такой подход в теории может привести к ситуации под названием «недостаточно памяти».  Чтобы понять, о чем я говорю, давайте посмотрим на пример с ограниченными ресурсами (512 Мб оперативной памяти), MySQL и php-cli. Давайте загрузим таблицу базы данных: // connect to mysql { // подключаемся к MySQL } $connection = new mysqli('localhost', 'username', 'password', 'database'); // create table of 400 columns { // создаем таблицу из 400 столбцов } $query = 'CREATE TABLE `test`(`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT'; for ($col = 0; $col < 400; $col++) {    $query .= ", `col$col` CHAR(10) NOT NULL"; } $query .= ');'; $connection->query($query); // write 2 million rows { // добавляем 2 миллиона строк } for ($row = 0; $row < 2000000; $row++) {    $query = "INSERT INTO `test` VALUES ($row";    for ($col = 0; $col < 400; $col++) {        $query .= ', ' . mt_rand(1000000000, 9999999999);    }    $query .= ')';    $connection->query($query); } Окей, а теперь давайте проверим, как используются ресурсы: // connect to mysql { // подключаемся к MySQL } $connection = new mysqli('localhost', 'username', 'password', 'database'); echo "Before: " . memory_get_peak_usage() . "\n"; $res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 1'); echo "Limit 1: " . memory_get_peak_usage() . "\n"; $res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 10000'); echo "Limit 10000: " . memory_get_peak_usage() . "\n"; Вот так выглядит результат: Before: 224704 Limit 1: 224704 Limit 10000: 224704 Отлично. Выглядит так, как будто с точки зрения ресурсов запрос благополучно управляется внутри.  Но, чтобы убедиться наверняка, давайте увеличим значение LIMIT до 100 000. Опа, и вот что мы получим: PHP Warning:  mysqli::query(): (HY000/2013):              Lost connection to MySQL server during query in /root/test.php on line 11 Что же произошло? Здесь проблема заключается в том, как работает модуль PHP mysql. По сути это просто прокси для libmysqlclient, который выполняет всю грязную работу. Когда часть данных уже выбрана, они попадаю непосредственно в память. А так как эта память никак не управляется менеджером PHP, функция memory_get_peak_usage() не сообщит нам о том, что количество использованных ресурсов возросло, так как мы увеличиваем LIMIT в нашем запросе. Это приводит к проблемам, о которых мы говорили выше. Получается, что нас обманным путем пытаются уверить, что с нашим управлением памятью все в порядке. Но по сути наше управление памятью далеко не идеально, и мы можем столкнуться с проблемами (такими как мы описывали выше).  Во всяком случае, вы можете избежать ситуаций с ложным использованием памяти, описанных выше. Для этого вам нужно использовать модуль mysqlnd. Хотя сам по себе он вам никак не поможет оптимизировать использование вашей памяти. Этот модуль скомпилирован как собственное расширение PHP, и он использует диспетчер памяти PHP. Таким образом, если мы запустим тестовый код, приведенный выше, и вместо mysql воспользуемся mysqlnd, то получим гораздо более реалистичную картину использования нашей памяти: Before: 232048 Limit 1: 324952 Limit 10000: 32572912 И кстати, она будет куда хуже. Если обратиться к документации PHP, то там можно найти, что mysql использует в два раза больше ресурсов для хранения данных, чем mysqlnd. Именно поэтому исходный сценарий, в котором мы использовали mysql, на самом деле использовал даже больше памяти, чем продемонстрировано здесь (примерно в два раза больше).  Дабы избежать такого рода проблем, попробуйте ограничить размер ваших запросов или использовать цикл с меньшим количеством итераций. Например, $totalNumberToFetch = 10000; $portionSize = 100; for ($i = 0; $i <= ceil($totalNumberToFetch / $portionSize); $i++) {    $limitFrom = $portionSize * $i;    $res = $connection->query(                         "SELECT `x`,`y` FROM `test` LIMIT $limitFrom, $portionSize"); } Если мы рассуждаем об этой ошибке и ошибке №4, то должны понимать, что необходимо найти золотую середину между слишком детализированными и повторяющимися запросами и огромными штучными запросами. Как и почти во всем в этой жизни, необходим баланс; любая крайность может навредить корректной работе PHP.   Ошибка №6. Игнорирование проблем с Unicode/UTF-8 В некотором смысле это скорее проблема самого PHP, нежели что-то, с чем вы столкнетесь при отладке кода. Однако эту проблему так толком и не решили. Ядро PHP 6 должно было поддерживать Unicode, но, так как разработка PHP 6 была приостановлена в 2010 году, с этим пришлось повременить.  Но это вовсе не значит, что разработчики могут игнорировать правильную обработку UTF-8 и думать, что все строки обязательно будут «старым добрым ASCII». Код, который не может правильно обработать не-ASCII строки, печально известен внедрением опасных гейзенбагов. Даже простые вызовы strlen($_POST['name']) могут вызвать проблемы, если некто с фамилией Schr?dinger захочет зарегистрироваться в вашей системе.  Вашему внимаю предоставляю небольшой чек-лист, который поможет вам избежать таких проблем в вашем коде: Если вы мало что знаете о Unicode и UTF-8, то изучите, по крайней мере, основы.  Вместо старых строковых функций используйте функции mb_* (убедитесь, что в вашей сборке PHP есть расширение multibyte). Убедитесь, что ваша база данных и таблицы настроены таким образом, что могут использовать Unicode (многие сборки MySQL все еще используют по умолчанию latin1). Помните, что json_encode() преобразует не-ASCII символы (например, Schr?dinger преобразуется в Schr\u00f6dinger), а serialize() - нет.  Убедитесь, что ваши файлы с PHP-кодом имеют кодировку UTF-8. Это необходимо, чтобы избежать конфликтов при объединении строк с жестко запрограммированными или сконфигурированными строковыми постоянными. Ошибка №7. Предполагать, что $_POST всегда будет содержать ваши данные POST-запроса Несмотря на свое название, массив $_POST не всегда будет содержать ваши данные POST-запроса. Он и вовсе может оказаться пустым. Чтобы разобраться в этом, давайте рассмотрим пример. Допустим, что мы делаем запрос к серверу, вызвав jQuery.ajax(): // js $.ajax({    url: 'http://my.site/some/path',    method: 'post',    data: JSON.stringify({a: 'a', b: 'b'}),    contentType: 'application/json' }); (К слову, обратите внимание, что здесь есть contentType: 'application/json'. Мы отправляем данные в формате JSON, что довольно популярно для API. Это стандартная практика, например, при добавлении службы $http в AngularJS.) Мы просто распечатываем массив $_POST на серверной стороне: // php var_dump($_POST); Сюрприз! А вот и результат: array(0) { } Но почему? Что случилось со строкой JSON {a: 'a', b: 'b'}? Ответ таков: PHP анализирует полезную нагрузку POST-запроса автоматически только в том случае, если она имеет тип содержимого application/x-www-form-urlencoded или multipart/form-data. Причины такого поведения исторически сложившиеся – много лег назад, когда $_POST был только реализован в PHP, это были единственные типы содержимого. Таким образом, PHP не загружает полезную нагрузку POST-запроса автоматически ни для каких других типов содержимого (даже для тех, которые считаются довольно популярными на сегодняшний день, например, application/json). Так как $_POST - это суперглобальная переменная, то переопределив ее один раз (желательно в самом начале нашего сценария), мы сможем пользоваться этим измененным значением (т.е. в том числе и полезной нагрузкой POST-запроса) во всем нашем коде. Это очень важно, так как фреймворки PHP и почти все пользовательские сценарии используют $_POST для того, чтобы извлекать и преобразовывать данные запроса.  Поэтому, если мы, например, обрабатываем полезную нагрузку POST-запроса с типом содержимого application/json, то мы должны вручную проанализировать содержимое запроса (т.е. декодировать данные JSON) и переопределить переменную $_POST: // php $_POST = json_decode(file_get_contents('php://input'), true); После чего, когда мы распечатаем массив, мы увидим, что он должным образом содержит полезную нагрузку POST-запроса: array(2) { ["a"]=> string(1) "a" ["b"]=> string(1) "b" } Ошибка №8. Думать о том, что PHP поддерживает символьный тип данных Посмотрите на следующий фрагмент кода и попробуйте догадаться, что он выведет: for ($c = 'a'; $c <= 'z'; $c++) {    echo $c . "\n"; } Если вы ответили, что он выведет буквы от «a» до «z», то, вы будете удивлены – это не так. Да, он выведет буквы от «а» до «z», но потом он также выведет сочетания букв от «аа» до «yz». Давайте разберемся, почему так происходит. В PHP нет такого типа данных как char; есть только тип string. Принимая это в расчет, мы должны понимать, что, увеличив строку z, PHP выдаст aa: php> $c = 'z'; echo ++$c . "\n"; aa Впрочем, запутаю вас еще больше: с лексикографической точки зрения aa меньше, чем z: php> var_export((boolean)('aa' < 'z')) . "\n"; true Именно поэтому пример кода, который мы привели в начале, сначала напечатает буквы от a до z, а потом также напечатает сочетания букв от aa до yz. Он остановится, когда достигнет значения za – первого значения, которое «больше», чем z: php> var_export((boolean)('za' < 'z')) . "\n"; false А раз так, то вот один из способов, как можно правильно перебрать в PHP значения от «а» до «z»: for ($i = ord('a'); $i <= ord('z'); $i++) {    echo chr($i) . "\n"; } Или вот так: $letters = range('a', 'z'); for ($i = 0; $i < count($letters); $i++) {    echo $letters[$i] . "\n"; } Ошибка №9. Игнорирование стандартов написания кода И хотя игнорирование стандартов написания кода не приводит сразу к необходимости отлаживать PHP-код, это все же, пожалуй, одна из самых важных тем, которую стоит здесь обсудить.  Если вы будете игнорировать эти стандарты, то можете столкнуться с целым рядом проблем в своем проекте. В самом лучшем случае это приведет к тому, что ваш код будет непоследовательным (так как каждый разработчик «занимается своим делом»). А в худшем случае вы получите PHP-код, который не будет работать или будет слишком сложен для перемещения по нему (в некоторых случаях это может быть практически невозможно), что, соответственно, приводит к тому, что его будет крайне сложно отлаживать, совершенствовать и сопровождать. А это значит, что продуктивность вашей команды снизится, а все из-за того, что вы будете тратить свои силы впустую.  К счастью для PHP-разработчиков, существуют Рекомендации по стандартам PHP (PSR - PHP Standards Recommendation). В ней есть пять стандартов: PSR-0: Стандарт автозагрузки PSR-1: Базовый стандарт оформления кода PSR-2: Рекомендации по оформлению кода PSR-3: Интерфейс протоколирования PSR-4: Улучшенная автозагрузка Изначально PSR был создан на базе материалов, полученных от лиц, сопровождающих наиболее узнаваемые на рынке платформы. Свой вклад внесли Zend, Drupal, Symfony, Joomla и прочие, и теперь они придерживаются этих стандартов. И даже PEAR, который много лет назад делал попытку стать стандартом, теперь является частью PSR. В некоторым смысле практически неважно, какой стандарт вы используете, если вы принимаете его «условия игры» и придерживаетесь его. Однако, лучше всего придерживаться именно PSR, если у вас нет каких-то веских причин этого не делать. Все больше и больше команд и проектов соответствуют требованиям PSR. На данный момент большая часть разработчиков PHP абсолютно точно признала его Стандартом, поэтому, используя его, вы можете быть уверены, что новым разработчикам, которые будут присоединяться к вашей команде, этот стандарт будет знаком, и они будут чувствовать себя комфортно.  Ошибка №10. Неправильное использование метода empty() Некоторые разработчики PHP очень любят использовать метод empty() для логических проверок практически всего. Однако бывают моменты, когда это может привести к путанице. Для начала давайте вернемся к массивам и экземплярам ArrayObject (которые имитируют массивы). Учитывая их сходство, можно предположить, что массивы и экземпляры ArrayObject будут вести себя одинаково. Но это далеко не так. Например, вот что происходит в PHP 5.0: // PHP 5.0 or later: { // PHP 5.0 и более поздние версии: } $array = []; var_dump(empty($array));  // outputs bool(true) { // выводит bool(true) } $array = new ArrayObject(); var_dump(empty($array));  // outputs bool(false) { // выводит bool(false) } // why don't these both produce the same output? { // почему мы получаем разный результат? } А знаете, что еще хуже? Для более ранних версий PHP (до 5.0) результаты были бы другими: // Prior to PHP 5.0: { // версии, предшествующие PHP 5.0: } $array = []; var_dump(empty($array));  // outputs bool(false) { // выводит bool(false) } $array = new ArrayObject(); var_dump(empty($array));  // outputs bool(false) { // выводит bool(false) } Как ни печально, но такой подход довольно популярен. Например, как предполагает документация, Zend\Db\TableGateway из Zend Framework 2 именно таким образом возвращает данные при вызове current() для результата TableGateway::select(). Разработчик может легко допустить ошибку, используя такие данные.  Для того, чтобы избежать таких проблем, лучше всего для проверки пустых массивов использовать функцию count(): // Note that this work in ALL versions of PHP (both pre and post 5.0): { // Обратите внимание, что это работает во ВСЕХ версиях PHP (и тех, что были до 5.0, и тех, что были после 5.0) } $array = []; var_dump(count($array));  // outputs int(0) { // выводит int(0) } $array = new ArrayObject(); var_dump(count($array));  // outputs int(0) { // выводит int(0) } И к слову, так как PHP преобразует 0 в false, функцию count() можно использовать для проверки пустых массивов в условной конструкции if (). Также стоит отметить, что эта функция имеет постоянную сложность (то есть O(1)), что еще больше доказывает тот факт, что это правильный выбор.  Есть еще один пример, когда метод empty() может быть опасной. Речь идет о ее совместном использовании с функцией класса Magic – функцией __get(). Давайте определим два класса и свойство test в каждом из них.  Для начала давайте определим класс Regular, у которого свойство test - это обычное свойство: class Regular { public $test = 'value'; } А теперь давайте определим класс Magic, который для доступа к свойству test использует оператор __get(): class Magic { private $values = ['test' => 'value']; public function __get($key) { if (isset($this->values[$key])) { return $this->values[$key]; } } } Окей, а теперь давайте посмотрим, что будет происходить, когда мы попытаемся получить доступ к свойству test каждого из этих классов: $regular = new Regular(); var_dump($regular->test);  // outputs string(4) "value" { // выводит string(4) “value” } $magic = new Magic(); var_dump($magic->test);  // outputs string(4) "value" { // выводит string(4) “value” } Пока все хорошо. А теперь давайте посмотрим, что будет происходить, когда вы вызовем метод empty() для каждого из эти классов: var_dump(empty($regular->test));  // outputs bool(false) { // выводит bool(false) } var_dump(empty($magic->test));  // outputs bool(true) { // выводит bool(true) } Ах ты! Так что, если мы прибегаем к помощи метода empty(), то мы можем прийти в неверным выводам, полагая, что свойство test ($magic) пусто, хотя на самом деле у него есть значение - 'value'. Увы, но если класс для получения значения свойства использует функцию __get(), то нет какого-то беспроигрышного способа проверить, является ли свойство пустым или нет. Если вы находитесь вне области видимости класса, то вы сможете лишь проверить, вернется ли нулевое значение (null), и это не обязательно значит, что соответствующее свойство не существует, ему просто могли присвоить значение null. Для сравнения, если мы попытаемся обратиться к несуществующему свойству экземпляра класса Regular, то вы получим вот такое уведомление: Notice: Undefined property: Regular::$nonExistantTest in /path/to/test.php on line 10 Call Stack:    0.0012 234704 1. {main}() /path/to/test.php:0 Так что, главное, что нужно запомнить, что метод empty() стоит использовать аккуратно, так как в противном случае вы можете получить довольно запутанные (или даже неправильные) результаты.  Заключение Простота использования PHP может усыпить бдительность разработчиков, внушив им ложное чувство комфорта. Однако из-за некоторых нюансов и особенностей языка разработчики могут попасть в ловушку под названием «утомительная отладка». В результате, PHP-код может просто не заработать или могут возникнуть проблемы, о которых мы говорили в этой статье.  PHP неплохо эволюционировал за свою 20-летнюю историю. Так что, вам определенно стоит ознакомиться с его тонкостями. Вы сможете создавать более масштабируемое, более надежное программное обеспечение, которое будет удобно в сопровождении. 
ЗИМНИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59