Веб-инжиниринг

Оглавление

			

Лекция первая. Введение в работу

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

Инженер — технический специалист «во всём». Где не является специалистом, самостоятельно разбирается в срок. Умеет нестандартно мыслить и, при необходимости, изобретать новый подход.

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

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

Ответственности, я учусь, по большей мере, равнением на Лебедева и бюрошников Ильяхова, Бирмана, Товеровского.

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

Не полное исполнение, но ответственность превыше всего.

Как будем работать

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

Используемая методология ведения проектов это недо ФФФ или недо SCRUM. Почитайте про гибкие методологии.

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

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

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

Гугл группы

Вы можете писать мне, но важную информацию я всё равно перенесу в Гугл группы.

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

Это работает следующим образом: я добавляю вас в список рассылки — вы соглашаетесь (по ссылке). Далее каждая созданная тема (как ветка на форуме или диалог во ВКонтакте) приходит всем. Соответственно, каждый ответ также рассылается всем.

Есть веб-интерфейс, где вы можете создавать новые темы и отвечать, а можно писать на электронную почту группы, создавая тем самым новую тему (берётся из темы письма). Или отвечать на полученную рассылку — ответ получат все.

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

Впрочем, инструкции по работе с Гугл-группами я вам также вышлю.

Главное, чтобы вы не стеснялись вести переписку. Помните, не стыдно показать незнание, стыдно не научиться.

Вопросы ко мне

Я рад вам помочь, так что пишите.

Но я призываю вас обращаться ко мне со сложными и подготовленными вопросами. Это значит, что вы придёте не с вопросом: «А как написать документацию?», а с вопросом:

Здравствуйте.

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

или

Здравствуйте.

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

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

Учимся искать в интернете

Суть работы программистов

Помните, что добавив всего одно ключевое слово вы измените контекст. То есть написав «заменить название файлов» не очень понятно будет. Но допишите: «window», «cmd», «php», «JavaScript» и мир заиграет новыми красками.

И ещё, если вы встречаете что-то назойливое и ненужное, но уже не понятно какое бы слово добавить, то напишите это назойливое слово, а перед ним минус: «клавиатура -купить» — тогда только реклама нерадивых директологов вам покажет ненужные ссылки.

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

Учимся системе управления версиями Mercurial

Логотип Mercurial

Вы когда-нибудь редактировали один ворд файл в разных окнах? Тогда-то вы знаете, что надо использовать систему управления версиями (СУВ).

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

В чём прелесть? Вы всегда видите кто совершил изменение и можете откатиться к прошлой версии «в один клик».

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

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

Демонстрация работы репозитория

Репозиторий работает примерно так: вы пишите на белом листе, исправляете на другом прозрачном листе, кладёте его сверху. Когда вы посмотрите на лист, то увидите последнюю версию, но стоит вам поднять верхний прозрачный листик, как вы увидите прошлую. Так это и работает.

Не так уж и плохо, если вы пройдете курс гита на Гикбрейне или туториал от гитхаба.

Может быть, вы уже слышали про git и github. Ими мы пользоваться не будем, потому что на github может использоваться только git, а он нам не подходит, в нём нет великолепного интерфейса TortoiseHG. Это программа-клиент. А репозитории хранить в Битбакете.

Используем bitbucket, а не github

Терминология

Репозиторий — хранилище версий изменений одного проекта.

Удалённый репозиторий — удалённое хранилище изменений. Используется для общей синхронизации изменений.

Локальный репозиторий — копия удаленного хранилища, куда программист записывает изменения.

Коммит — пакет изменений в рамках хранилища. Обязательно должен содержать осмысленный комментарий.

Ветка — набор коммитов.

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

Главная ветка — основная ветвь разработки проекта. Незначительные изменения вносятся прямо в него, все прочие изменения стоит проводить через ветки.

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

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

Слияние — объединение изменений в единую версию.

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

Обычный процесс работы

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

Изменяем дальше файлы проекта и коммитим новые изменения.

Пулим (затягиваем) изменения из главного репозитория. Затем пушим (проталкиваем) их на главный репозиторий.

Создание удаленного репозитория на битбакете

Для создания необходимо в верхнем главном меню выбрать «Репозитории» → «Создать репозиторий». Заполнить анкету.

Создание локального репозитория

Контекстное меню проводника

Для начала необходимо выбрать папку. Я рекомендую создать папку в корне диска C:// с именем home. А в ней уже создавать папки с репозиториями-проектами.

В необходимой папке кликайте правой кнопкой мыши (ПКМ) по белому пространству и выберите в контекстном меню «TortoiseHG», далее Clone… в случае, если у вас уже есть репозиторий в битбакете и Create Repository Here… если создаёте новый.

При клонировании указывается адрес типа: https://bitbucket.org/higimo/home в графе источник.

При создании нового, стоит проверить, чтоб стояла галка «Добавить файлы особого назначения».

Настройки TortoiseHg

Настройки делятся на глобальные и локальные (т. е. для каждого репозитория).

Глобальные Локальные настройки

Редактируются в главном меню: «Файл» → «Настройки».

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

higimo <higimo@gmail.com>

Редактируется по клику на репозитории правой кнопки мыши (ПКМ) → «Настройки».

«Наименование» — изменяет название репозитория в workbench, по умолчанию используется название рабочего каталога (папки).

Коммит

Интерфейс программы

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

В папке репозитория нажать ПКМ и выбрать «Hg Commit». После этого заполнить поле комментария, проверить изменения в файлах и нажать кнопку «Фиксировать».

В workbench зайти в репозиторий, выбрать вверху вкладку «Фиксировать» (иконка зелёной галочки), затем заполнить поле комментария, проверить изменения в файлах и нажать кнопку «Фиксировать».

Удаление, добавление и переименование файлов

Изначально любой файл не добавлен и не удалён и хранится в базе данных под определённым именем.

Для удаления при коммите кликнуть ПКМ по файлу и выбрать «Удалить» (действует только на действительно удалённых файлах).

Для переименования при коммите кликнуть ПКМ по файлу и выбрать «Переименовать» (действует только на действительно удалённых файлах).

Для добавления при коммите кликнуть ПКМ по файлу и выбрать «Добавить» (действует только на действительно удалённых файлах).

Клонирование репозитория

В bitbucket, необходимо зайти на основную страницу репозитория.

Скопировать его URL, например, https://bitbucket.org/higimo/home.

В каталоге, где находятся остальные репозитории нажать ПКМ и выбрать «TortoiseHg» → «Clone…».

В появившемся окне необходимо указать источник и удалить лидирующую приписку «hg clone», чтобы получить только url.

Нажать «Клонировать».

Синхронизация изменений

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

В верхнем меню кнопка «Вытянуть входящие изменения из …» (иконка из двух жёлтых цилиндров, зелёная стрелка от меньшего к большему).

Загрузка изменений на удалённый репозиторий

В верхнем меню кнопка «Протолкнуть выходящие изменения изменения в …» (иконка из двух жёлтых цилиндров, зелёная стрелка от большего к меньшему).

Когда использовать ветки

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

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

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

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

Что делать если две головы

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

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

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

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

Программа разрешения конфликтов называется Kdiff3. По умолчанию используется кодировка системы (т. е. win-1251 для Windows OS). Чтобы именить на православный UTF-8, нужно в верхнем меню выбрать «Settings» → «Configure Kdiff3» → Вкладка «Regional Settings» → выбрать кодировку для файлов, перезапустить программу.

  1. В WorkBench выбрать ту, в которую будете «сливать» изменения ПКМ.
  2. Выбрать пункт «Обновить…».
  3. Выбрать другую ветку, которую будете «вливать» с той, на которой сейчас ПКМ.
  4. Слить с локальной копией.
  5. Пройти диалог до места разбора конфликтов.
  6. Выбрать разрешение с помощью средства — специальной программы.
  7. Выбрать необходимые изменения
  8. Сохранить файл
  9. Повторить два предыдущих пункта для остальных файлов
  10. Завершить общение с диалоговым окном

Не всегда удаётся решить проблему таким образом. Иногда следует зайти в командной строке в этот каталог и выполнить пуш с флагом -f.

						cd /home/cinemaProject
						hg push -f
					

Учимся регистрировать хостинг

Всё, что вы делаете должно быть в интернете и доступно.

Хостинг — это место, где вы будете хранить сайт, а он там работать. Так что давайте учиться регистрировать хостинг.

Выбор хостинга

Для начала надо понять как его выбрать:

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

Смотрите на пинг. Чем он меньше, тем быстрее загрузится сайт.

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

Итак, после регистрации, вам, как правило, приходит письмо на почту, где написаны все доступы: ftp, к панели управления, вероятно ssh.

Как правило, вам ещё выдаётся тестовый домен, чтоб вы сразу могли оценить работу, а не дожидаться, пока произойдёт обновление DNS-серверов.

Панель управления

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

FTP-клиент

Итак, у вас появились доступы по ftp-протоколу, но что с ними делать? Для этого, установите Filezilla.

Интерфейс программы Filezilla

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

В целом, всё должно быть понятно: по информации из письма заполнять соответствующие поля.

Непонятно только какой порт указывать.

Порты это всегда числа. Например, стандартный порт для веб-сайта — 80, даже если вы это не указываете запрос браузер посылает всё равно на 80 порт. То есть vk.com:80 — это запрос с портом.

Таймвеб поддерживает 21 и 22 порты. 21 — это обычный, не защищённый FTP. А 22 это защищённый, также известный как SFTP (S — Security).

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

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

Справа, аналогично, но для сервера.

Ещё ниже панель задач. Там мелькают задачи скачивания и выгрузки.

Всё тоже самое с Sublime Text 3
Интерфейс программы Sublime Text

С помощью Sublime Text вы сможете редактировать файлы, сохраняя они автоматически будут загружаться на сервер. А ещё, загрузив на локальный компьютер все скрипты — можно искать строчку по всем файлам проекта.

  1. Устанавливаете Sublime Text 3.
  2. Устанавливаете в него Package Control по инструкции
  3. Через Package Control по инструкции устанавливаете SFTP-плагин.
  4. Открываете папку проекта в саблайме.
  5. Сохраняете проект
  6. Создаёте файл настроек подключения к серверу
  7. Указываете данные доступа и, что при сохранении выгружать файл на сервер.
  8. PROFIT!

Работа по ssh

Для этого вам понадобится Putty

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

Далее, необходимо запустить программу Midnight Commander

						mc
					
Левая панель Файл Команда Настройки Правая панель 1 Помощь 2 Меню 3 Про~тр 4 Правка 5 Копия 6 Пер~ос 7 НвК~ог 8 Уда~ть 9 МенюМС 0 Выход Совет: Переходите к часто спользуемым каталогам из справочника, набрав Ctrl+\ higimo@note:/$ Имя Размер Время правки Имя Имя /bin /boot ~cdrom /dev /etc about install.php *libtool aclocal.m4 /bin /boot /dev /etc ~cdrom about install.php *libtool aclocal.m4

Экран делится на четыре части, но почти весь экран занимают две панели. Слева и справа — файлы и некоторые данные о них. Самая нижняя строка — командная, чуть ниже неё отображены назначения клавиш начиная от F1 и заканчивая F11.

В самом верху — меню, его можно кликать и мышью (Putty позволяет это делать). Без мышки в неё можно перейти по клавише F9.

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

Жизнь слишком коротка, чтоб не использовать горячие клавиши.

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

Ctrl+L обновить экран MC.
Ctrl+X, C запустить команду Chmod к файлу или помеченным файлам.
Ctrl+X, O запустить команду Chown к текущему файлу или к помеченным файлам.
Ctrl+X, L Создание жесткой ссылки (Link). Создание жесткой ссылки к текущему файлу. Почти аналогично ярлыкам, в виндоус, но можно делать только ссылку на файл. Таким образом, файл будет доступен по двум именам и из разных мест.
Ctrl+X, S Создание символической ссылки (SymLink). Это аналогично ярлыкам в видовс. Обозначется @ в интерфейсе. Можно указывать разные права и атрибуты.
Alt+? выполняет поиск файла.
Insert для пометки файлов используется клавиша. Для снятия пометки следует пометить помеченные файлы ещё раз.
Alt+Enter копирует текущее имя файла в командную строку.
Alt+Tab дополняет до конца набираемое имя файла, команды, переменной, имя пользователя и удаленного компьютера.
Ctrl+X, T;
Ctrl+X, Ctrl+T
копирует помеченные файлы (если их нет, то берётся текущий файл) из активной панели (Ctrl+X, T) или из неактивной (Ctrl+X, Ctrl+T) в командную строку.
Ctrl+X, P; Ctrl+X Ctrl+P копирует путь активной панели (Ctrl+X, p) или неактивной (Ctrl+X Ctrl+P) в командную строку.
Alt+P, Alt+N используется для доступа к списку выполнявшихся команд (Alt+P отображает предыдущую команду, а Alt+N - следующую).
Alt+! Перенаправленный просмотр. Эта функция запрашивает команду с параметрами (аргумент к текущему выбранному файлу) и результат перенаправляется во встроенный вюьер файлов.
F1 Справка. Вызывает встроенный гипертекстовый вьюер помощи. Нажатие клавиши Tab приводит к выбору следующей ссылки, а Enter - к переходу по этой ссылке. Клавиши Space и Backspace позволяют перемещаться вперёд и назад. Для получения справки по используемым клавишам, нажмите F1 ещё раз.
F2 Меню. Вызывает меню пользователя. Оно позволяет создавать и добавлять дополнительные функции.
F3, Shift+F3 Просмотр. Показывает текущий файл. По умолчанию эта команда вызывает встроенный вьюер (просмотрщик) файлов. Если опция «Use internal view» не установлена, то вызывается внешний вьюер, указанный в переменной PAGER. Если эта переменная не определена, то вызывается команда «view». При нажатии Shift+F3, вьюер будет вызван без форматирования и предварительной обработки файла.
F4 Редактирование. Вызывается редактор, указанный в переменной оболочки EDITOR.
F5 Копирование. Выдаёт диалоговое окно с каталогом назначения, которым по умолчанию принимается каталог неактивной панели, и копирует выделенный файл или группу помеченных файлов в каталог, указанный в этом окне. Копирование можно прервать в любой момент, нажав Ctrl+C или Escape. Для подробной информации по установке маски копируемых файлов (обычно * или ^\(.*\)$, в зависимости от установки опции использования шаблонов оболочки) и возможных шаблонов каталога назначения.
F6 Переименование-Перемещение. Выдаёт диалоговое окно с каталогом назначения, которым по умолчанию принимается каталог неактивной панели, и перемещает выделенный файл или группу помеченных файлов в каталог, указанный в этом окне. Перемещение можно прервать в любой момент, нажав Ctrl+C или Escape.
F7 Создать Каталог. Выдаёт диалоговое окно с запросом на название каталога и создаёт каталог с указанным именем.
F8 Удаление. Удаляет текущий файл или группу помеченных файлов из активной панели. Удаление можно прервать нажав Ctrl+C или Escape.
F10, Shift+F10 Выход. Завершает работу MC. При выходе с помощью Shift+F10 текущим становится каталог, из которого был запущен MC, а не последний рабочий каталог.

Как подключиться по ftp к другому серверу и скачать с него файлы вы сможете найти самостоятельно. ftp-link я даже выложил у себя на сайте.

Пишем документацию

А лучше изучить дисциплины анализ требований и UML.

Существуют и ГОСТы: Техническое задание на создание автоматизированной системы 34.602-89 от 01.01.1990 года и Техническое задание. Требования к содержанию и оформлению 19.201-78 от 01.01.1980 года. Однако сейчас их полное выполнение принесёт больше вреда, следует использовать стандарт с умом.

Задача: разработать сайт о кинофильмах

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

Описание типичной страницы

Страница состоит из шапки, основного содержимого и подвала.

В шапке содержится логотип, для быстрой идентификации сайта и ссылки на основные функциональные преимущества сайта: «На неделе в кино», «Списки», «Профиль».

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

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

Главная страница

На главной странице расположено несколько «этажей».

Первый этаж

Обложки новинок в кинотеатрах, где по картинкам ссылки на детальные страницы фильмов.

Второй этаж

Пять фильмов, где больше всего оценок.

Третий этаж

Пять списков фильмов сайта.

SEO-требования к сайту

Требования к верстке

Используется HTML 5, поддерживаются две крайних вышедших версии браузеров.

Все изображения должны иметь атрибуты alt и title.

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

Логотип должен быть ссылкой на главную страницу.

Ни на одной страницы не должно быть ссылки самой на себя.

Обязательно используется ЧПУ в стиле дублирующей навигации (REST).

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

Должен подключаться только один минифицированный файл css в начале HTML-кода и только один минифицированный файл JS в самом конце.

Делимся на группы

Итак, из вас мне надо получить группы по 4 человека. Можете и в одиночку и в шестером, всё зависит от вас, во всяком случае вы должны будете убедительно объясниться.

Выбираем проекты

А вот список проектов, которые я вам дам:

Что делать дома в первую неделю

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

  1. Зарегистрироваться на bitbucket и в таймвебе.
  2. Установить тортойс и файлзиллу.
  3. Скинуть в тестовый хостинг свои уже готовые проекты, ссылку мне.
  4. Создать репозитории, выложить готовые проекты, прислать ссылку.
  5. Написать полную документацию по новому проекту.
  6. Сверстать первые страницы, выложить в репозиторий и хостинг (общий), прислать ссылки.

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

Лекция вторая. Языки. PHP и алгоритмы

Вот соглашения по кодированию.

Введение в понятия

Так работает скачивание страницы браузером:

Браузер Сервер DNS-сервер Мне нужен yandex.ru Он находится на 5.255.255.70 У тебя есть yandex.ru? Да есть Давай главную страницу Страница весит 2 КБ Скачиваю главную страницу Отдаю главную страницу А дай ещё favicon.ico? 404. Такого файла нет jquery.js есть у меня в кеше Дай logotype.jpg Он весит 1 КБ Скачиваю logotype.jpg Отдаю logotype.jpg Скачиваю main.js Отдаю main.js

Почитайте об этом подробнее на webref.

Зачем нужен и как работает сервер

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

Сервер, при определённых запросах может сразу выдать информацию «как есть», а может передать её интерпретирующей программе или вовсе выполнить другую программму. Всё это зависит от его настроек. Вот при запросе logo.png файл будет отдан «как есть». А при запросе index.php будет запущен интерпретатор php.

Разновидности языков

Разновидности языков программирования

Языков очень много. Нынче интерпретируемые и с JIT компиляцей стали задавать моду. А остальные считаются «классикой» или чем-то эзотерическим. Пока запомните: php — интерпретируемый язык, а это значит, что он медленнее Си и даже Java, а об ошибках вы узнаете только во время работы программы.

Зарезервированные слова

В каждом языке есть список зарезервированных слов (php, JavaScript, MySQL). Например, для объявления функций есть слово function, его нельзя использовать как имя переменной.

Как запускаются скрипты

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

						<?php // Открываем стандартным способом
						?> // Закрываем стандартным способом
						<? // Открываем с включенным short_open_tag
						?> // Закрываем с включенным short_open_tag
					

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

Вывод на экран

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

					<div><?php echo 'Привет'; ?></div> // Да, можно так
					<div><?='Привет'?></div> // и так
					echo '<div>' . 'Привет' . '</div>'; // и так
				

В миру считается, что использовать short_open_tag не круто. Потому что до версии 5.4 эта возможность или отсутствовала или была отключена. Мы не старпёры и бояться удобств не будем.

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

Разместив это в /public_html/index.php (обратите внимание, что index.html не будет выполнять php-код). Тогда эта страница будет доступна по адресу: domen.ru/index.html.

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

Типы данных

Виды типов данных
Название Значение Запись
string строка 'строка'
char символ '1'
int целых чисел () 4
float действительных чисел () 6.5
unsigned int натуральные числа 9
double действительные числа 9.4
boolean булево значение false

Я уже вывел слово «Привет». Символ, слово или целый текст — это строки. Строка — тип данных. Обозначается как string. В некоторых языках есть отдельный тип данных — символ. Обозначается как char. Есть ещё расширения и некоторые расхождения между языками. Например, в некоторых нет строк, вместо ни используются массивы символов. Собственно, эта особенность часто сохраняется.

Множество целых чисел (), обозначается как int.

Множество действительных чисел (), обозначается как float.

Множество натуральных числа unsigned int. Более точное десятичные дроби — double. Всё это связано с тем, как это хранится в памяти, а именно количество бит для хранения одной единицы. Впрочем, об этом лучше читать отдельно.

Логический или булев тип — boolean. Он может принимать только два значения: истина — true или 1; и ложь — false или 0. Он необходим, когда вы ставите условия или задаёте циклическое повторение набора команд. Например, если код выполняется на главной странице, то логотип будет простой картинкой, а если нет, то ссылка с картинкой.

Про типы надо знать, чтоб понимать, почему 3 + 'привет' будет равно '3привет'.

Разумеется, во всех языках есть перевод в другой тип данных через функции.

						$str = '1 строка';
						$int = intval($str); // 1
					

Переменные

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

Переменная — это некий резервуар с именем в который можно поместить всё, что угодно. Понятие переменной, разумеется, пришло из математики. Так что просто вспомните X²×5=… Так вот, здесь X — переменная. А значит, подставьте на её место любое число и произойдёт магия: калькулятор компьютер сосчитает результат.

Представление переменной в памяти Различия типо переменных

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

						$home = '/home'; // инициализированная переменная
						$homeStr; // не инициализированная переменная
					

В PHP переменным не указывается явно их тип.

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

То, что записано в переменную — называется значением.

						$one = 1;    // В $one «1»
						$two = $one; // В $one «1», в $two «1»
						$one = 2;    // В $one «2», в $two «1»
					

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

						$one = 1;		// В $one «1»
						$two = &$one;	// В $one «1», в $two «1»
						$one = 5;		// В $one «5», в $two «5»
					

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

Имя переменной

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

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

Область видимости

Схема работы области видимости

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

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

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

Утечки памяти

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

Константы

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

От переменных они отличаются тем, что константа однажды объявлена и гарантируется, что её значение не изменится.

Их удобно использовать, когда необходимо везде и всюду использовать повторяющуюся часть. Допустим, вы все файлы настоек храните с названиями в формате: %имя%_CONFIG_FILE.ini уже догадались какую часть необходимо положить в константу? Это гарантирует, что попробовав написать в шестой раз «_CONFIG_FILE.ini» вы точно не ошибётесь в одной букве и будет открывать необходимый файл, а не думать следующие пол часа: «Почему файл не открывается? Всё ведь верно сделал». К тому же, исправлять массово можно будет в одном месте — что бесценно.

Массивы

Список — лементы, которые расположены в памяти без определённого порядка.

Массив — элементы, которые расположены в памяти друг за другом.

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

Есть многомерные массивы. Чтоб понять, как это проведём простой и наглядный эксперемент.

Одномерный массив
Ключ [0] [1] [2] [3] [4] [5]
Значение 9 4 92 0 6 1
Двумерный массив
Ключ/ключ [0] [1] [2] [3] [4] [5]
[0] 99 3 9 41 94 0
[1] 99 4 8 40 95 1
[2] 99 5 7 39 96 2
[3] 100 6 6 38 96 3
[4] 99 7 5 37 97 4
[5] 99 8 4 36 96 5

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

Доступ к элементам получается так:

						$arr[0]; // доступ к одномерному массиву
						$arr[2][5]; // доступ к двумерному массиву

						// Объявление одномерного массива из цифр
						$arr = array(
							0, 2, 5, 10, 11, 11
						);
						// Объявление одномерного массива из цифр с явным приведением ключей
						$arr = array(
							0 => 0,
							1 => 2,
							2 => 5,
							3 => 10,
							4 => 11,
							5 => 11
						);
						// Объявление двумерного массива из цифр
						$arr = array(
							array(0, 2, 3),
							array(6, 7, 8),
							array(1, 5, 9),
						);
						// Объявление двумерного массива, где вложенный — ассоциативный массив
						$arr = array(
							array(
								'NAME'	=> 'Дмитрий',
								'PHONE'	=> '+7 901 412 30 10',
							),
							array(
								'NAME'	=> 'Дмитрий',
								'PHONE'	=> '+7 901 412 30 10',
							),
						);
					

Разумеется, в большинстве языков есть именнованные ключи (ассоциативный массив). В обычном случае, вы пишете: arr[0], а в случае с именнованным ключом: arr['name'].

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

						for ($i = 0; $i < count($arr); $i++) {
							echo $arr[$i] . '<br />';
						}
					

Операции

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

У оператора есть операнд — это то, к чему применяется оператор. Например: 5 * 2 – оператор умножения с левым и правым операндами или иначе: «аргумент оператора».

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

Бинарным называется оператор, который применяется к двум операндам. Например, 5 – 3 — операто вычитания.

Присваивание

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

						$variable = 978
					

Арифметические

Просто приведу список и код примера, сомневаюсь, что нужны какие-то комментарии.

+ 9 + 2 // 11
- 9 - 2 // 7
* 9 * 2 // 18
/ 9 / 2 // 4.5

Все остальные приятные математические вещи здесь являются функциями. Корень от числа: sqrt(81) (в JavaScript это Math.sqrt(81)), возведение в степень pow(9, 2) (в JavaScript это Math.pow()).

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

Логика

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

< Меньше $itemCount < 8
> Больше $itemCount > 8
<= Меньше или равно $itemCount <= 8
>= Больше или равно $itemCount >= 8
== Равно $itemCount == 8
!= Не равно $itemCount != 8
=== Тождественно равно $itemCount === 8
!== Не тождественно равно $itemCount !== 8
true Булево значение — истина
false Булево значение — ложь
!true Отрицание булева значения — вернёт ложь
1 Псевдоним (Алиас) для true
0 Псевдоним (Алиас) для false

Работа со строками

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

Для работы со строками довольно много всяких функций придумано. Стоит хотя бы знать про их существование.

Элементы строки

Строка, по-сути, это массив символов. Соответсвенно, будет такой же интерфейс оператор доступа.

						$str = 'Hello, %username%';
						echo $str[4]; // o
					

Это может помочь вам проходить «поэлементно» строку для каких-то алгоритмов. То есть не обязательно превращать строку в побуквенный массив. Он уже и есть такой.

Конкатенация (склеивание строк)

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

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

						echo 'Rus' . 'sia' . ' ' . 2016;
					

Выбор подстроки (разделение строк)

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

Давайте, для начала, решим, что нам надо обрезать строку. Ну, например, начиная с 6-го символа. Для этона надо использовать substr().

						// string substr(string $string, int $start [, int $length])
						echo substr('Hello, World!', 7) // 'World!'
						echo substr('Hello, World!', -5) // 'orld!'
						echo substr('Hello, World!', -6, 2) // 'Wo'
					

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

А может, вам надо разбить строку по три символа, а результат в виде массива?

						echo str_split('Не такая уж большая строка')
						// ['Не ', 'так', 'ая ', 'уж ', 'бол', 'ьша', 'я с', 'тро', 'ка']
					

А может, надо по определённому символу или регулярному выражению?

						echo preg_split('/|/i', 'Майл Бей|Стивен Спилберг|Кевин Смит');
						// ['Майл Бей', 'Стивен Спилберг', 'Кевин Смит']
					

Примерно аналогичное делается без регулярных выражений

						explode('|', 'Майл Бей|Стивен Спилберг|Кевин Смит')
						// ['Майл Бей', 'Стивен Спилберг', 'Кевин Смит']
					

Обратный эффект достигается таким образом:

						implode(', ', array('имя', 'почта', 'телефон')) // имя, почта, телефон
					

Поиск в строке

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

						// Проверяем тождественным равно, так как если искомая строка будет в конце
						// то вернётся 0, который преобразуется в false
						if (strpos('Mithgol the webmaster', 'master') !== false) {
							echo 'Мастер';
						} else {
							echo 'Не мастер';
						}
					

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

						$result = preg_match('/^def/', 'abcdef', $matches, PREG_OFFSET_CAPTURE);
						if ($result === 1) { // найдено
							echo '<pre>', var_dump($matches), '</pre>';
						} elseif ($result === false) {
							echo 'Произошла какая-то ошибка';
						}
					

Преобразования

Подстрока — часть строки. В строке «Немов» подстроками могут быть как «Немов», так и «в».

						// Экранирует специальные символы:
 						addslashes("O'Reilly?") // "O\'Reilly"

						// Деэкранирует специальные символы
						stripslashes('O\'Reilly?') // O'Reilly?

						// Частичное преобразование HTML-сущностей в HTML-мнемоники
						htmlspecialchars('<span />') // &lt;span />

						// Частичное преобразовани HTML-мнемоники в HTML-сущности
						htmlspecialchars_decode('&lt;span />') // '<span />'

						// Полное преобразование HTML-сущностей в HTML-мнемоники
						htmlentities()

						// Полное преобразовани HTML-мнемоники в HTML-сущности
						html_entity_decode()

						// Безвозвратное удаление HTML-тегов
						strip_tags('<b>Мир</b>, <i>Труд</i>, <u>Май</u>')
						// Мир, Труд, Май

						// Замена символа переноса строки (\n) <br>
						nl2br("Пример\nпереноса") // Пример<br />переноса

						// Разбиение строки на строки с заданной длинной, заданным набором символов
						echo chunk_split($str, 120, '<br />')
						echo wordwrap($str, 120, '<br />')

						// Шифрование в base64
						base64_encode()

						// Дешифрование из base64
						base64_decode()

						// Отрезать пробелы в конце и начале строки
						trim()

						// Отрезать пробелы справа (в конце строки)
						rtrim()

						// Отрезать пробелы слева (в начале строки)
						ltrim()

						// Заменить подстроку
						str_replace('%body%', 'black', '<body text="%body%">');

						// Заменить подстроку, без учёта регистра
						str_ireplace('%body%', 'black', '<body text="%BODY%">');
					

Форматирование

Иногда требуется сделать из строки что-то классное. Наиболее быстрым и читаемым способом является форматирование строк.s

Все эти функции работают по следующему принципу:

Первым вы указываете формат, а затем столько, сколько надо аргументов.

Обратите внимание, что функция sscanf() считывает строку и первым аргументом будет разбираемая строка. Аналогично для fprintf(), что записывает результат в файл и первым аргументом будет открытый ресурс файла.

Как задаются переменные в строке форматирования
% Аргумент не используется.
%b целое, выводится в виде двоичного числа.
%c целое, выводится в виде символа с соответствующим кодом ASCII.
%d целое, выводится в виде десятичного числа со знаком.
%e число в в научной нотации, например, 1.2e+2.
%E аналогично %e, но использует заглавную букву, например, 1.2E+2.
%f число с плавающей точкой и также выводится в зависимости от локали.
%F число с плавающей точкой и также выводится, но без зависимости от локали.
%g выбирает самую краткую запись из %e и %f.
%G выбирает самую краткую запись из %E и %f.
%o целое, выводится в виде восьмеричного числа.
%s строка.
%u целое, выводится в виде десятичного числа без знака.
%x целое, выводится в виде шестнадцатеричного числа (в нижнем регистре).
%X целое, выводится в виде шестнадцатеричного числа (в верхнем регистре).
						printf(string $format [, mixed $args [, mixed $... ]])
						fprintf(resource $handle, string $format [, mixed $args [, mixed $... ]])
						sprintf(string $format [, mixed $args [, mixed $... ]])
						sscanf(string $str, string $format [, mixed &$... ])
					

А вот несколько примеров, как с этим обращаться

						sprintf('%d попугаев', 48);		// 48 попугаев
						sprintf('%'.9d', 123);			// ......123
						sprintf('[%-10s]', 'monkey');	// [monkey    ]
					

А ещё вам может понадобиться форматировать денежную строку. Эти функции учитывают ваши региональные настройки. Однако, они не делают совсем круто, как у нас должно быть. Не ставится неразрывный пробел, отбиваются порядки до 10 тысяч. Короче, говоря, я предлагаю вам вот такую обёртку над этой функцией использовать всегда:

						function moneyFormat($value = 0, $banknote = ' руб.') {
							$formattedValue = number_format($value, 2, ',', ' ');
							if ($value < 9999) {
								$formattedValue = str_replace(' ', '', $formattedValue);
							}
							$formattedValue = str_replace('', '&nbsp;', $formattedValue . $banknote);
							return $formattedValue;
						}
					

Кодировка

Глупо что-то описывать, просто читаете про iconv().

Условия

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

					if (true) {
						// То, что выполнится при положительном исходе
					} else {
						// То, что выполнится в противном случае
					}
				

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

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

Как модернизируется оператор условия

Иногда, надо сделать несколько проверок подряд. Проверим, записано ли в переменной $name строка 'Dima', и проверим на строку 'Kolya', ну и неудачную ветку тоже запишем. Вот как преобразовать условный оператор в короткую запись, чтоб он не выглядел громоздко.

Сразу несколько советов, если php скрипт в принципе не должен что-то делать при таки-то условиях, то лучше внутри проверки на вшивость вызвать окончание скрипта.

							if ($normalMode) {
								// Тут очень много кода
							}
						
							if (!$normalMode) {
								return;
							}
							// Тут очень много кода
						

Если у вас в условии получается какое-то длинное выражение, то стоит его присвоить переменной с внятным названием и уже её указывать в условии:

					$isMainPage = $APP::getCurrentUrl() == '/';
					if (!$isMainPage) { // заодно, отрицание
						echo '<a href="/">';
					}
				

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

							if ($isMainPage) {
								$title = 'Главная страница';
							} else {
								$title = 'Не главная страница';
							}
						
							$title = ($isMainPage) ? 'Главная страница' : 'Не главная страница';
						

Циклы

Блок схема цикла

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

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

while — простейший цикл. Выполняется до тех пор, пока условие истино. То есть если условие сразу вернёт ложь, то тело цикла выполнено не будет вовсе.

						// наш счётчик, начинаем, по традиции с нуля
						$i   = 0;
						// создаём массив
						$arr = array(1, 2, 3, 4, 5);
						// записываем сколько у нас в массиве элементов.
						// Ради удобства эта функция возвращает
						// на один больше, чем надо, всегда
						$l   = count($arr);
						while ($i < $l) {
							echo $arr[$i];
							$i++;
						}
					

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

						$i = 0
						do {
							$i++
							echo $arr[$i];
						} while ($i < 1000)
					

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

						for ($i = 0, $l = count($arr); $i < $l; $i++) {
							echo $arr[$i];
						}
					

Заметьте, что здесь будут выводится только элементы с чисельными ключами. Ассоциативные массивы так не обойти.

Но потом надоело совсем обрабатывать лишние переменные и оставлять их в области видимости. Да и обходить ассоциативные массивы хотелось. Назвали это foreach:

						foreach ($variable as &$item) {
							echo $item;
						}
					

Заметьте, что в цикле я передал элемент по ссылке. Это сделано не только для того, чтобы сэкономить оперативную память (не выделяя лишнюю копию места), но и для того, чтобы при случае поменять значение элемента имея доступ через пседоним $item.

Вложенные циклы

Впрочем, иногда массивы бывают многоуровневыми. Тут надо, скажем так, понимать как это написать «стандартным способом».

Во-первых, вы не будете изобретать велосипед, а во-вторых, никто не будет задумываться: «Что тут происходит?» — при прочтении вашего кода.

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

						for ($i = 0, $l = count($arr); $i < $l; $i++) {
							for ($j = 0, $il = count($arr[$i]); $j < $il; $j++) {
								for ($k = 0, $jl = count($arr[$i][$j]); $k < $jl; $k++) {
									echo $arr[$i][$j][$k];
								}
							}
						}
					

Используйте имена счётчиков: i, j, k. Потому что они из математики и стандартны. Если их не хватает, то перепишите код заново.

Прерывание и продолжение цикла

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

Для этого придумано две директивы: continue и break.

Первая для того, чтоб пройти в следующую итерацию, а вторая, чтоб остановить его.

						$i   = 0;
						$odd = false;
						while ($i < 1000) { // Выведем все чётные от нуля до тысячи.
							$odd = !$odd; // Надо не забывать менять признак чётности / не чётности
							$i++; // Не забываем увеличивать счётчик
							if (!$odd) {
								continue;
							}
							echo $i;
							if ($i > 150) { // шутка, до 150
								break;
							}
						}
						// Пример с фором:
						// for ($i = 0; true; $i++)
					

Функции

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

Функции в программировании прямиком из математики. Помните ? Собственно, прямо так и перекочевало. Только имя функции часто не f, а что-то более осмысленное.

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

Допустим, вам понадобилось ожирнить строку, пишем функцию:

						function getStrongStr($str = '') {
							return '<strong>' . $str . '</strong>';
						}
						echo getStrongStr(); // '<strong></strong>'
						echo getStrongStr('higimo'); // '<strong>higimo</strong>'

						// функцию можно использовать в выражениях
						$name = 'higimo'
						$strongName = 'Привет, ' . getStrongStr($name);
					

function — ключевое слово, обозначающее, что объявляется функция.

getStrong — имя функции, по которому к ней можно будет обращаться.

скобки — перечисление аргументов функции.

$str — указанная переменная. Перечисляются через запятую.

= '' — указывается, что переменная не обязательная. То есть, если она не будет указана, то будет использоваться то значение, что указано после знака равенства.

фигурные скобки — это начало и конец тела функции.

return — оператор возврата значения. Что из функции получается на выходе. Дальше функция выполняться не будет.

Функция всегда что-то возвращает. Явно или нет — другой вопрос. Вы можете даже не писать ключевое слово return, но оно всё же будет выполнено интерпретатором. Что произойдёт, если не указать это ключевое слово? Вернётся тип, который принято называть void. С английского это что-то типа пустого, несуществующего места. То есть, мы можем явно возвращать значение, а можем неявно возвращать void.

						function getSquare($num) {
							return $num * $num * $num;
						}
						function showTitle($title) {
							echo '<title>' . $title . '</title>';
						}
					

В современном программировании эти понятия почти умерли, но стоит их понимать и отличать.

Функция - черный ящик возвращающий значение.

Процедура - черный ящик выполняющий работу.

В кратце, если напишете return - функция, если нет - процедура.

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

Функции могут вызываться прямо из других функций при передаче им параметров (аргументов). Что удобно в данном случае. Зачем переменная, если можно сразу вычислить?

						echo getSquare(5) + getSquare(80) * getSquare(10)
					

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

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

Подключение файлов в PHP

						include './hello.inc.php';
						require './hello.inc.php';
						include_once './hello.inc.php';
						require_once './hello.inc.php';
					

Выражение include и require подключают и выполняет указанный файл, используя указанный путь до файла. В случае, отстуствия будет выброшена ошибка: warning (в случае include) fatal error (в случае require). include_once и require_once тратят больше ресурсов, но гарантируют, что файл будет подключен лишь однажды.

Адресация или пути

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

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

						Корень
							└Безымянная папка/
							└homepage/
								└js/
									└main.js
								└php/
									└class/
										└main.php
								└img/
									└logotype.png
								└css/
									└style.css
								└index.php
								└favicon.ico
							└Безымянный.txt
					

Соответственно, до файла main.js из index.php можно добраться следующими путями:

						js/main.js             // без управляющей конструкции, не явно.
						./js/main.js           // явно указываем, что из текущей директории.
						../homepage/js/main.js // выходим на уровень выше, и зайдём опять в эту папку.
						/homepage/js/main.js   // начнём от корня.
					

Алгоритмы

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

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

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

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

Дискретность, то есть является процессом выполнения последовательных простых шагов.

Детерминированность (определённость), то есть на каждом шаге алгоритма будет точно определено состояние. Иными словами, при одинаковых вводных данных, надо получать одинаковые выходные данные.

Понятность, то есть все шаги должны быть определены и доступны исполнителю.

Завершаемость (конечность) — то есть, алгоритм должен завершаться за конечное число шагов.

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

Результативность, то есть по завершению будут известны результаты.

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

Виды алгоритмов

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

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

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

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

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

Циклическими алгоритмами называют, многократно повторяющиеся шаги.

Анализ алгоритмов

При анализе алгоритмов рассматривают как их корректность и точность, так и их ресурсоёмкость. Если с первым всё более-менее очевидно — программа обязана работать корректно и без сбоев. То со вторым, придётся привести немного объяснений.

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

Далее, важным термином является размер вычислений. Например, надо отсортировать 20 строк. Вот размер задачи — 20.

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

Вот таблица с наиболее распространёнными асимптотическими сложностями.

Сложность Комментарий Примеры
O(1) Постоянное время работы, не зависит от размера задачи. Ожидаемое время поиска в хеш-таблице.
O(n) Удвоение размера удвоит и необходимое время. Линейный поиск в массиве из n элементов.
O(log log n) Очень медленный рост необходимого времени. Ожидаемое время работы интерполирующего поиска n элементов.
O(log n) Удвоение размера увеличивает время работы на постоянную величину. Двоичный поиск в массиве из n элементов.
O(n log n) Удвоение размера задачи увеличит необходимое время чуть более, чем вдвое. Сортировка слиянием или кучей n элементов.
O(n²) Удвоение размера задачи увеличивает необходимое время в четыре раза. Элементарные алгоритмы сортировки.
O(n³) Удвоение размера задачи увеличивает необходимое время в восемь раз. Обычное умножение матриц.
O(cn) Увеличение размера задачи на 1 приводит к c-кратному увеличению необходимого времени; удвоение размера задачи увеличивает необходимое время в квадрат. Некоторые задачи коммивояжёра, алгоритмы поиска полным перебором.

Вот график, где почти то же самое наглядно:

График роста Операции Количество элементов О(1) О(logn) О(n) О(nlogn) О(n^2) О(2^n)

А теперь самый серьёзный вопрос, как самостоятельно определять эту сложность? Лучше почитать отдельную статью в книге. Но пока её нет, дам несколько намёток.

Каждый вложенный цикл добавляет степень Один вложенный — O(n^1) или просто O(n); два — n(n^2). Когда обходятся не все элементы, а количество постоянно сокращается на два — это O(log n). Логарифм — когда сложность растёт медленно. Когда никаких повторений — константа O(1).

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

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

До момента, когда вам действительно придётся придумать свой алгоритм, вроде Быстрой сортировки, тогда уже вы точно прочитаете нужную литературу по всем нужным темам.

Рекурсия

Чтобы понять рекурсию, нужно сначала понять рекурсию.

Демонстрация рекурсии

Рекурсия — вызов функции внутри самой себя, либо вызов другой программной единицы внутри самой себя.

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

О, тут-то вы поймёте, что значит stack overflow и что это не миленький сайтик с ответиками, а настоящая боль!

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

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

Мой любимый пример с факториалом:

						function factorial($x) {
							if ($x === 0) {
								return 1;
							} else {
								return $x * factorial($x - 1);
							}
						}
					

Важно понять, что функция внутри блока else вызывает сама себя.

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

						function factorial($x) {
							$f = array(1, 1);
							for ($i = 2; $i < $x; $i++) {
								$f[$i] = $f[$i - 1] + $f[$i - 2];
							}
						}
					

Замечание: рекурсивный вызов с глубиной более 100—200 уровней может вызвать переполнение стека и привести к аварийному завершению скрипта.

Что делать дома во вторую неделю

Почитайте «Несколько советов для PHP-разработчиков», «10 советов начинающему веб-разработчику», «Что нужно знать PHP разработчику в 2016 году», «Сборник советов и фактов по оптимизации», «40 советов по оптимизации» и «Оптимизация PHP скриптов».

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

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

  1. Вынести верстку шапки и подвала (если есть, к подвалу же прикреплять боковое меню) в отдельные файлы — подключать их на каждой странице специальной отдельной функцией (дать возможность указывать на страницах title, h1 и description.
  2. Создать файлы, где в массивах будет храниться вся информация необходимая сайту для вывода на страницах.
  3. Все запросы данных из этих файлов реализовать через функции.
  4. Вывести всю эту информацию при помощи циклов и условий на страницы с версткой.
  5. В идеале, должен получиться единственный вызов echo на странице.

Лекция третья. Типографика. ООП, оптимизация

Типографика

Учитесь профессионально оформлять текст. Это модно и правильно. Это по мотивам параграфа о экранной типографике. Побольше о правилах написано у Мильчина. Для работы используйте типограф и раскладку Бирмана.

Например, надо использовать тире (—), а не дефис (-), два дефиса (--) или короткое тире (-). Дефисы используются только внутри слов (по-русски, из-под, шлем-маска).

Знаки припенаний

, ; : . ! ? … от предшествующего слова пробелом не отделяются, но от последующего слова.

Травка зеленеет, солнышко блестит, ласточка с весною в сени к нам летит! (А. Н. Плещеев).

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

и т. п., А. С. Пушкин

и т.п., А.С.Пушкин

Для сохранения целостности сокращения, чтобы избежать разделения его элементов при переводе строки, используется так называемый неразрывный пробел (&nbsp;), на месте которого разделение строк невозможно, или специальный тег <nobr>, в случае если неразрывными пробелами требуется соединить несколько слов.

Скобки

Везде используйте круглые скобки: (). Вкладывать скобки можно, тогда внутри круглых будут квадратные [], внутри квадратных фигурные {}, затем, треугольные <>.

Открывающая (левая) скобка отделяется от предыдущего, и не отделяется последующего. Закрывающая (правая) не отделяется от предыдущего, но отделяется последующего.

Сепульки — элементы цивилизации андроидов, выпускаемые сепулькариями (устройства для сепуления [занятие ардритов {аборигенами <жителями планеты>}]).

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

Кавычки

Кавычки — парный знак препинания, заисывается к словам, подобно скобкам. В русском языке используют «ёлочки», а вложенные кавычки «лапки». Остальное ' " — не кавычки, в русском тексте не применяются.

Роман Хола Клемента «Экспедиция „Тяготение“».

Роман Хола Клемента "Экспедиция "Тяготение".

Тире (—)

Тире с обеих сторон отделяется пробелами, причём слева неразрывный пробелом, чтобы знак не переносился.

Тире, разделяющее диапозон, написанный цифрами не отбивается пробелами. Но в интервалах в словесной форме отбивается.

«Мухомор — гриб красивый, но совершенно несъедобный» — писал Чернодонцев в сентябре — октябре 1764 года, операжая всех учёных XIX—XX веков.

«Мухомор-гриб красивый, но совершенно несъедобный»-писал Чернодонцев в сентябре-октябре 1764 года, операжая всех учёных XIX-XX веков.

Минус (−)

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

2 − 5 = −3

2-5=-3

Знаки процента, промилле, номера и параграфа

Знаки % ‰ № § используются только с относящимися к ним числами, отделяются неразрывным пробелом. Сдвоенные знаки между собой не разделяются. Знак номера перед несколькими (многими) номерами не удваивается.

относительная влажность воздуха 87 %

в Большом Солёном озере солёность воды достигает 250 ‰

задание № 5; см. § 11

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

20%-я сметана (означает двадцатипроцентная сметана), 10%-й раствор, 20%-му раствору

Полужирный шрифт

Разумеется, стоит ознакомиться с параграфом про выделения.

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

Крова́ть — предмет мебели, предназначенный для…

Курсив

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

Когда к Нему подошли, сердце замерло.

Полужирный курсив

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

Выделение знаков препинания

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

В детстве я болел за «Спартак» (Москва), но судьба заставила поработать в ОАО «Газпром», а там без «Зенита» никак.

Числа

Правильную функцию moneyFormatter на php можно найти у меня в репозитории.

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

Запятая разделяет десятичные дроби, пробелом не отбивается. Числа после запятой также отбиваются поразрядно.

4444;
66 231;
5,8
10 000 000 000 000,000 00.
4 444.
3896558;
87,788,457;
1.000.000.000,0000;
4'572'468;

Представление чисел прописью или цифрами

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

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

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

Сочетание цифровой и словесной форм представления чисел

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

3 млрд человек производят 900 млн тонн пота на каждые 2 тыс. метров ходьбы.

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

Если два подряд числительных с одним наращением, то падежное окончание наращивается только последнему, в противном случае наращивается каждому.

5-й (пятый, пятой); 5-я (пятая); 5-е (пятое, пятые), 2, 3, 5-м;

5-го; 5-му; 20, 30-ми.

5-ый; 5-ой; 5-ая; 5-ое; 5-ые; 2-ым, 5-ым;

5-ого; 5-ому; 20-ыми, 30-ыми;

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

50—60-е годы XX века;

в 20—30-х годах;

в 20-м — 30-х секторах;

в конце 70-х — 80-е годы.

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

секции 6; формулу 4; на рис. 15; в табл. 8;

в 6-й секции; 4-ю формулу; на 15-м рисунке; 8-й таблице.

Кроме того, не наращивают окончания для номера года и числа месяца, если слово год и названия месяца следуют тотчас за числом. Но если слово «год» или название месяца опущено или стоит перед числом, то окончание следует наращивать. Например:

в 2005 году; 4 декабря 1954 года;

в августе 1943-го; в сентябре, числа 5-го.

Неправильно:

в 1991-м году; 5-го марта 1953-го года.

У количественных числительных окончания не наращивают:

собрание сочинений в 5 томах;

великолепная пятёрка.

Неправильно:

собрание сочинений в 5-ти томах;

великолепная 5-ка.

При этом: «слесарь с 7-летним стажем» (а не «слесарь с 7-и летним стажем»).

Диапазоны

Интервал значений разделяется знаком тире (—), иногда возможен знак многоточия (…).

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

Надо заштопать пять — десять костюмов за десять-двадцать минут.

В мае — июне я буду задерживаться на два-три часа.

Если интервал записан в чисельной форме, между числами тире пробелами не отбивается. Если диапозон имеет наименование, то оно записывается после один раз.

Надо заштопать 5—10 костюмов за 15—20 минут, это 5—10 % от общего числа.

Когда интервал представлен отрицательным числом вместо тире надо использовать многоточие.

рабочая температура −50…+70 °C.

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

Списки

Перечень элементов — список.

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

Традиционные стили бонсай

Лучшие фильмы

Элементом списка должны быть однородны.

С нами выгодно, потому что

  1. придумываем акции;
  2. имеем рассрочку без процентов;
  3. расположены в удобном транспортном узле города.

Наши преимущества

  1. Наша цель научить вас быть умным водителем.
  2. Постоянно проводятся акции.
  3. Рассрочка без %.
  4. Гибкий график обучения.
  5. Наш богатый опыт.
  6. Автошкола в выгодном районе.

Не стоит заводить списков преимуществ. Напишите преимущества там, где они нужны.

Быстрая доставка — при заказе укажите, что за пол часа доставите.

Низкая цена — укажите цену ближайщего конкурента.

Гарантия — отметьте это в карточке товара.

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

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

  1. Рядовой
  2. Ефрейтор
  3. Мл. Сержант
  4. Сержант
  1. Цесаревич
  2. Кесарь
  3. Цезарь
  4. Царь

Старайтесь вместо списков написать нормальный абзац.

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

Марианд изготавливать из лимонного сока, лука и перца с солью. По-вкусу добавить вина и мёда. Мед даёт неповторимую корочку на поджаренном мясе.

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

Сноски

Ссылки вне основного текста — сноски.

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

Не делайте сносок как в дешёвых рекламах, где поскупились дизайнеру заплатить за перевод на несколько языков:

Как делают отстойные и дорогие рекламы

Все против сносок.

Не делайте сносок, помечая обязательные поля.

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

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

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

Иллюстрации

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

Вот статьи про иллюстрирование: Википедия:Иллюстрирование, Почему ваша картинка к статье — говно (грубая версия)

Как писать тексты на сайт

Читайте Советы Ильяхова. И рассылку, а лучше подпишитесь.

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

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

Правило внешнего и внутреннего

Теорию близости в 2004 году сформулировал Артемий Лебедев. В 2014 его теорию уточнил Артём Горбунов.

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

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

Таблицы

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

Всё повторяющееся надо выносить в заголовок таблицы, строк или колонок или вовсе в поясняющий текст к таблице.

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

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

Ссылки

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

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

Ссылка должна быть опознаваема в тексте. Если пользователь без взаимодействия с компьютером может указать все ссылки на странице — всё хорошо.

К тексту ссылки применяется закон Фитца — она должна быть достаточно крупной, чтоб на неё можно было кликнуть: не менее трёх символов. Вспомните сноски в Википедии: [1].

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

Гиперссылка может находиться в четырёх состояниях: неактивная, предактивная (при наведении курсора :hover), активная (нажатая :active), посещённая (:visited). Эти состояния обязательно нужно показывать в контентной части. В меню, показывать посещённые ссылки не стоит, зато предактивную — полезно.

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

PHP без исключений

Давайте напишем функцию деления.

					function division($dividend, $divider) {
						return $dividend / $divider;
					}
				

Кажется, что всё просто. Но всегда ли будет такое выражение верным? Да, делить на ноль нельзя. Это исклюение, так сообщим об этом интерпритатору. Или вовсе доведём немного до абсурда, никому доверять не будем и обработаем ещё и проблему, когда в функцию переданы не цифры.

					function division($dividend, $divider) {
						if (!is_numeric($dividend) || !is_numeric($divider)) {
							throw new Exception('Делимое или делитель — не числа');
						}
						if ($divider === 0) { // Если пытаемся делить на ноль
							throw new Exception('Делить на ноль нельзя');
						}
						return $dividend / $divider;
					}
				

Окей, мы научились выбрасывать исключения, что произойдёт, когда оно будет выброшено? Дальше скрипт выполняться не будет скрипт. Будет выведена фатальная ошибка, которая пояснит на какой строке, и даже выведет «Stack trace» — стек вызовов. То есть функции, что были, чтоб вызвать это исключение.

Fatal error: Uncaught exception 'Exception' with message '%Error Processing Request%' in /home/%userName%/%domainName%/script.php:3 Stack trace: #0 {main} thrown in /home/%userName%/%domainName%/script.php on line 3

Здесь указано, что вызвано стандартное исключение (без модификации), которое называется «Exception». При выбрасывании указан текст «%Error Processing Request%». Произошло оно в файле /script.php на третьей строке. Стек вызовов показывает, что исполнялась только главная функция — main.

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

					try {
						echo division('ge', 'r5');
					} catch (ExceptionSuper $e) { // Это модифицированное исключение
						echo 'Произошло что-то невероятное';
					} catch (Exception $e) { // Стандартное исключение
						echo 'Произошла ошибка: ',  $e->getMessage(), "\n";
					} finally { // что сделать в любом случае (доступно с v5,5)
						echo "Первое finally.\n";
					}
				

То есть в блоке try вы указываете, что может дать исключения (а это может быть почти любая операция, кстати говоря). А блоками catch указываете какие исключения отлавливать и как их обрабатывать. Блок finally выполнится в любом случае.

Когда вы открываете соединение с базой данных, открываете файл, или соединяетесь с другим сервером. Или выделяете память. Это влечёт множество проблем за собой. Чтобы не сталкиваться с утечками памяти или с файлом, который больше нельзя открыть, потому что он уже открыт, а значит, придётся перезапускать программу. Для этого надо «в любом случае» освобождать память и закрывать соединения. А блок finally сделает это даже если было выброшено исключение.

Разумеется, блоки try .. catch можно вставлять друг в друга.

Наверное, самое важное, что вы можете модифицировать исключения и создавать свои фишки, на основании этого. Например, когда сайт выбрасывает ошибку 500, то он уже ни на что не способен. А вы можете перехватить это исключение, узнать номер строки, прочитать тот файл и вывести десять строк до, десять после и подсветить необходимую строку. Будет весьма удобно при разработке и крайне опасно в продакшене. Подробнее об этом на php.net

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

Ещё про исключения стоит почитать специальную статью на Хабре.

ООП

Объектно-ориентированное программирование (ООП) — методология в программировании. Представьте, что есть объект — человек — и он умеет кушать, ходить в туалет, спать, говорить, запоминать. Всё это — методы. Рост, вес, цвет глаз и волос — свойства объекта.

Кстати, стоит помнить, что есть объекты, а есть классы. Классы — люди, а объект — это экземпляр класса — человек. Животное (класс) и кабан Пумба (объект).

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

						class PageController {
							private $title;
							private $description;

							public function showHedaer() {}
							public function showFooter() {}
							public function setTitle() {}
							public function getTitle() {}
							public function setDescription() {}
							public function getDescription() {}
						}
						$page = $new PageController();
						$page->setTitle('Главная страница — higimo');
						$page->setDescription('higimo — это лучший способ похудеть');
						$page->showHedaer();
						$page->showFooter();
					

Инкапсуляция

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

Интерфейс Кишки
Интерфейс автомобиля Кишки автомобиля

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

Так и в коде, вы не совершаете ничего, а просто говорите объекту: газуй!

Ещё раз: скрыть всё лишнее — инкапсулирвать.

Как это выглядит в коде

						class Auto { // объявляем класс
							// приватные переменные доступны только «изнутри»
							private $petrol = 100;
							// не забываем, уменьшать количество топлива
							private function getPetrol() {
								$this->petrol -= 1;
							}
							// не забываем выбрасывать деньги на ветер
							private function getAir() {
								return 'money';
							}
							// когда жмём на газ
							public function pressGasPedal() {
								$this->getPetrol();
								$this->getAir();
							}
						}
						// создаём объект
						$opel = new Auto();
						// жмём на педаль газа
						$opel->pressGasPedal();
						// переменная стала 99, мы же один раз газанули
						echo $opel->petrol;
					

Пользователь (другой программист) не выстрелит себе в ногу.

Ничего не забудет, не сделает лишнего движения, не создат утечку памяти, да и вообще напишет меньше кода.

Представим, что «умелец» увеличивает подачу бензина, надеясь что станет быстрее ехать, но на деле проливает его на асфальт. Это мы называем выстрелом в ногу.

Пользователь не пострадает от обновления.

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

Вспомините, что пересаживаясь с Лады Калины на Ладу Гранту вы всё ещё можете ехать на этой машине без проблем — педали-то там же.

Для пользователя всё просто.

Он не видит, что вы написали три сотни проверок на все случаи жизни.

Под копотом Копейка и дизельная БМВ М4 2016 года имееют принципиальное различие. Двигатели вообще полностью разные. Но всё те же три педали, коробка передач, руль и стёкла.

Наследование

Наверное, когда вам скажут что-то про наследство, то вы вспоминаете завещание и бабушку или дедушку. Только подумайте не про квартиру, а то, что у вас от рождения унаследовано от родителей? Цвет глаз, да, прочие предрасположенности (свойства). А каким навыкам научили вас родители (методы)?

						class Father {
							// Если поставить приватным, то выводиться будет
							public $name = 'Дмитрий';
							// имя отца
							public $patronymic = 'Геннадиевич';
							private static $family = 'Уткин';
							public function getFullName() {
								return
									$this->name . ' ' .
									$this->patronymic . ' ' .
									Father::$family;
							}
						}
						class Son extends Father { // типа расширяем этот классом отца
							public $name = 'Дмитрий';
							public $patronymic = 'Дмитриевич';
						}

						$me = new Son();
						echo $me->getFullName();
					

Суть наследования в том, что класс-потомок получает всё от класса-родителя, но позволяет это изменять, а также получать доступ к некоторым частям класса-родителя.

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

Ему придётся унаследовать ваш код, чтоб исправить (модифицировать) его поведение.

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

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

Стоит знать, что даже адепты ООП делегировать функционал, а не наследовать.

Полиморфизм

Технически, это способность одной функции, обрабатывать данные разных типов.

Во-первых, стоит понять, что такое перегрузка разных типов.

Есть некие типы данных: числа, буквы, строки, числа с запятой. А есть целые структуры данных. Если, допустим, мы складываем «А» и 20, то PHP с этим справится (выдаст число 20). Но что, если мы сложим массив и объект?

Во-вторых стоит понять, что такое полиморфизм на примере фруктов.

Груша и мандарин в целом — фрукты. Оба съедобны. Но мандарин придётся чистить.

Вот тут и помогает полимрфизм. Мы описываем какое поведение должно быть при таких-то условиях. Опишем мандарину метод «съесть»:

  1. Почистить
  2. Разломить на дольки
  3. Положить в рот

А как компьютер поймёт, что делать, когда мы складываем мандарины? Ну вот, мы же их в одно и то же место складываем и всё. А не привариваем аргонной сваркой друг к другу.

						interface MyInterface {
							public function doThis();
							public function doThat();
							public function setName($name);
						}
						class MyClass implements MyInterface {
							protected $name;
							public function doThis() {
							}
							// методы обязаны быть публичными
							public function doThat() {
							}
							// Нельзя пропускать аргумент
							public function setName($name) {
							}
						}
					

Абстракция

Значит, выделение значимого и исключение незначимого. В сущности, это надо понимать как руководство к действию: «Не пиши лишнего».

Абстрагироваться просят постоянно. Например, мы создаём объект, который делает запросы в базу данных. Ему дела нет до каких-то там ваших болезней или потребностей. Он не должен знать что вы хотите получить. Он может передавать запрос в базу данных и возвращать результат. Разумеется, стоит предусмотреть случай ошибки. Значит, абстракция такова: передать запрос, вернуть ответ, обработать (если нужно) ошибку.

Разумеется, на следующем уровне мы сделаем другой уровень абстракции, где будет класс Пользователь, который может возвращать имя, делать запрос в базу при помощи того класса, что выше описали. Тоже самое сделаем для Новостей, Форума и так далее. Но пользователю (программисту) уже не важно как подключился к базе, обращение идёт к чёрному ящику, который всегда возвращает верный результат.

Статические методы и свойства

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

А ещё значение свойства будет единым для всех экзепляров классов.

						class A {
							public static $varMe = 0;
							public static function getVar() {
								return A::$varMe;
							}
							public function iterVar() {
								static::$varMe += 1;
							}
						}
						$obj1 = new A();
						$obj2 = new A();

						$obj1->iterVar();
						$obj2->iterVar();

						var_dump(A::$varMe); // 2
					

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

Вы такие смотрите на всё это многообразие ООП и думаете. И как мне это применять? Что с этим делать? Как правильно? Вам нужно применить шаблоны проектирования.

Вот статья «Шаблоны проектирования».

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

Оптимизация скриптов

Повторю тем, кто пропустил с первого раза: «Сборник советов и фактов по оптимизации», «40 советов по оптимизации» и «Оптимизация PHP скриптов», Оптимизация PHP

Есть кеширование данных из БД. Есть APC.

§ 8. Простой секрет ГИФа — про сохранение картинок. И вот ещё: Выбор формата картинок. А также Оптимизация картинок в Web.

А ещё надо настраивать кеширование и сжатие сервера: Кеширование с HTTP Etag, Анализ и оптимизация времени TTFB, Сжатие gzip для js/css/html, Кэширование статики и cache-control, Cache-control с динамикой.

Стандартная библиотека PHP (SPL) — сборник классов доступных в PHP по-умолчанию.

Буферизация вывода

Конечно, стоит прочитать документацию.

Если вы напишете echo до того, как вызовите setcookie() или header(), то получите ошибку. Это не приемлемо.

Для этого придумали механизм буфферизации. Он не выводит всё на экран а записывает данные в специальный буффер. Может даже использовать несколько уровней (как стек). Отдельно же записываются данные http-заголовков.

						echo 'Начало' . PHP_EOL;

						ob_start();
						echo 'первый буфер' . PHP_EOL;
						// setcookie("cookiename", "cookiedata"); // Если бы не эхо сверху
						$out1 = ob_get_clean();

						ob_start();
						?>Даже так<?
						echo 'второй буфер' . PHP_EOL;
						ob_end_clean();

						ob_start();
						echo 'третий буфер' . PHP_EOL;
						echo $out1;
						// Результат:
						// Начало
						// третий буфер
						// первый буфер
					

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

Буфер внутри буфера, разумеется, можно запускать. Есть и специальная функция, чтоб определять на какой вы «глубине»: ob_get_level().

Что делать дома на третью неделю

  1. Работа с файлами данных — отдельный класс, а то и два: контроллер с логикой, а второй — низкоуровневая прослойка
  2. Вывод на экран, подключение файлов, загрузка — классы
  3. Функции вывода на экран разных комбинированных типов данных (таблиц, картинок, фактоидов) — отдельные классы
  4. Провести работы по оптимизации скриптов (например, сократить количество echo на порядок, переписать всю конкатенацию на sprintf, размер всех картинок уменьшить в три раза, везде включить кеширование браузера и gzip) и составить отчёт на почту.
  5. Убрать сноски и списки, если они не имеют действительной ценности
  6. Проставить неразрывные пробелы и правильные символы на свои места

Лекция четвёртая. Базы данных, оптимизация запросов. Формы. Кеш

В первую очередь прочитайте учебник Сергея Моисеенко, и пройдите его тесты.

Вот читайте ЦитФорум про БД и sql.ru про MySQL и SQL.

Дальше читайте Техногрет О формах, о фрагментированном кешировании, Кеширование невозможного, Несколько принципов HTML.

Затем осильте ХмтлБук: Защита от дурака, Отправка данных формы, Создание формы, Сумасшедшие формы, Формы в HTML.

Базы данных

SQL — язык для создания, модификации и управления данными в реляционных базах данных.

Данные, с которыми работает SQL представлены в виде простых таблиц, разбитых на строки и столбцы, где хранятся данные.

Идентиф. экскурсии Время проведения График Срок действия Идентиф. ответственного Стоимость
1 10:00:00 Суббота 31.05.2010 2 150,00 руб.
2 11:00:00 Ежедневно 31.05.2010 3 90,00 руб.
3 15:00:00 По четвергам 30.06.2010 2 50,00 руб.
4 12:00:00 По нечётным 30.06.2010 1 300,00 руб.
5 15:00:00 Среда 30.09.2010 1 120,00 руб.

Вот пример связи между таблицами

Отдел Сотрудник Заказчик Контракт Исполнители Номер отдела Наименование отдела Имя заказчика Адрес Табельный номер Номер отдела Имя Номер контракта Имя заказчика Дата Сумма Табельный номер Номер контракта

Для начала, вам стоит прочитать «Руководство по проектированию реляционных баз данных»:

Инструментарий баз данных

Пользуйтесь phpMyAdmin. Вот видеоописание работы в интерфейсе программы и полуофициальная документация. А также полная документация по MySQL.

Синтаксис MySQL

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

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

CREATE TABLE. Создание таблиц

Документация оператора CREATE TABLE

Запросы CREATE TABLE подготавливают структуру таблцы для работы.

Создаётся таблица оператором CREATE TABLE, после которого указывается имя таблицы. В скобках указывается список столбцов, и информация о ключах. Каждому столбцу дается имя, указывается тип данных, указывается атрибут NULL или NOT NULL, и значение по умолчанию, если оно уместно.

						CREATE TABLE
							cities
							(
								id int(11) NOT NULL auto_increment,
								city_name varchar(50) NOT NULL default '',
								latitude varchar(15) NOT NULL default '',
								longitude varchar(15) NOT NULL default '',
								population int(11) NOT NULL default '0',
								country_code char(2) NOT NULL default '',
								PRIMARY KEY (id)
							)
						TYPE=MyISAM AUTO_INCREMENT=1
					

Типов (движков) баз данных несколько. Изучите их самостоятельно.

Когда точно известна длина строки, используйте CHAR. В противном случае, используйте VARCHAR — строки переменной длины, указывая только максимальную длину.

О нормализации читайте на Хабрахабре.

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

Возможен и процесс денормализации, чтобы снизить количество запросов к БД.

						CREATE TABLE countries
							(
								country_code char(2) NOT NULL default '',
								country_name varchar(100) NOT NULL default ''
							)
						TYPE=MyISAM;
					

Столбец country_code присутствует в обеих таблицах. Это отражает принцип связи: country_code в cities связан с одноименным столбцом в таблице countries. Таким образом, мы экономим на месте, указывая название страны в базе данных только однажды. Также это удобно, чтоб изменять country_name всегда в одном месте, не имея копий.

INSERT. Добавление данных

Документация оператора INSERT

Запросы INSERT INTO заполняют данными пустые таблицы. Накапливать данные — очень важный процесс.

Добавляются данные в таблицу оператором INSERT INTO, после которого указывается имя таблицы. Затем в скобках указываются имена столбцов. В блоке VALUES указываются записываемые значения в порядке, указанном в предыдущих скобках.

						INSERT INTO
							countries
							(
								country_code,
								country_name
							)
						VALUES
							(
								'ca',
								'Canada'
							)
					

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

Давайте занесем в таблицу cities данные города:

						INSERT INTO cities
							(
								id,
								city_name,
								latitude,
								longitude,
								population,
								country_code
							)
						VALUES
							(
								'',
								'Sherbrooke',
								'45 23 59.00',
								'-71 46 11.00',
								125000,
								'ca'
							);
					

Здесь, мы указываем пустое значение для id, потому что атрибут автоинкремента данного столбца обеспечивает автоматическое выставление уникального значения. Также следует обратить внимание, что значение population - числовое, поэтому не заключено в кавычки.

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

						INSERT INTO countries
							(
								country_code,
								country_name
							)
						VALUES
							(
								'zh',
								'China'
							);
						INSERT INTO cities
							(
								id,
								city_name,
								latitude,
								longitude,
								population,
								country_code
							)
						VALUES
							(
								'',
								'Shanghai',
								'31 13 58.00',
								'121 26 59.99',
								11000000,
								'zh'
							);
					

UPDATE. Обновление данных

Документация оператора UPDATE

Запросы UPDATE заменяют устаревшие данные. Чрезвычайно важно поддерживать данные в актуальном состоянии.

Обновляются данные в таблице оператором UPDATE, после которого указывается имя таблицы. В блоке SET указывается, имя столбца и новое значение. В блоке WHERE указывается в какой (их) строке (ах) заменять значение.

						UPDATE
							cities
						SET
							population = '130000'
						WHERE
							id = '1'
					

В блоке SET указан список модификаций (в нашем случае - только для столбца population) который записывается в формате «столбец = новое значение».

Мы видим, что в выражении присутствует условие: WHERE id = '1', в котором используется первичный ключ, чтобы ограничить изменение значения столбца population только данной строкой, т.е. только для данного города.

Если дописать limit 1, то гарантируется, что изменения не будут применены более чем к одной записи. За один запрос UPDATE могут быть изменены значения сразу нескольких столбцов.

DELETE. Удаление данных

Документация оператора DELETE

Запросы DELETE FROM удаляют строки из таблицы.

Удаляются данные из таблицы оператором DELETE FROM, после которого указывается имя таблицы. В блоке WHERE указывается какие строки удалять.

						DELETE FROM
							cities
						WHERE
							id = '1'
					

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

SELECT. Выборка данных

Документация оператора SELECT

Запросы SELECT позволяют получить ответы на вопросы: «Какие города имеют численность населения большую, чем данное число?».

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

							SELECT
								*
							FROM
								cities
						
							SELECT
								city_name,
								population
							FROM
								cities
							WHERE
								1
							ORDER BY
								population DESC
							LIMIT
								0, 30;
						

Здесь перечислены столбцы, а не запрашиваются все.

Условие WHERE 1, всегда истинно и выбирает все записи.

Появилось условие ORDER BY, после которого следует название столбца по которому мы хотим сортировать результат выборки, и ключевое слово DESC для сортировки по убыванию (ASC для сортировки по возрастанию).

WHERE. Условия в запросах

Условия позволяют выбирать не все подряд данные, но их часть.

Показать названия городов и население городов, находящихся в Китае.

							SELECT
								city_name,
								population
							FROM
								cities
							WHERE
								country_code = 'ch'
						

Найти города с населением более 100 000 и находящиеся в Канаде.

							WHERE
								population > 100000 AND
								country_code = 'ca'
						

Найти города, чьи названия начинаются с символа "A":

							WHERE
								city_name like 'A%'
						
GROUP. Функции группировки

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

							SELECT
								country_code,
								AVG(population)
							FROM
								cities
							GROUP BY
								country_code
						

Другие возможные функции группировки - MIN(), MAX(), SUM() и COUNT(), которые вычисляют соответственно минимальное значение, максимальное значение, сумму значений, и число записей. Например, с помощью следующего запроса мы можем получить число городов в стране:

							SELECT
								country_code,
								count(city_name)
							FROM
								cities
							GROUP BY
								country_code;
						
Объединения

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

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

							SELECT
								cities.city_name,
								cities.population,
								countries.country_name
							FROM
								cities,
								countries
							WHERE
								cities.country_code = countries.country_code
							LIMIT
								0, 30
						
Объединения через JOIN

JOIN можно использовать совместно с UPDATE

Имеем две таблицы: пользователи и отделы.

							USERS              DEPARTMENTS
							id name      d_id  id name
							-- ----      ----  -- ----
							1  Владимир  1     1  Сейлз
							2  Антон     2     2  Поддержка
							3  Александр 6     3  Финансы
							4  Борис     2     4  Логистика
							5  Юрий      4
						
							SELECT
								u.id,
								u.name,
								d.name AS d_name
							FROM
								users u
							INNER JOIN
								departments d
								ON
									u.d_id = d.id
						

Запрос вернет объединенные данные, которые пересекаются по условию, указанному в INNER JOIN ON %…%. В нашем случае условие users.d_id должен совпадать с departments.id.

							id name      d_name
							-- --------  ---------
							1  Владимир  Сейлз
							2  Антон     Поддержка
							4  Борис     Поддержка
							3  Юрий      Логистика
						

Здесь отсутствуют:

Внутреннее объединение INNER JOIN (INNER писать не обязательно), то есть выбираются только совпадающие данные, объединяемых таблиц.

Чтобы получить данные, которые подходят по условию частично, необходимо использовать внешнее объединение — OUTER JOIN.

Такое объединение вернет данные из обеих таблиц (совпадающие по условию объединения) и дополнит выборку оставшимися данными из внешней таблицы, которые по условию не подходят, заполнив недостающие данные значением NULL.

Существует два типа внешнего объединения OUTER JOIN: LEFT OUTER JOIN и RIGHT OUTER JOIN.

Работают они одинаково, разница заключается в том что LEFT — указывает что «внешней» таблицей будет находящаяся слева (в нашем примере это таблица users). Ключевое слово OUTER можно опустить. Запись LEFT JOIN идентична LEFT OUTER JOIN.

							SELECT
								u.id,
								u.name,
								d.name AS d_name
							FROM
								users u
							LEFT OUTER JOIN
								departments d
								ON
									u.d_id = d.id
						

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

							id name      d_name
							-- --------  ---------
							1  Владимир  Сейлз
							2  Антон     Поддержка
							3  Александр NULL
							4  Борис     Поддержка
							5  Юрий      Логистика
						

Добавив условие WHERE d.id IS NULL в выборке останется только 3#Александр, так как у него не назначен департамент.

RIGHT OUTER JOIN вернет полный список департаментов (правая таблица) и сопоставленных пользователей.

							SELECT
								u.id,
								u.name,
								d.name AS d_name
							FROM
								users u
							RIGHT OUTER JOIN
								departments d
								ON
									u.d_id = d.id
						
							id   name     d_name
							--   -------- ---------
							1    Владимир Сейлз
							2    Антон    Поддержка
							4    Борис    Поддержка
							NULL NULL     Финансы
							5    Юрий     Логистика
						

Дополнительно можно отфильтровать данные, проверяя их на NULL.

							SELECT
								d.id, d.name
							FROM
								users u
							RIGHT OUTER JOIN
								departments d
								ON
									u.d_id = d.id
							WHERE
								u.id IS NULL
						

В примере указав WHERE u.id IS NULL, мы выберем департаменты, в которых не числятся пользователи (3#Финансы).

CROSS/FULL JOIN

FULL JOIN возвращает «объединение» объединений LEFT и RIGHT таблиц, комбинируя результат двух запросов.

CROSS JOIN возвращает перекрестное (декартово) объединение двух таблиц. Результатом будет выборка всех записей первой таблицы объединенная с каждой строкой второй таблицы. Важным моментом является то, что для кросса не нужно указывать условие объединения.

Дублирование строк при использовании JOIN

При использовании объединения новички часто забывают что результирующая выборка может содержать дублирующиеся данные!

Если вам нужна одна запись, делайте объединение с подзапросом

							SELECT
								t1.*,
								t2.*
							FROM
								left_table t1
							LEFT JOIN (
									SELECT
										*
									FROM
										right_table
									WHERE
										some_column = 1
									LIMIT 1
								) t2
								ON
									t1.id = t2.join_id
						

Связи таблиц

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

Один ко многим

Например, когда один режиссёр снял четыре фильма, то одна строка в таблице режиссёров будет относится к четырём строкам в таблице фильмов. Тогда у фильма вместо имени режиссёра будет его уникальный идентификатор.

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

Многие ко многим

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

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

То есть, здесь две связи один ко многим (отдельно жанры к объединяющей таблице и отдельно фильмы к объединяющей таблице).

Один к одному

Например, у одно фильма может быть только одна аннотация (описание).

Чтобы реализовать связь необходимо сделать две таблицы, где всё будет аналогично связи один ко многим, только никаких «многих» не будет.

Пример организации ситемы тегов

Когда-то я задавался таким вопросом.

Что делать выборками MySQL в PHP

Во первых, изучите документацию к драйверу MySQL в PHP.

					$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');

					// Проверим на ошибки после соединения
					if ($mysqli->connect_errno) {
						printf('Не удалось подключиться: %s', $mysqli->connect_error);
						exit();
					}

					// Создание таблицы не возвращает результирующего набора
					if ($mysqli->query('CREATE TEMPORARY TABLE myCity LIKE City') === true) {
						printf('Таблица myCity успешно создана.');
					}
					// Select запросы возвращают результирующий набор
					$result = $mysqli->query('SELECT Name, CountryCode FROM City LIMIT 10')
					if ($result) {
						// Выведем количество строк
						printf('Select вернул %d строк.', $result->num_rows);

						// создаём ассоциативный массив построчно
						while ($row = $result->fetch_assoc()) {
							// Выведем данные
							printf ('%s (%s)', $row['Name'], $row['CountryCode']);
						}

						// очищаем результирующий набор
						$result->close();
					}

					// Если нужно извлечь большой объем данных, используем MYSQLI_USE_RESULT
					$result = $mysqli->query('SELECT * FROM City', MYSQLI_USE_RESULT);
					if ($result) {
						// Важно заметить, что мы не можем вызывать функции,
						// которые взаимодействуют с сервером, пока не закроем
						// результирующий набор. Все подобные вызовы будут
						// вызывать ошибку 'out of sync'
						if (!$mysqli->query('SET @a:="this will not work"')) {
							printf('Ошибка: %s', $mysqli->error);
						}
						$result->close();
					}

					$mysqli->close();
				

Обратите внимание, что при запросе в базу функция mysqli_query() возвращает объект mysqli_result. У объекта есть свои методы, один из них — fetch_assoc() — мы и применяем. Иначе, вывести что-то не получится. Запомните это, потому что вы точно один раз попытаетесь, даже зная это.

В сущности алгоритм работы с базой заключается в следующем:

  1. Подключаемся к БД
  2. Проверяем успешность соединения
  3. Делаем запрос к базе
  4. Работаем с результатом запроса — закрываем соединение запроса.
  5. Закрываем соединение с базой.

Использование кеша

В сущности, кеширование — это запоминание данных, полученных ресурсоёмким путем.

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

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

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

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

Проверяем устарел ли кеш

Осталось только решить самую важную проблему: когда решать, что что этот файл устаревший? Можно добавлять ему дату.

Дату надо добавить не простую, а отметая минуты и секунды (будем час хранить файл). Но также хранить в не стандартном для русскоязычного человека формате: 14:00:00 26.10.2016, а в так называемом timestamp. Это число, которое содержит в себе количество секунд начиная от сотворения мира от рождества UNIX. Не важно, главное, что там будет что-то типа: 1480158000.

Таким образом, если у нас 19:45 на часах, то мы получаем файл со временем 19:00 и используем его как ни в чём не бывало.

Но, важно, что надо обновлять кеш. Запускать обновление мы будем за пять минут. Притом, надо оставить какой-то след, что кто-то уже обновляет кеш. Самым простым вариантом могу предложить мгновенное создание файла с нужным именем. И если он есть, то это будет сигналом, что кеш кто-то делает.

Алгоритм кеширования

Теперь используем все накопленные знания.

Сами решите как вам удобнее будет записать данные в кеш:

Будьте внимательны на обработку ошибок, чтоб при случае, когда файл кеша не создаётся (например, место на диске кончилось) — сайт продолжал работу.

  1. Проверить сколько времени осталось жить предыдущему кешу.
  2. Если осталось меньше пяти минут (или вообще время отрицательное).
    1. Создаём новый файл.
  3. Убеждаемся, что файл существует.
    1. Берём из него данные.
  4. Опрашиваем, есть ли мусорные файлы за несколько прошлых часов.
    1. Удаляем их

Работа с формами

Ввод данных в PHP — формы

Пока мы изучали то, как выводить информацию. А теперь надо переходить к WEB 2.0, где страницы не статичные, а можно любому писать своё на любом сайте.

В PHP есть три способа: get и post, cookie.

Всё связано с запросами, что посылаются на сервер браузером.

Вот, что посылает браузер:

							GET /index.php?higimo=1 HTTP/1.1
							Host: yandex.ru
							Connection: keep-alive
							Upgrade-Insecure-Requests: 1
							User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)
								Chrome/53.0.2785.143 Safari/537.36
							Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
							Accept-Encoding: gzip, deflate, sdch
							Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
							Cookie: PHPSESSID=7irm7alcpvmirpirvelfibnlr2; _ym_uid=1476622592502028960;
						

То же самое мы можем увидеть в адресной строке:

							yandex.ru/index.php?higimo=1
						

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

Конечно, мало где действительно оправдано делать форму, с которой передавать параметры через гет. С ходу можно придумать только поисковую строку — всё же, эту ссылку надо передавать.

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

Генерировать запрос можно через форму или путём составления через аякс.

Форма на HTML
							<form action="./obrabotchic.php" enctype="multipart/form-data" method="post">
								<label>
									Имя:
									<input type="text" id="name" name="name" size="30" />
								</label>
								<label>
									Резюме:
									<input type="file" name="resume" />
								</label>
								<button>Отправить</button>
							</form>
						
Обработчик формы на php
							try {
								// проверка на взлом
								if (!isset($_POST['name']) || !isset($_FILES['resume'])) {
									throw new Exception(
										'Похоже, вы воспользовались не формой на странице',
										1
									);
								}
								// Если нет ни одного символа в строке — фу
								if (!strlen($_POST['name'])) {
									throw new Exception(
										'Нам необходимо знать как к вам обращаться',
										1
									);
								}
								// при загрузке файла тут будет массив
								if (!is_array($_FILES['resume'])) {
									throw new Exception(
										'Без резюме мы не рассмотрим кандидатуру',
										1
									);
								}
								// Если при загрузке какая-то ошибка, то работать дальше тоже уже не надо
								if ($_FILES['resume']['error']) {
									throw new Exception(
										'Файл не удалось загрузить, может, он слишком велик?',
										1
									);
								}

								// Вроде проверили существование всего, с чем будем работать.
								// Осталось проверить правильность введённых данных.

								// открываем поток, чтоб узнать mime-тип
								$finfo = finfo_open(FILEINFO_MIME_TYPE);
								// Проверяем, pdf-ли это
								if (finfo_file($finfo, $_FILES['resume']['tmp_name']) == 'application/pdf') {
									// У меня до finally версии PHP не хватает
									finfo_close($finfo);
									throw new Exception('Файл должен быть в формате PDF', 1);
								}
								// Закрываем открытый поток — не транжирим ресурсы
								finfo_close($finfo);

								// Люблю этот алгоритм транслита, его легко в JS веревести
								function translit($originalStr) {
									// это только, чтоб избавиться от бага с русским языком
									mb_internal_encoding('UTF-8');
									// Эти якобы таблица соответствий. Да, буквы не все, так задумано.
									// Записываем строкой, потому что удобно — далее превращаем это в массив
									$rus = preg_split('/ +/i', 'щ   ш  ч  ц  ю  я  ё  ж  ы э а б в ' .
										'г д е з и й к л м н о п р с т у ф х');
									$eng = preg_split('/ +/i', 'shh sh ch cz yu ya yo zh i e a b v ' .
										'g d e z i j k l m n o p r s t u f x');
									// Мне нужны только буквы нижнего регистра,
									// плюс вместо пробелов подчёркивания
									$resStr = preg_replace('/\ +/i', '_', mb_strtolower($originalStr));
									// Дальше проходим по русским буквам и заменяем их на английские
									// там, где они встречаются, при помощи регулярного выражения.
									for ($i = 0; $i < count($rus); $i++) {
										$resStr = preg_replace('/' . $rus[$i] . '/i', $eng[$i], $resStr);
									}
									// в итоге, вырезаем все лишние буквы и символы
									// (цифры, например нам уже не нужны)
									return preg_replace('/[^_a-z]/i', '', $resStr);
								}

								// Как фильтровать переменные читать на Хабре
								// Гарантируем, что там будет хотя бы пустая строка
								$fileName = trim($_POST['name']);
								// Чистим от проявлений html
								$fileName = htmlspecialchars($fileName);
								// чистим от вредных sql-injection
								$fileName = mysql_escape_string($fileName);
								// Наконец, транслитирируем
								$fileName = translit($fileName);
								// Имя файла уникализируем
								$fileName = $fileName . '_' . time() . '.pdf';

								// Загруженные изображения надо переносить из временного файла
								if (!move_uploaded_file($_FILES['resume']['tmp_name'], $fileName)) {
									throw new Exception(
										'Не удалось сохранить файл, попробуйте через пару минут перезагрузить',
										1
									);
								}
								if (!mail(
										'higimo@gmail.com',
										'Новое резюме',
										'http://higimo.ru/' . $fileName)
									) {
									throw new Exception(
										'Не получилось создать уведомление для работодателя, ' .
										'если ошибка повторяется — напишите администратору: higimo@gmail.com',
										1
									);

								}
							} catch (Exception $e) {
								echo '<b>Произошла ошибка</b>: ' . $e->getMessage();
							}
						

Смотрим как обрабатывать.

Самое главное, что стоит помнить.

Никогда не доверяйте пользователю.

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

Вот массив о котором я говорил.

							array(
								'name'		=> 'sde.svg',
								'type'		=> 'image/svg+xml',
								'tmp_name'	=> '/tmp/phpCp98zZ',
								'error'		=> 0,
								'size'		=> 2833,
							);
						

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

Фильтруем полученное от пользователя

SQL инъекция (SQL injection) - уязвимость которая возникает при недостаточной проверке и обработке данных, которые передаются от пользователя, и позволяет модифицировать и выполнять непредвиденные кодом программы SQL запросы.

Чтобы делать это осмысленно лучше почитать хорошую статью на Хабре.

Где хранить данные

Есть три хранилища:

  1. localStorage
  2. Cookie
  3. SESSION

Первый сейчас описывать не стоит — он может быть использован только JavaScript-ом.

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

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

В дизайн-собаке на сайте Бюро Артёма Горбунова с помощью этой технологии не надо больше авторизовываться — запоминается компьютер.

Использовать куки удобно, приятно, модно, молодёжно, профессионально, весело и правильно.

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

Куки можно задавать специальным мета-тегом, JavaScript-ом и через php.

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

Используйте подготовленную функцию setcookie.

					// Запишем значение на бесконечный срок, с urlencode
					setcookie('visit', 1);
					// Аналогично без кодирования
					setrawcookie('visit', 1);
					// Значение хранится час
					setcookie('visit', 1, time() + 3600);
					// будет установлено только в https://higimo.ru/~lal/
					setcookie('visit', 1, time() + 3600, '/~lal/', 'higimo.ru', 1);

					echo $_COOKIE['visit'];
				

Как быть с паролями

Конечно, есть функция crypt(), но рекомендуют использовать password_hash().

Что делать в четвертую неделю дома

  1. Установите phpMyAdmin на свой сервер, настройте его работу.
  2. Напишите, используя его, запросы к базе данных и интегрируйте их в свой сайт.
  3. Заполните базу данных реальными данными и сохраните бекап. Плюс тем, кто сделает скрипт автоматизации этого процесса. Ещё больший плюс тому, кто сделает для этого скрипта интерфейс.
  4. Написать класс кеширования данных БД и подключить его к низкоуровневой функции запроса к БД
  5. Запрограммировать формы там, где они необходимы

Лекция пятая. JavaScript, ajax

Отличия синтаксиса JavaScript от PHP

JS — регистрозависимый язык. intval(), IntVal(), intVal() — больше не прокатит.

В JS область видимости инвертирована к PHP. Глобальные переменные всегда такие и доступны отовсюду. То есть объявлять локальную переменную надо при помоши оператора var.

					// Создание ассоциированного (map) массива
					$arr = array(
						'key1' => 'value1',
						'key2' => 'value2',
					);

					// Создание обычного массива
					$arr2 = array(0, 1, 3, 4);

					// объявление функции
					function doSmthWithArray($arr) {
						// записываем новое значение
						$arr['key3'] = 'value3';
					}

					// вызываем функцию
					doSmthWithArray($arr);
					// Вывод в
					echo '<pre>', var_dump($arr), '<pre>';
					// 'key1 => value1, key2 => value2' — массив не изменился
					// Конкатенация строки
					echo $arr2[0] . ' Tmp';
				
					// объявление объекта (доступ к полям возможен через [])
					var arr = {
						key1: 'value1',
						key2: 'value2'
					}
					// объявление обычного массива
					// запятая, чтоб ещё раз не писать var
					, arr2 = [0, 1, 3, 4];

					// объявление глобальной функции
					function doSmthWithArray(arr) {
						// записывать новые поля объекта можно «на ходу»
						arr['key3'] = 'value3';
					}

					// вызов функции
					doSmthWithArray(arr);
					// Вывод в консоль (не на экран)
					console.log(arr); // выведет 'key1 => value1, key2 => value2, key3 => value3'
					// Конкатенация
					console.log(arr2[0] + ' Tmp');
				

Разницы немного. По большому счёту теперь больше не будет долларов, массивы создаются квадратными скобками ([]), а не директивой array().

Как работать с JavaScript

Подробнее ознакомиться с консолью хрома можно в документации

В браузере Google Chrome на любой странице нажмите сочетание Ctrl+Shift+J. Перед вами откроется консоль браузера. Туда-то первым делом и будем вводить коды.

Далее, изучим, как JavaScript работает на странице.

Подключение файлов

JavaScript можно подключать двумя способами:

Отдельным файлом Напрямую в текст страницы
										<script type="text/javascript" src="./main.js"></script>
									
										<script type="text/javascript">
											alert('Hello, World!')
										</script>
									

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

Чтобы подключить несколько скриптов, используйте несколько тегов:

						<script src="/js/script1.js"></script>
						<script src="/js/script2.js"></script>
					

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

Если указан атрибут src, то содержимое тега игнорируется.

						<script src="file.js">
							alert(1); // Не выполнится
						</script>
					

Выполнение скрипта. Асинхронные скрипты: defer/async

Браузер загружает и отображает HTML постепенно. Там где указан скрипт, он и выполняется на месте, затем продолжает загружаться страница. Подключив скрипт до <body> — будьте уверены, он выполнится до появления текста. Такое поведение называется синхронным. И попытки обратиться к данным страницы будут неуспешными. Для этого, есть специальные события-загрузки страницы.

						window.onload = function() {
							// Здесь и записывается весь код.
							// Стандартный способ JavaScript-а
							// В этот момент загрузилась вся разметка, а не картинки
						}
						document.addEventListener('DOMContentLoaded', function() {
							// В этот момент загрузилось всё
						})
						$(document).ready(function() {
							// Тот же способ, используя jQuery
						})
						$(function() {
							// Более «продвинутый способ» в jQuery
						})
					

Помните, что синхронная вставка в начале страницы приемлема для счётчиков аналитики и вредна для всего остального.

Указав скрипты в head загрузка остальной страницы приостановится на каждом скрипте. Чтобы не блокировать загрузку, стоит применить один из атрибутов: async (все браузеры, кроме IE9–) или defer (все браузеры).

						<script type="text/javascript" src="./main.js" defer="true"></script>
						<script type="text/javascript" src="./main.js" async="true"></script>
					

Лучше использовать только defer. Если будете использовать оба — выполнится async, если он поддерживается, в противном случае defer.

«Фишка» defer в том, что он ставит скрипты в очередь. Если какой-то, указанный ниже будет загружен раньше, то не будет исполнен, пока его предшественники не подгрузятся. Это очень удобно, если есть какие-то зависимости. А, как правило, они всегда есть. Ну и ещё, этот атрибут поддерживают все браузеры. Больше аргументов и не надо.

defer гарантирует, что будет сохранён относительный порядок вызова скриптов, а начало вызова будет только, когда весь HTML-документ загружен. С async сработает сразу тот, который первый загрузится (может и третий по счёту).

Работа со строками в JavaScript

Это те же строки, что и в PHP: доступ к символу через квадратные ссылки, длина хранится в свойстве .length. Конкатенация через +, а не точку.

Так как строки в JS тоже объекты, то у них есть куча методов, вместо функций в PHP.

Методы

Функции

В JavaScript есть функции, что добавляются в глобальную область имен, а есть те, что видно только в текущей (локальной).

Локальная область видимости Глобальная область видимости
										var showMessage = function() {
											alert('Привет всем присутствующим!')
										}
									
										function showMessage() {
											alert('Привет всем присутствующим!')
										}
									

Есть ещё важная особенность. Указывать функцию можно сколь угодно раз — используйте её имя. А вот способ вызова всегда один: написать круглые скобки.

Функции (и только они) в JS призваны создавать собственную область видимости. То есть все переменные объявленные с var будут видны строго внутри этой функции.

						var code = 'script'
						function showMyName() {
							// знакомых с языками типа Си
							// такой код должен повергнуть в шок
							if (true) {
								// переменная с var и видна только внутри функции
								var name = 'higimo'
							}
							// выводит объявленную в функции переменную
							console.log(name)
							// также имеем доступ к переменным внешней области
							console.log(code)
						}
						showMyName()
						// выведет 'undefined', т. к. переменная не объявлена
						console.log(name)
					

Аргументы (параметры) по умолчанию

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

Если параметр не передан, то его значение равно 'undefined'.

						function showMessage(from, text) {
							// Первый способ
							if (text === undefined) {
								text = 'текст не передан'
							}
							// Второй способ
							text = text || 'текст не передан'

							console.log(from + ': ' + text)
						}
					

Псевдомассив аргументов «arguments»

В JavaScript любая функция может быть вызвана с произвольным количеством аргументов.

						function go(a,b) {
							console.log(a, b)
						}

						go(1)		// 1, undefined
						go(1,2)		// 1, 2
						go(1,2,3)	// 1, 2
					

В JavaScript нет «перегрузки» функций. При попытке перегрузки функции будет вызвана последняя объявленная при любых аргументах. Проверять какие значения были задействованы необходимо внутри функции.

Не забудьте прочитать урок по arguments.

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

						var sprintf = function() {
							if (arguments.length) {
								for (var i = 1; i < arguments.length; i++) {
									arguments[0] = arguments[0].replace('%s', arguments[i])
								}
								return arguments[0]
							}
							return ''
						}
					

Не изменяйте arguments — это вам никогда не понадобиться.

«Именованные аргументы»

Именованные аргументы – альтернативная техника работы с аргументами, когда передаётся один параметр, а arguments не используется вовсе. Большинство jQuery-плагинов используют такой шаблон проектирования.

Некоторые языки программирования (питон, например) позволяют передать параметры как-то так: f(width=100, height=200) — по именам, не аргументами. Это полезно, когда аргументов много, а значит, их порядок и имена сложно запомнить, а большинству сойдет и значение по-умолчанию.

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

						function showWarning(options) {
							// по умолчанию
							var width = options.width || 200,
								height = options.height || 100,
								contents = options.contents || 'Предупреждение'
						}
						// Достаточно передать объект, указав только нужные аргументы
						showWarning({
							contents: 'Вы вызвали функцию'
						})
					

Представьте аналогичный вызов, где реализация через arguments.

						showWarning(null, null, 'Предупреждение!')
						showWarning(, , 'Предупреждение!')
					

Кроме того, этот объект можно задать переменной и использовать несколько раз.

Анонимные функции

В php есть анонимные функции (лямбда-исчесление), но они как-то совсем не нужны оказываются, потому что крайне мало так называемых колбеков. Использовать их вполне нужно для событий или usort(). Всё, что будте описано ниже, справедливо и для PHP, но с немного другим синтаксисом.

Анонимная функция это та, которая не имеет имени. Кстати, именовать функции стоит также как переменные. Только имя должно быть глаголом.

Вот так используются функции-замыкания. Это просто анонимная функция, которая «объявляется» на месте использования. Функцией замыканием называется, потому что в ней «замыкаются» внешние переменные и внутренние.

						setTimeout(function() {
							console.log('Hello')
						}, 1000)
					

Но функцию можно ещё присвоить переменной. Кстати, обычно объявленная функция будет доступна отовсюду, а присвоенная переменной будет доступна только после объявления.

						var show = function(str) {
							echo str;
						}
						show('Hello, Dolly!');
						// Hello, Dolly!
					

Функции-выражения

Иногда, необходимо использовать все преимущества функций, но вызвать её на месте. В обычном случае, мы бы написали так:

						function initCrousel() {
							var minCrousel = $('#minCrousel')
							minCrousel.owlCarousel()
						}
						initCrousel()
					

То есть вызвать сразу после объявления. А можно вызывать сразу, оставляя функцию анонимной. Хитрость состоит в том, чтоб сделать не объявление функции, а выражение:

						(function() {
						// ..
						})()
					

Как мы помним из арифметических выражений их операторы делают выражение исполняемым. Типа, g + weight, то будет сумма. (isBlock ? 'dd' : 'gg'). Собственно, последний способ я и показал. Пустые скоб

А если у нас есть переменные с простыми именами, которые ни в коем случае не должны перезаписываться извне и мешать снаружи?

Всё, что я описал выше про функции ранее — это декларирование, то есть объявление. Мы как бы заявляем, что функция такая-то делает то-то.

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

События

Событие – это сигнал от браузера о том, что что-то произошло.

Событию можно назначить обработчик, то есть функцию, которая сработает, как только событие произошло.

Обработчик устанавливается на имя метода формируемого по принципу: on%имя события%, например onclick. Вызываются именем события с круглыми скобками.

Используемые события

События мыши
События на элементах управления
Клавиатурные события
События документа

Установка обработчика события

Указание обработчика в скрипте Указание обработчика в HTML
										<input id="elem" type="button" value="Нажми меня" />
										<script>
											document.getElementById('elem').onclick = function() {
												alert('Спасибо');
											};
										</script>
									
										<input onclick="alert('Спасибо')" id="elem" type="button" value="Нажми меня" />
									

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

Обработчиком можно назначить и уже существующую функцию, обратите внимание, что присваивать надо без скобок. Ну а когда обработчик ненужен, всегда можно перезаписать его на null.

						function sayThanks() {
							alert('Спасибо!')
						}
						elem.onclick = sayThanks
						elem.onclick = null
						elem.onclick = function() {
							sayThanks()
						}
					

Обратите внимание, что каждый раз присваивая новый обработчик вы убираете старый. Никакой очереди выстроено не будет.

Обратите внимание, что sayThanks присваивается без скобок, как имя функции. Это очень важно, так как со скобками будет вызвана функция и передан её результат (то есть в случае, если оператора return нет будет присвоен 'undefined'). При указании обработчика атрибутом (onclick="..."), напротив, необходимо указывать именно вызов функции. И не используйте .setAttribute('onclick') — длинно и бесполезно.

						suBtn.onclick = function() {
							alert(1);
						}
						// Затрёт предыдущий обработчик
						suBtn.onclick = function() {
							alert(2);
						}
					

Очень печальное зрелище, но есть и таблетка, которая позволяет нацеплять сколько угодно обработчиков для события, которые будут выполнятся в порядке указания. Кроме того, доступ к некоторым событиям (например, DOMContentLoaded) возможен только таким образом.

						var suBtnClickHandler = function() {
							console.log('Меня нажали');
						}
						var suBtnClickAlertHandler = function() {
							alert('Меня нажали');
						}
						suBtn.addEventListener('onclick', suBtnClickHandler)
						suBtn.addEventListener('onclick', suBtnClickAlertHandler)
						// анонимные функции удалять бестолку —
						// никогда не будут удалены, так как считаются другой функцией
						suBtn.removeEventListener('onclick', suBtnClickHandler)
					

Доступ к элементу через this

Внутри обработчика события this ссылается на текущий элемент, то есть на тот, на котором он сработал. Потому что событие — метод объекта и вы как модифицируете его.

Так можно получить свойства или изменить элемент.

						document.querySelector('.item').onmouseenter = function() {
							// Добавить класс hovered всем элементам
							// с классом item, при наведении на них курсора
							this.classList.add('hovered')
						}
					

Циклы и таймеры

В JS очень странное понятие асинхронности. В остальных явзыках всё просто, пока выполняется какое-то действие — ничего другого выполняться не может. Поэтому придумывают некие конструкции языка, чтоб дать распараллеливание. Здесь, всё по-другому. Если бы страница блокировалась, пока JS что-то там считает, мы бы умерли с горя, потому что каждую секунду там что-то считается в пяти местах.

При этом никакой многопоточности в JS нет. Просто, максимально не блокируются состояния и всё.

Однако, мы можем откладывать некоторые функции по времени (Б-же, никогда не делайте бесконечных циклов с проверкой по времени!).

					// Можно вызывать как строку с кодом
					setTimeout('alert("прошла секунда")', 1000)
					// А можно и функцию передать
					// Если запомнить таймаут в переменную
					var myTimeout = setTimeout(function() {
						alert('прошла секунда')
					}, 1000)
					// Таймер можно стереть
					clearTimeout(myTimeout)
				

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

					var interval = setInterval(function() {
						alert('прошла секунда')
					}, 1000)
					setTimeout(function() {
						clearInterval(interval)
					}, 15000)
				

А такой код будет каждую секунду в течении 15-ти говорить вам про то, что прошла секунда, а потом перестанет.

Интервал удобно использовать, чтоб постоянно обновлять состояние какого-то элемента. Или кливать в кнопку «Показать ещё» с интервалом в 800 мс.

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

Ajax

AJAX (аббревиатура от «Asynchronous JavaScript And XML») – технология обращения к серверу без перезагрузки страницы. Кстати, использовать именно XML не обязательно. Интерфейс без перезагрузки страницы серьезно сокращает время отклика, а значит, воспринимается как десктопное, быстрое, удобное приложение. Тотально используется кодировка UTF-8, повлиять на это нельзя.

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

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

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

						// Создаём новый объект XMLHttpRequest
						var xhr = new XMLHttpRequest();

						// Конфигурируем его: GET-запрос на URL 'phones.json'
						// Третий параметр асинхронность запроса.
						// При false страница подвиснет, пока загружается информация
						xhr.open('GET', 'phones.json', false);

						// Отсылаем запрос
						xhr.send();

						// Если код ответа сервера не 200, то это ошибка
						if (xhr.status == 200) {
							// вывести результат
							// responseText -- текст ответа.
							alert(xhr.responseText);
							// в случае JSON
							console.log(JSON.parse(xhr.responseText))
						} else {
							// обработать ошибку
							// пример вывода: 404: Not Found
							alert(xhr.status + ': ' + xhr.statusText);
						}
					

Немного дополнительных настроек.

						// Устанавливает заголовки
						xhr.setRequestHeader('Content-Type', 'application/json');
						// Возвращает значения заголовков
						xhr.getResponseHeader('Content-Type')
						// Возвращает все заголовки ответа
						xhr.getAllResponseHeaders()

						// Время ожидания ответа — 30 с
						xhr.timeout = 30000;
						// Задаем что будет по истечению таймера
						xhr.ontimeout = function() {
							alert('Извините, запрос превысил максимальное время');
						}
						// запрос начат.
						xhr.onloadstart
						// браузер получил очередной пакет данных,
						// можно прочитать текущие полученные данные в responseText.
						xhr.onprogress
						// запрос отменён вызовом xhr.abort().
						xhr.onabort
						// произошла ошибка.
						xhr.onerror
						// запрос успешно (без ошибок) завершён.
						xhr.onload
						// запрос прекращён по таймауту.
						xhr.ontimeout
						// запрос завершён (успешно или неуспешно)
						xhr.onloadend
					

jQuery также имеет обёртку запроса.

						jQuery.ajax({
							// флаг асинхронного запроса
							async:			1,
							// событие перед отправкой запроса
							beforeSend:		function,
							// флаг кеширования запроса
							cache:			1,
							// событие после завершения запроса
							complete:		function,
							// Тип содержимого посылаемого запроса
							contentType:	'application/x-www-form-urlencoded',
							// то, что будет в this
							context:		document.body,
							// кросс-доменный запрос
							crossDomain:	false,
							// данные, отправляемые на сервер
							data:			{'name': 'higimo'},
							// событие, выполняемое перед передачей данных
							// в событие success
							dataFilter:		function,
							// формат, в котором ожидается ответ
							dataType:		'json',
							// событие ошибки запроса (нет интернета)
							error:			function,
							// заголовки
							headers:		{},
							// флаг, проверки на модификацию страницы
							ifModified:		false,
							// флаг, выполнения jsonp-запроса
							jsonp:			false,
							// событие, выполняемое
							jsonpCallback:	function,
							// мим-тип ожидаемых данных
							mimeType:		'html',
							// логин, для авторизации
							username:		'higimo',
							// пароль, для аутендификации
							password:		'qwerty',
							// флаг, обработки отправляемых данных
							processData:	true,
							// набор обработчиков событий, соответственно кодам ответа
							statusCode:		{404: function},
							// событие удачного ответа сервера
							success:		function,
							// время ожидания ответа
							timeout:		8000,
							// тип запроса
							type:			'GET',
							// адрес, на который посылать запрос
							url:			'/ajax/mainHandler.php',
						})
					

GET и POST запросы

Просто так передать параметры не получится. Вы, вероятно, уже видели как кодируются URL. Например, пробел — %20, 'В' — %D0%92 (UTF-8 кодирует один символ в двух битах). В наших запросах также необходимо кодировать информацию. В этом поможет функция encodeURIComponent()

То есть, методом GET передает следующий код. Кстати, браузер сам добавит заголовки Content-Length и Connection (удобство в целях безопасности).

						var params = 'name=' + encodeURIComponent(name) +
							'&surname=' + encodeURIComponent(surname);

						var xhr = new XMLHttpRequest();
						// спец. код, чтоб php отличил аякс-запрос
						xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
						xhr.open('GET', '/submit?' + params, true);
						xhr.onreadystatechange = function
						xhr.send();
					

Для POST запросов есть три кодировки: application/x-www-form-urlencoded, multipart/form-data, text-plain (для файлов используется multipart/form-data). А данные передаются в методе .send()

						var params = 'name=' + encodeURIComponent(name) +
							'&surname=' + encodeURIComponent(surname),
							xhr = new XMLHttpRequest();

						// Можно данные формы получить и так.
						var params = new FormData(document.forms.person) // form[name="person"]
						// И даже вписывать дополнительные данные
						params.append('patronym', 'Робертович');

						// А можно даже так
						var params = JSON.stringify({
							name:		name,
							surname:	surname
						});

						xhr.open('POST', '/submit', true)
						xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
						xhr.onreadystatechange = function;
						xhr.send(params);
					

Кросс-доменные запросы

Как выполняются кросс-доменные запросы вам лучше изучить самостоятельно и следить за безопасностью и рисками.

Прочие приёмы ajax

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

Опасность Ajax

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

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

ООП в JavaScript

Так как в JavaScript не доступны классы (в новейших стандартах для NodeJS есть), да и сам язык реализует прототипную модель. Следовательно, код также изменится.

Как создавать объекты

JavaScript славится своим синтаксисом объектов. Его даже приняли как стандарт называв JSON (JavaScript Object Notation). Суть в том, что для создания объекта достаточно указать {}, а не как в других языках вызывать имя класса с оператором new. Впрочем, можно и так.

							// Эквивалентные записи
							var obj = new Object()
							var obj = {}
						

Добавлять свойства также легко, аналогично свойства задаются и методы:

							// Эквивалентные записи
							obj.test = 5
							obj['test'] = function() {}

							// А можно и при создании объекта
							var obj = {
								test: 5,
								method: function() {}
							}
						

Важно понять, что в любой объект в любое время можно добавить ещё один метод или свойство.

Удобно то, что при обращении к несуществующему свойству не будет выбрашена ошибка. Вернётся 'undefined'.

Существуют оператор delete, который только и умеет удалять свойства объектов. Больше ничего другого удалить самостоятельно из памяти нельзя. На это существует крайне хитрый Сборщик мусора (Garbage Collector).

							delete o.test;
						

Обычные объекты ({}) не подходят для концепции ООП. Для этого используют «функции-конструкторы», которые вызывают через оператор new.

							function Animal(name) {
								this.name = name;
								this.canWalk = true;
							}
							var animal = new Animal('ёжик');
						

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

							animal = {
								name:		'ёжик',
								canWalk:	true
							}
						

Так как функция-конструктор — это функция, то объявленные внутри переменные — локальны и не будут видны извне.

Создание методов в конструкторе
							function User(name) {
								this.name = name;

								this.sayHi = function() {
									alert('Hi, ' + this.name);
								}
							}

							var ivan = new User('higimo');
							ivan.sayHi(); // Hi, higimo
						

Инкапсуляция

						function CoffeeMachine(power) {
							this.waterAmount = 0;
							var WATER_HEAT_CAPACITY = 4200;
							var self = this;
							function getBoilTime() {
								return self.waterAmount * WATER_HEAT_CAPACITY * 80 / power;
							}

							function onReady() {
								alert('Кофе готово!');
							}
							this.run = function() {
								setTimeout(onReady, getBoilTime());
							};
						}

						var coffeeMachine = new CoffeeMachine(100000);
						coffeeMachine.waterAmount = 200;

						coffeeMachine.run();
					

Наследование

«Классический» способ

						function extend(Child, Parent) {
							var F = function() { }
							F.prototype = Parent.prototype
							Child.prototype = new F()
							Child.prototype.constructor = Child
							Child.superclass = Parent.prototype
						}

						// создали базовый класс
						function Animal(..) { ... }

						// создали класс
						// и сделали его потомком базового
						function Rabbit(..)  { ... }
						extend(Rabbit, Animal)

						// добавили в класс Rabbit методы и свойства
						Rabbit.prototype.run = function(..) { ... }

						// все, теперь можно создавать объекты
						// класса-потомка и использовать методы класса-родителя
						rabbit = new Rabbit(..)
						rabbit.animalMethod()
					

Или «Хак», где перезаписывается prototype.

						var Parent = function(data) {
							this.data = data || false;
							this.public_method = function() {
								return 'Public Method';
							}
						}

						var Child = function() {
							this.public_method = function() {
								return 'Redefined public method';
							}
							this.getData = function() {
								return 'Data: ' + this.data;
							}
						}

						Child.prototype = new Parent('test');
						var Test = new Child();

						// => "Data: test"
						Test.getData();
						// => "Redefined public method"
						Test.public_method();
						// => "test"
						Test.data;
					

Полиморфизм

						// TODO: Хорошо бы наследовать, как показывал выше
						// Конструктор родительского класса
						function Animal(name) {
							this.name = name;
						}

						Animal.prototype.speak = function() {
							alert(this.name + ' says:');
						}

						// Конструктор унаследованного класса «Dog»
						function Dog(name) {
							Animal.call(this, name);
						}

						Dog.prototype.speak = function() {
							Animal.prototype.speak.call(this);
							alert('woof');
						}

						// Конструктор унаследованного класса «Cat»
						function Cat(name) {
							Animal.call(this, name);
						}

						Cat.prototype.speak = function() {
							Animal.prototype.speak.call(this);
							alert('miaow');
						}

						// Создает экземпляр Dog
						var d = new Dog('Fido');
						// Вызывает функцию speak() класса Dog
						d.speak();

						// Создает экземпляр Cat
						var c = new Cat('Lucy');
						// Вызывает функцию speak() класса Cat
						c.speak();
					

DOM, BOM — ном-ном-ном

Сам по себе язык JavaScript не предусматривает работы с браузером.

Он вообще не знает про HTML. Но позволяет легко расширять себя новыми функциями и объектами.

На рисунке ниже схематически отображена структура, которая получается если посмотреть на совокупность браузерных объектов с «высоты птичьего полёта».

window DOM BOM JavaScript document navigator screen location frames history XMLHttpRequest Object Array Function

Как видно из рисунка, на вершине стоит window.

У этого объекта двоякая позиция – он с одной стороны является глобальным объектом в JavaScript, с другой – содержит свойства и методы для управления окном браузера, открытия новых окон, например:

						// открыть новое окно/вкладку с URL http://ya.ru
						window.open('http://ya.ru');
					

Объектная модель браузера (BOM)

BOM – это объекты для работы с чем угодно, кроме документа.

Объект navigator содержит общую информацию о браузере и операционной системе. Особенно примечательны два свойства: navigator.userAgent – содержит информацию о браузере и navigator.platform – содержит информацию о платформе, позволяет различать Windows/Linux/Mac и т.п.

Объект location содержит информацию о текущем URL страницы и позволяет перенаправить посетителя на новый URL.

Функции alert(), confirm(), prompt() – тоже входят в BOM.

						alert(location.href); // выведет текущий адрес
					

Большинство возможностей BOM стандартизированы в HTML 5, хотя различные браузеры и предоставляют зачастую что-то своё, в дополнение к стандарту.

Объектная модель документа (DOM)

Он и громадное количество его свойств и методов описаны в стандарте W3C DOM.

Основным инструментом работы и динамических изменений на странице является DOM (Document Object Model) – объектная модель, используемая для XML/HTML-документов. DOM – это представление документа в виде дерева объектов, доступное для изменения через JavaScript.

Согласно DOM-модели, документ является иерархией, деревом. Каждый HTML-тег образует узел дерева с типом «элемент». Вложенные в него теги становятся дочерними узлами. Для представления текста создаются узлы с типом «текст».

Глобальный объект document даёт возможность взаимодействовать с содержимым страницы.

							document.body.style.background = 'red';
							alert('Элемент BODY стал красным, а сейчас обратно вернётся');
							document.body.style.background = '';
						

Построим, для начала, дерево DOM для следующего документа.

							<!DOCTYPE HTML>
							<html>
							<head>
								<title>О лосях</title>
							</head>
							<body>
								Правда о лосях
							</body>
							</html>
						

Его вид:

HTML HEAD #text ↵␣␣␣␣ TITLE #text О лосях #text ↵␣␣␣␣ #text ↵␣␣␣␣ BODY #text Правда о лосях

Браузер автоматически корректирует ваш HTML до необходимого для отображения и интерпретации в DOM. Например, если тега <html> не будет в коде, то он будет автоматически создан в необходимом месте. Кстати, комментарии также отображаются в DOM, потому что всё, что есть в HTML отображается и в DOM.

Возможности, которые дает DOM

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

Вот схема работы с узлами DOM.

Кстати, если элемент не найдет — вернётся null.

DOM-коллекции, такие как childNodes и другие, которые мы увидим далее, не являются JavaScript-массивами.

Дочерние элементы — элементы, которые расположены непосредственно внутри данного.

Потомки — все элементы, лежащщие внутри данного на все уровни вложенности.

							// <body>
							document.body
							// псевдомассив дочерних элементов
							document.body.childNodes
							// Первый элемент
							document.body.firstChild
							// Последний элемент
							document.body.lastChild
							// Предыдущий DOM-сосед
							document.body.previousSibling
							// Следующий DOM-сосед
							document.body.nextSibling
							// только дочерние узлы-элементы, то есть соответствующие тегам
							document.body.children
							// первый дети-элементы
							document.body.firstElementChild
							// последний дети-элементы
							document.body.lastElementChild
							// Перыдущий сосед-элемент
							document.body.previousElementSibling
							// Следующий сосед-элемент
							document.body.nextElementSibling
							// родитель-элемент
							document.body.parentElement
						

Спецификация: HTML5: tabular data.

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

Поиск по DOM

Метод Ищет по… Вернет Ищет внутри
элемента?
Поддержка
getElementById('bim')
<em id="bim">Hello</em>

Ищет элемент по атрибуту id=""

id элемент × везде
getElementsByName('bim')
<em name="bim">Hello</em>

Ищет элементы по атрибуту name=""

name коллекцию × везде
getElementsByTagName('em')
<em>Hello</em>

Ищет элементы по тегу

тегу или '*' коллекцию везде
getElementsByClassName('bim')
<em class="bim">Hello</em>

Ищет элементы по классу

классу коллекцию кроме IE8-
querySelector('[id="bim"]')
<em id="bim">Hello</em>

Ищет элемент по css-селектору

CSS-селектору Элемент везде
querySelectorAll('[name="bim"]')
<em name="bim">Hello</em>

Ищет элементы по css-селектору.

CSS-селектору коллекцию везде
elem.closest('.parent')
<em name="bim">Hello</em>

Ищет ближайший элемент вверх по иерархии.

CSS-селектору коллекцию Chrome 41+
Firefix 35+
Opera 28+

По большому счёту, есть три функции, которые ищут элементы или один элемент по документу. Вам их стоит знать. getElementById — это самый быстрый способ добраться до элемента.

Но самым удобным способом является css-селектор querySelectorAll. Именно выборку по коллекциям стоит использовать, если вы не будете писать никакой умной обёртки. Так вы не будете ошибаться и при любом раскладе будете работать с коллекцией. А сколько элементов (один или десять) уже другой вопрос.

По стандарту, id должен быть уникальным (единственным) в документе. Если в документе несколько элементов с одинаковым идентификатором, то будет выбран случайный из них.

						document.querySelectorAll('ul > li:last-child');
					

Он есть во всех современных браузерах, включая IE8+ (в режиме соответствия стандарту).

Псевдо-классы в CSS-селекторе, в частности :hover и :active, также поддерживаются. Например, document.querySelectorAll(':hover') вернёт список, в порядке вложенности, из текущих элементов под курсором мыши.

Проверка matches

elem.matches(css) проверяет, удовлетворяет ли elem селектору css, возвращает true или false.

Этим методом удобно отфильтровывать лишнее.

							var elems = document.body.children;

							for (var i = 0; i < elems.length; i++) {
								if (elems[i].matches('a[href$="zip"]')) {
									alert("Ссылка на архив: " + elems[i].href);
								}
							}
						
XPath в современных браузерах

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

XPath довольно мощная и сложная штука. Вот пример поиска заголовков H2, которые содержат строку XPath.
							var result = document.evaluate(
									"//h2[contains(., 'XPath')]", // Запрос
									document.documentElement, // начать с <html>
									null,
									XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
									null
								);
							for (var i = 0; i < result.snapshotLength; i++) {
								alert(result.snapshotItem(i).outerHTML);
							}
						

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

Всё, что нужно знать про поиск элементов
							// кешируйте выборку
							var elements = document.querySelectroAll('.active')
							// перед использованием, и обратите внимание на переменную l
							for (var i = 0, l = elements.length; i < l; i++) {
								console.log(i + ' из ' + l)
							}
						

Работа с атрибутами элементов

Метод/описание Пример
elem.hasAttribute(name)

Проверяет наличие атрибута

										elem.hasAttribute('data-tab')
									
elem.getAttribute(name)

Получает значение атрибута

										elem.getAttribute('data-tab')
									
elem.setAttribute(name, value)

Устанавливает атрибут

										elem.setAttribute('data-tab', 4)
									
elem.removeAttribute(name)

Удаляет атрибут

										elem.removeAttribute('data-tab')
									

Свойства элементов

Свойство/описание Пример
elem.innerHTML

Содержимый внутри элемента код.

										console.log(elem.innerHTML)
										elem.innerHTML += '*'
									
elem.value

Значение атрибута value (есть на элементах, где это предусмотренно).

										console.log(elem.value)
										elem.value += '*'
									
elem.className

Значение атрибута class в виде строки.

										console.log(elem.className)
										elem.className += ' hover'
									
elem.classList

Выдает объект с классами элемента.

										elem.classList.add('hover')
										elem.classList.remove('hover')
									
elem.id

Хранит идентификатор элемента.

										console.log(elem.id)
									

Редактирование DOM

Метод/описание Пример
parentElem.appendChild(elem)

Добавляет elem в конец дочерних элементов parentElem.

										var newLi = document.createElement('li');
										newLi.innerHTML = 'Привет, мир!';

										list.appendChild(newLi);
									
parentElem.insertBefore(elem, nextSibling)

Вставляет elem в коллекцию детей parentElem, перед элементом nextSibling.

										var newLi = document.createElement('li');
										newLi.innerHTML = 'Привет, мир!';

										list.insertBefore(newLi, list.children[1]);
									
elem.cloneNode(depth)

Клонирует элемент.

										// С дочерними элементами и атрибутами
										elem.cloneNode(true)
										// Без дочерних элементов и атрибутов
										elem.cloneNode(false)
									
parentElem.removeChild(elem)

Удаляет elem из списка детей parentElem.

										var div = document.createElement('div');
										div.innerHTML =
											'Миссия не выполнима, Итан. ' +
											'Через секунду сообщение ' +
											'самоуничтожится.';

										document.body.appendChild(div);

										setTimeout(function() {
											div.parentNode.removeChild(div);
										}, 1000);
									

Работа со стилями

Метод/описание Пример
element.style

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

										console.log(elem.style.backgroundColor)
										console.log(elem.style.zIndex)
										console.log(elem.style.borderLeftWidth)
									
element.cssText

Позволяет задавать стиль строкой.

										elem.cssText = 'margin: 2em 1em;'
									
getComputedStyle()

Получает используемые для элемента стили.

										var computedStyle = getComputedStyle(document.body);
										console.log(computedStyle.marginTop);
										console.log(computedStyle.color);
									

Размеры и отступы в документах

Метод/описание Пример
elem.hasAttribute(name)

Проверяет наличие атрибута

										elem.hasAttribute('data-tab')
									
					offsetWidth = 390 – внешняя ширина блока, её можно получить сложением CSS-ширины (300px, но её часть на рисунке выше отнимает прокрутка, поэтому 284 + 16), полей(2*20px) и рамок (2*25px).
					offsetHeight = 290 – внешняя высота блока.
					clientLeft = 25 – ширина левой рамки
					clientTop = 25 – ширина верхней рамки
					clientWidth/Height Эти свойства – размер элемента внутри рамок border.
					scrollWidth/Height Эти свойства – аналоги clientWidth/clientHeight, но с учетом прокрутки.

					alert(window.innerWidth); // вся ширина окна
					alert(document.documentElement.clientWidth); // ширина минус прокрутка

					alert('Текущая прокрутка сверху: ' + window.pageYOffset);
					alert('Текущая прокрутка слева: ' + window.pageXOffset);

					scrollTo, scrollBy, scrollIntoView
					

This в JavaScript

Гораздо подробнее об этом рассказано на Хабре.

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

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

Как функция func(name) this = window
Как метод obj.say(name)
obj['say'](name)
this = obj
Изменение контекста func.apply(obj, name)
func.call (obj, name)
this = obj
Как конструктор new const(name) this = новый объект

Что делать дома на пятую неделю

  1. Добавить ajax для всех форм
  2. Добавить ajax для загрузки информации по кнопкам
  3. Придумать и реализовать динамику на страницах
  4. Изучить что такое полифил в JS, прислать пример кода для расширения DOM, и Array.

Лекция шестая. Бутстрап, Друпал, Джумла, Битрикс

Видео в твитере @glorphindale «Примерно так выглядит современная разработка ПО».

Bootstrap

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

Для работы требуется только библиотека джиквери и два файла бутстрапа.

Очень советую прочитать серию уроков по Бутстрапу от Дедушки.

Плагины бутстрапа

Верстка и сетка бутстрапа

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

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

Самое главное правило Бутстрапа

Исправляйте его css, в зависимости от требованй. Либо в самом файле бутстрапа, либо переопределяйте стиля дополнительным файлом.

Пример шаблона

Я написал пример, который вам стоит изучить на тему как это делать шаблон на бутстрапе.

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

Создание сайта на Друпале

Здесь всё построено на узлах (node), таксономии и представлениях (view).

Преимущества Друпала

Установка Друпала

Скачайте последнюю версию, распакуйте архив на хостинг. Пройдите форму.

Поддержка Друпала

У вас будет только русскоязычный форум.

Изменение шаблонов

Как это делать лучше почитать на Хабре.

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

Создание сайта на джумле

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

Создание сайта на Битриксе

Здесь все построено на инфоблоках и компонентах.

Преимущества Битрикса

На Битриксе я реализовал больше проектов, поэтому скажу, чем он мне мил:

Установка Битрикса

Для начала, выберите редакцию, потом купите лицензию.

Скачать Битрикс надо на официальном сайте.

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

Поддержка Битрикс

Есть официальный форум на русском языке. Есть техподдержка.

Документация битрикса

В Битриксе есть, так называемое «старое API» и новое ядро Документация d7.

Есть платные Сертификации, которые повышают и подтверждают квалификацию. Видео-треннинги и учебные онлайн-курсы.

Изменение шаблонов

Как это делать лучше почитать официальную документацию.

Основной смысл в том, чтоб разрезать готовый шаблон на части и разнести по разным файлам.

Что делать дома на шестую неделю

Лекция седьмая. Прощай

© 2016, higimo