Full Stack Web Development.

Материал не дописан, в процессе

Цель

Обучение веб-разработке полного цикла:
  1. Хранение данных в базе SLQ;
  2. Генерация динамических страниц на стороне сервера;
  3. Отображение разметки, стилей и выполнение скриптов на стороне клиента;

Стек технологий

  1. HTML5 - Языки разметки;
  2. CSS3 - Каскадные таблицы стилей;
  3. SCSS - Препроцессор CSS;
  4. TypeScript / JavaScript - Клиент-серверный язык программирования;
  5. Node.js - Среда для выполнения TypeScript / JavaScript на сервере;
  6. PHP - Серверный язык программирования;
  7. MariaDB (бывш. MySQL) - база данных типа SQL.

ПО

Список ПО, которое потребуется нам для практики (под Windows):

Начало

Принцип работы web: что происходит при запросе URI HTML страницы через любой современный браузер с поддержкой HTML5? На примере Google Chrome.

Человек ввёл адрес, например, https://www.example.com/test/?foo=bar -- что происходит?

  1. DNS запрос (сопоставление домена -- example.com -- и IPv4 || IPv6 адреса);
  2. Установка соединения HTTPS или HTTPS2 (2017 -- конец эры HTTP без шифрования);
  3. HTTP заголовки (такие, как: кодировка страницы, переадресация, инструкции для кеширования);
  4. Инициализация сессии, установка файлов COOKIES;
  5. Обработка инструкций для браузера, в случае Chrome -- manifest.json;
  6. Начало отдачи контента в браузер: генерация и отдача HTML;
  7. Статичные ресурсы (JS, CSS, Шрифты, Иконки, Изображения, Видео, ...). Каждый вид таких ресурсов, отличный от текстового, должен поддерживаться браузером + быть описан в таблице mime-types на веб-сервере; Некоторые ресурсы (JS и CSS) могут динамически запрашивать другие;
  8. Установка Service Worker -- фонового JavaScript, продолжающего свою работу после того, как человек ушёл со страницы.

Эпоха HTML5 и CSS3, главные отличия

  1. Упрощение: теперь допустимо <a><div></div></a> и прочие вольности;
  2. Новые типы полей ввода type=date, type=number и другие;
  3. Новые атрибуты data-* для передачи переменных в JS *
  4. Можно декларировать свои теги, например: <footer>Я футер</footer>;
  5. CSS: Новые селекторы. Можно выбрать буквально каждый объект DOM: по имени, родителю, состоянию, содержимому, порядку и т.д. Список селекторов.
  6. @media-запросы, запрмер: стили при определённых разрешениях ( или стиль для печати ), теги-контейнеры для адаптивных изображений
  7. Системы сбора и компиляции фронт-енда: во-первых, препроцессинг CSS с добавлением вложенных структур, переменных и даже функций; Современный исходник стилей выглядит примерно так:
    $my_color: #FF3C00;
    
    .my_class{
      
      background-color: $my_color;
      
      .my_child_class{
        
        background-color: darken( $my_color, 10% );
        
        &:hover,
        &:focus,
        &:active {
          
          background-color: darken( $my_color, 20% );
          
          a {
            color: red;
            
          }
          
        }
        
        &__on {
          color: white;
          
        }
        
        &__off {
          color: black;
          
        }
        
        @media screen and (max-width: 376px) {
      	  max-width: 100%;
      	}
        
      }
      
    }
    
    Некоторые препроцессоры: SASS, Less, Stylus ... список. Мы будем использовать SASS из-за наибольшей распространённости и широких возможностей;
  8. Во-вторых, автоматический префиксинг для разных браузеров.;
  9. Анимация;

Почему именно PHP

Q – PHP --устаревшая фигня! Вот «технологияНейм» -- это даа!
A –

Источник

Серверная часть: PHP

Главная особенность PHP -- при поступлении запроса к скрипту, он каждый раз начинает свою работу заново. То есть, снова инициализируется каждый объект, снова устанавливается подключение к БД, и после обработки пользовательского запроса и отдачи результата, скрипт полностью завершает свою работу и выгружается из памяти.

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

Ну а плюсы -- простота использования: нет необходимости управлять памятью и выводить программу из зависших состояний.

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

Установка

Встроенный сервер

PHP содержит отладочный сервер и вы можете запустить его прямо так:
	1. Откройте консоль
	2. Перейдите в папку, которая будет «корнем» вашего хоста
	3. выполните команду «php -S localhost:8000»
Теперь, если php установлен и добавлен в переменные окружения OS, вы можете обратиться к выбранной папке по адресу http://localhost:8000

Окружение

Мы работаем на сервере Apache из пакета XAMPP. На практике, Apache сейчас -- самый распространйнный веб-сервер, хотя и начинает стремительно сдавать позиции nginx.

Apache поддерживает Файлы инструкций «.htaccess». Действие такого файла распространяется на каталог, в котором он находится, и на его подкаталоги.

Добавим в корень нашего сайта файл «.htaccess»:

	AddType application/x-httpd-php .php
	DirectoryIndex index.php

Это минимальный набор инструкций для обработки php файлов (причём, вы можете выбрать любое расширение вместо '.php'). Обычно они включены в конфигурацию веб-сервера по-умолчанию, но иметь эти строки в .htaccess тоже не помешает.

Раз уж мы затронули файл .htaccess, давайте допишем остальные инструкции. Файл целиком:


#enable php
AddType application/x-httpd-php .php
DirectoryIndex index.php

#add some more types
AddType image/svg+xml .svg .svgz

#some settings
php_value upload_max_filesize 30M
php_value post_max_size 30M
php_value memory_limit 30M

#default charset
AddDefaultCharset utf-8
AddCharset utf-8 .atom .css .js .json .rss .vtt .xml .html .php

<IfModule mod_headers.c>
  <FilesMatch "\.(js|css|xml|gz)$">
	Header append Vary: Accept-Encoding
	
  </FilesMatch>
  
	Header unset ETag
	#disable framing
	Header set X-Frame-Options SAMEORIGIN env=!allow_framing
</IfModule>

<IfModule mod_charset.c>
	CharsetSourceEnc utf-8
	CharsetDefault utf-8
	
</IfModule>


#dont send system files to browsers
<FilesMatch "(\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|swp)|~)$">
	## Apache 2.2
	# Order allow,deny
	# Deny from all
	# Satisfy All

	## Apache 2.4
	Require all denied
</FilesMatch>


#disable directory browsing
Options All -Indexes


#custom error pages
ErrorDocument 500 "Houston, we have a problem."
#ErrorDocument 401 http://example.com/mordor.html
#ErrorDocument 404 /errors/halflife3.html
Мы ещё вернёмся к этому файлу, когда дойдём до mod_rewrite.

Проверим, что PHP работает. Поместим в корень нашего сервера файл 'test.php' с содержанием:


<?php

phpinfo();

Откроем в браузере URL: 'yourtestdomain.zzz/test.php';
Если вы увидели не исходный код файла, а результат его выполнения -- поздравляем, интерпретатор php работает и обрабатывает php файлы.

Переменные

Переменные обозначаются знаком $:

$foo = 'bar';

В отличии от констант:

echo MY_CONSTANT;

стиль константы принято называть БОЛЬШИМИ_БУКВАМИ

стиль обычные переменные именуют сточными_буквами, или сточнымиБуквами (т.н. «верблюжий стиль»)

стиль переменные, содержащие экземпляры классов, называются с БольшойБуквы (как и сами классы).

В PHP возможны некоторые необчные вещи, типа переменных переменных:

$foo = 'bar';
$var_name = 'foo';
	
echo $$var_name; //bar

Видимость переменных

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

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

А чтобы избежать путаницы в больших проектах, в PHP были введены пространства имён:

Пространства имён

начиная с 5 версии, в PHP появились пространства имён. Поместите в начало файла:

namespace MyProject;
Теперь, если ваш проект будет подключен, как библиотека, из глобального пространства имён к его объектам и методам можно будет обратиться через "квалифицированные имена" (Qualified name), например:
namespace MyProject;
class MyClass {
	function myMethod() {
		return true;
		
	}
	
} 

//Квалифицированное имя метода myMethod:
MyProject\MyClass::myMethod();

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

Типизация

PHP является языком с динамической и неявной типизацией:

//тип переменной определяется прямо по мере выполнения программы
$my_var = 1; 
//мы нигде не указывали тип переменной, но таки $my_var -- это int -- тип присвоил интерпретатор

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

$my_var = 1; //сейчас это число

$my_var = 'привет'; //теперь строка
$my_var = '1'; //кстати, это тоже строка

$my_var = [ 'раз', 'два', 3, 4 ]; //а теперь массив

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

Другие языки программирования с динамической типизацией: Python, JavaScript, Ruby, Perl.
Со статической типизацией: Pascal, Java, C, C++;

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

  1. String.
  2. Integer.
  3. Float (floating point numbers / double)
  4. Boolean.
  5. Array.
  6. Object.
  7. NULL.
  8. Resource.

Integer / целое

$my_var = 1;

Float (Double) / число с "плавающей запятой"

$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;

NULL / Ничто

Специальное значение NULL представляет собой переменную без значения. NULL - это единственно возможное значение типа null. Переменная считается null, если:

Не следует путать NULL с 0 или пустой строкой '', несмотря на то, что при приведении типов они могут быть равны.

Boolean / true||false

Булева переменная, Истина либо Ложь

String / строка

$my_var = 'привет';
$other_var = 'Вася';

Важно: отличие двойных и одинарных кавычек.

$my_var = 'привет';
$other_var = 'Вася';

//Одинарные кавычки -- статичный текст.
$new_string = '$my_var, $other_var!';
echo $new_string; // $my_var, $other_var!

//В двойных кавычках преобразуются переменные и выполняется код
$new_string = "$my_var, $other_var!";
echo $new_string; // привет, Вася!
Всегда используйте одинарные кавычки, а переменные вставляйте в явном виде.
Это ускорит программу, сделает её более читаемой, а иногда и безопасной.

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

echo $my_var . ' ' . $other_var;

Дополнение строки (в конец, append)

$my_var = 'привет';
$my_var .= ', Петя!'

echo $my_var; //привет, Петя!

Дополнение строки (в начало, prepend)
Логично было бы предположить обратную функцию '=.', но увы:

$my_var = 'привет';
$my_var = 'И снова ' . $my_var

echo $my_var; //И снова привет

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

$my_var = (3 + 2);
echo 'В корзине лежит' . $my_var . ' яблок';

Некоторые полезные функции для работы со строками

  1. strlen( $string ); возвращает длину строки;
  2. strpos( $string, 'needle' ); возвращает позицию первого вхождения подстроки;
  3. rtrim( $string ); обрезает лишние пробелы в конце;
  4. htmlspecialchars( $string ); преобразует спецсимволы HTML;
  5. strip_tags( $string ); удаляет теги HTML (при этом некоторые можно разрешить);
  6. str_replace( 'replace_from', 'replace_to', $string ); автозамена, str_ireplace -- регистронезависимая;
  7. Дополнительное форматирование строки -- sprintf
  8. print_r( $var ) -- распечатать переменную как строку, в т.ч. массивы

Array / Массив

До PHP7:
//одномерный массив
$my_arr = array( 'Иванов', 'Иван', 'Иваныч', 'М', 46 );


//Числовые индексы будут назначены автоматически
echo $my_arr[0]; //Иванов
	
//Многомерные массивы
$my_arr = array( 
	array( 'Иванов', 'Иван', 'Иваныч', 'М', 46 ),
	array( 'Светлана', 'Петрова', 'Петровна', 'Ж', 12 ),
);

echo $my_arr[0][0]; //Иванов
echo $my_arr[0][2]; //Иваныч
echo $my_arr[1][2]; //Петровна

//Зададим текстовые ключи -- получаем ассоциативный массив (пары ключ => значение )
//Наиболее распространённая (в силу удобства) конструкция
//Такие ассоциативные массивы легко получить с помощью запросов к БД SQL
$my_arr = array( 
	'fio1' => 'Иванов',
	'fio12' => 'Иван',
	'fio13' => 'Иваныч',
	'gender' => 'М',
	'age' = > 46,
);

echo $my_arr['age']; //46

//Вложенные массивы
//Например, профиль пользователя в интернет-магазине с заказами
$my_var = array(
	'fio1' => 'Иванов',
	'fio12' => 'Иван',
	'fio13' => 'Иваныч',
	'gender' => 'М',
	'age' => 46,
	
	'orders' => array (
		
		array(
			'id' => 1,
			'status' => 'delivered',
			'address' => 'Москва, Островной проезд, 8',
			'items' => array(
				
				array (
					'id' => 213,
					'price' => 5000,
					'name' => 'Футболка',
				),
				array (
					'id' => 12,
					'price' => 7000,
					'name' => 'Часы',
				),
				
			),
			
		),
		
		array(
			'id' => 1,
			'status' => 'new',
			'address' => 'Москва, Кутузовский проспект, 35',
			'items' => array(
				
				array (
					'id' => 100,
					'price' => 2000,
					'name' => 'Кепка',
				),
				
			),
			
		),
	
	),

);

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

PHP7 -- новый синтаксис:

$my_var = [1, 2, 3];	
-- как в JavaScript. Впрочем, старое написание тоже поддерживается, и в случае многомерных массивов даже выглядит удобнее. В любом случае, разницы, кроме совместимости с версией РНР, нет.

Некоторые функции для работы с массивами

$my_arr = array( 'Иванов', 'Иван', 'Иваныч', 'М', 46 );

//Добавить в конец массива:
$my_arr[] = 'Менеджер';

//Изменить значение по индексу
$my_arr[0] = 'Петров';

//Удалить по индексу
unset( $my_arr[0] );

//Перемешать массив
shuffle( $my_arr );

//вернём исходное значение
$my_arr = array( 'Иванов', 'Иван', 'Иваныч', 'М', 46 );

//Сереализовать в строку через разделитель '|'
$my_string = implode ( '|' , $my_arr ); //строка 'Иванов|Иван|Иваныч|М|46'

//Десериализовать
$arr = explode ( '|' , $my_string );
Для обработки массивов подходит цикл 'foreach as' -- «для каждого элемента как»:
$my_arr = array( 'Иванов', 'Иван', 'Иваныч', 'М', 46 );
foreach ( $my_arr as $var) {
	//в теле каждого цикла очередное значение массива будет записано в переменную $var
	echo '
'.$var.''; } //А теперь вернёмся к ассоциативному массиву $my_arr = array( 'fio1' => 'Иванов', 'fio12' => 'Иван', 'fio13' => 'Иваныч', 'gender' => 'М', 'age' = > 46, ); $buffer = ''; foreach ( $my_arr as $key => $var) { $buffer .= ' '.$key.' '.$var.' '; } echo ''.$buffer.'
';

Object / Объект

Объекты

class User
{
	public $name; //эта переменная будет доступна извне, причём и для чтения, и для записи (и лучше так не делать, разве что с совсем неважными данными)
	private $age; //а эта -- нет
	
	function __construct() {
		$this->name = ' Вася';
		echo '<br>Один из магических методов -- конструктор. Будет выполнен при инициализации объекта.';
		
	}
	
	//Метод Присвоить возраст
	//Принимает целое число $years
	//Отдаёт boolean
	function set_age( int $years ) : bool {
		$this->age = $years;

		return true;
		
	}
	
	//Метод Запросить возраст, отдаёт целое число
	function get_age() : int {
		echo '<br>Получите-распишитесь';
		return $this->age;
		
	}
	
	//Перегрузка метода с другим количеством аргументов
	function get_age( int $something ) : int {
		
		echo '<br>Вот: ';
		return ( $this->age + $something  );
		
	}
	
	//Ещё один метод
	function justSomeMethod() {
		echo '<br>Ура!';
		
	}
	
}

$User = new User();
echo $User->name;

$User->name = 'Петя';
echo $User->name;

$User->set_age( '16' );
echo $User->get_age();

echo $User->get_age( 5 );

//вызов метода без инициализации класса
User::justSomeMethod;

стиль У класса открывающая скобка '{' ставится на новой строке. У функций и методов -- с той же строки.

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

Resource / Ресурс

Абстрактный тип данных, обозначающий что угодно вне PHP. Например, подключение к базе данных или сокету.

Функция запроса типа ресурса:

$c = mysql_connect();
echo get_resource_type($c); // mysql link

Функции для работы с переменными

Приведение типов

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

//обратный пример из Java
int number = 123;
String numberAsString = Integer.toString(number);

в PHP необычное приведение типов, к которому придётся привыкнуть.

Приведение типов осуществляется через "касты", например:

$my_var = '123'; //строка, '123'
$my_var = (int) $my_var; //число, 123

$my_var = (bool) $my_var; //булева, true
$my_var = (string) $my_var; //строка, "1"

$my_var = false;
$my_var = (string) $my_var; //пустая строка, ""

$my_var = 'привет123'; //строка, 'привет123'
$my_var = (int) $my_var; //число, 123

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

== не транзитивен. "foo" == TRUE, и "foo" == 0… но, конечно же, TRUE != 0.

== конвертирует в число, если возможно. Далее конвертирует в float'ы, если возможно. Получается, что большие шестнадцатиричные строки(например, хеши паролей) могут неожиданно быть равными, когда они не равны.
По тем же причинам, "6" == " 6", "4.2" == "4.20" и "133" == "0133". Но прошу заметить, что 133 != 0133, потому что 0133 восьмеричное.

=== сравнивает значения и тип… но не для объектов, где === истинно если оба операнда один и тот же объект!

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

Сравнение  не согласовано: NULL < -1, и NULL == 0. Сортировка не детерменирована; она зависит от порядка в котором, алгоритм будет сравнивать элементы.
Операторы сравнения пытаются сортировать массивы, двумя разными способами: сначала по длине, затем по элементам.

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

Объекты при сравнении всегда больше всего остального… кроме других объектов, которые не больше и не меньше.

Для более типобезопасного ==, у нас есть ===. Для типобезопасного < у нас… нет ничего. "123" < "0124", всегда, что бы вы не делали.

Такие вещи с трудом можно простить PHP только за скорость (и, как следствие, дешевизну) разработки, а также за то, что от версии к версии он таки становится лучше. Так что, имеем ввиду, что от приведения типов в PHP можно ждать подвоха, и продолжаем.

Функции запроса типов переменных
$my_var = 1;
echo is_int( $my_var ); //true

$my_var = 'привет';
echo is_string( $my_var ); //true

$my_var = [ 'раз', 'два', 3, 4 ];
echo is_array( $my_var ); //true

$my_var = 1.5;
echo is_float( $my_var ); //true
echo is_numeric( $my_var ); //true -- является ли вообще числом?

//по аналогии
is_bool( is_array( $my_var ) ) //true

is_object( $my_var ); //false

Объявлена ли переменная?

isset( $my_var ); //true

Удалить перменную

unset( $my_var );

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

$arr = get_defined_vars();

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

Создадим новый, пустой файл PHP 'test1.php' и посмотрим, какие глобальные переменные у нас будут определены по-умолчанию, помимо тех, что мы определили сами:

$my_var = 1234;
$my_other_var = 'hello';

$arr = get_defined_vars();
print_r($arr);

Этот список будет отличаться на разных ОС и конфигурациях PHP, но нас в первую очередь будут интересовать массивы, которые присутствуют почти в любом случае: [_SERVER] и [_REQUEST] (он же -- [_GET] + [_POST] +[_FILES] ).

[_SERVER] содержит сведения о соединении, в том числе,
HTTP_USER_AGENT -- сведения о браузере пользователя (так мы можем определить тип устройства)
REMOTE_ADDR -- его ip (вести логи),
SERVER_NAME -- адрес сервера, к которому обращаются
REQUEST_URI -- адрес запроса

лаба Выведите на экран таблицу <table> c элементами массива [_SERVER]

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

Разберём пример: обработка POST-запроса через единственное поля 'username':

<form method="POST">
	<input type="text" name="username" />
	<input type="submit" />
</form>

Пример на Java

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MyServlet extends HttpServlet {
  public void doPost(HttpServletRequest request,
					HttpServletResponse response)
	  throws ServletException, IOException {

	String userName = request.getParameter("username");
	
	System.out.println("Вот наше имя пользователя: " + userName );
  }
}

Пример на PHP

echo 'Вот наше имя пользователя: ' . $_POST['username'];

Отступление: отличие GET и POST запросов

...

лаба Напишите калькулятор. Требования: функции сложения, умножения, вычитания, деления; поддержка положительных и отрицательных чисел

Условные операторы

if / elseif / else

if ($a > $b) {
	 echo "a больше, чем b";
	 
} elseif ( $a === $b ) {
	echo "a равно b и типы этих переменных совпадают";
	
} elseif ( $a == $b ) {
	echo "a равно b";
	
} else {
	 echo "a НЕ больше, чем b";
}

Упрощённый синтаксис 'if/else'

echo 'Здравствуйте, ' . ( $gender == 'male' ? 'господин' : 'госпожа' ) . ' ' . $name . '!';

echo 'У вас ' . ( $item > 15 ? 'много' : 'мало' ) . ' вещей';
Логические операторы
Пример Название Результат
$a and $b И TRUE если и $a, и $b TRUE.
$a or $b Или TRUE если или $a, или $b TRUE.
$a xor $b Исключающее или TRUE если $a, или $b TRUE, но не оба.
! $a Отрицание TRUE если $a не TRUE.
$a && $b И TRUE если и $a, и $b TRUE.
$a || $b Или TRUE если или $a, или $b TRUE.

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

$a = 0;
$b = 1;

( $a === 0 || $b === 3 || $a+$b === 4 ) //true
( $a === 0 && ( $b === 2 || $a+$b === 4 ) ) //false

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

unset( $a ); //такой переменной точно нет
if ( $a == 0 ) {
	echo 'Правда'; //по версии '=='
}

//или из примера выше:
if ( 133 != 0133 ) //true

Для строк типами можно спокойно принебречь.

Зачем же нужно такое условное приведение типов? Для скорости и удобства в некоторых случаях. Например, здесь нам неважно, что, после всех наших действий, оказалось в переменной $my_arr['age'] -- пустая строка '', NULL из БД или 0 после каких-то ошибочных вычислений -- мы выводим переменную с разметкой только если она есть (с человеческой точки зрения)

echo ( $my_arr['age'] ? '<strong>'.(int)$my_var.'</strong>' : '' );

switch / case

Т.н. "конструкция выбора", отличается от if/elseif/else только лишь иногда более удобным написанием, когда выбор действия зависит только от одной переменной.

$x="Яблоко";

switch ($x) {
	case "Яблоко":
		echo "Это Яблоко";
		break;
		
	case "Груша":
		echo "Это Груша";
		$x .= ' висит нельзя скушать';
		break;
		
	case "Арбуз":
		echo "Это Арбуз";
		break;

	case "Лимон":
	case "Апельсин":
	case "Мандарин":
		echo "Это что-то из цитрусовых";
		break;

	default:
		echo "Непонятно";
		break;

}

Циклы

Тут всё стандартно, C-подобные
while,
foreach as,
for,

while

Работает, пока единственное условие == true

$i = 1;

while ($i <= 10) {
	echo $i++;
}
//завершится через 10 итераций

while ($i <= 10) {
	if ( $i === 5 ) {
		break;
	}
	echo $i++;
}
//по условию, завершится через 5 итераций

while ($i <= 10) {
	if ( $i === 3 ) {
		$i = 4;
		continue;
	}
	echo $i++;
}
//пропустит цифру 3

while ( true ) {
	echo $i++;
}
//вечный цикл, будет остановлен только пользователем (кнопкой "стоп" в браузере, если не запрещено) или веб-сервером по достижению максимального времени выполнения скрипта.

foreach as

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

$my_arr = array( 
	'fio1' => 'Иванов',
	'fio12' => 'Иван',
	'fio13' => 'Иваныч',
	'gender' => 'М',
	'age' => 46,
	
);

foreach ( $my_arr as $key => $var) {
	echo '<br><strong>'. $key .'</strong> = ' . $var;
		
}	

For

Тот самый цикл с тремя условиями: (начальное состояние; условие для продолжения; функция, отрабатывающая после каждой итерации );

for ($i = 1; $i <= 10; $i++) {
	echo $i;
	
	//пример вложенного условия -- если '$i' кратно 3
	if ( $i % 3 == 0 ) {
		echo '! ';
	}
	
}

Обработка ошибок

Исключения

В PHP появились исключения и их обработка. Можно "поймать" исключение от стандартных библиотек, или "выбросить" своё:

function inverse($x) {
    if (!$x) {
        throw new Exception('Division by zero!');
    }
    return 1/$x;
}

try {
    echo inverse(5) . "\n";
    echo inverse(0) . "\n";
	
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
	
}

Ошибки стандартной библиотеки и синтаксиса

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

function sys_errorHandler($errno, $errstr, $errfile, $errline) {
    if ( !(error_reporting() & $errno) ) {
        return;
    }

    switch ($errno) {
    case E_USER_ERROR:
        echo "<pre>[$errno] $errstr\n";
        echo "Fatal error: string $errline , file $errfile\n";
        echo "Exit.\n</pre>";
        exit(1);
        break;

    case E_USER_WARNING:
        echo "<pre>Warning: [$errno]\n$errstr\n";
		echo "string $errline , file $errfile\n";
        echo "\n</pre>";
        break;

    case E_USER_NOTICE:
        echo "<pre>Notice [$errno]: $errstr\n</pre>";
        break;
	
    default:
        echo "<!-- Error [$errno]: $errstr\n";
		echo "string $errline , file $errfile\n";
        echo "\n -->";
        break;
    }
	
    /* Не запускаем внутренний обработчик ошибок PHP */
    return true;
}

set_error_handler("sys_errorHandler");

Положим этот пример в файл '/sys/error_handler.php' и позже подключим к проекту.

Организация проекта на PHP

Подключение скриптов

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

Создадим такую структуру директорий, где "/" -- корневая папка нашего хоста:

│   .htaccess
│   index.php
│   
└───sys
    └───classes
        └───User
                User.php

Теперь, в index.php подключим некий файл, для начала статично. Допустим, нам надо подключить файл с классом:

require( $_SERVER['DOCUMENT_ROOT'].'/sys/classes/User/User.php' );

Теперь скрипт будет исполняться последовательно, с начала index.php до конца User.php

Два варианта подключения:
require() -- затребовать: если такого файла нет, программа завершится с ошибкой;
include() -- включить: если такого файла нет, программа будет продолжена без включения;

Также, require_once() и include_once() -- подключение с проверкой, что файл не был подключен раньше.

Как понять, в каком файле мы сейчас находимся, если они уже много раз вложены один в другой?
debug_backtrace() -- получить массив с отладочной информацией;
debug_print_backtrace() -- вевести массив с отладочной информацией на экран;

Авторегистрация классов

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

Создадим файл /sys/class_loader.php со следующим php кодом:

//объявляем функцию загрузки классов
function sys_class_loader( $className ) {
   if ( !preg_match('/[^A-Za-z0-9-]/', $className) ) {
		echo 'Имя класса содержит недопустимые символы';
		exit(1);
		
	}
	
	require_once ( $_SERVER['DOCUMENT_ROOT'] . '/sys/classes/'.$className .'/'. $className .'.php' );
	
}

//регистрируем её в качестве загрузчика
spl_autoload_register( 'sys_class_loader' );

Включим его в наш /index.php:

require( $_SERVER['DOCUMENT_ROOT'].'/sys/class_loader.php' );

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

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

Маршрутизация, часть 1

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

Классы у нас теперь подключаются автоматически, но ещё нам нужно отвечать на запросы пользователей (запросы по URL). Пока что, сделаем первую, простую версию.

Расширим нашу структуру папок:

│   .htaccess
│   index.php
│   _TREE-Output.txt
│   
└───sys
    ├───classes
    │   ├───Module
    │   │       Module.php
    │   │       
    │   └───User
    │           User.php
    │           
    └───modules
        ├───contacts
        │       contacts.php
        │       
        ├───index
        │       index.php
        │       
        └───profile
                profile.php

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

Т.к. аргументом функций include / require является строка, мы можем предварительно составить её, используя параметры, переданыне в запросе. Например, используем для нашего первого роутинга $_GET['module'] , аналогично тому, как мы сделали это для функции sys_class_loader.

лаба 1 добиться подключения нужного файла из папки /sys/modules/

// подсказка: что-то типа
$_SERVER['DOCUMENT_ROOT'].'/sys/modules/'.$module.'/'.$module.'.php';

лаба 2 описать класс Module, реализующий метод 'render'

// подсказка: что-то типа
Module::render( $_GET['module'] );

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

Точки входа

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

В нашем случае, точкой входа будет являться '/', он же -- 'index.php'. А в папку /sys мы положим файл .htaccess с единственной строкой:

deny from all

База данных

Актуальные версии PHP предлагают несколько вариантов подключения к той же самой MariaDB:

  1. mysql() -- самый первый вариант подключения, уже помечен как устаревший и скоро будет удалён;
  2. mysqli() -- "продвинутый" метод сперциально для MySQL / MariaDB;
  3. ODBC -- для работы через ODBC API;
  4. PDO -- PHP Data Objects, универсальный интерфейс к SQL базам (в т.ч. и sqlite, MS SQL, Oracle и другим) -- его мы и рассмотрим.
Расширения для работы с базами данных Какие драйверы PDO (для каких БД) установлены в системе?
echo 'Драйверы PDO: '.print_r( PDO::getAvailableDrivers(), true );
//    [0] => mysql
//    [1] => odbc
//    [2] => sqlite
Описывая подключение и запросы к БД методами PDO можно подменить базу данных, оставив PHP приложение без изменений.

Создадим пользователя MariaDB и базу данных, например, с помощью PHPMyAdmin:

-- phpMyAdmin SQL Dump
-- version 4.5.1
-- http://www.phpmyadmin.net
--
-- Хост: 127.0.0.1
-- Время создания: Окт 12 2016 г., 18:11
-- Версия сервера: 5.5.25
-- Версия PHP: 7.0.6

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

--
-- База данных: `nopg_cars`
--

-- --------------------------------------------------------

--
-- Структура таблицы `sys_page`
--

CREATE TABLE `sys_page` (
  `page:id` int(4) NOT NULL,
  `page:uri` varchar(64) NOT NULL,
  `page:title` varchar(255) NOT NULL,
  `page:text` longtext NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Индексы сохранённых таблиц
--

--
-- Индексы таблицы `sys_page`
--
ALTER TABLE `sys_page`
  ADD PRIMARY KEY (`page:id`),
  ADD UNIQUE KEY `page:uri` (`page:uri`);

--
-- AUTO_INCREMENT для сохранённых таблиц
--

--
-- AUTO_INCREMENT для таблицы `sys_page`
--
ALTER TABLE `sys_page`
  MODIFY `page:id` int(4) NOT NULL AUTO_INCREMENT;
Попробуем подключиться:
$mysql_host = 'localhost';
$mysql_db = 'newbridge';
$mysql_user = 'newbridge';
$mysql_pass = '34rt3rtfq34tw54t456yw54y';

try {
	$db = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_db.';charset=utf8', $mysql_user, $mysql_pass);
	$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
	$db->exec("SET NAMES UTF8");
	
} catch(PDOException $ex) {
	echo 'Ошибка 503. Мы уже в курсе, ведутся технические работы. Зайдите чуть позже.');
	exit(1);
	
}

Если ошибки не возникло -- у нас в переменной $db типа "ресурс" содержится коннект к БД. Попробуем выполнить пару запросов:

try {
	//выберем все записи из таблицы с пользователями
	$sql = "SELECT * FROM `sys_page`";
	
	//если всё ОК -- в $result у нас объект с результатом запроса
	$result = $db->query( $sql );

} catch(\Exception $e){
	//это просто пример
	//на практике тут надо показывать красивую ошибку для посетителя,
	//а админу
	//на дев-сервере показывать детали
	//а на продакшне отправлять уведомления
	
	echo( $sql );
	echo( 'Sql error:' );
	echo( $e->getMessage() );
			
	echo( 'POST: ' );
	echo( $_POST );
	
	exit(1);
  
}

if ( $result->rowCount() > 0 ) {
	
	//метод fetch(PDO::FETCH_ASSOC) формирует из результата ассоциативный массив
	while ( $row = $result->fetch(PDO::FETCH_ASSOC) ) {
		
	
	}
	
} else {
	echo 'пусто';
	exit(0);
	
}

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

лаба Напишите функцию-обёртку.

/**
 * пример обёртки для запросов к базе
 */
function sys_query( $sql ) {
	global $db;

	try {
		$result = $db->query( $sql );
		return $result;
		
	} catch(\Exception $e){
		echooo( $sql );
		echooo( 'Sql error:' );
		echooo( $e->getMessage() );
				
		echooo( 'POST:' );
		echooo( $_POST );
		
		// sys_alert(  );
		
		exit(1);
	  
	}

}

Либо, если есть желание, попробуйте использовать одну из ORM. prepare statements / Подготовленные запросы

$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

// вставим одну строку
$name = 'one';
$value = 1;
$stmt->execute();

// теперь другую строку с другими значениями
$name = 'two';
$value = 2;
$stmt->execute();
PDO::quote

addslashes()

strip_tags()

str_replace()
/**
 * Пример подготовки строк
 */
function sys_dbstring( $string, bool $allow_html = false ) {
	global $db;
	
	//TODO:добавить маску по символам

	return $db->quote(
		addslashes(
		
			( $allow_html
				? $string 
				: htmlspecialchars( 
					strip_tags( $string )
				)
				
			)
			
		)
	);

}
https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

Маршрутизация, часть 2

#mod_rewrite
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteOptions Inherit

# no-slashes to slashes
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ /$1/ [R=301,L]

RewriteRule ^([A-Za-z0-9_^/]+)/([A-Za-z0-9-_^/]+)/$ /index.php?mod=$1&id=$2 [QSA,L,NC]
RewriteRule ^([A-Za-z0-9_^/]+)/([A-Za-z0-9/-_^/]+)$ /index.php?mod=$1&id=$2 [QSA,L,NC]
RewriteRule ^([A-Za-z0-9_^/]+)/$ /index.php?mod=$1 [QSA,L]
RewriteRule ^([A-Za-z0-9_^/]+)$ /index.php?mod=$1 [QSA,L]

# Rewrite www to no-www domain
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http%{ENV:https}://%1%{REQUEST_URI} [R=301,L]

#RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(.*)index\.(php|html)/?(.*)\ HTTP/
#RewriteRule . http%{ENV:https}://%{HTTP_HOST}/%1%3 [R=301,L]

Клиентская часть: HTML + CSS + JS

Формы

<form method="POST">
	<br /><label>строка <input type="text" name="user_name"></label>
	
	<br /><label>число <input type="number" name="user_age" min="18" max="130"></label>
	
	<br /><label>дата <input type="date" name="user_bday" required></label>
	
	<br /><label>время <input type="time" name="user_bday"></label>
	
	<br /><label>email <input type="email" name="user_email"></label>
	
	<br /><label>пароль <input type="password" name="user_pass"></label>
	
	<br /><label>период <input type="range" name="user_points" min="0" max="10"></label>
	
	<br />радиокнопки <div>
			<input type="radio" name="gender" value="one"> Раз<br>
			<input type="radio" name="gender" value="two"> Два<br>
			<input type="radio" name="gender" value="three"> Три
	</div>
	
	<br />флаги / чекбоксы <div>
			<input type="checkbox" name="vehicle" value="Bike"> I have a bike<br>
			<input type="checkbox" name="vehicle" value="Car" checked> I have a car<br>
	</div>
	
	<br /><input type="submit" name="button" value="отправить">
	
</form>
	








радиокнопки
Раз
Два
Три

флаги / чекбоксы
I have a bike
I have a car

Результаты отправки через эту форму:
						

лаба Попробуйте заполнить форму данными из запроса к SQL

лаба Попробуйте сохранить данные из формы в БД SQL

Система сборки контента Gulp.js

Установка под Windows в %WORK_HDD%\%WORK_DIR%:

cd %WORK_HDD%\%WORK_DIR%
%WORK_HDD%

npm install -g gulp && npm install --save-dev gulp-clean && npm install --save-dev gulp-minify-css && npm install --save-dev gulp-sass && npm install --save-dev gulp-autoprefixer && npm install --save-dev gulp-sourcemaps && npm install --save-dev gulp-notify && npm install --save-dev gulp-order && npm install --save-dev gulp-concat && npm install --save-dev gulp-uglify && npm install --save-dev gulp-imagemin && npm install --save-dev imagemin-pngquant && npm install --save-dev gulp-jshint && npm install --save-dev gulp-fixmyjs && npm install --save-dev consoleplusplus && npm install --save-dev gulp-concat-css && npm install --save-dev gulp-replace && npm install --save-dev delete && npm install --save-dev gulp-strip-css-comments && npm install --save-dev gulp-insert && npm install gulp-typescript &&npm install gulp

файл %WORK_HDD%\%WORK_DIR%\gulpfile.js

// Doctorrr's gulpfile

// npm install --save-dev gulp
// npm install --save-dev gulp-clean
// npm install --save-dev gulp-minify-css
// npm install --save-dev gulp-sass
// npm install --save-dev gulp-autoprefixer
// npm install --save-dev gulp-sourcemaps
// npm install --save-dev gulp-notify
// npm install --save-dev gulp-order
// npm install --save-dev gulp-concat
// npm install --save-dev gulp-uglify
// npm install --save-dev gulp-imagemin
// npm install --save-dev imagemin-pngquant
// npm install --save-dev gulp-jshint
// npm install --save-dev gulp-fixmyjs
// npm install --save-dev consoleplusplus
// npm install --save-dev gulp-concat-css
// npm install --save-dev gulp-replace
// npm install --save-dev delete
// npm install --save-dev gulp-csso
// npm install --save-dev gulp-strip-css-comments

// Install 1: Node.js
// Install 2: Window / Run / cmd
// Install 3: cd your project root directory (www)
// Install 4: run :
// break>package.json && npm install -g gulp && npm install --save-dev gulp-clean && npm install --save-dev gulp-minify-css && npm install --save-dev gulp-sass && npm install --save-dev gulp-autoprefixer && npm install --save-dev gulp-sourcemaps && npm install --save-dev gulp-notify && npm install --save-dev gulp-order && npm install --save-dev gulp-concat && npm install --save-dev gulp-uglify && npm install --save-dev gulp-imagemin && npm install --save-dev imagemin-pngquant && npm install --save-dev gulp-jshint && npm install --save-dev gulp-fixmyjs && npm install --save-dev consoleplusplus && npm install --save-dev gulp-concat-css && npm install --save-dev gulp-replace && npm install --save-dev delete && npm install --save-dev gulp-strip-css-comments && npm install --save-dev gulp-insert && npm install gulp-typescript &&npm install gulp && gulp

var gulp = require('gulp');
var clean = require('gulp-clean');
var sass = require('gulp-sass');
var minifycss = require('gulp-minify-css');
var autoprefixer = require('gulp-autoprefixer');
var notify = require('gulp-notify');
var sourcemaps = require('gulp-sourcemaps');
var order = require('gulp-order');
var concatCss = require('gulp-concat-css');
var jshint = require('gulp-jshint');
var fixmyjs = require('gulp-fixmyjs');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');
var replace = require('gulp-replace');
var del = require('delete');
// var csso = require('gulp-csso');
var imagemin = require('gulp-imagemin');
var sourcemaps = require('gulp-sourcemaps');
// var typescript = require('gulp-typescript');
var stripCssComments = require('gulp-strip-css-comments');

Number.prototype.pad = function(size) {
  var s = String(this);
  while (s.length < (size || 2)) {s = "0" + s;}
  return s;
}


gulp.task('allCSS', function () {

	  gulp.src( [
		'./s/scss/bootstrap.scss'
	  ] )
		.pipe(sass().on('error', sass.logError))
		.pipe(concatCss('./s/css/style.min.css'))
		.pipe( stripCssComments({preserve: false }) )
		.pipe( minifycss() )
		.pipe( autoprefixer( 'last 10 version' ) )
			.pipe(replace('../../../../../../s/', '/s/'))
			.pipe(replace('../../../fonts/bootstrap/', '/s/fonts/'))
			.pipe(replace('url(..//s/img/', 'url(/s/img/'))
			.pipe(replace('../../../fonts/', '/s/fonts/'))
			.pipe(replace('url(..//s/fonts/', 'url(/s/fonts/'))
			.pipe(replace('url(/fonts/', 'url(/s/fonts/'))
			.pipe(replace('url(/font/', 'url(/s/fonts/'))
			.pipe(replace('url(../../../font/roboto/', 'url(/s/fonts/roboto/'))
			.pipe(replace('../../../../../../img/overlays', '/s/img/overlays'))
		.pipe( gulp.dest('./') );
		
		console.log('=============== UPDATECSS');
	
	
	return true;

});

gulp.task('sass', function () {
  gulp.src('./s/scss/*.scss')
    .pipe(sass().on('error', sass.logError))
	.pipe(gulp.dest('./s/css'))
});

gulp.task('css', function () {
	return gulp.src('./s/css/*.css')
		.pipe( minifycss() )
		.pipe( autoprefixer( 'last 10 version' ) )
		.pipe( gulp.dest('./s/css/min') );
		// .pipe( notify({message:'gulp updated'}) );
	
});

gulp.task('concatCss', function () {
	return gulp.src('./s/css/min/*.css')
		.pipe(concatCss('concat/style.min.css'))
		
		.pipe( minifycss() )
		.pipe( autoprefixer( 'last 10 version' ) )
		.pipe(replace('../../../../../../s/fonts/', '/s/fonts/'))
		.pipe(replace('..//s/fonts/', '/s/fonts/'))
		.pipe(replace('../../../../../../img/', '/s/img/'))
		.pipe( gulp.dest('./s/css/min') )
});

gulp.task('concatJS', function () {
	var d = new Date();
	var date_str = '; console.log(\'рџ•ђ JS last modified: ' 
						+ d.getFullYear() + '.'
						+ ( ( d.getMonth() + 1 ) ).pad() + '.'
						+ ( d.getDate() ).pad() + ' '
						
						+ ( d.getHours() ).pad() + ':'
						+ ( d.getMinutes() ).pad() + ':'
						+ ( d.getSeconds() ).pad() + ''
						
						+ '\');';
	
	return gulp.src( [
			//"./s/js/jquery-2.2.0.js",
			//"./s/js/bootstrap.js",
			"./s/js/project.js",
		] )
		.pipe(fixmyjs())
		.pipe(uglify())
		.pipe(concat("main.min.js"))
		// .pipe(sourcemaps.write())
		.pipe(gulp.dest("./s/js/opt"));
});

gulp.task('checkMyJS', function () {
	return gulp.src("./s/js/project.js")
		.pipe(jshint())
		.pipe(jshint.reporter('default'));
});

gulp.task('imagemin', function () {
	// return gulp.src('./s/img/*.{gif,jpg,png,svg}')
        // .pipe(imagemin({
            // progressive: true,
            // optimizationLevel: 3,
            // svgoPlugins: [{removeViewBox: false}],
            // use: [pngquant()]
        // }))
        // .pipe(gulp.dest('./s/img/opt'));
});

gulp.task('default', function () {
	
	del.sync( './s/css/style.min.css' );
	del.sync( './s/css/main.min.js' );
	
	
	gulp.run( 'allCSS' );
	gulp.run( 'concatJS' );
	// gulp.run('imagemin');
	
	gulp.watch('./s/scss/*.scss', function () {
		
		gulp.run('allCSS');
		console.log('=============== UPDATE SASS');
		
	});
	
	gulp.watch('./s/js/*.js', function () {
		console.log(" ======================== UPDATE JS ======================== ");
		// gulp.run('checkMyJS');
		gulp.run('concatJS');
		
	});

	gulp.watch('./s/img/*.{gif,jpg,png,svg}', function () {
		console.log(" ======================== UPDATE IMG ======================== ");
		// gulp.run('imagemin');
		
	});
	
});


// EOF

HTML

Emmet ( бывш. Zen Coding )

http://emmet.io/

(S)CSS

Селекторы

#айди <strong id="#selected">am i red?</strong> #selected { color: red; }
.класс <strong class="someclass someotherclass">am i red?</strong> .someclass { color: green; }
.someotherclass { zoom:200%; }
тип элемента <strong>am i red?</strong> strong { color: orange; }
положение элемента <strong class="someclass">i am <a href="#">link</a></strong> .someclass > a { text-decoration: underline }
/* SCSS */ .someclass { a { text-decoration: underline } }
атрибут <a href="/" target="_blank">am i red?</a> a[target=_blank] { font-family: 'Verdana'; }
состояние <a href="/" target="_blank">am i red?</a> a:hover { font-weight: bold; }
Список селекторов

Позиционирование

position: absolute | fixed | relative | static | sticky

absolute – Абсолютное позиционирование. Указывает, что элемент абсолютно позиционирован, при этом другие элементы отображаются на веб-странице словно абсолютно позиционированного элемента и нет. Положение элемента задаётся свойствами left, top, right и bottom, также на положение влияет значение свойства position родительского элемента. Так, если у родителя значение position установлено как static или родителя нет, то отсчёт координат ведётся от края окна браузера. Если у родителя значение position задано как relative, то отсчёт координат ведётся от края родительского элемента.

fixed – Фиксированное позиционирование. По своему действию это значение близко к absolute, но в отличие от него привязывается к указанной свойствами left, top, right и bottom точке на экране и не меняет своего положения при прокрутке веб-страницы.

relative – Относительное позиционирование. Положение элемента устанавливается относительно его исходного места. Добавление свойств left, top, right и bottom изменяет позицию элемента и сдвигает его в ту или иную сторону от первоначального расположения.

static – Статичное позиционирование. Элементы отображаются как обычно. Использование свойств left, top, right и bottom не приводит к каким-либо результатам.

sticky – Это сочетание относительного и фиксированного позиционирования. Элемент рассматривается как позиционированный относительно, пока он не пересекает определённый порог, после чего рассматривается как фиксированный. Обычно применяется для фиксации заголовка на одном месте, пока содержимое, к которому относится заголовок, прокручивается на странице. (пока нет, альтернативы)

Остальные свойства

Справочник CSS

Медиа-запросы

------- CSS: ------- 
.myclass {
	color: red;
	
}

@media screen and (max-width: 600em) {

	.myclass {
		color: green;
		
	}
	
}

@media print { /* Стиль для печати */
    body {
		font-family: Times, 'Times New Roman', serif; /* Шрифт с засечками */
		
    }
	
}


------- SCSS -------

.myclass {
	color: red;
	
	@media screen and (max-width: 600em) {
		color: green;
	}
	
}

------- Медиа типы -------

all – стили для всех типов медиа
aural – для синтезаторов речи
braille – для устройств чтения символов Брайля
embossed – для устройств печати символов Брайля
handheld – для портативных устройств
print – для печати на принтере
projection – для демонстрации с помощью проекторов
screen – для показа на экране монитора
tty – для показа на терминалах и телетайпах
tv – для показа на экране ТВ

TEMP