PHP MySQL гениальные решения

MYSQL ВЫБОРКА СЛУЧАЙНЫХ ЗАПИСЕЙ (SQL ВЫБРАТЬ ЗАПИСИ РАНДОМОМ

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

  SELECT * FROM таблица WHERE 'some shit' ORDER BY RAND(NOW()) LIMIT n

где n - количество случайных строк

КАПЧА

Реализация capcha.rar

СОЗДАЁМ ОДНОМЕРНЫЙ МАССИВ ИЗ МНОГОМЕРНОГО

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

$arrIn = array(
  array('a','b','c','d'),
  array(1,2,3),
  array('e',4,'f',5),
);

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

$arrOut = array();
foreach($arrIn as $subArr){
  $arrOut = array_merge($arrOut,$subArr);
}

Можно конечно и поизвращаться, но больше для расширения кругозора, чем для реальной практики в данной задаче. Будем использовать две функций: array_map() - применяет callback-функцию к каждому элементу массива и функцию array_merge(), которую рассматривали выше.

$arrOut = array();
array_map(function($a) use(&$arrOut){
  $arrOut = array_merge($arrOut,$a);
}, $arrIn);

Немного расшифрую: в качестве callback-функции, мы используем анонимную функцию, которая получает поочерёдно каждый из вложенных массивов в переменную "$a" и сливает с массивом "$arrOut", который в итоге и будет содержать все значения исходного массива, но уже как одномерным. Так как внутри callback-функции массив "$arrOut" будет не виден (если только он не объявлен глобальным), мы используем ключевое слово use, которое позволяет использовать внешние переменные. Но передаём массив не как значение, а как "ссылку", поставив перед переменной символ амперсанда "&". Если бы мы этого не сделали, то каждый раз, мы получали бы пустой массив "$arrOut", который сливали с текущим "$a". Для новичков всё это пока малопонятно и, пытаясь адаптировать код под свои нужды, могут наделать ошибок. Не пугайтесь - есть решение "в пару строк";) Используем функцию call_user_func_array(), которая, по сути, сделает всё то, что мы делали во втором примере, но, так сказать, одним махом. $arrOut = call_user_func_array('array_merge', $arrIn); В результате всех вышеупомянутых вариантов, мы получим массив такого вида:

Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => d
    [4] => 1
    [5] => 2
    [6] => 3
    [7] => e
    [8] => 4
    [9] => f
    [10] => 5
)

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

$arrOut = array();
foreach($arrIn as $subArr){
  $arrOut = array_merge($arrOut,array_values($subArr));
}

Остаётся выяснить, какой из трёх вариантов работает быстрее. Я сгенерировал массив, состоящий из сотни вложенных массивов, в каждом из которых по десять элементов и вот средние результаты, которые говорят сами за себя:
Способ секунд
Цикл + array_merge 0.0109
array_map + array_merge 0.0170
call_user_func_array 0.0009
С двумерными массивами немного разобрались, теперь перейдём к более сложной задаче - трехмерные (и более) массивы или многоуровневые массивы с неизвестной вложенностью. И первое решение, которое напрашивается - это использование рекурсии.

$arrIn = array( // исходный массив
  'A' => array('first',2,3),
  'B' => array(
    'b1' => array(4,5,6,7),
    'b2' => array('a','b','c')
  ),
  'C' => array(
    0 => array(8,9),
    1 => array(
      'c01' => array(
        array(10,11,12),
        'last'
      )
    )
  )
);
function makeSingleArray($arr){
  if(!is_array($arr)) return false; 
  $tmp = array();
  foreach($arr as $val){
    if(is_array($val)){
      $tmp = array_merge($tmp, makeSingleArray($val)); 
    } else {
      $tmp[] = $val;
    }
  }
  return $tmp;
}
$arrOut = makeSingleArray($arrIn);

Вполне нормальный способ, который, на мой взгляд, не требует каких-то подробных разъяснений и хорошо справляется со своей задачей. Многие, даже не задумываясь, именно его бы и применили. Но я хочу показать, как это же можно сделать буквально парой строк кода, используя "итераторы" из стандартной библиотеки PHP (SPL):

$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($arrIn));
$arrOut = iterator_to_array($iterator, false);

Вот и всё! И результат будет, как и в первом случае такой:

Array
(
    [0] => first
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
    [6] => 7
    [7] => a
    [8] => b
    [9] => c
    [10] => 8
    [11] => 9
    [12] => 10
    [13] => 11
    [14] => 12
    [15] => last
)

Ну, и как в первом случае, показываю среднюю скорость выполнения двух вариантов:
Способ секунд
Рекурсия 0.2558
Классы итераторов 0.0346
Разница в скорости - более, чем в семь раз и кода меньше примерно во столько же! Дальнейшие комментарии излишни... ;) В общем, вывод можно сделать следующий: создать одномерный массив из многомерного можно достаточно легко, но если еще и хорошо порыться в документации, то "не изобретая свой велосипед", это можно сделать буквально несколькими строками кода и работать будет лучше и быстрее.

НАЙТИ МАКСИМАЛЬНО БЛИЗКОЕ ЗНАЧЕНИЕ

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

function closestNum($arr,$num){
  $res = false;
  sort($arr);
  $less = array_filter($arr, function($a) use ($num){return $a <= $num;});
  $greater = array_filter($arr, function($a) use ($num){return $a > $num;});
  $min = end($less);
  $max = reset($greater);
  $avg = (($min + $max) / 2);
  $res = 'Число '.$num.' - ';
  if($min && $max){
    if($num == $min) {
      $res .= ' равно числу '.$min;
    } else {
      $res .= $num == $avg ? 'ровно между '.$min.' и '.$max : 'ближе к '.($num > $avg ? $max : $min);
    }
  } else {
    $res .= ' ближе всего к '.($min ? $min : $max);
  }
  return $res;
}
// Тестируем
$arr = array(755, 100, 320, 410, 200, 555);
echo closestNum($arr,5); // Число 5 - ближе всего к 100
echo closestNum($arr,320); // Число 320 - равно числу 320
echo closestNum($arr,100.1); // Число 100.1 - ближе к 100
echo closestNum($arr,150); // Число 150 - ровно между 100 и 200
echo closestNum($arr,10000); // Число 10000 - ближе всего к 755

В данном примере, я расписываю ответ функции для наглядности, но а в реальном случае, вам нужно только возвращать само число, которое будет ближе всего к заданному. Теперь давайте пошагово пробежимся по тому, что происходит в этой функции: Сортируем исходный массив - sort() Фильтруем массив с помощью функции array_filter(). В первом случае, в переменную "$less" попадут все значения, которые меньше или равны указанному числу "$num", а во втором случае, в переменную "$greater" - только те значения, которые больше числа "$num" Из первого отфильтрованного массива ("$less"), мы вытаскиваем последний элемент в переменную "$min", а из второго ("$greater") - первый элемент в переменную "$max" с помощью функций end() и reset() соответственно. Находим среднее арифметическое чисел "$min" и "$max" и присваиваем переменной "$avg", которая нам поможет при сравнении дальше. Если визуализировать процесс, то получится что-то вроде такого: исходный массив содержит числа "5,2,1,4,3", ищем ближайшее к числу "3", после сортировки массив - "1,2,3,4,5", первый отфильтрованный массив - "1,2,3", второй - "4,5", из первого массива получаем "3", а из второго - "4". Теперь остаётся сравнивать значения, но это, я так думаю, что описывать не нужно ;) Единственное, что нужно отметить - это то, что для версии PHP 5.2 и ниже, функцию array_filter() надо будет записать немного по-другому, т.к. в этих версиях нет поддержки use():

/* ... */
$less = array_filter($arr, create_function('$a, $b='.$num, 'return $a <= $b;'));
$greater = array_filter($arr, create_function('$a, $b='.$num, 'return $a >= $b;'));
/* ... */

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

function closestNum($arr,$num){
  $tmp = array();
  foreach($arr as $val){
    $tmp[$val] = abs($val - $num);
  }
  asort($tmp);
  return key($tmp);
}

Мы находим модуль разницы каждого значения массива и заданного числа - abs(), записываем полученное во временный массив "$tmp", где ключами элементов будут сами значения массива, сортируем массив "$tmp" сохраняя ключи - asort() - и возвращаем ключ - key() - первого элемента. В этом случае, если в массиве были значения 100 и 200, а заданное число - 150, то функция нам вернет 100. В остальном, мы получим такой же результат, как и в первом варианте. А теперь, делая плавный переход, я бы хотел показать интересный способ нахождения ближайшего значения на MySQL, который строится по тому же принципу, как и во втором примере:

SELECT 
  `num` 
FROM 
  `numbers`
ORDER BY ABS(`num` - 99.99) ASC LIMIT 1

"num" - поле с числовыми значениями, "numbers" - имя таблицы, "99.99" - некое заданное число, к которому и ищем ближайшее значение в базе. Функция ABS() в MySQL - идентична одноименной функции в PHP. Думаю, что комментировать дальше не имеет смысла ;) Данные способы нахождения ближайшего значения может и не претендуют на какую-то оригинальность, но работают исправно и вполне подходят для задач такого плана. Я буду только рад, если вы предложите альтернативные варианты. Ведь лишних способов не бывает и для каждого можно найти своё применение в той или иной ситуации. Оставляйте свои решения в комментариях и они займут достойное место в коллекции, которая пригодится другим ;)

ПАРСИНГ EXCEL (XML) - ДОКУМЕНТА

Небольшой скрипт для парсинга xml документа - Excel

function parse_excel_xml($xml_file)
{

  //Загружаем файл
  if($xmlfile = simplexml_load_file($xml_file))
  {

    //Внешний цикл (строки)
    for($y = 0; $y < count($xmlfile->Worksheet->Table->Row); $y++)
    {
      //Внутренний цикл(столбцы)
      for($x = 0; $x < count($xmlfile->Worksheet->Table->Row[$y]->Cell); $x++)
      {

        //Двухмерный массив
        $xml_array[$y][$x] = $xmlfile->Worksheet->Table->Row[$y]->Cell[$x]->Data;
      }
    }
    return $xml_array;
  }
  else
  {
    return false;
  }
}
Все.

ЗАЧЕМ СИМВОЛ @ (СОБАКА) В PHP?

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

$sql='ТУТ ПЕРЕМЕННЫЕДАННЫЕ';
mysql_query($sql,$link);

То вверху страницы выдаст ошибку. Если будет знак @ перед функциями PHP, то никакой ошибки не будет :)

$sql='ТУТ ПЕРЕМЕННЫЕДАННЫЕ';
@mysql_query($sql,$link);

Так же, "собачку" можно поставить перед

@require_once ENGINE_DIR.'/data/dbconfig.php';
@include_once

и т.д.

02.03.2023

176
A B i U S JS

PHP HTML CSS
Чат
    Для входа только имэйл или имя и апроль
    Можно сменить аватар
    Имэйл Ваше имя
    Пароль