По вашему запросу ничего не найдено :(
Убедитесь, что запрос написан правильно, или посмотрите другие наши статьи:
img
  Введение Типы данных применяются для того, чтобы вы могли классифицировать один конкретный тип данных в языках программирования. Например, число и строка символов – это разные типы данных, которые JavaScript будет обрабатывать по-разному.  Это очень важно, так как тип данных, который вы используете, будет определять, какие значения вы можете присвоить переменной и что вы можете с ней делать. Этим я хочу сказать, что для того, чтобы выполнять операции с переменными в JavaScript, вы должны знать тип данных любой заданной переменной.  В этой статье мы обсудим то, как работают типы данных в JavaScript, а также основные типы данных, которые есть в этом языке. Этот обзор нельзя назвать полноценным, но он поможет вам ознакомиться с дополнительными возможностями JavaScript. Динамическая типизация JavaScript работает с динамическими типами данных, а это значит, что проверка соответствия типов выполняется на этапе выполнения программы, а не на этапе ее компиляции. Типы данных Python также имеют динамическую типизацию. Если мы говорим о динамически типизированных языках, то здесь переменная с одним и тем же именем может хранить разные типы данных. Например, переменная  t , которая была определена с помощью ключевого слова  let (не забывайте, что  let ограничивает область действия переменной), может хранить разные типы данных, а может быть инициализирована, но не определена: let t = 16; // t is a number { // t – это число } let t = "Teresa"; // t is a string { // t – это строка } let t = true; // t is a Boolean { // t – это логическое значение } let t; // t is undefined { // t неопределена } Все переменные, перечисленные выше, могут иметь любой тип данных, который есть в JavaScript; тип этих переменных явно объявлять не требуется.  Числа  В JavaScript есть всего один числовой тип, то есть здесь нет отдельных обозначений для целых чисел и чисел с плавающей точкой. Вследствие этого числа в JavaScript можно записывать как с десятичной точкой, так и без нее: let num1 = 93; let num2 = 93.00; И в том, и в другом случае тип данных – число, и он один и тот же, неважно записано число с десятичной точкой или нет.  В JavaScript можно использовать экспоненциальное представление для того, чтобы сократить очень большие и очень маленькие числа: let num3 = 987e8; // 98700000000 let num4 = 987e-8; // 0.00000987 Точность чисел в JavaScript составляет 15 цифр. Это значит, что после того, как запись числа дойдет до 16-ой цифры, число будет округлено: let num5 = 999999999999999; // remains as 999999999999999 { // остается как 999999999999999 } let num6 = 9999999999999999; // rounded up to 10000000000000000 { // округляется до 10000000000000000 } Помимо обычного представления чисел, у числового типа данных в JavaScript есть три символьных значения: Infinity – числовое значение, которое символизирует  положительное число, близкое к бесконечности. -Infinity – числовое значение, которое символизирует  отрицательное число, близкое к бесконечности. NaN – числовое значение, которое символизирует  нечисло , обозначающее не числовое значение.  Вы получите в качестве результата  Infinity или  -Infinity , если вычислите число, которое находится за пределами диапазона доступных чисел в JavaScript, а также для значений, которые не определены, например, если поделите на ноль: let num7 = 5 / 0; // will return Infinity { // вернет Infinity } let num8 = -5 / 0; // will return –Infinity { // вернет - Infinity } Говоря техническим языком,  Infinity отобразится в том случае, если число превысит значение  1.797693134862315E+308 , которое считается верхним пределом JavaScript.  И аналогично,  -Infinity отобразится в том случае, если число превысит нижний предел, а именно  1.797693134862316E+308 . Кроме того,  Infinity можно использовать в циклах: while (num9 != Infinity) { // Code here will execute through num9 = Infinity       { // Код будет выполняться вплоть до num9 = Infinity } } NaN  будет отображаться для недопустимых чисел. В случае, если вы попытаетесь выполнить какую-нибудь математическую операцию над числом и нечислом, то в качестве результата вы получите  NaN : let x = 20 / "Shark"; // x will be NaN { // переменной x будет присвоено значение NaN } Из-за того, что число  20 нельзя разделить на строку  “Shark” (мы не получим в результате число), возвращаемое значение для переменной  x будет равно  NaN . Однако, если строку можно определить как числовое значение, то математическое выражение вполне может быть выполнено: let y = 20 / "5"; // y will be 4 { // переменной y будет присвоено значение 4 } В примере выше строка  "5" может быть определена как число, именно поэтому JavaScript может ее интерпретировать как число, и оно будет работать с математическим оператором деления.  Если переменной, которая используется в операции, присвоено значение  NaN , то в результате вы также получите значение  NaN , даже если другой операнд является допустимым числом: let a = NaN; let b = 37; let c = a + b;  // c will be NaN { // переменной c будет присвоено значение NaN } В JavaScript есть всего один числовой тип данных. Если вы работаете с числами, то любое число, которое вы введете, будет интерпретировано как тип данных для чисел; и поскольку JavaScript имеет динамическую типизацию, вам не нужно явно объявлять тип данных. Строки Строка – это последовательность, состоящая из одного или нескольких символов (букв, цифр, символов). Строки могут оказаться довольно полезными, так как могут представлять текстовые данные.  В JavaScript строки можно записывать как в одинарных кавычках, так и в двойных. Так что, для того, чтобы создать строку, вам просто нужно заключить последовательность символов в кавычки: let singleQuotes = 'This is a string in single quotes.'; let doubleQuotes = "This is a string in double quotes."; Вы можете использовать любые кавычки (одинарные или двойные), но они должны быть одинаковыми во всей программе.  Программа «Hello, World!» демонстрирует то, как можно использовать строки в программировании. Так, символы, которые составляют фразу  Hello, World! в  alert() ниже, представляют собой строку.   hello.html

Когда мы запустим код и нажмем кнопку  Click me , мы увидим всплывающее окно со следующим текстом: Output Hello, World! По аналогии с другими типами данных, строки можно хранить в переменных: let hw = "Hello, World!"; А также выводить строки, указав в  alert() эту самую переменную: hello.html ... ... Output Hello, World! Существует огромное количество операций, которые можно выполнять со строками, чтобы достичь того результата, который мы хотим. Строки нужны для того, чтобы программа могла передавать информацию пользователю, а пользователь мог передавать информацию обратно в программу. Логические значения Переменные  логического типа данных могут принимать только два значения:  true или  false (истина или ложь). Логические значения используются для того, чтобы представлять значения истинности, которые относятся к логическому разделу математики, используемому в алгоритмах информатики.  Во многих математических операциях ответы можно определить, как истинные или ложные: больше чем 500 > 100 -  true 1 > 5 -  false меньше чем 200 < 400 -  true 4 < 2 -  false равно 5 = 5 -  true 500 = 400 -  false Как и другие типы данных, логические значения можно хранить в переменных: let myBool = 5 > 8; // false Поскольку 5 не больше, чем 8, то это значит, что значение переменной  myBool будет равно  false . По мере того, как вы будете практиковаться и писать все больше и больше программ на JavaScript, вы начнете лучше понимать, как работают логические значения и различные функции и операции, вычисляющие значения  true или  false , которые, в свою очередь, могут в корне изменить ход программы. Массивы Массив может хранить сразу несколько значений в одной переменной. Это значит, что внутри массива вы можете хранить список значений и перебирать их. Значения внутри массива называются  элементами . Обращаться к элементам массива можно с помощью их порядковых номеров.  Строки – это символы в кавычках, а массивы – это значения в квадратных скобках.  Массив строк, например, будет выглядеть вот так: let fish = ["shark", "cuttlefish", "clownfish", "eel"]; Если мы вызовем переменную  fish , то получим следующее: ["shark", "cuttlefish", "clownfish", "eel"] Массивы – это очень гибкий тип данных. Это так, поскольку они являются изменяемы, то есть мы можем добавлять элементы в массив, удалять элементы из массива и менять элементы в массиве.   Объекты Объекты в JavaScript могут хранить несколько значений в виде пар  имя:значение . Таким образом, обеспечивается удобный способ хранения данных и доступа к ним. Синтаксис объектного литерала следующий: пара имя:значение, разделенные двоеточием и заключенные в фигурные скобки. Как правило, объекты используются для того, чтобы хранить какие-то связанные данные, например, информацию, содержащуюся в ID. Если свойств несколько, то объектный литерал записывается следующим образом: let sammy = {firstName:"Sammy", lastName:"Shark", color:"blue", location:"ocean"}; Есть альтернативный вариант, который в особенности касается объектов с большим количеством пар имя:значение, - объекты можно записать в несколько строк с пробелом после каждого двоеточия: let sammy = {    firstName: "Sammy",    lastName: "Shark",    color: "blue",    location: "Ocean" }; Переменная типа объект  sammy во всех примерах выше имеет четыре свойства:  firstName ,  lastName ,  color и  location , и все эти переданные значения разделены двоеточием.  Работа с несколькими типами данных Несмотря на то, что программа, которую вы создаете, будет содержать несколько типов данных, важно не забывать о том, что операции, как правило, вы будете выполнять с одним и тем же типом данных. То есть математические операции вы будете выполнять с числами, а срезы делать у строк.  Если вы используете оператор, который может работать с разными типами данных, например, оператор + (который может складывать числа и объединять строки), то можете получить довольно неожиданные результаты.  Например, если вы будете использовать оператор + с числами и строками одновременно, числа будут интерпретироваться как строки (то есть они будут объединены со строками). Однако порядок типов данных может влиять на то, как они будут объединяться. Таким образом, если вы создадите переменную, которая выполняет следующую операцию объединения, то JavaScript будет интерпретировать все элементы как строки: let o = "Ocean" + 5 + 3; И если вы вызовете переменную  o , то получите вот такой результат: Output Ocean53 Однако, если первым типом данных будет число, то JavaScript сначала сложит эти два числа, а когда программа дойдет до строки  "Ocean" , это число будет проинтерпретировано как строка. Именно поэтому в качестве результата вы получите сумму двух чисел, объединенную со строкой: let p = 5 + 3 + "Ocean"; Output 8Ocean Из-за того, что могут происходить вот такие непредвиденные вещи, вы, вероятнее всего, будете выполнять все операции исключительно с одним типом данных, а не с несколькими. Так или иначе, в отличие от некоторых других языков программирования JavaScript не возвращает ошибок, если вы одновременно используете разные типы данных.  Заключение Теперь вы знаете некоторые основные типы данных, которые есть в JavaScript. Все эти типы могут оказаться полезными при разработке программных проектов на JavaScript.
img
С помощью PHP можно относительно легко создать веб-приложение, и это является одной из причин, по которой он так популярен. Однако, несмотря на простоту его использования, PHP стал довольно навороченным языком с большим количеством различных нюансов и тонкостей, которые могут усложнить процесс отладки для разработчиков так, что они волосы начнут на себе рвать. В этой статье я выделил десять самых распространенных ошибок, которых PHP-разработчикам следует избегать.  Ошибка №1. Оставлять висячие ссылки на массивы после циклов foreach Не знаете точно, как использовать циклы foreach в PHP? Если вы хотите работать с каждым элементом итерируемого массива в отдельности, использование ссылок в циклах foreach может оказаться полезным.  $arr = array(1, 2, 3, 4); foreach ($arr as &$value) {    $value = $value * 2; } // $arr is now array(2, 4, 6, 8) { // $arr – это теперь array(2, 4, 6, 8) } Проблема в том, что, если вы не будете достаточно осторожны, это может привести к некоторым нежелательным побочным эффектам и последствиям. В частности, если мы посмотрим на пример выше, то увидим, что после того, как код выполнится, $value останется в области видимости и будет хранить ссылку на последний элемент массива. Таким образом, если вы будете использовать $value и дальше, то можете случайно изменить последний элемент массива.  Главное запомните, что у foreach нет области видимости. А значит, $value в приведенном выше примере является ссылкой на верхнюю часть сценария. На каждой итерации foreach задает ссылку, которая указывает на следующий элемент массива $array. Так что, после того, как цикл закончит свою работу, $value так и будет указывать на последний элемент $array и останется в области видимости.  Ниже приведен пример ошибок, к которым это может привести. Такие ошибки сбивают с толку и их довольно трудно обнаружить. $array = [1, 2, 3]; echo implode(',', $array), "\n"; foreach ($array as &$value) {}  // by reference { // по ссылке } echo implode(',', $array), "\n"; foreach ($array as $value) {} // by value (i.e., copy) {  // по значению (то есть путем копирования) } echo implode(',', $array), "\n"; Приведенный выше код выдаст следующий результат: 1,2,3 1,2,3 1,2,2 Нет, это не опечатка. Последнее значение в последней строке и правда равно 2, а не 3.  Почему же? После того, как мы прошли первый цикл foreach, $array остается таким же, но, как мы уже говорили выше, $value остается в качестве висячей ссылки на последний элемент $array (так как цикл foreach обращался к $value по ссылке). В результате, когда мы проходим по второму циклу foreach, начинают происходить всякие «странные вещи». В частности, так как теперь мы осуществляем доступ к $value по значению (то есть путем копирования), foreach на каждом шаге цикла копирует каждый последующий элемент массива $array в $value. Таким образом, на каждом шаге второго цикла происходит следующее: Проход 1: $array[0] (т.е. «1») копируется в $value (которое является ссылкой на $array[2]), поэтому теперь $array[2] равен 1. Так что, теперь $array содержит [1, 2, 1].  Проход 2: $array[1] (т.е. «2») копируется в $value (которое является ссылкой на $array[2]), поэтому теперь $array[2] равен 2. Так что, теперь $array содержит [1, 2, 2]. Проход 3: $array[2] (которое теперь равно «2») копируется в $value (которое является ссылкой на $array[2]), так что $array[2] так и остается равным 2. Поэтому теперь $array содержит [1, 2, 2]. Для того, чтобы быть иметь возможность использовать преимущества ссылок в циклах foreach и при этом быть уверенным в том, что подобных проблем возникать не будет, вызовите функцию unset() для переменной сразу после цикла foreach, чтобы удалить ссылку. Например,  $arr = array(1, 2, 3, 4); foreach ($arr as &$value) {    $value = $value * 2; } unset($value); // $value no longer references $arr[3] { // $value уже не является ссылкой на $arr[3] } Ошибка №2. Непонимание того, как ведет себя функция isset() Несмотря на свое название, функция  isset() возвращает значение false не только для несуществующих элементов, но и для нулевых значений (null).  Такое поведение функции может привести к куда более серьезным проблемам, чем кажется на первый взгляд. Именно оно и является довольно-таки распространенным источником проблем.  Давайте посмотрим на следующий фрагмент кода: $data = fetchRecordFromStorage($storage, $identifier); if (!isset($data['keyShouldBeSet']) {    // do something here if 'keyShouldBeSet' is not set    { // если 'keyShouldBeSet' не существует, выполняются действия } } Тот, кто писал этот код, судя по всему, хотел проверить, существует ли keyShouldBeSet в $data. Но, как мы уже говорили ранее, функция isset($data['keyShouldBeSet']) вернет false, даже если $data['keyShouldBeSet'] существует, но имеет значение null. Так что, логика, приведенная выше, неверна. А вот другой пример: if ($_POST['active']) {    $postData = extractSomething($_POST); } // ... if (!isset($postData)) {    echo 'post not active'; } Код выше предполагает, что, если $_POST['active'] возвращает true, то postData обязательно существует, а, значит, isset($postData) вернет true. И наоборот, если isset($postData) возвращает false, то и $_POST['active'] возвращает false.  Но это не так.  Как мы уже говорили, isset($postData) может также вернуть false, если $postData имеет значение null. Таким образом, функция isset($postData) может вернуть false, даже если $_POST['active'] вернул true. И опять получается, что логика, приведенная выше, неверна. К слову, если цель приведенного выше кода на самом деле заключалась в том, чтобы еще раз проверить, возвращает ли $_POST['active'] значение true, прибегнуть к помощи функции isset() было не самым хорошим решением. Вместо этого было бы гораздо лучше просто перепроверить $_POST['active'], то есть: if ($_POST['active']) {    $postData = extractSomething($_POST); } // ... if ($_POST['active']) {    echo 'post not active'; } Правда, говоря о случаях, когда нам нужно проверить, существует ли переменная на самом деле (то есть отличить переменную, которая не существует, от переменной, значение которой равно null), гораздо более надежным решением будет использование метода array_key_exists(). Например, вы могли бы переписать первый пример следующим образом: $data = fetchRecordFromStorage($storage, $identifier); if (! array_key_exists('keyShouldBeSet', $data)) {    // do this if 'keyShouldBeSet' isn't set    { // если ‘keyShouldBeSet’ не существует, то выполняются действия, указанные здесь } } Более того, вы можете проверить, существует ли переменная в текущей области видимости. Для этого вам нужно объединить методы array_key_exists() и get_defined_vars(): if (array_key_exists('varShouldBeSet', get_defined_vars())) {    // variable $varShouldBeSet exists in current scope    { // переменная $varShouldBeSet существует в текущей области видимости } } Ошибка №3. Путаница с возвратом по ссылке и по значению Давайте посмотрим на следующий фрагмент кода: class Config {    private $values = [];    public function getValues() {        return $this->values;    } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test']; Если вы запустите этот код, то получите следующий результат: PHP Notice:  Undefined index: test in /path/to/my/script.php on line 21 Что же не так? Проблема в том, что в коде, который мы привели выше, произошла путаница с возвратом массива по ссылке и по значению. Если вы явно не укажите PHP, чтобы он вернул массив по ссылке (то есть с помощью &), то он по умолчанию вернет массив «по значению». Это значит, что будет возвращена копия массива, и, как следствие, вызываемая функция и вызывающая сторона будут обращаться к разным экземплярам массива.  Так что, вызываемая выше функция getValues() возвращает копию массива $values, а не ссылку на него. Помня об этом, вернемся к двум ключевым строкам из примера выше: // getValues() returns a COPY of the $values array, so this adds a 'test' element // to a COPY of the $values array, but not to the $values array itself. { // функция getValues() возвращает КОПИЮ массива $values, а значит, что элемент test добавляется в КОПИЮ массива $values, в не в сам массив. } $config->getValues()['test'] = 'test'; // getValues() again returns ANOTHER COPY of the $values array, and THIS copy doesn't // contain a 'test' element (which is why we get the "undefined index" message). { // функция getValues() снова возвращает ДРУГУЮ КОПИЮ массива $values, и в ЭТОЙ копии нет элемента test (собственно говоря, почему мы и получаем сообщение о неопределенном индексе) } echo $config->getValues()['test']; Одно из возможных решений этой проблемы заключается в следующем: вы можете сохранить первую копию массива $values, которую возвращает функция getValues(), и дальше работать с этой копией. Например,  $vals = $config->getValues(); $vals['test'] = 'test'; echo $vals['test']; Этот код сработает отлично, то есть он выведет элемент test, не выдавая сообщения о неопределенном индексе. Однако такой подход может оказаться непригодным; это зависит от ваших целей. В частности, с помощью этого кода вы не сможете изменить исходный массив $values. Так что, если вы хотите, чтобы изменения (например, добавление элемента test) касались также исходного массива, вам нужно изменить функцию getValues() так, чтобы она возвращала ссылку на сам массив $values. Вы можете это сделать, добавив & перед именем функции, показывая таким образом, что она должна вернуть ссылку: class Config {    private $values = [];    // return a REFERENCE to the actual $values array    { // возвращает ССЫЛКУ на фактический массив $values }    public function &getValues() {        return $this->values;    } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test']; Как мы и хотели, результатом будет test.  Но давайте запутаем ситуацию еще больше. Рассмотрим следующий код: class Config {    private $values;    // using ArrayObject rather than array    { // используем ArrayObject вместо массива }    public function __construct() {        $this->values = new ArrayObject();    }    public function getValues() {        return $this->values;    } } $config = new Config(); $config->getValues()['test'] = 'test'; echo $config->getValues()['test']; Если вы считаете, что здесь мы получим ту же ошибку с неопределенным индексом (что и в предыдущем примере), то вы ошибаетесь. На самом деле, этот код будет отлично работать. Это связано с тем, что PHP всегда передает объекты по ссылке (в отличие от массивов). (ArrayObject – это объект SPL, который полностью имитирует использование массивов, но при этом работает как объект.)  Как вы могли заметить, в PHP не всегда можно сразу понять, работаете вы с копией или с ссылкой. Поэтому важно знать, как ведут себя те или иные объекты по умолчанию (т.е. то, что переменные и массивы передаются по значению, а объекты – по ссылке). Кроме того, стоит хорошо изучить документацию API для функции, которую вы вызываете, чтобы понимать, что она возвращает (значение, копию массива, ссылку на массив или ссылку на объект). При этом стоит отметить, что возвращение ссылок на массив или ArrayObject, как правило, считается не самой лучшей практикой, так как вызывающая сторона может изменить частные данные экземпляра. Это противоречит понятию инкапсуляции. Вместо этого лучше использовать старомодные геттеры и сеттеры. Например, class Config {    private $values = [];       public function setValue($key, $value) {        $this->values[$key] = $value;    }       public function getValue($key) {        return $this->values[$key];    } } $config = new Config(); $config->setValue('testKey', 'testValue'); echo $config->getValue('testKey');  // echos 'testValue' При таком подходе вызывающая сторона может задать или получить любое значение массива, не предоставляя общий доступ к самому массиву $values, который скрыт для других.  Ошибка №4. Выполнение запросов внутри цикла Нередко можно столкнуться с чем-то подобным, что приводит к тому, что ваш PHP-код не работает: $models = []; foreach ($inputValues as $inputValue) {    $models[] = $valueRepository->findByValue($inputValue); } По сути, здесь все верно, но если вы проследите за логикой кода, то можете обнаружить, что безвредный на первый взгляд вызов $valueRepository->findByValue() в итоге приводит к появлению некоторого запроса: $result = $connection->query("SELECT `x`,`y` FROM `values` WHERE `value`=" . $inputValue); В результате, с каждой итерацией будет выполняться запрос к базе данных. Поэтому, если вы передадите в цикл массив из 1000 элементов, то он создаст 1000 отдельных запросов к базе данных! А если этот сценарий будет вызываться в нескольких потоках, то в теории это может привести к полной остановке системы.   Именно поэтому очень важно уметь распознавать, когда ваш код создает запросы, и по возможности собирать эти значения, чтобы выполнить один запрос, который вернет все результаты.  Существуют довольно распространенные ситуации, когда запросы выполняются неэффективно (т.е. в цикле). Например, когда отправляется форма со списком значений (например, идентификаторов). После чего код будет перебирать массив в цикле и выполнять отдельный SQL-запрос для каждого идентификатора, чтобы получить полные данные записи для каждого из них. В большинстве случаев это будет выглядеть следующим образом: $data = []; foreach ($ids as $id) {    $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` = " . $id);    $data[] = $result->fetch_row(); } Но то же самое можно выполнить гораздо более эффективным способом. Вам понадобиться всего один SQL-запрос: $data = []; if (count($ids)) {    $result = $connection->query("SELECT `x`, `y` FROM `values` WHERE `id` IN (" . implode(',', $ids));    while ($row = $result->fetch_row()) {        $data[] = $row;    } } Таким образом, очень важно уметь распознавать, выполняет ваш код запросы напрямую или нет. По возможности соберите значения, а потом, чтобы получить все результаты, выполните единый запрос. И все же даже здесь следует быть осторожным. И здесь мы плавно переходим к следующей распространенной ошибке… Ошибка №5. Ложное и нерациональное использование памяти Несмотря на то, что извлечение множества записей за раз куда как более эффективно, чем выполнение запросов для каждой строки, которую вы хотите извлечь, если вы используете расширение PHP mysql и библиотеку libmysqlclient, то такой подход в теории может привести к ситуации под названием «недостаточно памяти».  Чтобы понять, о чем я говорю, давайте посмотрим на пример с ограниченными ресурсами (512 Мб оперативной памяти), MySQL и php-cli. Давайте загрузим таблицу базы данных: // connect to mysql { // подключаемся к MySQL } $connection = new mysqli('localhost', 'username', 'password', 'database'); // create table of 400 columns { // создаем таблицу из 400 столбцов } $query = 'CREATE TABLE `test`(`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT'; for ($col = 0; $col < 400; $col++) {    $query .= ", `col$col` CHAR(10) NOT NULL"; } $query .= ');'; $connection->query($query); // write 2 million rows { // добавляем 2 миллиона строк } for ($row = 0; $row < 2000000; $row++) {    $query = "INSERT INTO `test` VALUES ($row";    for ($col = 0; $col < 400; $col++) {        $query .= ', ' . mt_rand(1000000000, 9999999999);    }    $query .= ')';    $connection->query($query); } Окей, а теперь давайте проверим, как используются ресурсы: // connect to mysql { // подключаемся к MySQL } $connection = new mysqli('localhost', 'username', 'password', 'database'); echo "Before: " . memory_get_peak_usage() . "\n"; $res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 1'); echo "Limit 1: " . memory_get_peak_usage() . "\n"; $res = $connection->query('SELECT `x`,`y` FROM `test` LIMIT 10000'); echo "Limit 10000: " . memory_get_peak_usage() . "\n"; Вот так выглядит результат: Before: 224704 Limit 1: 224704 Limit 10000: 224704 Отлично. Выглядит так, как будто с точки зрения ресурсов запрос благополучно управляется внутри.  Но, чтобы убедиться наверняка, давайте увеличим значение LIMIT до 100 000. Опа, и вот что мы получим: PHP Warning:  mysqli::query(): (HY000/2013):              Lost connection to MySQL server during query in /root/test.php on line 11 Что же произошло? Здесь проблема заключается в том, как работает модуль PHP mysql. По сути это просто прокси для libmysqlclient, который выполняет всю грязную работу. Когда часть данных уже выбрана, они попадаю непосредственно в память. А так как эта память никак не управляется менеджером PHP, функция memory_get_peak_usage() не сообщит нам о том, что количество использованных ресурсов возросло, так как мы увеличиваем LIMIT в нашем запросе. Это приводит к проблемам, о которых мы говорили выше. Получается, что нас обманным путем пытаются уверить, что с нашим управлением памятью все в порядке. Но по сути наше управление памятью далеко не идеально, и мы можем столкнуться с проблемами (такими как мы описывали выше).  Во всяком случае, вы можете избежать ситуаций с ложным использованием памяти, описанных выше. Для этого вам нужно использовать модуль mysqlnd. Хотя сам по себе он вам никак не поможет оптимизировать использование вашей памяти. Этот модуль скомпилирован как собственное расширение PHP, и он использует диспетчер памяти PHP. Таким образом, если мы запустим тестовый код, приведенный выше, и вместо mysql воспользуемся mysqlnd, то получим гораздо более реалистичную картину использования нашей памяти: Before: 232048 Limit 1: 324952 Limit 10000: 32572912 И кстати, она будет куда хуже. Если обратиться к документации PHP, то там можно найти, что mysql использует в два раза больше ресурсов для хранения данных, чем mysqlnd. Именно поэтому исходный сценарий, в котором мы использовали mysql, на самом деле использовал даже больше памяти, чем продемонстрировано здесь (примерно в два раза больше).  Дабы избежать такого рода проблем, попробуйте ограничить размер ваших запросов или использовать цикл с меньшим количеством итераций. Например, $totalNumberToFetch = 10000; $portionSize = 100; for ($i = 0; $i <= ceil($totalNumberToFetch / $portionSize); $i++) {    $limitFrom = $portionSize * $i;    $res = $connection->query(                         "SELECT `x`,`y` FROM `test` LIMIT $limitFrom, $portionSize"); } Если мы рассуждаем об этой ошибке и ошибке №4, то должны понимать, что необходимо найти золотую середину между слишком детализированными и повторяющимися запросами и огромными штучными запросами. Как и почти во всем в этой жизни, необходим баланс; любая крайность может навредить корректной работе PHP.   Ошибка №6. Игнорирование проблем с Unicode/UTF-8 В некотором смысле это скорее проблема самого PHP, нежели что-то, с чем вы столкнетесь при отладке кода. Однако эту проблему так толком и не решили. Ядро PHP 6 должно было поддерживать Unicode, но, так как разработка PHP 6 была приостановлена в 2010 году, с этим пришлось повременить.  Но это вовсе не значит, что разработчики могут игнорировать правильную обработку UTF-8 и думать, что все строки обязательно будут «старым добрым ASCII». Код, который не может правильно обработать не-ASCII строки, печально известен внедрением опасных гейзенбагов. Даже простые вызовы strlen($_POST['name']) могут вызвать проблемы, если некто с фамилией Schr?dinger захочет зарегистрироваться в вашей системе.  Вашему внимаю предоставляю небольшой чек-лист, который поможет вам избежать таких проблем в вашем коде: Если вы мало что знаете о Unicode и UTF-8, то изучите, по крайней мере, основы.  Вместо старых строковых функций используйте функции mb_* (убедитесь, что в вашей сборке PHP есть расширение multibyte). Убедитесь, что ваша база данных и таблицы настроены таким образом, что могут использовать Unicode (многие сборки MySQL все еще используют по умолчанию latin1). Помните, что json_encode() преобразует не-ASCII символы (например, Schr?dinger преобразуется в Schr\u00f6dinger), а serialize() - нет.  Убедитесь, что ваши файлы с PHP-кодом имеют кодировку UTF-8. Это необходимо, чтобы избежать конфликтов при объединении строк с жестко запрограммированными или сконфигурированными строковыми постоянными. Ошибка №7. Предполагать, что $_POST всегда будет содержать ваши данные POST-запроса Несмотря на свое название, массив $_POST не всегда будет содержать ваши данные POST-запроса. Он и вовсе может оказаться пустым. Чтобы разобраться в этом, давайте рассмотрим пример. Допустим, что мы делаем запрос к серверу, вызвав jQuery.ajax(): // js $.ajax({    url: 'http://my.site/some/path',    method: 'post',    data: JSON.stringify({a: 'a', b: 'b'}),    contentType: 'application/json' }); (К слову, обратите внимание, что здесь есть contentType: 'application/json'. Мы отправляем данные в формате JSON, что довольно популярно для API. Это стандартная практика, например, при добавлении службы $http в AngularJS.) Мы просто распечатываем массив $_POST на серверной стороне: // php var_dump($_POST); Сюрприз! А вот и результат: array(0) { } Но почему? Что случилось со строкой JSON {a: 'a', b: 'b'}? Ответ таков: PHP анализирует полезную нагрузку POST-запроса автоматически только в том случае, если она имеет тип содержимого application/x-www-form-urlencoded или multipart/form-data. Причины такого поведения исторически сложившиеся – много лег назад, когда $_POST был только реализован в PHP, это были единственные типы содержимого. Таким образом, PHP не загружает полезную нагрузку POST-запроса автоматически ни для каких других типов содержимого (даже для тех, которые считаются довольно популярными на сегодняшний день, например, application/json). Так как $_POST - это суперглобальная переменная, то переопределив ее один раз (желательно в самом начале нашего сценария), мы сможем пользоваться этим измененным значением (т.е. в том числе и полезной нагрузкой POST-запроса) во всем нашем коде. Это очень важно, так как фреймворки PHP и почти все пользовательские сценарии используют $_POST для того, чтобы извлекать и преобразовывать данные запроса.  Поэтому, если мы, например, обрабатываем полезную нагрузку POST-запроса с типом содержимого application/json, то мы должны вручную проанализировать содержимое запроса (т.е. декодировать данные JSON) и переопределить переменную $_POST: // php $_POST = json_decode(file_get_contents('php://input'), true); После чего, когда мы распечатаем массив, мы увидим, что он должным образом содержит полезную нагрузку POST-запроса: array(2) { ["a"]=> string(1) "a" ["b"]=> string(1) "b" } Ошибка №8. Думать о том, что PHP поддерживает символьный тип данных Посмотрите на следующий фрагмент кода и попробуйте догадаться, что он выведет: for ($c = 'a'; $c <= 'z'; $c++) {    echo $c . "\n"; } Если вы ответили, что он выведет буквы от «a» до «z», то, вы будете удивлены – это не так. Да, он выведет буквы от «а» до «z», но потом он также выведет сочетания букв от «аа» до «yz». Давайте разберемся, почему так происходит. В PHP нет такого типа данных как char; есть только тип string. Принимая это в расчет, мы должны понимать, что, увеличив строку z, PHP выдаст aa: php> $c = 'z'; echo ++$c . "\n"; aa Впрочем, запутаю вас еще больше: с лексикографической точки зрения aa меньше, чем z: php> var_export((boolean)('aa' < 'z')) . "\n"; true Именно поэтому пример кода, который мы привели в начале, сначала напечатает буквы от a до z, а потом также напечатает сочетания букв от aa до yz. Он остановится, когда достигнет значения za – первого значения, которое «больше», чем z: php> var_export((boolean)('za' < 'z')) . "\n"; false А раз так, то вот один из способов, как можно правильно перебрать в PHP значения от «а» до «z»: for ($i = ord('a'); $i <= ord('z'); $i++) {    echo chr($i) . "\n"; } Или вот так: $letters = range('a', 'z'); for ($i = 0; $i < count($letters); $i++) {    echo $letters[$i] . "\n"; } Ошибка №9. Игнорирование стандартов написания кода И хотя игнорирование стандартов написания кода не приводит сразу к необходимости отлаживать PHP-код, это все же, пожалуй, одна из самых важных тем, которую стоит здесь обсудить.  Если вы будете игнорировать эти стандарты, то можете столкнуться с целым рядом проблем в своем проекте. В самом лучшем случае это приведет к тому, что ваш код будет непоследовательным (так как каждый разработчик «занимается своим делом»). А в худшем случае вы получите PHP-код, который не будет работать или будет слишком сложен для перемещения по нему (в некоторых случаях это может быть практически невозможно), что, соответственно, приводит к тому, что его будет крайне сложно отлаживать, совершенствовать и сопровождать. А это значит, что продуктивность вашей команды снизится, а все из-за того, что вы будете тратить свои силы впустую.  К счастью для PHP-разработчиков, существуют Рекомендации по стандартам PHP (PSR - PHP Standards Recommendation). В ней есть пять стандартов: PSR-0: Стандарт автозагрузки PSR-1: Базовый стандарт оформления кода PSR-2: Рекомендации по оформлению кода PSR-3: Интерфейс протоколирования PSR-4: Улучшенная автозагрузка Изначально PSR был создан на базе материалов, полученных от лиц, сопровождающих наиболее узнаваемые на рынке платформы. Свой вклад внесли Zend, Drupal, Symfony, Joomla и прочие, и теперь они придерживаются этих стандартов. И даже PEAR, который много лет назад делал попытку стать стандартом, теперь является частью PSR. В некоторым смысле практически неважно, какой стандарт вы используете, если вы принимаете его «условия игры» и придерживаетесь его. Однако, лучше всего придерживаться именно PSR, если у вас нет каких-то веских причин этого не делать. Все больше и больше команд и проектов соответствуют требованиям PSR. На данный момент большая часть разработчиков PHP абсолютно точно признала его Стандартом, поэтому, используя его, вы можете быть уверены, что новым разработчикам, которые будут присоединяться к вашей команде, этот стандарт будет знаком, и они будут чувствовать себя комфортно.  Ошибка №10. Неправильное использование метода empty() Некоторые разработчики PHP очень любят использовать метод empty() для логических проверок практически всего. Однако бывают моменты, когда это может привести к путанице. Для начала давайте вернемся к массивам и экземплярам ArrayObject (которые имитируют массивы). Учитывая их сходство, можно предположить, что массивы и экземпляры ArrayObject будут вести себя одинаково. Но это далеко не так. Например, вот что происходит в PHP 5.0: // PHP 5.0 or later: { // PHP 5.0 и более поздние версии: } $array = []; var_dump(empty($array));  // outputs bool(true) { // выводит bool(true) } $array = new ArrayObject(); var_dump(empty($array));  // outputs bool(false) { // выводит bool(false) } // why don't these both produce the same output? { // почему мы получаем разный результат? } А знаете, что еще хуже? Для более ранних версий PHP (до 5.0) результаты были бы другими: // Prior to PHP 5.0: { // версии, предшествующие PHP 5.0: } $array = []; var_dump(empty($array));  // outputs bool(false) { // выводит bool(false) } $array = new ArrayObject(); var_dump(empty($array));  // outputs bool(false) { // выводит bool(false) } Как ни печально, но такой подход довольно популярен. Например, как предполагает документация, Zend\Db\TableGateway из Zend Framework 2 именно таким образом возвращает данные при вызове current() для результата TableGateway::select(). Разработчик может легко допустить ошибку, используя такие данные.  Для того, чтобы избежать таких проблем, лучше всего для проверки пустых массивов использовать функцию count(): // Note that this work in ALL versions of PHP (both pre and post 5.0): { // Обратите внимание, что это работает во ВСЕХ версиях PHP (и тех, что были до 5.0, и тех, что были после 5.0) } $array = []; var_dump(count($array));  // outputs int(0) { // выводит int(0) } $array = new ArrayObject(); var_dump(count($array));  // outputs int(0) { // выводит int(0) } И к слову, так как PHP преобразует 0 в false, функцию count() можно использовать для проверки пустых массивов в условной конструкции if (). Также стоит отметить, что эта функция имеет постоянную сложность (то есть O(1)), что еще больше доказывает тот факт, что это правильный выбор.  Есть еще один пример, когда метод empty() может быть опасной. Речь идет о ее совместном использовании с функцией класса Magic – функцией __get(). Давайте определим два класса и свойство test в каждом из них.  Для начала давайте определим класс Regular, у которого свойство test - это обычное свойство: class Regular { public $test = 'value'; } А теперь давайте определим класс Magic, который для доступа к свойству test использует оператор __get(): class Magic { private $values = ['test' => 'value']; public function __get($key) { if (isset($this->values[$key])) { return $this->values[$key]; } } } Окей, а теперь давайте посмотрим, что будет происходить, когда мы попытаемся получить доступ к свойству test каждого из этих классов: $regular = new Regular(); var_dump($regular->test);  // outputs string(4) "value" { // выводит string(4) “value” } $magic = new Magic(); var_dump($magic->test);  // outputs string(4) "value" { // выводит string(4) “value” } Пока все хорошо. А теперь давайте посмотрим, что будет происходить, когда вы вызовем метод empty() для каждого из эти классов: var_dump(empty($regular->test));  // outputs bool(false) { // выводит bool(false) } var_dump(empty($magic->test));  // outputs bool(true) { // выводит bool(true) } Ах ты! Так что, если мы прибегаем к помощи метода empty(), то мы можем прийти в неверным выводам, полагая, что свойство test ($magic) пусто, хотя на самом деле у него есть значение - 'value'. Увы, но если класс для получения значения свойства использует функцию __get(), то нет какого-то беспроигрышного способа проверить, является ли свойство пустым или нет. Если вы находитесь вне области видимости класса, то вы сможете лишь проверить, вернется ли нулевое значение (null), и это не обязательно значит, что соответствующее свойство не существует, ему просто могли присвоить значение null. Для сравнения, если мы попытаемся обратиться к несуществующему свойству экземпляра класса Regular, то вы получим вот такое уведомление: Notice: Undefined property: Regular::$nonExistantTest in /path/to/test.php on line 10 Call Stack:    0.0012 234704 1. {main}() /path/to/test.php:0 Так что, главное, что нужно запомнить, что метод empty() стоит использовать аккуратно, так как в противном случае вы можете получить довольно запутанные (или даже неправильные) результаты.  Заключение Простота использования PHP может усыпить бдительность разработчиков, внушив им ложное чувство комфорта. Однако из-за некоторых нюансов и особенностей языка разработчики могут попасть в ловушку под названием «утомительная отладка». В результате, PHP-код может просто не заработать или могут возникнуть проблемы, о которых мы говорили в этой статье.  PHP неплохо эволюционировал за свою 20-летнюю историю. Так что, вам определенно стоит ознакомиться с его тонкостями. Вы сможете создавать более масштабируемое, более надежное программное обеспечение, которое будет удобно в сопровождении. 
img
Доскональное понимание принципов работы межсетевых экранов (брандмауэров) и относящихся к ним технологий крайне необходимо для любого человека, который желает развиваться в сфере информационной безопасности. Так же это помогает настраивать и управлять системой информационной безопасности правильно и с минимальным количеством ошибок. Слово «межсетевой экран» как правило обозначает систему или устройство, которое находится на границе между внутренней(доверенной) сетью и внешней. Несколько различных межсетевых экранов предлагают пользователям и приложениям особые политики управления безопасностью для различных угроз. Так же они часто обладают способностью записи событий, для предоставления системному администратору возможности идентифицировать, изучить, проверить и избавиться от угрозы. Кроме того, несколько программных продуктов могут запускаться на рабочей станции только для защиты конкретной машины. Сетевые брандмауэры обладают несколькими ключевыми особенностями, для того что бы обеспечивать защиту сети по ее периметру. Основной задачей сетевого брандмауэры является запрет или разрешение на пропуск траффика, который попадает в сеть, основываясь на предварительно настроенных политиках. Ниже перечислены процессы, позволяющие предоставлять или блокировать доступ траффику: Однокритериальные (простые) методики фильтрации пакетов Многокритериальные методики фильтрации пакетов Прокси-серверы Проверка состояния пакетов Трансляция сетевого адреса Методы фильтрации пакетов Основная цель пакетных фильтров – просто контроль доступа к отдельным сегментам сети путем определения разрешенного трафика. Фильтры, как правило, исследуют входящий трафик на 2 уровне модели OSI (транспортном). К примеру, пакетные фильтры способны анализировать пакеты TCP и UDP и оценивать их по ряду критериев, которые называются листами контроля доступа. Они проверяют следующие элементы внутри пакета: Исходящий сетевой адрес Адрес назначения Исходящий порт Порт назначения Протокол Различные брандмауэры основанные на технике пакетной фильтрации так же могут проверять заголовки пакетов для определения источника пакета – т.е из какой сессии он появился: новой или уже существующий. Простые методики фильтрации пакетов, к сожалению, имеют определенные недостатки: Листы контроля доступа могут быть крайне велики и трудны для управления Их можно обойти путем подмены пакетов, злоумышленник может послать пакет, в заголовке которого будет разрешенный листом контроля доступа сетевой адрес. Очень многие приложения могут постоянно строить множественные соединения со случайно используемыми портами. Из-за этого становится действительно тяжело определить какие порты будут использованы после установления соединения. К примеру, таким приложением являются различные мультимедиа программы – RealAudio, QuickTime и прочие. Пакетные фильтры не воспринимают протоколы выше транспортного и их специфику, связанную с каждым конкретным приложением и предоставление такого доступа с использованием листов контроля доступа, является очень трудоёмкой задачей. Прокси-серверы Прокси-серверы — это устройства, которые являются промежуточными агентами, которые действуют от имени клиентов, которые находятся в защищенной или частной сети. Клиенты на защищенной стороне посылают запросы на установление соединения к прокси-серверу для передачи информации в незащищенную сеть или в Интернет. Соответственно, прокси-сервер или приложение совершает запрос от имени внутреннего пользователя. Большинство прокси брандмауэров работает на самом верхнем, седьмом уровне модели OSI (прикладном) и могут сохранять информацию в кэш-память для увеличения их производительности. Прокси-технологии могут защитить сеть от специфических веб-атак, но в общем и целом они не являются панацеей, и, кроме того, они плохо масштабируются. Трансляция сетевого адреса Некоторые устройства, работающие на третьем уровне(сетевом) могут совершать трансляцию сетевых адресов, или NAT (Network Address Translation). Устройство третьего уровня транслирует внутренний сетевой адрес хоста в публичный, который может маршрутизироваться в сети Интернет. В следствие малого числа сетевых адресов в протоколе IP, данная технология используется повсеместно. Брандмауэры с проверкой состояния пакетов Такие межсетевые экраны имеют дополнительные преимущества по сравнению с брандмауэрами с однокритериальной пакетной фильтрацией. Они проверяют каждый пакет, проходящий через их интерфейсы на корректность. Они исследуют не только заголовок пакета, но и информацию с прикладного уровня и полезную загрузку пакета. Таким образом, возможно создание различных правил, основанных на различных типах трафика. Такие брандмауэры так же позволяют контролировать состояние соединения и имеют базу данных с данной информацию, которая так же называется «база данных состояний». В ней описываются состояния соединений, т.е такие как «установлено», «закрыто», «перезапуск», «в процессе согласования». Такие брандмауэры хорошо защищают сеть от различных сетевых атак. Количество различных брандмауэров велико, и в настоящее время в них совмещаются различные техники предотвращения атак. Главное – сеть всегда должна находиться под защитой. Однако нельзя забывать, что не стоит увлекаться, и тратить на защиту информации больше средств, чем стоит сама информация.
ЗИМНИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59