PHP https://chel-center.ru/php-yfc Курс молодого бойца Sun, 27 Dec 2020 11:08:27 +0000 ru-RU hourly 1 https://wordpress.org/?v=5.9.3 https://chel-center.ru/php-yfc/wp-content/uploads/sites/14/2019/10/cropped-avatar05-512x512-2-32x32.png PHP https://chel-center.ru/php-yfc 32 32 Зачем нужен PHP? https://chel-center.ru/php-yfc/zachem-nuzhen-php/ https://chel-center.ru/php-yfc/zachem-nuzhen-php/#respond Mon, 09 Sep 2019 03:14:59 +0000 http://chel-center.ru/php-yfc/?p=116 Читать далее «Зачем нужен PHP?»

]]>
Обсудим диалог клиента и сервера при отсутствии PHP. На рисунке 1 все реплики пронумерованы.

  1. Вы вводите www.example.com/catalog.html в строке веб-адреса, находящейся в верхней части окна браузера.
  2. Браузер посылает сообщение через Интернет на компьютер по адресу www.example.com, запрашивая страницу /catalog.html.
  3. НТТР-сервер Apache, работающий на компьютере по адресу www.example.com, получает сообщение и читает файл catalog.html из своего накопителя на жестких дисках.
  4. Веб-сервер посылает содержимое файла обратно на ваш компьютер через Интернет в качестве ответа на запрос браузера.
  5. Браузер отображает страницу на экране вашего компьютера, следуя инструкциям, указанным в дескрипторах НТМL-разметки данной страницы.

Связи клиента с сервером без PHP
Связи клиента с сервером без PHP

Всякий раз, когда браузер запрашивает страницу по адресу www.example.com/catalog.html, веб-сервер посылает обратно содержимое того же самого файла catalog.html. Ответ веб-сервера изменится лишь в том случае, если кто-нибудь отредактирует запрашиваемый файл на сервере. Но если задействовать РНР, то сервер сможет сделать нечто большее со своей стороны диалога. На рисунке 2 наглядно показано, что произойдет, когда веб-браузер запросит страницу, сгенерированную средствами РНР.

Отдельные реплики рассматриваемого здесь диалога с участием PHP пронумерованы на рисунке 2.

  1. Вы вводите www.example.com/catalog/yak.php в строке веб-адреса, находящейся в верхней части окна браузера.
  2. Браузер посылает сообщение через Интернет на компьютер по www.example.com, запрашивая страницу /catalog/yak.php.
  3. НТТР-сервер Apache, работающий на компьютере по адресу www.example.com, получает сообщение и обращается к интерпретатору РНР, также работающему на компьютере по адресу www.example.com, со следующим вопросом: "Как выглядит страница /catalog/yak.php?"
  4. Интерпретатор РНР читает файл yak.php из накопителя на жестких дисках.
  5. Интерпретатор РНР выполняет команды из файла yak.php, возможно, обмениваясь данными с системой управления базой данных, например MySQL.
  6. Интерпретатор РНР принимает результат выполнения программы из файла yak.php и посылает его обратно на НТТР-сервер Apache в качестве ответа на вопрос "Как выглядит страница /catalog/yak.php?"
  7. НТТР-сервер Apache посылает содержимое страницы, полученное обратно от интерпретатора РНР, на ваш компьютер через Интернет в ответ на запрос браузера.
  8. Этот браузер отображает страницу на экране вашего компьютера, следуя инструкциям, указанным в дескрипторах НТМL-разметки данной страницы.
Связи клиента с сервером c PHP
Связи клиента с сервером c PHP

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

]]>
https://chel-center.ru/php-yfc/zachem-nuzhen-php/feed/ 0
Чем зарядить компьютер для разработки Web‑систем https://chel-center.ru/php-yfc/chem-zaryadit-kompjuter-dlya-razrabotki-web-sistem/ https://chel-center.ru/php-yfc/chem-zaryadit-kompjuter-dlya-razrabotki-web-sistem/#respond Fri, 27 Sep 2019 16:20:58 +0000 http://chel-center.ru/php-yfc/?p=11 Читать далее «Чем зарядить компьютер для разработки Web‑систем»

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

Итак, по-порядку:

  1. XAMPP, самая популярная среда разработки для Web — Apache+MariaDB+PHP+Perl+инструменты ;
  2. FTP-клиент — FileZilla ;
  3. Среда для разработки front-end — WebStorm ;
  4. Среда для разработки back-end — PHPStorm ;
  5. Клиент для работы с различными протоколами — Putty ;
  6. Распределённая система контроля версий — Git ;
  7. Редактор кода (дело вкуса — кому какой нравится, а лучше поставить все три):
    1. Notepad++ ;
    2. Sublime Text ;
    3. Atom .
  8. Графический редактор — PainNet ;
  9. Измеритель цвета пикселей — ColorPic ;
  10. Экранная линейка — Ruler .
  11. Менеджер БД MYSQL — dbForce Studio Express ;
  12. Редактор звуковых файлов — Audasity ;
  13. Браузеры для тестирования своих приложений:
    1. Firefox Quantum ;
    2. Google Chrome ;
    3. Opera ;
    4. Microsoft Edge ;
    5. 360 Safety Browser .

P.S.

  1. Обязательно заведите себе аккаунты на Яндексе и Github, если их ещё нет. Возможно, будем пользовать API Яндекса, а систему контроля версий будем пользовать однозначно.
  2. Во время проведения занятий все учебные материалы будут выкладываться на наш Github.
]]>
https://chel-center.ru/php-yfc/chem-zaryadit-kompjuter-dlya-razrabotki-web-sistem/feed/ 0
Осваиваем PhpStorm https://chel-center.ru/php-yfc/osvaivaem-phpstorm/ https://chel-center.ru/php-yfc/osvaivaem-phpstorm/#respond Fri, 27 Sep 2019 16:38:59 +0000 http://chel-center.ru/php-yfc/?p=24 PhpStorm — это профессиональная кросс-платформенная среда разработки от компании JetBrains написанная на языке JAVA. Это по настоящему мощная и компактная IDE предназначенная для программирования на таких языках как: PHP и JavaScript. Эта среда будет нашим основным инструментов для блока Back-end разработки Web-систем.

]]>
https://chel-center.ru/php-yfc/osvaivaem-phpstorm/feed/ 0
Структура PHP https://chel-center.ru/php-yfc/struktura-php/ https://chel-center.ru/php-yfc/struktura-php/#respond Sat, 28 Sep 2019 06:00:29 +0000 http://chel-center.ru/php-yfc/?p=251 Читать далее «Структура PHP»

]]>
В этом уроке будет рассмотрено довольно много основных положений. Разобраться во всем этом несложно, но я рекомендую проработать материал как можно тщательнее, поскольку он служит основой для понимания всего остального.

Содержание урока

Комментарии

Существует два способа добавления комментариев к коду PHP. Первый, предусматривающий размещение в начале строки двух прямых слешей, превращает в комментарий отдельную строку:

// Это комментарий

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

// echo "X equals $x";

Такой комментарий можно также вставить сразу же после строки кода, чтобы описать ее действие:

$x += 10; // Увеличение значения $x на 10

Когда понадобится комментарий, состоящий из нескольких строк, нужно воспользоваться вторым способом комментирования, который показан ниже:

/* Это область
многострочного комментария,
которая не будет
подвергаться интерпретации */

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

Типичная ошибка — применение пар символов /* и */ для того, чтобы закомментировать большой фрагмент кода, уже содержащий закомментированную область, в которой используются эти же пары символов. Комментарии не могут быть вложенными друг в друга, поскольку PHP-интерпретатор не поймет, где заканчивается комментарий, и выведет на экран сообщение об ошибке. Но если вы используете редактор программ или интегрированную среду разработки с подсветкой синтаксиса, то ошибку такого рода нетрудно будет заметить.

Основной синтаксис

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

Точка с запятой

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

$x += 10;

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

Символ $

Символ $ используется в разных языках программирования в различных целях. Например, в языке BASIC символ $ применялся в качестве завершения имен переменных, чтобы показать, что они относятся к строкам. А в PHP символ $ должен ставиться перед именами всех переменных. Это нужно для того, чтобы PHP‑парсер работал быстрее, сразу же понимая, что имеет дело с переменной. К какому бы типу ни относились переменные — к числам, строкам или массивам, все они должны выглядеть так, как показано в ниже:

<?php
$mycounter = 1;
$mystring = "Hello";
$myarray = array("One", "Two", "Three");
?>

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

Переменные

Понять, что такое переменные PHP, поможет простая метафора. Думайте о них как о небольших (или больших) спичечных коробках! Именно как о спичечных коробках, которые вы раскрасили и на которых написали некие имена.

Строковые переменные

Представьте, что у вас есть коробок, на котором написано слово username (имя пользователя). Затем вы пишете на клочке бумаги Fred Smith и кладете эту бумажку в коробок. Этот процесс похож на присваивание переменной строкового значения:

$username = "Fred Smith";
Переменные можно представить в виде спичечного коробка,   содержащего какие-то предметы
Переменные можно представить в виде спичечного коробка,
содержащего какие-то предметы

Кавычки служат признаком того, что Fred Smith является строкой символов. Каждую строку нужно заключать либо в двойные, либо в одинарные кавычки (апострофы). Между этими двумя видами кавычек есть весьма существенное различие, которое будет рассмотрено далее. Когда хочется посмотреть, что находится внутри коробка, вы его открываете, вынимаете бумажку и читаете, что на ней написано. В PHP подобное действие выглядит следующим образом:

echo $username;

Можно также присвоить содержимое другой переменной (сделать ксерокопию бумажки и поместить ее в другой коробок):

$current_user = $username;

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

<?php // test1.php
$username = "Fred Smith";
echo $username;
echo "
"; $current_user = $username; echo $current_user; ?>

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

http://localhost/test1.php

Результатом запуска этого кода будет двойное появление имени Fred Smith, первое — в результате выполнения команды echo $username, а второе — в результате выполнения команды echo $current_user.

Числовые переменные

Переменные могут содержать не только строки, но и числа. Если вернуться к аналогии со спичечным коробком, сохранение в переменной $count числа 17 будет эквивалентно помещению, скажем, 17 бусин в коробок, на котором написано слово count:

$count = 17;

Можно также использовать числа с плавающей точкой (содержащие десятичную точку); синтаксис остается прежним:

$count = 17.5;

Чтобы узнать о содержимом коробка, его нужно просто открыть и посчитать бусины. В PHP можно присвоить значение переменной $count другой переменной или вывести его с помощью браузера на экран, воспользовавшись командой echo.

Массивы

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

Вдоль всей верхней стороны склеенных вместе коробков напишем слово team (см. рисунок ниже). В PHP эквивалентом этому действию будет следующий код:

$team = array('Bill', 'Mary', 'Mike', 'Chris', 'Anne');
Массив похож на несколько склеенных вместе спичечных коробков
Массив похож на несколько склеенных вместе спичечных коробков

Этот синтаксис несколько сложнее рассмотренных ранее инструкций. Код создания массива представляет собой следующую конструкцию:

array();

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

echo $team[3]; // Эта команда отображает имя Chris

Использование в предыдущем примере числа 3, а не 4 обусловлено тем, что первый элемент PHP‑массива является, как правило, нулевым, поэтому номера игроков распределяются в интервале от 0 до 4.

Двумерные массивы

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

Многомерный массив, смоделированный с помощью коробков
Многомерный массив, смоделированный с помощью коробков

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

<?php
$oxo = array(array('x', ' ', 'o'),
array('o', 'o', 'x'),
array('x', 'o', ' ' ));
?>

Мы сделали еще один шаг к усложнению, но смысл его нетрудно понять, если усвоен основной синтаксис массива. Здесь три конструкции array() вложены во внешнюю по отношению к ним конструкцию array().
Для возвращения в дальнейшем третьего элемента во второй строке этого массива можно воспользоваться следующей PHP-командой, которая отобразит символ «x»:

echo $oxo[1][2];

Не забывайте о том, что отсчет индексов массива (указателей на элементы внутри массива) начинается с нуля, а не с единицы, поэтому в предыдущей команде индекс [1] ссылается на второй из трех массивов, а индекс [2] — на третью позицию внутри этого массива. Эта команда вернет содержимое третьего слева и второго сверху коробка.

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

Правила присваивания имен переменным

При создании PHP-переменных следует придерживаться четырех правил.

  1. Имена переменных должны начинаться с буквы или с символа _ (подчеркивания).
  2. Имена переменных могут содержать только символы: a–z, A–Z, 0–9 и _ (подчеркивание).
  3. Имена переменных не должны включать в себя пробелы. Если имя переменной нужно составить более чем из одного слова, то в качестве разделителя следует использовать символ подчеркивания (например, $user_name).
  4. Имена переменных чувствительны к регистру символов. Переменная $High_Score отличается от переменной $high_score.

Чтобы позволить использование ASCII-символов, включающих диакритические знаки, PHP также поддерживает в именах переменных байты от 127 и до 255. Но пока ваш код не будет поддерживаться только теми программистами, которые знакомы с такими символами, от их применения лучше отказаться, поскольку программисты, использующие английские раскладки клавиатуры, будут испытывать трудности при доступе к таким символам.

Операторы

Операторы — это математические, строковые, логические команды и команды сравнения, такие как «плюс», «минус», «умножить» и «разделить». Код PHP во многом похож на обычные арифметические записи. Например, в результате работы следующего оператора выводится число 8:

echo 6 + 2;

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

Арифметические операторы

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

Таблица. Арифметические операторы

Оператор
Описание
Пример
+
Сложение
$j + 1
Вычитание
$j – 6
*
Умножение
$j * 11
/
Деление
$j / 4
%
Модуль (остаток от деления)
$j % 9
++
Инкремент (приращение)
++$j
––
Декремент (отрицательное приращение)
––$j

Операторы присваивания

Эти операторы используются для присваивания значений переменным. К ним относится самый простой оператор =, а также операторы +=, –= и т. д. (см. таблицу). Оператор += вместо полного замещения находящегося слева значения добавляет к нему значение, которое находится справа от него. Итак, если переменная $count имела начальное значение 5, то оператор:

$count += 1;

устанавливает значение $count равным 6 точно так же, как более привычный оператор присваивания:

$count = $count + 1;

Таблица. Операторы присваивания

Оператор
Пример
Эквивалент
=
$j = 15
$j = 15
+=
$j += 5
$j = $j + 5
–=
$j –= 3
$j = $j – 3
*=
$j *= 8
$j = $j * 8
/=
$j /= 16
$j = $j / 16
.=
$j .= $k
$j = $j . $k
%=
$j %= 4
$j = $j % 4

У строк есть собственный оператор, точка (.), который более подробно будет рассмотрен в разделе Объединение строк.

Операторы сравнения

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

Таблица. Операторы сравнения

Оператор
Описание
Пример
==
Равно
$j == 4
!=
Не равно
$j != 21
>
Больше
$j > 3
<
Меньше
$j < 100
>=
Больше или равно
$j >= 15
<=
Меньше или равно
$j <= 8

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

Логические операторы

Если логические операторы вам раньше не встречались, то поначалу они могут показаться чем-то необычным. Нужно представить, что вы делаете логические заключения на простом разговорном языке. Например, можно сказать самому себе: «Если время уже больше 12, но меньше 14 часов, значит, нужно пообедать». В PHP код для такого высказывания может выглядеть следующим образом:

if ($hour > 12 && $hour < 14) dolunch();

Здесь набор инструкций для самого обеда помещен в функцию по имени dolunch, которую позже нужно будет создать. В этой инструкции отсутствует элемент then (тогда), поскольку его присутствие само собой разумеется. Как видно из предыдущего примера, логический оператор обычно используется для объединения результатов работы двух операторов сравнения, показанных в предыдущем разделе. Результат работы одного логического оператора может служить входным значением для другого логического оператора («Если время уже больше 12, но меньше 14 часов или же если в комнате пахнет жареным и тарелки уже стоят на столе...»). Как правило, если какое-то действие имеет истинное или ложное значение — TRUE или FALSE, оно может служить входным значением для логического оператора, который берет два истинных или ложных входных значения и выдает в качестве результата истинное или ложное значение. Логические операторы показаны в таблице.

Таблица. Логические операторы

Оператор
Описание
Пример
&&
И
$j == 3 && $k == 2
and
Низкоприоритетное И
$j == 3 and $k == 2
||
ИЛИ
$j < 5 || $j > 10
or
Низкоприоритетное ИЛИ
$j < 5 or $j > 10
!
НЕ
! ($j == $k)
xor
Исключающее ИЛИ
$j xor $k

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

mysql_select_db($database) or die("Невозможно выбрать базу данных");

Наиболее непривычным из этих операторов является xor, предназначенный для операции исключающего ИЛИ, который возвращает истинное значение TRUE, если любое из входных значений истинно, и возвращает ложное значение FALSE, если оба они имеют значение TRUE или FALSE. Чтобы понять его работу, представьте, что хотите изобрести чистящее средство для дома. Как аммиак (ammonia), так и хлорка (bleach) обладают хорошими чистящими свойствами, поэтому нужно, чтобы ваше средство содержало одно из этих веществ. Но оба они не могут в нем присутствовать, поскольку их сочетание опасно. В PHP это можно представить в следующем виде:

$ingredient = $ammonia xor $bleach;

В представленном фрагменте, если любая из двух переменных, $ammonia или $bleach, имеет значение TRUE, то значение переменной $ingredient также будет установлено в TRUE. Но если обе они имеют значение TRUE или значение FALSE, то значение переменной $ingredient будет установлено в FALSE.

Присваивание значений переменным

Синтаксис присваивания значения переменной всегда имеет вид переменная = значение. Для передачи значения другой переменной он имеет немного иной вид другая_переменная = переменная.
Есть еще несколько дополнительных операторов присваивания, которые могут оказаться полезными. Например, нам уже встречался оператор:

$x += 10;

Он предписывает PHP‑парсеру добавить значение, расположенное справа от него (в данном случае это значение равно 10), к значению переменной $x. Подобным образом можно вычесть значение:

$y -= 10;

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

++$x;
--$y;

В сочетании с проверкой (инструкцией if) можно воспользоваться таким кодом:

if (++$x == 10) echo $x;

Этот код предписывает PHP сначала увеличить значение переменной $x на единицу, а затем проверить, не имеет ли она значение 10; если переменная имеет такое значение, его следует вывести на экран. Можно также потребовать от PHP увеличить значение переменной на единицу (или, как в следующем примере, уменьшить на единицу) после того, как ее значение будет проверено:

if ($y-- == 0) echo $y;

что дает несколько иной результат. Предположим, что первоначальное значение переменной $y до выполнения оператора было равно нулю. Операция сравнения вернет результат TRUE, но после того, как она будет проведена, переменной $y будет присвоено значение –1. Тогда что же отобразит инструкция echo: 0 или –1? Попробуйте догадаться, а потом, чтобы подтвердить свою догадку, испытайте работу инструкции в PHP⏹процессоре. Поскольку такая комбинация операторов может вас запутать, ее можно применять только в качестве обучающего примера, но ни в коем случае не рассматривать в качестве приемлемого стиля программирования.

Короче говоря, когда именно увеличено или уменьшено на единицу значение переменной, до или после проверки, зависит от того, где помещен оператор инкремента или декремента — перед именем переменной или после него. Кстати, правильный ответ на предыдущий вопрос таков: инструкция echo отобразит результат –1, потому что значение переменной $y было уменьшено на единицу сразу же после того, как к ней получила доступ инструкция if, и до того, как к ней получила доступ инструкция echo.

Объединение строк

При объединении строк, когда к одной строке символов добавляется другая строка, используется символ точки (.). Самый простой способ объединения строк выглядит следующим образом:

echo "У вас " . $msgs . " сообщений.";

Если предположить, что переменной $msgs присвоено значение 5, то эта строка кода выведет следующую информацию:

У вас 5 сообщений.

Так же как с помощью оператора += можно добавить значение к числовой переменной, с помощью оператора .= можно добавить одну строку к другой:

$bulletin .= $newsflash;

В данном случае, если в переменной $bulletin содержится сводка новостей, а в переменной $newsflash — экстренное сообщение, команда добавляет это сообщение к сводке новостей, и теперь переменная $bulletin включает в себя обе строки текста.

Типы строк

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

$info = 'Предваряйте имена переменных символом $, как в данном примере: $variable';

В данном случае переменной $info присваивается каждый символ, находящийся внутри строки в одинарных кавычках. Если воспользоваться двойными кавычками, то PHP попытается вычислить $variable и получить значение переменной. В то же время, если требуется включить в состав строки значение переменной, используется строка, заключенная в двойные кавычки:

echo "На этой неделе ваш профиль просмотрело $count человек ";

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

Изменение предназначения символов

Иногда в строке должны содержаться символы, которые имеют специальное предназначение и могут быть неправильно интерпретированы. Например, следующая строка кода не будет работать, потому что вторая кавычка (апостроф), встреченная в слове spelling's, укажет PHP‑парсеру на то, что достигнут конец строки. Следовательно, вся остальная часть строки будет отвергнута как ошибочная:

$text = 'My spelling's atroshus'; // Ошибочный синтаксис

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

$text = 'My spelling\'s still atroshus';

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

$text = "She wrote upon it, \"Return to sender\".";

Кроме того, для вставки в строку различных специальных символов, например табуляции, новой строки и возврата каретки, могут применяться управляющие символы: \t, \n и \r соответственно. Вот пример, в котором символы табуляции используются для разметки заголовка (они включены в строку исключительно для иллюстрации применения символа обратного слеша, поскольку существуют более подходящие способы разметки веб-страниц):

$heading = "Дата\tИмя\tПлатеж";

Эти специальные символы, предваряемые символами обратного слеша, работают только в строках, заключенных в двойные кавычки. Если заключить предыдущую строку в одинарные кавычки, то вместо символов табуляции в ней будут отображены нелепые последовательности символов \t. Внутри строк, заключенных в одинарные кавычки, в качестве символов с измененным предназначением распознаются только измененный апостроф (\') и сам измененный обратный слеш (\\).

Многострочные команды

Иногда нужно вывести из PHP большой объем текста, а использование нескольких инструкций echo (или print) заняло бы много времени и было бы неразумным. PHP предлагает два удобных средства, предназначенных для того, чтобы справиться с подобной ситуацией. Первое из них состоит в заключении в кавычки нескольких строк. Переменным также можно присвоить значения способом, показанным в примерах ниже.

<?php
$author = "Steve Ballmer";
echo "Developers, Developers, developers, developers, developers,developers, developers, developers, developers! - $author.";
?>

 

<?php
$author = "Bill Gates";
$text = "Measuring programming progress by lines of code is like measuring aircraft building progress by weight. - $author.";
?>

В PHP можно также воспользоваться многострочной последовательностью, используя оператор <<<, который обычно называют here-document («здесь документ») или heredoc. Он представляет собой способ указания строкового литерала, сохраняющего в тексте обрывы строк и другие пустые пространства (включая отступы). Его использование показано в примере, где еще один вариант инструкции echo, использующей сразу несколько строк

<?php
$author = "Brian W. Kernighan";
echo <<<_END
Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it.
- $author.
_END;
?>

Этот код предписывает PHP вывести все, что находится между двумя тегами _END, как будто все это является строкой, заключенной в двойные кавычки (за исключением того, что изменять предназначение кавычек в heredoc не нужно). Это означает, что разработчику можно, например, написать целый раздел HTML‑кода прямо в коде PHP, а затем заменить конкретные динамические части переменными PHP. Важно запомнить, что закрывающий тег _END; должен появляться строго в начале новой строки и быть единственным содержимым этой строки — к ней не разрешается добавлять даже комментарии (нельзя ставить даже одиночный пробел). Как только многострочный блок закрыт, можно снова воспользоваться тем же самым именем тега.

Запомните: используя heredocಉконструкцию <<<_END..._END;, вы не должны добавлять символы \n, чтобы отправить команду на перевод строки, достаточно просто нажать клавишу Enter и приступить к набору новой строки. В отличие от других строк, заключенных в одинарные или двойные кавычки, внутри конструкции heredoc можно по своему усмотрению совершенно свободно пользоваться всеми одинарными или двойными кавычками, не изменяя их первоначального предназначения с помощью обратного слеша (\).

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

<?php
$author = "Scott Adams";
$out = <<<_END
Normal people believe that if it ain't broke, don't fix it.
Engineers believe that if it ain't broke, it doesn't have enough
features yet.
- $author.
_END;
echo $out;
?>

После этого переменная $out будет наполнена содержимым, размещенным между двумя тегами. Если не присваивать, а добавлять значение, то для добавления строки к значению переменной $out вместо оператора = можно воспользоваться оператором .=. Будьте внимательны, не ставьте точку с запятой сразу же за первым тегом _END, поскольку она прервет многострочный блок еще до его начала и вызовет сообщение об ошибке синтаксического разбора — Parse error. Точку с запятой нужно ставить только после закрывающего тега _END, хотя внутри блока можно свободно пользоваться точкой с запятой как обычным текстовым символом. Кстати, тег _END — лишь один из многих, я выбрал его для этих примеров, поскольку его использование где-нибудь еще в коде PHP маловероятно, и поэтому он уникален. Вы можете использовать по собственному усмотрению любой тег, например _SECTION1 или _OUTPUT и т.д. И еще, чтобы отличать подобные теги от переменных или функций, обычно в начале их имени ставят знак подчеркивания; но если не хотите, можете им не пользоваться.

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

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

Типы переменных

PHP относится к очень слабо типизированным языкам. Это значит, что переменные не требуют объявления перед своим использованием и что PHP всегда преобразует переменные в тот тип, который требуется для их окружения на момент доступа к ним. Например, можно создать число, состоящее из нескольких цифр, и извлечь из него n-ю цифру, просто предположив, что это число является строкой. В следующем фрагменте кода перемножаются числа 12 345 и 67 890 и возвращается результат 838 102 050, который затем помещается в переменную $number.

<?php
$number = 12345 * 67890;
echo substr($number, 3, 1);
?>

Когда присваивается значение, $number является числовой переменной. Но во второй строке кода вызов значения этой переменной помещен в PHP-функцию substr, которая должна вернуть из переменной $number один символ, стоящий на четвертой позиции (не забывайте, что в отсчет позиции начинается с нуля). Для выполнения этой задачи PHP превращает $number в строку, состоящую из девяти символов, чтобы функция substr могла получить к ней доступ и вернуть символ, в данном случае 1. То же самое происходит при необходимости превратить строку в число и т.д. А в следующем примере переменной $pi присвоено строковое значение, которое затем в третьей строке кода автоматически превращается в число с плавающей точкой, чтобы стать частью уравнения по вычислению площади круга, которое выводит значение 78,539 817 5.

<?php
$pi = "3.1415927";
$radius = 5;
echo $pi * ($radius * $radius);
?>

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

Константы

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

define("ROOT_LOCATION", "/usr/local/www/");

Затем для чтения содержимого константы нужно просто сослаться на нее как на обычную переменную (но не предваряя ее имя знаком доллара):

$directory = ROOT_LOCATION;

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

Важно помнить о двух основных особенностях констант: перед их именами не нужно ставить символ $ (как перед именами обычных переменных) и их можно определить только с помощью функции define.

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

Предопределенные константы

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

Таблица. Волшебные константы PHP

Волшебная константа
Описание
__LINE__
Номер текущей строки в файле
__FILE__
Полное путевое имя файла. Если используется внутри инструкции include, то возвращается имя включенного файла. В версии PHP 4.0.2 __FILE__ всегда содержит абсолютный путь с раскрытыми символическими ссылками, а в предыдущих версиях при определенных обстоятельствах она может содержать относительный путь __DIR__ Каталог файла. Если используется внутри инструкции include, то возвращается каталог включенного файла. Такой же результат дает применение функции dirname(__FILE__). В этом имени каталога отсутствует замыкающий слеш, если только этот каталог не является корневым. (Добавлена в PHP 5.3.0)
__FUNCTION__
Имя функции. Начиная с PHP 5, возвращает имя функции, под которым она была объявлена (с учетом регистра символов). В PHP 4 возвращаемое значение всегда составлено из символов нижнего регистра. (Добавлена в PHP 4.3.0)
__CLASS__
Имя класса. Начиная с PHP 5, возвращает имя класса, под которым он был объявлен (с учетом регистра символов). В PHP 4 возвращаемое значение всегда составлено из символов нижнего регистра. (Добавлена в PHP 4.3.0)
__METHOD__
Имя метода класса. Возвращает имя метода, под которым он был объявлен (с учетом регистра символов). (Добавлена в PHP 5.0.0)
__NAMESPACE__
Имя текущего пространства имен (с учетом регистра символов). Эта константа определена во время компиляции. (Добавлена в PHP 5.3.0)

 

Эти константы полезны при отладке, когда нужно вставить строку кода, чтобы понять, до какого места дошло выполнение программы:

echo "Это строка " . __LINE__ . " в файле " . __FILE__;

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

Различие между командами echo и print

Нам уже встречались разнообразные способы использования команды echo для вывода текста с сервера в браузер. В одних случаях выводится строковый литерал, в других сначала происходило объединение строк или вычисление значений переменных. Был также показан вывод, распространяющийся на несколько строк. Но команде echo есть альтернатива, которой также можно воспользоваться: команда print. Эти две команды очень похожи друг на друга, но print — конструкция, похожая на функцию, воспринимающую единственный параметр и имеющую возвращаемое значение (которое всегда равно 1), а echo — в чистом виде конструкция языка PHP.

В общем, команда echo работает при выводе обычного текста быстрее print, поскольку она не устанавливает возвращаемое значение. С другой стороны, поскольку она не является функцией, ее, в отличие от print, нельзя использовать как часть более сложного выражения. В следующем примере для вывода информации о том, является значение переменной истинным (TRUE) или ложным (FALSE), используется функция print, но сделать то же самое с помощью команды echo не представляется возможным, поскольку она выведет на экран сообщение об ошибке синтаксического разбора — Parse error:

$b ? print "TRUE" : print "FALSE";

Использование вопросительного знака — самый простой способ задать вопрос о том, какое значение имеет переменная $b, истинное или ложное. Команда, которая располагается слева от двоеточия, выполняется в том случае, если $b имеет истинное значение, а команда, которая располагается справа, выполняется, если $b имеет ложное значение. Тем не менее в приводимых здесь примерах чаще всего используется команда echo, и я рекомендую применять именно ее до тех пор, пока вам при PHP‑разработке реально не потребуется задействовать функцию print.

Функции

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

<?php
function longdate($timestamp)
{
    return date("l F jS Y", $timestamp);
}
?>

Эта функция использует в качестве входных данных отметку времени системы UNIX (целое число, отображающее дату и время на основе количества секунд, прошедших с нуля часов 1 января 1970 года), а затем вызывает PHP-функцию date с нужным форматом строки, чтобы вернуть дату в формате «Вторник май 2 2017». Между стоящими после имени функции круглыми скобками может размещаться любое количество параметров, но для этой функции выбран прием только одного параметра. Весь код, который выполняется при последующем вызове функции, заключается в фигурные скобки. Чтобы с помощью этой функции вывести сегодняшнюю дату, нужно поместить в свой код следующий вызов:

echo longdate(time());

В этом вызове для извлечения текущей отметки времени UNIX и передачи ее только что созданной функции longdate, которая затем возвращает для отображения соответствующую строку команде echo, используется встроенная PHP-функция time. Если требуется вывести дату семнадцатидневной давности, нужно сделать следующий вызов:

echo longdate(time() - 17 * 24 * 60 * 60);

в котором функции longdate передается текущая отметка времени UNIX, уменьшенная на количество секунд, которое прошло за 17 дней (17 дней, 24 ч, 60 мин, 60 с). Функции могут также воспринимать несколько параметров и возвращать несколько результатов, используя технологию, которая будет рассмотрена в следующих главах.

Область видимости переменной

Если программа очень длинная, то с подбором подходящих имен переменных могут возникнуть трудности, но, программируя на PHP, можно определить область видимости переменной. Иными словами, можно, к примеру, указать, что переменная $temp будет использоваться только внутри конкретной функции, чтобы забыть о том, что она после возврата из кода функции применяется где-нибудь еще. Фактически именно такой в PHP является по умолчанию область видимости переменных. В качестве альтернативы можно проинформировать PHP о том, что переменная имеет глобальную область видимости и доступ к ней может быть осуществлен из любого места программы.

Локальные переменные

Локальные переменные создаются внутри функции, и к ним имеется доступ только из кода этой функции. Обычно это временные переменные, которые используются до выхода из функции для хранения частично обработанных результатов. Одним из наборов локальных переменных является перечень аргументов функции. В предыдущем разделе была определена функция, воспринимающая параметр по имени $timestamp. Значение этого параметра действительно только в теле функции, за пределами этой функции его значение нельзя ни получить, ни установить. Чтобы привести еще один пример локальной переменной, рассмотрим функцию longdate еще раз в немного измененном варианте.

<?php
function longdate($timestamp)
{
    $temp = date("l F jS Y", $timestamp);
    return "Дата: $temp";
}
?>

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

Неудачная попытка получить доступ к переменной $temp в функции longdate

<?php
$temp = "Дата: ";
echo longdate(time());
function longdate($timestamp)
{
    return $temp . date("l F jS Y", $timestamp);
}
?>

Но поскольку переменная $temp не была создана внутри функции longdate, а также не была передана ей в качестве параметра, функция longdate не может получить к ней доступ. Поэтому этот фрагмент кода выведет только дату без предшествующего ей текста. На самом деле сначала будет отображено сообщение об ошибке, предупреждающее об использовании неопределенной переменной (Notice: Undefined variable: temp). Причина в том, что по умолчанию переменные, созданные внутри функции, являются локальными для этой функции, а переменные, созданные за пределами любой функции, могут быть доступны только из того кода, который не входит в код ни одной из функций. В примерах ниже показано несколько способов исправления кода, приведенного в примере выше. Решить проблему можно путем переноса ссылки на переменную $temp в ее локальную область видимости

<?php
$temp = "Дата: ";
echo $temp . longdate(time());
function longdate($timestamp)
{
    return date("l F jS Y", $timestamp);
}  
?>

Здесь ссылка на $temp перемещена за пределы функции. Эта ссылка появляется в той же области видимости, в которой была определена переменная. Альтернативное решение: передача $temp в качестве аргумента

<?php
$temp = "Дата: ";
echo longdate($temp, time());
function longdate($text, $timestamp)
{
    return $text . date("l F jS Y", $timestamp);
}
?>

Здесь принято другое решение: передать значение переменной $temp функции longdate в качестве дополнительного аргумента. Функция longdate считывает это значение во временную переменную, которую она создает под именем $text, и выводит желаемый результат.

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

Глобальные переменные

Бывают случаи, когда требуется переменная, имеющая глобальную область видимости, поскольку нужно, чтобы к ней имелся доступ из всего кода программы. К тому же некоторые данные могут быть настолько объемными и сложными, что их не захочется передавать функциям в качестве аргументов. Чтобы объявить переменную, имеющую глобальную область видимости, используется ключевое слово global. Предположим, что существует некий способ входа пользователей на ваш сайт и нужно, чтобы весь код программы знал, с кем он имеет дело — с зарегистрированным пользователем или с гостем. Один из способов решения этой задачи заключается в создании глобальной переменной $is_logged_in:

global $is_logged_in;

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

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

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

Статические переменные

В пункте «Локальные переменные» выше было упомянуто, что значение переменной стирается сразу же после выхода из функции. Если функция вызывается многократно, она начинает свою работу со свежей копией переменной и ее прежние установки не имеют никакого значения. Интересно, а что, если внутри функции есть такая локальная переменная, к которой не должно быть доступа из других частей программы, но значение которой желательно сохранять до следующего вызова функции? Зачем? Возможно, потому, что нужен некий счетчик, чтобы следить за количеством вызовов функции. Решение, показанное ниже заключается в объявлении статической переменной.

<?php
function test()
{
    static $count = 0;
    echo $count;
    $count++;
}
?>

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

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

<?php
static $int = 0;         //	Допустимо
static $int = 1+2;       //	Недопустимо (вызовет ошибку синтаксического разбора (Parse error))
static $int = sqrt(144); //	Недопустимо
?>

Суперглобальные переменные

Начиная с версии PHP 4.1.0, стали доступны некоторые предопределенные переменные. Они известны как суперглобальные переменные. Смысл этого названия заключается в том, что они предоставляются средой окружения PHP и имеют глобальную область видимости внутри программы, то есть доступны абсолютно из любого ее места.
В этих суперглобальных переменных содержится масса полезной информации о текущей работающей программе и ее окружении (таблице "Суперглобальные переменные PHP"). Такие переменные имеют структуру ассоциативных массивов, которые будут рассмотрены в главе 6.

Таблица. Суперглобальные переменные PHP

Имя суперглобальной переменной
Ее содержимое
$GLOBALS
Все переменные, которые на данный момент определены в глобальной области видимости сценария. Имена переменных служат ключами массива
$_SERVER
Информация о заголовках, путях, местах расположения сценариев. Элементы этого массива создаются веб-сервером, и это не дает гарантии, что каждый веб-сервер будет предоставлять какую-то часть информации или ее всю
$_GET
Переменные, которые передаются текущему сценарию методом HTTP GET
$_POST
Переменные, которые передаются текущему сценарию методом HTTP POST
$_FILES
Элементы, подгруженные к текущему сценарию методом HTTP POST
$_COOKIE
Переменные, переданные текущему сценарию посредством HTTP cookies
$_SESSION
Переменные сессии, доступные текущему сценарию
$_REQUEST
Содержимое информации, переданной от браузера; по умолчанию $_GET, $_POST и $_COOKIE
$_ENV
Переменные, переданные текущему сценарию методом environment

В именах всех суперглобальных переменных (за исключением $GLOBALS) присутствует один знак подчеркивания и используются только заглавные буквы, поэтому, чтобы избежать путаницы, не следует присваивать своим переменным имена, оформленные в таком же стиле. Для иллюстрации порядка применения суперглобальных переменных рассмотрим часть той информации, которая может быть использована сайтами. Среди многой другой интересной информации, предоставляемой суперглобальными переменными, есть и URL‑адрес той страницы, с которой пользователь был перенаправлен на текущую веб-страницу. Эта информация может быть получена следующим образом:

$came_from = $_SERVER['HTTP_REFERRER'];

Как видите, ничего сложного. Если же пользователь зашел непосредственно на вашу страницу, к примеру набрав ее URL‑адрес непосредственно в браузере, переменной $came_from будет присвоена пустая строка.

Суперглобальные переменные и проблемы безопасности

Обратите внимание, что суперглобальные переменные часто используются злоумышленниками, пытающимися отыскать средства для атаки и вмешательства в работу вашего сайта. Они загружают в $_POST, $_GET или в другие суперглобальные переменные вредоносный код, например команды UNIX или MySQL, которые, если вы по незнанию к ним обратитесь, могут разрушить или отобразить незащищенные данные. Именно поэтому перед применением суперглобальных переменных их всегда следует подвергать предварительной обработке. Для этого можно воспользоваться PHP‑функцией htmlentities. Она занимается преобразованием всех символов в элементы HTML. Например, символы «меньше» и «больше» (< и >) превращаются в строки < и >, то же самое делается для перевода в безопасное состояние всех кавычек, обратных слешей и т. д. Поэтому более подходящий способ доступа к $_SERVER (и другим суперглобальным переменным) выглядит следующим образом:

$came_from = htmlentities($_SERVER['HTTP_REFERRER']);

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

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

]]>
https://chel-center.ru/php-yfc/struktura-php/feed/ 0
Выражения и управление процессом выполнения программы в PHP https://chel-center.ru/php-yfc/vyrazheniya-i-upravlenie-protsessom-vypolneniya-programmy-v-php/ https://chel-center.ru/php-yfc/vyrazheniya-i-upravlenie-protsessom-vypolneniya-programmy-v-php/#respond Sat, 28 Sep 2019 06:02:28 +0000 http://chel-center.ru/php-yfc/?p=265 Читать далее «Выражения и управление процессом выполнения программы в PHP»

]]>
В предыдущем уроке уже упоминались темы, которые более полно будут здесь рассмотрены, например, выбор (ветвление) и создание сложных выражений. Здесь мне хотелось сконцентрировать внимание на наиболее общих вопросах синтаксиса и работы в PHP, но при этом невозможно было не затронуть темы более высокого уровня. А вот теперь можно преподнести вам основы, необходимые для полноценного использования всех сильных сторон PHP.

В этом уроке будет заложен фундамент практики программирования на PHP и рассмотрены основные способы управления процессом выполнения программы.

Содержание

Выражения

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

y = 3({\mid 2x \mid} + 4)

что в PHP приобретает следующий вид:

$y = 3 * (abs(2*$x) + 4);

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

TRUE или FALSE?

Элементарное булево значение может быть либо истинным — TRUE, либо ложным — FALSE. Например, выражение 20 > 9 (20 больше 9) является истинным (TRUE), а выражение 5 == 6 (5 равно 6) — ложным (FALSE). (Булевы, или логические, операции могут быть объединены путем использования таких операторов, как И, ИЛИ и исключающее ИЛИ, то есть AND, OR и XOR, которые будут рассмотрены в этой главе.)

Обратите внимание, что для имен TRUE и FALSE я использую буквы верхнего регистра. Это обусловлено тем, что в PHP они являются предопределенными константами. При желании можно также применять и их версии, составленные из букв нижнего регистра, поскольку они также являются предопределенными константами. Кстати, версия, в которой задействуются буквы нижнего регистра, более надежна, потому что PHP не допускает ее переопределения, а версия, использующая буквы верхнего регистра, может быть переопределена, и это нужно иметь в виду при импортировании чужого кода.

В примере показаны некоторые простые выражения: два, о которых уже упоминалось, плюс еще два выражения. Для каждой строки выводится буква от a до d, за которой следуют двоеточие и результат выражения (тег <br> используется в HTML для переноса и разбивает выходную информацию на четыре строки).

Теперь, когда HTML5 уже полностью вошел в обиход, и XHTML не планируется на замену HTML, больше уже не нужно использовать самозакрывающуюся форму <br />; тега <br>, или любых пустых элементов (не имеющих закрывающих тегов), поскольку теперь символ / необязателен. Поэтому в данной книге мой выбор пал на использование более простого стиля. Если же где-нибудь сделать непустые теги HTML самозакрывающимися (например, <div />), в HTML5 они не сработают, потому что символ / будет проигнорирован, и их нужно будет, к примеру, заменить структурой <div> ... </div>. Но при работе с XHTML нужно по-прежнему пользоваться формой HTML-синтаксиса <br />.

<?php
	echo "a: [" . (20 > 9) . "]
"; echo "b: [" . (5 == 6) . "]
"; echo "c: [" . (1 == 0) . "]
"; echo "d: [" . (1 == 1) . "]
"; ?>

Этот код выведет следующую информацию:

<?php
	a: [1]
	b: []
	c: []
	d: [1]
?>

Обратите внимание, что результаты вычисления обоих выражений, a: и d:, являются истинными (TRUE), имеющими значение 1. А результаты вычисления выражений b: и c: ложны (FALSE) и вообще не показывают никакого значения, поскольку в PHP константа FALSE определена как NULL (ничто). Чтобы убедиться в этом, можно ввести код, приведенный в следующем примере:

<?php
	echo "a: [" . TRUE  . "]
"; echo "b: [" . FALSE . "]
"; ?>

Этот код выведет следующую информацию:

<?php // test2.php
echo "a: [" . TRUE . "]
"; echo "b: [" . FALSE . "]
"; ?>

Этот код выведет следующую информацию:

a: [1]
b: []

Кстати, в некоторых языках константа FALSE может быть определена как 0 или даже как –1, поэтому в каждом языке ее определение стоит проверить.

Литералы и переменные

Простейшей формой выражения является литерал, означающий нечто, вычисляющееся само в себя, например число 73 или строка Hello. Выражение может также быть просто переменной, которая вычисляется в присвоенное этой переменной значение. Обе формы относятся к типам выражений, поскольку они возвращают
значение.

В примере показаны три литерала и две переменные, все они возвращают значения, хотя и разных типов.

<?php
	$myname = "Brian";
	$myage = 37;
	echo "a: " . 73      . "
"; // Числовой литерал echo "b: " . "Hello" . "
"; // Строковый литерал echo "c: " . FALSE . "
"; // Литерал константы echo "d: " . $myname . "
"; // Строковая переменная echo "e: " . $myage . "
"; // Числовая переменная ?>

Как и ожидалось, в выходной информации вы увидите возвращаемое значение всех этих выражений, за исключением выражения c:, результат вычисления которого является FALSE и ничего не возвращает:

a: 73
b: Hello
c:
d: Brian
e: 37

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

В первой из них осуществляется присваивание результата выражения 366 - $day_number переменной $days_to_new_year, а во второй выводится приветственное сообщение, если выражение $days_to_new_year < 30 вычисляется как TRUE.

<?php
	$days_to_new_year = 366 - $day_number;	// Выражение
	if ($days_to_new_year < 30)
	{
		echo "Скоро Новый год!";			// Инструкция
	}
?>

Операторы

В PHP имеется множество мощных операторов, от арифметических, строковых и логических до операторов присваивания, сравнения и многих других операторов
(см. таблицу).

Таблица: Типы операторов PHP

Оператор
Описание
Пример
Арифметический
Элементарная математика
$a + $b
Для работы с массивом
Слияние массивов
$a + $b
Присваивания
Присваивание значений
$a = $b + 23
Поразрядный
Манипуляция битами в байте
12 ^ 9
Сравнения
Сравнение двух значений
$a < $b
Выполнения
Выполнение содержимого, заключенного в обратные кавычки
`ls -al`
Инкремента/декремента
Добавление или вычитание единицы
$a++
Логический
Выполнение булевых сравнений
$a and $b
Строковый
Объединение строк
$a . $b

 
Различные типы операторов воспринимают разное количество операндов.

  • Унарные операторы, такие как оператор инкремента ($a++) или изменения знака числа (-$a), воспринимают только один операнд.
  • Бинарные операторы, представленные большим количеством операторов PHP, включая операторы сложения, вычитания, умножения и деления, воспринимают два операнда.
  • Один трехкомпонентный оператор, имеющий форму x ? y : z. По сути, это состоящая из трех частей однострочная инструкция if, в которой осуществляется выбор между двумя выражениями, зависящий от результата вычисления третьего выражения.

Приоритетность операторов

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

1 + 2 + 3 - 4 + 5
2 - 4 + 5 + 3 + 1
5 + 2 - 4 + 1 + 3

Из примера видно, что, несмотря на перестановку чисел (и предшествующих им операторов), результат каждого выражения имеет значение 7, поскольку у операторов «плюс» и «минус» одинаковый уровень приоритета. Можно проделать то же самое с операторами умножения и деления, что показано чуть ниже.

1 * 2 * 3 / 4 * 5
2 / 4 * 5 * 3 * 1
5 * 2 / 4 * 1 * 3

В этом примере получаемое значение всегда равно 7,5. Но все меняется, когда в выражении присутствуют операторы с разными уровнями приоритета. Вот три выражения, в которых присутствуют операторы с разными уровнями приоритета:

1 + 2 * 3 - 4 * 5
2 - 4 * 5 * 3 + 1
5 + 2 - 4 + 1 * 3

Если бы не существовало приоритетности операторов, то в результате вычисления этих выражений получались бы числа 25, –29 и 12 соответственно. Но поскольку операторы умножения и деления имеют более высокий уровень приоритета по сравнению с операторами сложения и вычитания, вокруг частей выражения с их участием предполагается наличие скобок, и если их сделать видимыми, выражения будут выглядеть так, как в трёх выражениях, в которых отображены предполагаемые скобки

1 + (2 * 3) - (4 * 5)
2 - (4 * 5 * 3) + 1
5 + 2 - 4 + (1 * 3)

Очевидно, что PHP должен сначала вычислить подвыражения, заключенные в скобки, чтобы получились частичные вычисленные, показанные в выражениях после вычисления подвыражений в скобках

1 + (6) - (20)
2 - (60) + 1
5 + 2 - 4 + (3)

Окончательный результат вычисления этих выражений равен соответственно –13, –57 и 6 (что абсолютно отличается от результатов 25, –29 и 12, которые мы увидели бы при отсутствии приоритетности операторов). Разумеется, исходную приоритетность операторов можно отменить, расставив собственные скобки, и принудительно получить результаты, показанные в самом начале, которые были бы получены в отсутствие приоритетности операторов:

((1 + 2) * 3 - 4) * 5
(2 - 4) * 5 * 3 + 1
(5 + 2 - 4 + 1) * 3

Теперь, если скобки расставлены правильно, мы увидим значения 25, –29 и 12 соответственно. В таблице перечислены операторы в порядке их приоритетности от самого высокого до самого низкого уровня.

Таблица: Операторы PHP, расположенные по уровню их приоритетности (сверху вниз)

Оператор(ы)
Тип
()
Скобки
++ ––
Инкремент/декремент
!
Логический
* / %
Арифметические
+ -
Арифметические и строковые
<< >>
Побитовые
< <= > >= <>
Сравнения
== != === !==
Сравнения
&
Поразрядный (и ссылочный)
^
Поразрядный
|
Поразрядный
&&
Логический
||
Логический
? :
Трехкомпонентный
= += –= *= /= .= %= &= != ^= <<= >>=
Присваивания
and
Логический
xor
Логический
or
Логический

Взаимосвязанность операторов

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

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

Таблица: Взаимосвязанность операторов

Оператор
Описание
Взаимосвязанность
CLONE NEW
Создание нового объекта
Отсутствует
< <= >= == != === !== <>
Сравнение
Отсутствует
!
Логическое НЕ
Правая
~
Поразрядное НЕ
Правая
++ −−
Инкремент и декремент
Правая
(int)
Преобразование в целое число
Правая
(double) (float) (real)
Преобразование в число с плавающей точкой
Правая
(string)
Преобразование в строку
Правая
(array)
Преобразование в массив
Правая
(object)
Преобразование в объект
Правая
@
Подавление сообщения об ошибке
Правая
= += −= *= /=
Присваивание
Правая
.= %= &= |= ^= <<= >>=
Присваивание
Правая
+
Сложение и унарный плюс
Левая
Вычитание и отрицание
Левая
*
Умножение
Левая
/
Деление
Левая
%
Модуль
Левая
 
Конкатенация строк
Левая
<< >> & ^ |
Поразрядные операции
Левая
? :
Операция с тремя операндами
Левая
|| && and or xor
Логические операции
Левая
,
Разделение
Левая

Рассмотрим оператор присваивания, показанный в примере, где всем трем переменным присваивается значение 0.

<?php
$level = $score = $time = 0;
?>

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

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

Операторы отношения

Операторы отношения проверяют значения двух операндов и возвращают логический результат, равный либо TRUE, либо FALSE. Существует три типа операторов отношения: операторы равенства, сравнения и логические операторы.

Операторы равенства

С оператором равенства == (двойным знаком равенства) мы уже не раз встречались в этой книге. Его не следует путать с оператором присваивания = (одинарным знаком равенства). В примере ниже первый оператор присваивает значение, а второй проверяет его на равенство:

<?php
$month = "Март";
if ($month == "Март") echo "Весна наступила";
?>

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

К примеру, любые строки, составленные полностью из цифр, при сравнении с числами будут преобразованы в числа. В примере ниже переменные $a и $b являются двумя разными строками, и поэтому вряд ли стоило ожидать, что какая-то из инструкций if выведет результат.

<?php
$a = "1000";
$b = "+1000";
if ($a == $b) echo "1";
if ($a === $b) echo "2";
?>

Но если запустить этот пример, то он выведет число. Это означает, что результат вычисления первой инструкции if является TRUE. Причина в том, что обе строки сначала конвертируются в числа, и 1000 имеет такое же числовое значение, что и +1000.

В отличие от первой, во второй инструкции if используется оператор тождественности — тройной знак равенства, который удерживает PHP от автоматического преобразования типов. Поэтому переменные $a и $b сравниваются как строки и теперь считаются отличающимися друг от друга, и на экран ничего не выводится. Как и в случае с принудительным заданием уровня приоритетности операторов, если возникнут сомнения в том, будет ли PHP конвертировать типы операндов, для отмены такого поведения интерпретатора можно воспользоваться оператором тождественности.

Аналогично применению оператора равенства для определения равенства операндов можно проверить их на неравенство, используя оператор неравенства !=. В примере ниже операторы равенства и тождественности были заменены противоположными им операторами.

<?php
$a = "1000";
$b = "+1000";
if ($a != $b) echo "1";
if ($a !== $b) echo "2";
?>

Как, наверное, и ожидалось, первая инструкция if не выводит на экран число 1, потому что в коде ставится вопрос о неравенстве числовых значений переменных $a и $b. Вместо этого будет выведено число 2, поскольку вторая инструкция if ставит вопрос о нетождественности прежнего типа операндов переменных $a и $b, и ответом будет TRUE, потому что они не тождественны.

Операторы сравнения

Используя операторы сравнения, можно расширить круг проверок, не ограничивая его только равенством и неравенством. PHP предоставляет вам для этого операторы > (больше), < (меньше), >= (больше или равно) и <= (меньше или равно). Здесь показано использование этих операторов.

<?php
	$a = 2; $b = 3;
	if ($a > $b)  echo "$a больше $b
"; if ($a < $b) echo "$a меньше $b
"; if ($a >= $b) echo "$a больше или равно $b
"; if ($a <= $b) echo "$a меньше или равно $b
"; ?>

Этот пример, в котором переменная $a имеет значение 2, а переменная $b — значение 3, выведет на экран следующую информацию:

2 меньше 3
2 меньше или равно 3

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

Логические операторы

Логические операторы выдают истинные или ложные результаты. Всего имеется четыре таких оператора (см. таблицу "Логические операторы").

Таблица: Логические операторы

Логический оператор
Описание
AND
Возвращает истинное значение (TRUE), если оба операнда имеют истинные значения
OR
Возвращает истинное значение (TRUE), если любой из операндов имеет истинное значение
XOR
Возвращает истинное значение (TRUE), если один из двух операндов имеет истинное значение
NOT
Возвращает истинное значение (TRUE), если операнд имеет ложное значение, или ложное значение (FALSE), если он имеет истинное значение

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

<?php
	$a = 1; $b = 0;
	echo ($a AND $b) . "
"; echo ($a or $b) . "
"; echo ($a XOR $b) . "
"; echo !$a . "
"; ?>

Этот пример выводит на экран NULL, 1, 1, NULL. Это значит, что только вторая и третья инструкции echo получают в результате вычисления значение TRUE. (Следует помнить, что NULL, или ничто, отображает значение FALSE.) Такой результат получается, потому что оператору AND, чтобы вернуть значение TRUE, нужно, чтобы оба операнда имели истинное значение, а четвертый оператор проводит над значением переменной $a операцию NOT, превращая его из TRUE (значения, равного единице) в FALSE. Если есть желание поэкспериментировать, запустите этот код, присваивая переменным $a и $b разные значения, выбранные из 1 и 0.

Занимаясь программированием, следует помнить, что у операторов AND и OR более низкий уровень приоритета, чем у других версий этих операторов — && и ||. Поэтому в сложных выражениях более безопасным будет, наверное, применение операторов && и ||.

Использование в инструкции if оператора OR может стать причиной непредвиденных проблем, поскольку второй операнд не будет вычисляться, если в результате вычисления первого операнда уже получено значение TRUE. В примере функция getnext никогда не будет вызвана, если переменная $finished имеет значение 1.

<?php
if ($finished == 1 OR getnext() == 1) exit;
?>

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

<?php
$gn = getnext();
if ($finished == 1 OR $gn == 1) exit;
?>

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

В таблице ниже показаны все допустимые варианты использования логических операторов. Следует заметить, что !TRUE является эквивалентом FALSE, а !FALSE — эквивалентом TRUE.

Таблица: Все логические выражения, допустимые в PHP

 
Входные данные
Операторы и результаты
 
 
a
b
AND
OR
XOR
 
 
TRUE
TRUE
TRUE
TRUE
FALSE
 
 
TRUE
FALSE
FALSE
TRUE
TRUE
 
 
FALSE
TRUE
FALSE
TRUE
TRUE
 
 
FALSE
FALSE
FALSE
FALSE
FALSE
 

Условия

Условия изменяют процесс выполнения программы. Они позволяют задавать конкретные вопросы и по-разному реагировать на полученные ответы. Условия играют важную роль при разработке динамических веб-страниц — основной цели использования PHP, поскольку облегчают создание разных вариантов выводимой при каждом просмотре веб-страницы информации. Существует три типа нециклических условных инструкций: if, switch и ?. Называя их нециклическими, я имел в виду, что после действий, инициированных инструкцией, процесс выполнения программы продолжается, а при использовании циклических условных инструкций (которые еще предстоит рассмотреть) код выполняется снова и снова до тех пор, пока не будет соблюдено определенное условие.

Инструкция if

Процесс выполнения программы можно представить себе как езду на машине по однополосной магистрали. Эта магистраль большей частью прямолинейна, но иногда встречаются различные дорожные знаки, задающие направление движения. Когда встречается инструкция if, можно представить, что машина подошла к знаку объезда, предписаниям которого необходимо следовать, когда определенные условия вычисляются как TRUE. При этом вы съезжаете с магистрали и следуете по объездному пути до тех пор, пока не вернетесь снова на магистраль и не продолжите движение по исходному маршруту. Или же, если условие не вычисляется как TRUE, вы игнорируете объезд и продолжаете ехать по магистрали как ни в чем не бывало (см. рисунок).

Процесс выполнения программы похож на движение по однополосной магистрали
Процесс выполнения программы похож на движение по однополосной магистрали

Содержимым условной инструкции if может быть любое допустимое PHP‑выражение, включая равенство, сравнение, проверку на нуль и NULL и даже значения, возвращаемые функциями (как встроенными, так и созданными самостоятельно). Действия, предпринимаемые при вычислении условия в TRUE, помещаются, как правило, в фигурные скобки { }. Но эти скобки можно опустить, если нужно выполнить всего одну инструкцию. Тем не менее, если всегда использовать фигурные скобки, можно избежать «охоты» на трудно отслеживаемые ошибки, возникающие, к примеру, когда к условной инструкции добавляется еще одна строка, но забывается о необходимости добавить фигурные скобки, из-за чего строка не вычисляется.

(Учтите, что в целях экономии места и доходчивости материала, если в примерах, приводимых в книге, была всего одна исполняемая инструкция, я не следовал этому совету и опускал фигурные скобки.) В примере следует представить, что подошел конец месяца и нужно платить по всем счетам, поэтому вы проводите некоторые операции с банковским счетом.

<?php
if ($bank_balance < 100)
{
$money
= 1000;
$bank_balance += $money;
}
?>

В этом примере проверяется, не стал ли баланс ниже $100 (или 100 единиц другой используемой вами валюты). Если баланс стал ниже этой суммы, вы платите сами себе $1000, а затем прибавляете их к балансу. (Хорошо бы так просто зарабатывать деньги!) Если баланс счета в банке равен $100 или превышает эту сумму, условные инструкции игнорируются и процесс выполнения программы переходит на следующую строку кода (которая здесь не показана). Одни разработчики предпочитают ставить первую фигурную скобку справа от условного выражения, а другие начинают с нее новую строку. В этой книге открывающая фигурная скобка располагается обычно на новой строке. Подойдет любой из этих вариантов, поскольку PHP позволяет оставлять на ваше усмотрение какие угодно свободные пространства (пробелы, символы новых строк и табуляции). Но код будет легче читаться и отлаживаться, если у каждого уровня условий будет свой отступ, сформированный с помощью символа табуляции.

Инструкция else

Бывают случаи, когда условие не вычисляется как TRUE, но вам не хочется сразу же продолжать выполнение основного кода программы, а вместо этого нужно сделать что-либо другое. Тогда пригодится инструкция else. С ее помощью на вашей магистрали можно организовать второй объезд, показанный на рисунке. Если при использовании конструкции if...else условие вычисляется как TRUE, то выполняется первая условная инструкция. Но если это условие вычисляется как FALSE, то выполняется вторая условная инструкция. Для выполнения должна быть выбрана одна из этих двух инструкций, но обе сразу они не будут выполнены ни при каких условиях, и обязательно будет выполнена хотя бы одна из них.

Использование конструкции if...else показано в примере:

<?php
if ($bank_balance < 100)
{
$money
= 1000;
$bank_balance += $money;
}
else
{
$savings
+= 50;
$bank_balance -= 50;
}
?>
Теперь у магистрали есть объезд if и объезд else
Теперь у магистрали есть объезд if и объезд else

Если в этом примере будет установлено, что в банке лежит $100 или более, то выполняется инструкция else, с помощью которой часть этих денег перемещается на ваш сберегательный счет. Точно так же, как и у if, если у инструкции else есть только одна условная инструкция, то фигурные скобки можно не ставить. (Хотя фигурные скобки рекомендуется использовать в любом случае. Во-первых, при их наличии проще разобраться в коде, а во-вторых, они облегчают последующее добавление инструкций к этому ветвлению.)

Инструкция elseif

Случается, что на основе последовательности условий нужно осуществить сразу несколько действий. Достичь желаемого результата можно, используя инструкцию elseif. Можно предположить, что она похожа на инструкцию else, за исключением того, что до кода условия вставляется еще одно условное выражение. Полноценная конструкция if...elseif...else показана в примере:

<?php
if ($bank_balance < 100)
{
$money
= 1000;
$bank_balance += $money;
}
elseif ($bank_balance > 200)
{
$savings
+= 100;
$bank_balance -= 100;
}
else
{
$savings
+= 50;
$bank_balance -= 50;
}
?>

В этом примере инструкция elseif была вставлена между инструкциями if и else. Она проверяет, не превышает ли баланс банковского счета сумму $200, и если превышает, принимается решение о том, что в этом месяце можно позволить себе положить на сберегательный счет $100. Это все можно представить в виде набора объездов в нескольких направлениях (cv рис.). Инструкция else завершает либо конструкцию if...else, либо конструкцию if...elseif...else. Если она не нужна, то финальную инструкцию else можно опустить, но ни одна из этих инструкций не должна стоять перед инструкцией elseif, точно так же, как ни одна инструкция elseif не должна стоять перед инструкцией if.

Магистраль с объездами if, elseif и else
Магистраль с объездами if, elseif и else

Количество используемых инструкций elseif не ограничено. Но по мере роста количества этих инструкций будет лучше, наверное, обратиться к инструкции switch, если, конечно, она отвечает вашим запросам. Именно ее мы сейчас и рассмотрим.

Инструкция switch

Инструкция switch применяется в тех случаях, когда у одной переменной или у результата вычисления выражения может быть несколько значений, каждое из которых должно вызывать особую функцию. Рассмотрим, например, управляемую кодом PHP систему меню, которая в соответствии с пожеланием пользователя передает отдельную строку коду основного меню. Предположим, что есть следующие варианты: Home, About, News, Login и Links, а переменная $page принимает одно из этих значений в соответствии с информацией, введенной пользователем.

Код реализации этого замысла с использованием конструкции if...elseif...
else может иметь вид, показанный в примере 4.22.

<?php
	if     ($page == "Home")  echo "Вы выбрали Home";
	elseif ($page == "About") echo "Вы выбрали About";
	elseif ($page == "News")  echo "Вы выбрали News";
	elseif ($page == "Login") echo "Вы выбрали Login";
	elseif ($page == "Links") echo "Вы выбрали Links";
?>

Код, в котором используется инструкция switch, показан в примере:

<?php
  switch ($page)
  {
      case "Home":  
          echo "Вы выбрали Home";
         break;
      case "About": 
          echo "Вы выбрали About";
         break;
      case "News":  
          echo "Вы выбрали News";
         break;
      case "Login": 
          echo "Вы выбрали Login";
         break;
      case "Links": 
          echo "Вы выбрали Links";
         break;
  }
?>

Как видите, переменная $page используется только один раз — в начале инструкции switch. После этого все соответствия проверяются командой case. Когда найдено соответствие, выполняется его условная инструкция. Разумеется, в настоящей программе в этом месте будет применяться код отображения или перехода на страницу, а не простое сообщение пользователю о том, что именно он выбрал.

В инструкциях switch внутри команд case фигурные скобки не используются. Вместо этого инструкции начинаются с двоеточия и заканчиваются командой break. Тем не менее весь перечень команд case в инструкции switch заключается в фигурные скобки.

Прекращение работы инструкции switch

Если нужно, чтобы инструкция switch прекратила свою работу из-за выполнения условия, используется команда break. Она предписывает PHP прекратить работу инструкции switch и перейти к выполнению следующей инструкции.

Если в примере, приведённом выше, не расставить команды break и результат вычисления команды case, проверяющей условие Home, получится TRUE, то будут выполнены все пять условных инструкций, следующих за командами case. Или же, если переменная $page имела значение News, то, начиная с этого места, будут выполнены все оставшиеся команды case. Это сделано преднамеренно для расширения возможностей программирования, но в большинстве случаев не следует забывать ставить команду break во всех местах, где набор условных инструкций, следующих за командами case, завершает свою работу. Надо сказать, что случайный пропуск команд break является весьма распространенной ошибкой.

Действие по умолчанию

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

default: echo "Нераспознанный выбор";
break;

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

Альтернативный синтаксис

Открывающую фигурную скобку инструкции switch можно заменить двоеточием, а закрывающую — командой endswitch (см. пример ниже). Такой вариант используется довольно редко, и здесь он упоминается на тот случай, если придется столкнуться с ним в коде, созданном кем-нибудь другим.

<?php
	switch ($page):
		case "Home":
			echo "Вы выбрали Home";
			break;
		// и т.д. ...
		case "Links":
			echo "Вы выбрали Links";
			break;
	endswitch;
?>

Оператор ?

Использование трехкомпонентного оператора ? позволяет избежать многословности инструкций if и else. Необычность этого оператора заключается в том, что он использует не два, как большинство других операторов, а три операнда. У нас уже состоялось краткое знакомство с этим оператором при выяснении разницы между print и echo, где он приводился в качестве примера оператора, который хорошо работает с print, но не работает с echo. Оператору ? передаются выражение, которое он должен вычислить, и два выполняемых оператора: один для выполнения, когда результат вычисления выражения TRUE, а другой — когда FALSE.

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

<?php
echo $fuel <= 1 ? "Требуется дозаправка" : "Топлива еще достаточно";
?>

Если топлива остается всего 1 галлон или меньше (иными словами, переменная $fuel имеет значение, равное единице или меньше ее), то этот оператор возвращает предыдущей команде echo строку «Требуется дозаправка». В противном случае он возвращает строку «Топлива еще достаточно». Значение, возвращаемое оператором ?, можно также присвоить какой-нибудь переменной:

<?php
$enough = $fuel <= 1 ? FALSE : TRUE;
?>

В этом примере переменной $enough будет присвоено значение TRUE только в том случае, если в баке более 1 галлона топлива, в противном случае ей будет присвоено значение FALSE. Если вы считаете синтаксис оператора ? слишком запутанным, то можете вместо него воспользоваться инструкцией if, но о нем все равно нужно знать, поскольку он может встретиться в программном коде, созданном другим программистом. Чтение кода, в котором используется этот оператор, может быть сильно затруднено из-за частого применения в нескольких местах одной и той же переменной. Например, весьма популярен код такого вида:

$saved = $saved >= $new ? $saved : $new;

Понять, что он делает, можно только после тщательного разбора:

$saved =                  // Присваивание значения переменной $saved
        $saved >= $new    // Сравнение $saved и $new
    ?                     // Если сравнение выдает истинный результат ...
        $saved            // ... ей присваивается текущее значение $saved
    :                     // Если сравнение выдает ложный результат ...
        $new;             // ... ей присваивается значение переменной $new

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

Организация циклов

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

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

Представление цикла как части программы магистральной разметки
Представление цикла как части программы магистральной разметки

Циклы while

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

<?php
	 $fuel = 10;
	 while ($fuel > 1)
	 {
		// Продолжение поездки...
		echo "Топлива еще достаточно";
	 }
?>

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

В примере показан еще один вариант использования цикла while, в котором выводится таблица умножения на 12.

<?php
$count = 1;
while ($count <= 12)
{
	echo "Число $count, умноженное на 12, равно " . $count * 12 . "
"; ++$count; } ?>

В этом примере переменной $count присваивается начальное значение 1, а затем запускается цикл while, в котором используется выражение сравнения $count <= 12. Цикл будет выполняться до тех пор, пока значение переменной не станет больше 12.

Данный код выведет следующий текст:

Число 1, умноженное на 12, равно 12
Число 2, умноженное на 12, равно 24
Число 3, умноженное на 12, равно 36

и т.д.

Внутри цикла осуществляется вывод строки, а также значения переменной $count, умноженного на 12. Чтобы упорядочить вывод, после всего этого использован тег <br />, вызывающий переход на новую строку. Затем перед закрывающей фигурной скобкой, предписывающей PHP вернуться к началу цикла, значение переменной $count увеличивается на единицу.

Теперь значение переменной $count опять проверяется, чтобы узнать, не превышает ли оно число 12. Оно не превышает этого числа, но теперь оно равно 2, и после 11 последующих прохождений цикла оно станет равно 13. Когда это произойдет, код, находящийся внутри цикла while, будет пропущен и станет выполняться код, следующий за циклом, в данном случае это будет завершение программы. При отсутствии оператора ++$count (вместо которого с таким же успехом может быть применен оператор $count++) этот цикл будет похож на первый, показанный в этом разделе. Он никогда не закончится и будет снова и снова выводить один и тот же результат 1 ⋅ 12.

Но есть и более изящный способ написания этого цикла, который должен вам понравиться. Посмотрите:

<?php
$count = 0;
while (++$count <= 12)
echo "Число $count, умноженное на 12, равно " . $count * 12 . "
"; ?>

В этом примере оператор ++$count был удален из тела цикла while и помещен непосредственно в выражение условия цикла. Теперь PHP вычисляет значение переменной $count в начале каждого прохода цикла (итерации) и, заметив, что перед именем переменной стоит оператор инкремента, сначала увеличивает значение переменной на 1 и только потом сравнивает его с числом 12. Следовательно, теперь переменной $count присваивается начальное значение 0, а не 1, поскольку это значение увеличивается сразу же, как только происходит вход в цикл. Если оставить начальное значение, равное 1, то будут выведены результаты для чисел между 2 и 12.

Циклы do...while

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

<?php
$count = 1;
do
	echo "Число $count, умноженное на 12, равно " . $count * 12 . "
"; while (++$count <= 12); ?>

Заметьте, что теперь мы вернулись к присваиванию переменной $count начального значения 1 (а не 0), потому что код выполняется сразу же, без увеличения значения переменной на 1. Во всем остальном этот код очень похож на показанный в предыдущем примере. Разумеется, если внутри цикла do...while находится несколько инструкций, то не следует забывать ставить вокруг них фигурные скобки, как показано:

<?php
$count = 1;
do {
	echo "Число $count, умноженное на 12, равно " . $count * 12;
	echo "
"; } while (++$count <= 12); ?>

Циклы for

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

<?php
for ($count = 1 ; $count <= 12 ; ++$count)
echo "Число $count, умноженное на 12, равно " . $count * 12 . "
"; ?>

Как видите, весь код сведен к одной инструкции for, в которой содержится одна условная инструкция. И вот что из этого получается. Каждая инструкция for воспринимает три параметра:

  • выражение инициализации;
  • выражение условия;
  • выражение модификации.

Эти три выражения отделяются друг от друга точкой с запятой: for (выражение1 ; выражение2 ; выражение3). В начале первой итерации выполняется выражение инициализации. В нашем коде таблицы умножения переменная $count инициализируется значением 1. Затем при каждой итерации проверяется выражение условия (в данном случае $count <= 12), и выход из цикла осуществляется только в том случае, если результат вычисления условия будет TRUE. И наконец, в завершение каждой итерации выполняется выражение модификации. В случае с таблицей умножения значение переменной $count увеличивается на 1.

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

<?php
for ($count = 1 ; $count <= 12 ; ++$count)
{
echo "Число $count, умноженное на 12, равно " . $count * 12;
echo "
"; } ?>

Сравним условия, при которых следует использовать циклы for, с условиями, при которых нужно применять циклы while. Цикл for явно создавался под отдельное значение, изменяющееся на постоянную величину. Обычно мы имеем дело с увеличивающимся значением — это похоже на то, как если бы вам был передан перечень того, что выбрал пользователь, и от вас требуется обработать каждый его выбор по очереди. Но переменную можно видоизменять по вашему усмотрению. Более сложная форма инструкции for позволяет даже осуществлять со всеми тремя параметрами сразу несколько операций:

for ($i = 1, $j = 1 ; $i + $j < 10 ; $i++ , $j++)
{
// ...
}

Но новичкам использовать такую сложную форму не рекомендуется. Здесь главное — отличать запятые от точки с запятой. Все три параметра должны быть отделены друг от друга точкой с запятой. Несколько операторов внутри каждого параметра должны быть отделены друг от друга запятыми. Первый и третий параметры в предыдущем примере содержат по два оператора:

$i = 1, $j = 1   // Инициализация переменных $i и $j
$i + $j < 10     // Условие окончания работы цикла
$i++ , $j++     // Модификация $i и $j в конце каждой итерации

Главное, что следует уяснить из этого примера, — три секции параметров должны разделяться точкой с запятой, а не запятыми (которые могут использоваться только для разделения операторов внутри каждой секции параметров). Тогда при каких условиях следует отдавать предпочтение инструкциям while перед инструкциями for? Когда ваше условие не зависит от простого изменения переменной на постоянной основе. Например, инструкция while применяется в том случае, если нужно проверить, не введено ли какое-то определенное значение или не возникла ли какая-то конкретная ошибка, и завершить цикл сразу же, как только это произойдет.

Прекращение работы цикла

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

<?php
$fp = fopen("text.txt", 'wb');
for ($j = 0 ; $j < 100 ; ++$j)
{
	$written = fwrite($fp, "data");
	if ($written == FALSE) break;
}
fclose($fp);
?>

Это наиболее сложный из всех ранее приведенных фрагментов кода, но вы уже готовы к его пониманию. Команды обработки файлов будут рассмотрены в одной из следующих глав, а сейчас нужно лишь знать, что в первой строке кода открывается файл text.txt для записи в двоичном режиме, а затем переменной $fp возвращается указатель на него, который в дальнейшем используется для ссылки на этот открытый файл. Затем осуществляется 100 проходов цикла (от 0 до 99), записывающих строку data в файл. После каждой записи функция fwrite присваивает переменной $written значение, представляющее собой количество успешно записанных символов. Но если происходит ошибка, то функция fwrite присваивает этой переменной значение FALSE.

Поведение функции fwrite облегчает коду проверку переменной $written на наличие значения FALSE, и если она имеет такое значение, код прекращает работу цикла и передает управление инструкции, закрывающей файл.

При желании улучшить код можно упростить строку:

if ($written == FALSE) break;

за счет использования оператора NOT:

if (!$written) break;

Фактически пара инструкций, находящихся внутри цикла, может быть сокращена до одной:

if (!fwrite($fp, "data")) break;

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

break 2;

Инструкция continue

Инструкция continue немного похожа на команду break, только она предписывает PHP остановить процесс текущего цикла и перейти непосредственно к его следующей итерации, то есть вместо прекращения работы всего цикла PHP осуществляет выход только из текущей итерации.

Этот прием может пригодиться в тех случаях, когда известно, что нет смысла продолжать выполнение текущего цикла и нужно сберечь процессорное время или избежать ошибки путем перехода сразу к следующей итерации цикла. В примере ниже инструкция continue используется для того, чтобы избежать ошибки деления на нуль за счет ее вызова в тот момент, когда переменная $j имеет значение 0.

<?php
$j = 10;
while ($j > –10)
{
	$j--;
	if ($j == 0) continue;
	echo (10 / $j) . "
"; } ?>

Для всех значений переменной $j в диапазоне чисел между 10 и –10, за исключением 0, отображается результат деления числа 10 на значение переменной $j. Но для конкретного случая, когда значение $j равно 0, вызывается инструкция continue и дальнейшее выполнение итерации сразу же пропускается с переходом к следующей итерации цикла.

Неявное и явное преобразование типов

PHP является языком со слабой типизацией, который позволяет объявлять переменную и ее тип путем простого использования этой переменной. При необходимости он также осуществляет автоматическое преобразование одного типа в другой. Этот процесс называется неявным преобразованием типов. Однако могут возникнуть ситуации, когда присущее PHP неявное преобразование типов станет совсем нежелательным действием. Рассматривая пример ниже, обратите внимание на то, что входные данные для операции деления являются целыми числами. По умолчанию PHP осуществляет преобразование выходных данных к числу с плавающей точкой, чтобы получалось наиболее точное значение — 4,66 и 6 в периоде.

<?php
$a = 56;
$b = 12;
$c = $a / $b;
echo $c;
?>

Но что делать, если вместо этого нужно получить значение переменной $c в виде целого числа? Этого можно добиться разными способами, одним из которых является принудительное преобразование результата $a/$b в целое число путем использования оператора преобразования (int):

$c = (int) ($a / $b);

Такой способ называется явным преобразованием типов. Обратите внимание, что для обеспечения преобразования в целое число значения всего выражения это выражение помещено в круглые скобки. В противном случае преобразованию подверглось бы только значение переменной $a, что не имело бы никакого смысла, поскольку деление на значение переменной $b все равно вернуло бы результат в виде числа с плавающей точкой. Можно провести явное преобразование значений в те типы, которые показаны в таблице но обычно его можно избежать, используя преобразование за счет вызова одной из встроенных функций PHP. Например, для получения целочисленного значения можно использовать функцию intval. Этот раздел, как и многие другие в данной книге, предназначен в основном для того, чтобы помочь разобраться с чужим кодом, который может вам встретиться.

Таблица: Типы преобразований, доступных в PHP

Тип преобразования
Описание
(int) (integer)
Преобразование в целое число путем отбрасывания десятичной части
(bool) (boolean)
Преобразование в логическое значение
(float) (double) (real)
Преобразование в число с плавающей точкой
(string)
Преобразование в строку
(array)
Преобразование в массив
(object)
Преобразование в объект

Динамическое связывание в PHP

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

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

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

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

Таким образом, вы существенно облегчите себе будущую разработку каждого нового свойства и обслуживание уже имеющихся.

Динамическое связывание в действии

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

Платформа WordPress, предназначенная для ведения блогов, написана на PHP
Платформа WordPress, предназначенная для ведения блогов, написана на PHP

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

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

]]>
https://chel-center.ru/php-yfc/vyrazheniya-i-upravlenie-protsessom-vypolneniya-programmy-v-php/feed/ 0
Функции и объекты PHP https://chel-center.ru/php-yfc/funktsii-i-obekty-php/ https://chel-center.ru/php-yfc/funktsii-i-obekty-php/#respond Sat, 28 Sep 2019 06:10:46 +0000 http://chel-center.ru/php-yfc/?p=293 Читать далее «Функции и объекты PHP»

]]>
Содержание

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

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

По сравнению с непрерывным линейным кодом у функций есть масса преимуществ.

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

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

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

]]>
https://chel-center.ru/php-yfc/funktsii-i-obekty-php/feed/ 0
Массивы в PHP https://chel-center.ru/php-yfc/massivy-v-php/ https://chel-center.ru/php-yfc/massivy-v-php/#respond Sat, 28 Sep 2019 06:11:20 +0000 http://chel-center.ru/php-yfc/?p=295 Читать далее «Массивы в PHP»

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

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

]]>
https://chel-center.ru/php-yfc/massivy-v-php/feed/ 0
PHP: практический тренинг №1 https://chel-center.ru/php-yfc/php-prodvinutyj-praktikum/ https://chel-center.ru/php-yfc/php-prodvinutyj-praktikum/#respond Sat, 28 Sep 2019 08:13:56 +0000 http://chel-center.ru/php-yfc/?p=32 Читать далее «PHP: практический тренинг №1»

]]>
Как показывает многолетняя практика, если решить все 1052 задачи из книги Задачи по программированию, то можно приобрести уверенность в таком деле, как программирование, и смело браться за решение коммерческих задач. Но это для особо усердных и тех кому программирование действительно нравится, тех кто хочет сделать программирование своей работой. Завидуйте им.

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

Для продуктивных консультаций коды решений всех задач сохраняете в своих аккаунтах на github.

Задачи для самостоятельной работы

Линейные алгоритмы

  1. Нарисуйте блок-схему к следующей задаче: Преобразовать дату в "компьютерном" представлении (системную дату) в "российский" формат, т.е. день/месяц/год (например, 17/05/2009).
    Постановка задачи: Системная дата имеет вид 2009-06-15. Нужно преобразовать это значение в строку, строку разделить на компоненты (символ→разделитель→дефис), потом из этих компонентов сконструировать нужную строку.
  2. Даны действительные числа А, В, С. Найти максимальное и минимальное из этих чисел.
  3. Известны длины трёх сторон треугольника. Вычислить периметр треугольника и площадь (указание: формула Герона, использовать модуль math и функцию sqrt ()).
  4. Задан вес в граммах. Определить вес в тоннах и килограммах.
  5. Известен объем информации в байтах. Перевести в килобайты, мегабайты.
  6. Определить значение функции Z=1/(XY) при X и Y не равных 0.

Ветвления и оператор выбора

  1. Дано натуральное число. Определить, будет ли это число: чётным, кратным 4.
  2. Дано натуральное число. Определить, будет ли это число: нечётным, кратным 5.
  3. Дано натуральное число. Определить, будет ли это число: нечётным, кратным 7.
  4. Дано натуральное число. Определить, будет ли это число: чётным, кратным 10.
  5. Имеется коробка со сторонами: A × B × C. Определить, пройдёт ли она в дверь с размерами M × K.
  6. Дано вещественное число. Определить, какое это число: положительное, отрицательное, ноль.
  7. Можно ли из бревна, имеющего диаметр поперечного сечения D, выпилить квадратный брус шириной A?
  8. Можно ли в квадратном зале площадью S поместить круглую сцену радиусом R так, чтобы от стены до сцены был проход не менее K?
  9. Дан номер места в плацкартном вагоне. Определить, какое это место: верхнее или нижнее, в купе или боковое.
  10. Известна денежная сумма. Разменять её купюрами 500, 100, 10 и монетой 2 руб., если это возможно.
  11. Имеются две ёмкости: кубическая с ребром A, цилиндрическая с высотой H и радиусом основания R. Определить, поместится ли жидкость объёма M в первую ёмкость, во вторую, в обе.
  12. Имеются две ёмкости: кубическая с ребром A, цилиндрическая с высотой H и радиусом основания R. Определить, можно ли заполнить жидкостью объёма M первую ёмкость, вторую, обе.
  13. Даны вещественные числа: X, Y, Z. Определить, существует ли треугольник с такими длинами сторон и, если существует, будет ли он прямоугольным.
  14. Дано число X. Определить, принадлежит ли это число заданному промежутку [a,b].
  15. Определить значение функции Z = 1/(XY ) при произвольных X и Y .
  16. Даны вещественные числа: A, B, C. Определить, выполняются ли неравенства A < B B > C и какое именно неравенство выполняется.
  17. Даны двещественные числа X и Y . Вычислить Z. Z = √(X x Y) при X > Y, Z = ln(X + Y ) в противном случае.
  18. Даны вещественные положительные числа a, b, c, d. Выясните, может ли прямоугольник со сторонами a,b уместиться внутри прямоугольника со сторонами c,d так, чтобы каждая сторона внутреннего прямоугольника была параллельна или перпендикулярна стороне внешнего прямоугольника.
  19. Дано вещественное число A. Вычислить f(A), если f(x) = x2 + 4x + 5, при x ≤ 2; в противном случае f(x) = 1/(x2 + 4x + 5).
  20. Дано вещественное число A. Вычислить f(A), если f(x) = 0, при x ≤ 0; f(x) = x при 0 < x < 1, в противном случае f(x) = x4.
  21. Дано вещественное число A. Вычислить f(A), если f(x) = 0 при x ≤ 0; f(x) = x2 − x при 0 < x < 1, в противном случае f(x) = x2 − sin(πx2).
  22. Составить алгоритм и программу для реализации логических операций «И» и «ИЛИ» для двух переменных.
  23. Известен ГОД. Определить, будет ли этот год високосным, и к какому веку этот год относится

Указание. При вычислении корней и логарифмов используйте функции sqrt() и log() модуля math. В этом же модуле определена константа pi (math.pi).

Циклические алгоритмы. Обработка последовательностей и одномерных массивов

  1. Составьте блок-схему поиска максимального элемента в одномерном массиве.
  2. Нарисуйте полную блок-схему алгоритма сортировки массива «методом пузырька».
  3. Дан одномерный массив числовых значений, насчитывающий N элементов. Поменять местами элементы, стоящие на чётных и нечётных местах: A[1] ↔ A[2]; A[3] ↔ A[4] ...
  4. Дан одномерный массив числовых значений, насчитывающий N элементов. Выполнить перемещение элементов массива по кругу вправо, т. е. A[1] → A[2]; A[2] → A[3]; ... A[n] → A[1].
  5. Дан одномерный массив числовых значений, насчитывающий N элементов. Поменять местами первую и вторую половины массива.
  6. Дан одномерный массив числовых значений, насчитывающий N элементов. Поменять местами группу из M элементов, начинающихся с позиции K с группой из M элементов, начинающихся с позиции P.
  7. Дан одномерный массив числовых значений, насчитывающий N элементов. Вставить группу из M новых элементов, начиная с позиции K.
  8. Дан одномерный массив числовых значений, насчитывающий N элементов. Сумму элементов массива и количество положительных элементов поставить на первое и второе место.
  9. Дан одномерный массив числовых значений, насчитывающий N элементов.Исключить из него M элементов, начиная с позиции K.
  10. Дан одномерный массив числовых значений, насчитывающий N элементов. Исключить все нулевые элементы.
  11. Дан одномерный массив числовых значений, насчитывающий N элементов. После каждого отрицательного элемента вставить новый элемент, равный квадрату этого отрицательного элемента.
  12. Дан одномерный массив числовых значений, насчитывающий N элементов. Определить, образуют ли элементы массива, расположенные перед первым отрицательным элементом, возрастающую последовательность.
  13. Дан одномерный массив числовых значений, насчитывающий N элементов. Определить, образуют ли элементы массива, расположенные перед первым отрицательным элементом, убывающую последовательность.
  14. Дан одномерный массив числовых значений, насчитывающий N элементов. Из элементов исходного массива построить два новых. В первый должны входить только элементы с положительными значениями, а во второй — только элементы с отрицательными значениями.
  15. Дан одномерный массив числовых значений, насчитывающий N элементов. Добавить столько элементов, чтобы элементов с положительными и отрицательными значениями стало бы поровну.
  16. Дан одномерный массив числовых значений, насчитывающий N элементов. Добавить к элементам массива такой новый элемент, чтобы сумма элементов с положительными значениями стала бы равна модулю суммы элементов с отрицательными значениями.
  17. Дан одномерный массив числовых значений, насчитывающий N элементов. Дано положительное число T. Разделить это число между положительными элементами массива пропорционально значениям этих элементов и добавить полученные доли к соответствующим элементам.
  18. Дан одномерный массив числовых значений, насчитывающий N элементов. Исключить из массива элементы, принадлежащие промежутку [B; C].
  19. Дан одномерный массив числовых значений, насчитывающий N элементов. Вместо каждого элемента с нулевым значением поставить сумму двух предыдущих элементов массива.
  20. Дан одномерный массив числовых значений, насчитывающий N элементов. Определить, имеются ли в массиве два подряд идущих нуля.
  21. Дан одномерный массив числовых значений, насчитывающий N элементов. Подсчитать количество чисел, делящихся на 3 нацело, и среднее арифметическое чисел с чётными значениями. Поставить полученные величины на первое и последнее места в массиве (увеличив массив на 2 элемента).
  22. Заданы M строк символов, которые вводятся с клавиатуры. Найти количество символов в самой длинной строке. Выровнять строки по самой длинной строке, поставив перед каждой строкой соответствующее количество звёздочек.
  23. Заданы M строк символов, которые вводятся с клавиатуры. Из заданных строк, каждая из которых представляет одно слово, составить одну длинную строку, разделяя слова пробелами.
  24. Заданы M строк слов, которые вводятся с клавиатуры. Подсчитать количество гласных букв в каждой из заданных строк.
  25. Заданы M строк слов, которые вводятся с клавиатуры (в каждой строке – одно слово). Вводится слог (последовательность букв). Подсчитать количество таких слогов в каждой строке.
  26. Заданы M строк слов, которые вводятся с клавиатуры (в каждой строке – одно слово). Вводится слог (последовательность букв). Удалить данный слог из каждой строки.
  27. Заданы M строк символов, которые вводятся с клавиатуры. Напечатать все центральные буквы строк нечетной длины.
  28. Заданы M строк символов, которые вводятся с клавиатуры. Каждая строка содержит слово. Записать каждое слово в разрядку (вставить по пробелу между буквами).
  29. Задана строка символов, в которой встречается символ «.». Поставить после каждого такого символа системное время ПК.
  30. Заданы M строк, которые вводятся с клавиатуры. Подсчитать количество пробелов в каждой из строк.
  31. Заданы M строк символов, которые вводятся с клавиатуры. Каждая строка представляет собой последовательность символов, включающих в себя вопросительные знаки. Заменить в каждой строке все имеющиеся вопросительные знаки звёздочками.
  32. Последовательно вводятся числа. Определить сумму чисел с нечётными номерами и произведение чисел с чётными номерами (по порядку ввода). Подсчитать количество слагаемых и количество сомножителей. При вводе числа 55555 закончить работу.
  33. Определить сумму вводимых положительных чисел. Причём числа с нечётными номерами (по порядку ввода) суммировать с обратным знаком, а числа с чётными номерами перед суммированием возводить в квадрат. Подсчитать количество слагаемых. При вводе первого отрицательного числа закончить работу.
  34. Даны число P и число H. Определить сумму чисел меньше P, произведение чисел больше H и количество чисел в диапазоне значений P и H. При вводе числа равного P или H, закончить работу.
  35. Суммировать вводимые числа, среди которых нет нулевых. При вводе нуля обеспечить вывод текущего значения суммы. При вводе числа 99999 закончить работу.
  36. Вводятся положительные числа. Определить сумму чисел, делящихся на положительное число B нацело. При вводе отрицательного числа закончить работу.
  37. Для вводимых чисел определить процент положительных и отрицательных чисел. При вводе числа −65432 закончить работу.

Обработка двумерных массивов (матриц)

  1. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти наибольший элемент столбца матрицы A, для которого сумма абсолютных значений элементов максимальна.
  2. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти наибольшее значение среди средних значений для каждой строки матрицы.
  3. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти наименьший элемент столбца матрицы A, для которого сумма абсолютных значений элементов максимальна.
  4. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти наименьшее значение среди средних значений для каждой строки матрицы.
  5. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Определить средние значения по всем строкам и столбцам матрицы. Результат оформить в виде матрицы из N + 1 строк и M + 1 столбцов.
  6. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти сумму элементов всей матрицы. Определить, какую долю в этой сумме составляет сумма элементов каждого столбца. Результат оформить в виде матрицы из N + 1 строк и M столбцов.
  7. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти сумму элементов всей матрицы. Определить, какую долю в этой сумме составляет сумма элементов каждой строки. Результат оформить в виде матрицы из N строк и M+1 столбцов.
  8. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Определить, сколько отрицательных элементов содержится в каждом столбце и в каждой строке матрицы. Результат оформить в виде матрицы из N + 1 строк и M + 1 столбцов.
  9. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Определить, сколько нулевых элементов содержится в верхних L строках матрицы и в левых К столбцах матрицы.
  10. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Перемножить элементы каждого столбца матрицы с соответствующими элементами K-го столбца.Аим
  11. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Просуммировать элементы каждой строки матрицы с соответствующими элементами L-й строки.
  12. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Разделить элементы каждой строки на элемент этой строки с наибольшим значением.
  13. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Разделить элементы каждого столбца матрицы на элемент этого столбца с наибольшим значением.
  14. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Разделить элементы матрицы на элемент матрицы с наибольшим значением.
  15. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Все элементы имеют целый тип. Дано целое число H. Определить, какие столбцы имеют хотя бы одно такое число, а какие не имеют.
  16. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Исключить из матрицы строку с номером L. Сомкнуть строки матрицы.
  17. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Добавить к матрице строку и вставить ее под номером L.
  18. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Найти сумму элементов, стоящих на главной диагонали, и сумму элементов, стоящих на побочной диагонали (элементы главной диагонали имеют индексы от [0,0] до [N,N], а элементы побочной диагонали – от [N,0] до [0,N]).
  19. Создать квадратную матрицу A, имеющую N строк и N столбцов со случайными элементами. Определить сумму элементов, расположенных параллельно главной диагонали (ближайшие к главной). Элементы главной диагонали имеют индексы от [0,0] до [N,N].
  20. Создать квадратную матрицу A, имеющую N строк и N столбцов со случайными элементами. Определить произведение элементов, расположенных параллельно побочной диагонали (ближайшие к побочной). Элементы побочной диагонали имеют индексы от [N,0] до [0,N].
  21. Создать квадратную матрицу A, имеющую N строк и N столбцов со случайными элементами. Каждой паре элементов, симметричных относительно главной диагонали (ближайшие к главной), присвоить значения, равные полусумме этих симметричных значений (элементы главной диагонали имеют индексы от [0,0] до [N,N]).
  22. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Исходная матрица состоит из нулей и единиц. Добавить к матрице еще один столбец, каждый элемент которого делает количество единиц в каждой строке чётным.
  23. Создать квадратную матрицу A, имеющую N строк и N столбцов со случайными элементами. Найти сумму элементов, расположенных выше главной диагонали, и произведение элементов, расположенных выше побочной диагонали (элементы главной диагонали имеют индексы от [0,0] до [N,N], а элементы побочной диагонали — от [N,0] до [0,N]).
  24. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Дан номер строки L и номер столбца K, при помощи которых исходная матрица разбивается на четыре части. Найти сумму элементов каждой части.
  25. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Определить, сколько нулевых элементов содержится в каждом столбце и в каждой строке матрицы. Результат оформить в виде матрицы из N + 1 строк и M + 1 столбцов.
  26. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Дан номер строки L и номер столбца K, при помощи которых исходная матрица разбивается на четыре части. Найти среднее арифметическое элементов каждой части.
  27. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Все элементы имеют целый тип. Дано целое число H. Определить, какие строки имеют хотя бы одно такое число, а какие не имеют.
  28. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Исключить из матрицы столбец с номером K. Сомкнуть столбцы матрицы.
  29. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Добавить к матрице столбец чисел и вставить его под номером K.
  30. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Добавить к элементам каждого столбца такой новый элемент, чтобы сумма положительных элементов стала бы равна модулю суммы отрицательных элементов. Результат оформить в виде матрицы из N + 1 строк и M столбцов.
  31. Создать прямоугольную матрицу A, имеющую N строк и M столбцов со случайными элементами. Добавить к элементам каждой строки такой новый элемент, чтобы сумма положительных элементов стала бы равна модулю суммы отрицательных элементов. Результат оформить в виде матрицы из N строк и M + 1 столбцов.

Работа с ассоциативными массивами (таблицами данных)

  1. Используя данные таблицы отсортировать блюда по возрастанию цены. Вывести отсортированный вариант списка блюд.
    Блюдо
    Цена

    Борщь
    35

    Котлета
    40

    Каша
    20

    Чай
    3

  2. Имеется список учеников и результаты трёх тестов (баллы от 0 до 100).Определить средний балл каждого ученика по трём тестам, вывести список учеников по убыванию среднего балла.
  3. Известны данные о количестве мальчиков и девочек в нескольких классах. Отсортировать названия классов по возрастанию процента мальчиков, определить количество классов, в которых мальчиков больше, чем девочек, и вывести названия этих классов отдельно.
  4. Решить задачу, связанную с оценкой экономической деятельности группы предприятий на основе известных данных:

    • название предприятий;
    • плановый объем розничного товарооборота;
    • фактический объем розничного товарооборота.

    Требуется определить:

    1. процент выполнения плана каждым предприятием;
    2. количество предприятий, недовыполнивших план на 10% и более;
    3. наименьший плановый товарооборот;
    4. упорядочить предприятия по убыванию планового товара.
]]>
https://chel-center.ru/php-yfc/php-prodvinutyj-praktikum/feed/ 0
Фундаментальные структуры данных, которые вам следует знать для практического программирования https://chel-center.ru/php-yfc/fundamentalnye-struktury-dannyh-kotorye-vam-sleduet-znat-dlya-prakticheskogo-programmirovaniya/ https://chel-center.ru/php-yfc/fundamentalnye-struktury-dannyh-kotorye-vam-sleduet-znat-dlya-prakticheskogo-programmirovaniya/#respond Thu, 03 Oct 2019 03:00:42 +0000 http://chel-center.ru/php-yfc/?p=61 Читать далее «Фундаментальные структуры данных, которые вам следует знать для практического программирования»

]]>

или к чему быть готовым на собеседовании

Источник перевода

Никлаус Вирт, швейцарский ученый-информатик, в 1976 году написал книгу под названием «Алгоритмы + Структуры данных = Программы».

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

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

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

Изучение структур данных — незаменимое дело, даже если вы просто стараетесь профессионально совершенствоваться на нынешней работе. Начнем с основ.

Что такое структура данных?

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

Зачем нужны структуры данных?

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

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

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

Наиболее распространенные структуры данных

Сначала давайте перечислим наиболее распространенные структуры данных, а затем разберем каждую по очереди:

  1. Массивы
  2. Стеки
  3. Очереди
  4. Связные списки
  5. Деревья
  6. Графы
  7. Боры (в сущности, это тоже деревья, но их целесообразно рассмотреть отдельно).
  8. Хеш-таблицы

Массивы

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

Здесь показан простой массив размером 4, содержащий элементы (1, 2, 3 и 4).

Массив
Массив

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

Существуют массивы двух типов:

  • Одномерные (такие, как показанный выше)
  • Многомерные (массивы, в которые вложены другие массивы)

Простейшие операции с массивами

  • Insert — вставляем элемент на позицию с заданным индексом
  • Get — возвращаем элемент, занимающий позицию с заданным индексом
  • Delete — удаляем элемент с заданным индексом
  • Size — Получаем общее количество элементов в массиве

Вопросы по массивам, часто задаваемые на собеседованиях

  • Найти второй минимальный элемент массива
  • Найти неповторяющиеся целые числа в массиве
  • Объединить два отсортированных массива
  • Переупорядочить положительные и отрицательные значения в массиве

Стеки

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

Стек можно сравнить с высокой стопкой книг. Если вам нужна какая-то книга, лежащая около центра стопки, вам сначала придется снять все книги, лежащие выше. Именно так работает принцип LIFO (Последним пришел — первым вышел).

Так выглядит стек, содержащий три элемента данных (1, 2 и 3), где 3 находится сверху — поэтому будет убран первым:

Стэк
Стэк

Простейшие операции со стеком:

  • Push — Вставляет элемент в стек сверху
  • Pop — Возвращает верхний элемент после того, как удалит его из стека
  • isEmpty — Возвращает true, если стек пуст
  • Top — Возвращает верхний элемент, не удаляя его из стека

Вопросы о стеке, часто задаваемые на собеседованиях

  • Вычислить постфиксное выражение при помощи стека
  • Отсортировать значения в стеке
  • Проверить сбалансированные скобки в выражении

Очереди

Очередь, как и стек — это линейная структура данных, элементы в которой хранятся в последовательном порядке. Единственное существенное отличие между стеком и очередью заключается в том, что в очереди вместо LIFO действует принцип FIFO (Первым пришел — первым вышел).

Идеальный реалистичный пример очереди — это и есть очередь покупателей в билетную кассу. Новый покупатель становится в самый хвост очереди, а не в начало. Тот же, кто стоит в очереди первым, первым приобретет билет и первым ее покинет.

Вот изображение очереди с четырьмя элементами данных (1, 2, 3 и 4), где 1 идет первым и первым же покинет очередь:

Очереди
Очереди

Простейшие операции с очередью

  • Enqueue() — Добавляет элемент в конец очереди
  • Dequeue() — Удаляет элемент из начала очереди
  • isEmpty() — Возвращает true, если очередь пуста
  • Top() — Возвращает первый элемент в очереди

Вопросы об очередях, часто задаваемые на собеседованиях

  • Реализуйте стек при помощи очереди
  • Обратите первые k элементов в очереди
  • Сгенерируйте двоичные числа от 1 до n при помощи очереди

Связный список

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

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

При помощи связных списков реализуются файловые системы, хеш-таблицы и списки смежности.

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

Связный список
Связный список

Существуют такие типы связных списков:

  • Односвязный список (однонаправленный)
  • Двусвязный список (двунаправленный)

Простейшие операции со связными списками:

  • InsertAtEnd — Вставляет заданный элемент в конце связного списка
  • InsertAtHead — Вставляет заданный элемент в начале (с головы) связного списка
  • Delete — Удаляет заданный элемент из связного списка
  • DeleteAtHead — Удаляет первый элемент в связном списке
  • Search — Возвращает заданный элемент из связного списка
  • isEmpty — Возвращает true, если связный список пуст

Вопросы о связных списках, часто задаваемые на собеседованиях:

  • Обратите связный список
  • Найдите петлю в связном списке
  • Возвратите N-ный узел с начала связного списка
  • Удалите из связного списка дублирующиеся значения

Графы

Граф — это множество узлов, соединенных друг с другом в виде сети. Узлы также называются вершинами. Пара (x,y) называется ребром, это означает, что вершина x соединена с вершиной y. Ребро может иметь вес/стоимость — показатель, характеризующий, насколько затратен переход от вершины x к вершине y.

Графы
Графы

Типы графов:

  • Неориентированный граф
  • Ориентированный граф

В языке программирования графы могут быть двух видов:

  • Матрица смежности
  • Список смежности

Распространенные алгоритмы обхода графа:

  • Поиск в ширину
  • Поиск в глубину

Вопросы о графах, часто задаваемые на собеседованиях:

  • Реализуйте поиск в ширину и поиск в глубину
  • Проверьте, является граф деревом или нет
  • Подсчитайте количество ребер в графе
  • Найдите кратчайший путь между двумя вершинами

Деревья

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

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

Вот схема простого дерева и базовая терминология, связанная с этой структурой данных:

Деревья
Деревья

Существуют деревья следующих типов:

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

Вопросы о деревьях, часто задаваемые на собеседованиях:

Найдите высоту двоичного дерева

Найдите k-ное максимальное значение в двоичном дереве поиска

Найдите узлы, расположенные на расстоянии “k” от корня

Найдите предков заданного узла в двоичном дереве

Бор

Бор, также именуемый «префиксное дерево» — это древовидная структура данных, которая особенно эффективна при решении задач на строки. Она обеспечивает быстрое извлечение данных и чаще всего применяется для поиска слов в словаре, автозавершений в поисковике и даже для IP-маршрутизации.

Вот как три слова top (верх), thus (следовательно), and their (их) хранятся в бору:

Бор
Бор

Слова располагаются в направлении сверху вниз, и зеленые узлы «p», «s» и «r» завершают, соответственно, слова «top», «thus» и «their».

Вопросы о борах, часто задаваемые на собеседованиях:

  • Подсчитайте общее количество слов, сохраненных в бору
  • Выведите на экран все слова, сохраненные в бору
  • Отсортируйте элементы массива при помощи бора
  • Постройте слова из словаря, воспользовавшись бором
  • Создайте словарь T9

Хеш-таблица

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

Как правило, хеш-таблицы реализуются при помощи массивов.

Производительность хеширующей структуры данных зависит от следующих трех факторов:

  • Хеш-функция
  • Размер хеш-таблицы
  • Метод обработки коллизий

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

Хеш-таблица
Хеш-таблица

Вопросы о хешировании, часто задаваемые на собеседованиях:

  • Найдите симметричные пары в массиве
  • Отследите полную траекторию пути
  • Найдите, является ли массив подмножеством другого массива
  • Проверьте, являются ли массивы непересекающимися

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

Удачи и интересного обучения! 🙂

Источник перевода

]]>
https://chel-center.ru/php-yfc/fundamentalnye-struktury-dannyh-kotorye-vam-sleduet-znat-dlya-prakticheskogo-programmirovaniya/feed/ 0
Эффективные фундаментальные структуры данных в PHP7 https://chel-center.ru/php-yfc/effektivnye-fundamentalnye-struktury-dannyh-v-php7/ https://chel-center.ru/php-yfc/effektivnye-fundamentalnye-struktury-dannyh-v-php7/#respond Thu, 03 Oct 2019 05:28:17 +0000 http://chel-center.ru/php-yfc/?p=57 Читать далее «Эффективные фундаментальные структуры данных в PHP7»

]]>
PHP имеет всего одну структуру данных для управления всем. array — сложный, гибкий, гибридный, сочетает в себе поведение list и linked map. Но мы используем его для всего, потому что PHP придерживается прагматичного подхода: иметь предельно правильный, здравый и реалистичный способ решения проблемы, исходящий из практических, а не теоретических рассуждений. array позволяет делать работу, хотя о нем и так много рассказывают на лекциях по информатике. Но, к сожалению, с гибкостью приходит и сложность.

Последний релиз PHP вызвал большое оживление в сообществе. Мы не могли дождаться того, чтобы начать использовать новые возможности и почувствовать вкус ~2х прироста производительности. Одна из причин, почему это случилось — структура array была переработана. Но массивы все также придерживаются принципа «оптимизировано для всего; оптимизировано для ничего», еще не все идеально, есть возможности для совершенствования.

А что насчет структур данных SPL?

К сожалению… они ужасны. Раньше, до PHP7, они предлагали _некоторые_ преимущества, но сейчас мы дошли до точки, когда использование SPL не имеет практического смысла.

Почему мы не можем просто поправить и улучшить их?

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

«SPL data structures are horribly designed.»
Anthony Ferrara


Введение: php-ds — расширение для PHP7, добавляющее структуры данных. Этот пост кратко охватывает поведение, производительность и преимущества каждой из них. Также в конце вы найдете список ответов на ожидаемые вопросы.

Github: https://github.com/php-ds
Пространство имен: Ds\
Интерфейсы: Collection, Sequence, Hashable
Классы: Vector, Deque, Stack, Queue, PriorityQueue, Map, Set


Collection (Коллекция)

Collection — это базовый интерфейс, охватывающий общую функциональность: foreach, echo, count, print_r, var_dump, serialize, json_encode, и clone.

Sequence (Последовательность)

Sequence описывает поведение элементов, организованных в единый, линейный размер. В некоторых языках такая структура назвается List (список). Подобен array, который использует инкрементальные ключи, за исключением некоторых особенностей:

  • Значения всегда должны быть индексированы как [0, 1, 2, …, size - 1]
  • Извлечение или добавление приводит к обновление индекса всех последовательных значений
  • Поддерживает доступ к значениям только из индекса [0, size - 1]

Варианты использования

  • Везде, где вы бы хотели использовать array как список (без ключей)
  • Более эффективная альтернатива SplDoublyLinkedList и SplFixedArray

Vector (Вектор)

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

Сильные стороны

  • Очень маленькое потребление памяти
  • Очень быстрые итерации
  • get, set, push и pop имеют сложность O(1)

Недостатки

  • insert, remove, shift and unshift имеют сложность O(n)

Структурой номер один в Photoshop были Вектора.
— Sean Parent, CppCon 2015

Deque (Двусвязная очередь)

Deque (произносится как «deck») — это последовательность значений, объединенных в непрерывный буфер, увеличивающийся и уменьшающийся автоматически. Название является общепринятым сокращением от «double-ended queue». Используется внутри Ds\Queue.

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

Доступ к значению по индексу требует вычисления соответствующей позиции в буфере: ((head + position) % capacity).

Сильные стороны

  • Очень маленькое потребление памяти
  • get, set, push, pop, shift и unshift имеют сложность O(1)

Недостатки

  • insert, remove имеют сложность O(n)
  • Емкость буфера должна иметь степень двойки (2ⁿ)

Следующий бенчмарк показывает общее затраченное время и память, используемую для операции push 2ⁿ случайных чисел. array, Ds\Vector и Ds\Deque отрабатывают быстро, но SplDoublyLinkedList стабильно показывает результат более чем в 2 раза хуже.

SplDoublyLinkedList выделяет память для каждого значения по отдельности, поэтому и происходит ожидаемый рост по памяти. array и Ds\Deque при своей реализации выделяют память порционно для поддержания достаточного объема для 2ⁿ элементов. Ds\Vector имеет фактор роста 1.5, что влечет за собой увеличение количества выделений памяти, но меньший расход в целом.


Следующий бенчмарк показывает время, затраченное на unshift единственного элемента в последовательности значений размером 2ⁿ. Время, требующееся на установку значений не учитывается.

На графике видно, что array_unshift имеет сложность O(n): всякий раз, когда объем выборки удваивается, растет и время, необходимое для unshift. Это объясняется тем, что каждый числовой показатель в диапазоне [1, size - 1] должен быть обновлен.

Но и Ds\Vector::unshift также O(n), так почему же он намного быстрее? Имейте ввиду, что array хранит каждое значение в bucket вместе с его ключем и хэшем. Поэтому приходится проверять каждый элемент и обновлять хэш, если индекс является числовым. На самом деле array_unshift выделяет новый массив для этого и заменяет старый, когда все значения скопированы.

В векторе же индекс значения — это прямое отображение его индекса в буфере, поэтому все, что нам нужно сделать — сдвинуть каждое значение в диапазоне [1, size — 1] вправо на одну позицию. Делается это при помощи всего одной операции memmove.

Ds\Deque и SplDoublyLinkedList в свою очередь очень быстры, потому что на время для unshift значения не влияет размер выборки, т.е. его сложность будет O(1).

На следующем тесте видно сколько памяти используется при 2ⁿ pop операций. Другими словами при изменении размера от 2ⁿ до нуля

Интересно тут то, что array всегда держит выделенную память, даже если его размер существенно уменьшается. Ds\Vector and Ds\Deque позволяют в два раза уменьшить выделяемые ресурсы, если их размер падает ниже четверти своего текущего потенциала. SplDoublyLinkedList освобождает память после каждого удаления из выборки, поэтому мы можем наблюдать линейное снижение.

Stack (Стек)

Стек — является коллекцией, организованной по принципу «последним пришёл — первым вышел» или «LIFO» (last in — first out), позволяющей получить доступ только к значению на вершине структуры. Вы можете думать о нем как об оружейном магазине с динамической емкостью.

Ds\Stack использует внутри себя Ds\Vector.

SplStack наследуется от SplDoublyLinkedList, поэтому производительность будет эквивалентна сравнению Ds\Vector to SplDoublyLinkedList из предыдущих тестов. Посмотрим на время, необходимое для выполнения 2ⁿ pop-операций, изменения размера от 2ⁿ до нуля.

Queue (Очередь)

Очередь — тип данных с парадигмой доступа к элементам «первый пришел — первый вышел» («FIFO», «First In — First Out»). Такая коллекция позволяет получить доступ к элементам в порядке их добавления. Ее название говорит само за себя, представьте себе структуру как линию людей, стоящих в очереди на кассу в магазине.

Ds\Queue использует внутри себя Ds\Deque. SplQueue наследуется от SplDoublyLinkedList, поэтому производительность будет эквивалентна сравнению Ds\Deque с SplDoublyLinkedList, показанному в предыдущем бенчмарке.

PriorityQueue (Очередь с приоритетом)

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

Реализация очереди с приоритетом использует max-heap.

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

А что же с производительностью? Следующий бенчмарк показывает время и память, требующиеся для операции push 2ⁿ случайных чисел со случайным приоритетом в очередь. Те же случайные числа будут использоваться для каждого из тестов. В тесте для Queue также генерируется случайный приоритет, хотя он и не используется.

Это, наверное, самый значимый из всех бенчмарков. Ds\PriorityQueue работает более чем в два раза быстрее чем SplPriorityQueue и использует только 5% от его памяти — это в 20 раз более эффективное решение по памяти.

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

Ds\PriorityQueue поддерживает только целочисленные приоритеты, поэтому каждой паре выделяется 24 байта. Но это все еще недостаточная разница для объяснения результата.

Если вы посмотрите на исходный код SplPriorityQueue::insert, то заметите, что он инициализирует массив для хранения пары значение-приоритет.

Т.к. массив имеет минимальную емкость 8, то для каждой пары на самом деле выделяется zval + HashTable + 8 * (Bucket + hash) + 2 * zend_string + (8 + 16) byte string payloads = 16 + 56 + 36 * 8 + 2 * 24 + 8 + 16 = 432 байта (64 бит).

«Так… почему же массив?»

SplPriorityQueue использует ту же внутреннюю структуру SplMaxHeap, которая требует от значения быть типом zval. Очевидный (но неэффективный) способ создания zval-пары, т.к. zval сам используется как array.

Hashable

Интерфейс, позволяющий объектам быть использованными в качестве ключей. Это альтернатива spl_object_hash, который детерминирует объект в хэш, базирующийся на его handle:. Это означает, что два объекта, которые считались бы равными при сравнении, не имели бы равный хэш, т.к. они не являются одним и тем же экземпляром.

Hashable вводит только два метода: hash и equals. Многие другие языки поддерживают это изначально: в Java — hashCode и equals, или в Python ___hash___ и __eq__. Было несколько RFC, добавляющих подобное поведение и в PHP, но ни один из не был принят.

Все структуры, будут возвращать spl_object_hash, если ключи объектов, хранящиеся в них не реализуют в себе Hashable.

Структуры данных, работающие с интерфейсом Hashable: Map и Set.

Map (Ассоциативный массив)

Map является последовательной коллекцией пар ключ-значение, практически идентичной array в аналогичном контексте. Ключи могут быть любого типа, единственное условие — уникальность. При повторном добавлении ключа значения заменяются.

Как и в array, порядок вставки сохраняется.

Сильные стороны

  • Производительность и эффективность использования памяти практически идентичны array
  • Автоматическое освобождение памяти при уменьшении размера
  • Ключи и значения могут быть любого типа, включая объекты
  • Поддерживает работу с объектами, реализующими интерфейс Hashable
  • put, get, remove и containsKey имеют сложность O(1)

Недостатки

  • Не может быть преобразован в array при наличии ключей-объектов
  • Нет возможности получить доступ к значениям по индексу (позиции)

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

Set (Множество)

Set — коллекция уникальных значений. Учебники скажут вам, что в структуре Set значения неупорядочены, если реализация не предусматривает иное. Возьмем для примера Java, java.util.Set — это интерфейс с двумя основными реализациями: HashSet и TreeSet. HashSet обеспечивает сложность O(1) для add и remove, a TreeSet обеспечивает сортированный набор данных, но сложность add и remove возрастает до O(log n).

Set использует ту же внутреннюю структуру, что и Map, также основываясь на array. Это означает, что Set может быть отсортирован за время O(n * log(n)) когда это понадобится, в остальном он такой же простой как Map и array.

Сильные стороны

  • add, remove и contains имеют сложность O(1)
  • Поддерживает работу с объектами, реализующими интерфейс Hashable
  • Поддерживает любой тип значений (SplObjectStorage поддерживает только объекты).
  • Имеет эквивалент поразрядных логических операций (intersection, difference, union, exclusive or)

Недостатки

  • Не поддерживает push, pop, insert, shift или unshift
  • get имеет сложность O(n) если есть удаленные значения до момента индексации, в ином случае — O(1)

Следующий бенчмарк показывает время, затраченное на добавление 2ⁿ новых экземпляров stdClass. Он показывает, что Ds\Set немного быстрее, чем SplObjectStorage, и использует примерно в половину меньше памяти.


Распространенным способом создания массива с уникальными значениями является array_unique, который создает новый array, содержащий только уникальные значения. Но важно иметь ввиду, что значения в массиве не индексируются, in_array является линейным поиском со сложность O(n). array_unique работает только со значениями, без учета ключей, каждая проверка на наличие значения массива — линейный поиск, что даем нам в сумме сложность O(n²) по времени и O(n) по потреблению памяти.

Ответы на ожидаемые вопросы и мнения

Есть ли тесты?

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

Документация? Справочник по API?

На момент написания этой статьи пока еще нет полной документации, но она появится вместе с первым стабильным релизом.

Однако, существуют некоторые хорошо документированные файлы-заглушки.

Можем ли мы посмотреть как устроены бенчмарки? Есть что-то о них?

Все бенчмарки прогонялись на стандартном билде PHP 7.0.3 на 2015 Macbook Pro. Результаты могут отличаться в зависимости от версии и платформы.

Почему Stack, Queue, Set и Map — не интерфейсы?

Я не верю, что есть необходимость в какой-либо альтернативной реализации. 3 интерфейса и 7 классов — это хороший баланс между прагматизмом и специализацией.

Когда мне использовать Deque вместо Vector?

Если вы точно знаете, что не будете использовать shift и unshift, используйте Vector. Для удобного тайпхинтинга можно указать в качестве типа Sequence.

Почему все классы являются финализированными?

Дизайн API php-ds применяет парадигму «Composition over inheritance.

Структуры SPL являются хорошим примером того, как наследование может быть использовано не по назначение. Например, SplStack расширяет SplDoublyLinkedList, который поддерживает произвольный доступ по индексу, shift и unshift — так что технически это не Стек.

Фреймворк Java-коллекций также имеет несколько интересных случаев, когда наследование порождает двусмысленность. ArrayDeque имеет три метода добавления элементов: add, addLast и push. Это не плохо, т.к. ArrayDeque имплементирует Deque и Queue, что объясняет одновременное наличие addLast и push. Однако, все три метода сразу, делающие одно и тоже, вызывают путаницу и непоследовательность.

Старый java.util.Stack расширял java.util.Vector, тем самым заявляя, что „более полный и последовательный набор операций LIFO обеспечивается интерфейсом Deque и его реализациями“, но Deque включает в себя методы addFirst и remove(x), которые не должны быть часть stack структуры по API.

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

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

Наследование также вызвало бы лишние сложности во внутренней реализации.

Зачем нужен еще и ds класс в глобальном пространстве имен?

Он обеспечивает альтернативный синтаксис:

Почему нет связного списка (Linked List)?

Класс LinkedList на самом деле появился первым, это казалось хорошим стартом. Но в итоге я решил удалить его, когда понял, что он не сможет конкурировать с Vector или Deque при любом раскладе. Две основные причины возможной поддержки: распределение накладных расходов и локальность ссылок.

В связном списке мы добавляем или убираем зарезервированную память для элемента структуры (node) всякий раз, когда значение добавляется или удаляется. Нода содержит в себе два указателя (в случае с двусвязным списком), чтобы ссылаться на предыдущую и последующую ноды. Обе структуры, Vector и Deque, выделяют буфер памяти заранее, поэтому нет необходимости делать это настолько часто. Они также не нуждаются в дополнительных указателях, чтобы знать какое значение до и какое после, тем самым снижаются накладные расходы.

Будет ли связный список использовать меньше памяти, т.к. там нет буфера?

Только когда коллекция очень мала. Верхней границей количества памяти для Vector будет (1.5 * (size - 1)) * zval байт, не менее *10 * zval*. В двусвязном списке же будет использоваться (size * (zval + 8 + 8)). Поэтому связный список будет использовать меньше памяти, чем Vector только тогда, когда его размер меньше 6 элементов.

Окей… связный список использует больше памяти, но почему он медленный?

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

»Несмежность данных в структурах является корнем всех зол производительности. Конкретно, пожалуйста, скажите нет связным спискам"
«Нет почти ничего вреднее из того что вы можете сделать чтобы убить все плюсы современных микропроцессоров, чем использовать связный список»
— Chandler Carruth (CppCon 2014)

PHP — это язык для веб-разработки — производительность не важна.

Производительность не должна быть вашим главным приоритетом. Код должен быть последовательным, ремонтопригодным, надежным, предсказуемым, безопасным и легко понимаемым. Но это не означает, что производительность «не важна».

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

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

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


Обсуждения: Twitter, Reddit, Room 11
Исходный код: github.com/php-ds
Бенчмарки: github.com/php-ds/benchmarks

Автор оригинала: Rudi Theunissen

]]>
https://chel-center.ru/php-yfc/effektivnye-fundamentalnye-struktury-dannyh-v-php7/feed/ 0