ФорумПрограммированиеПыхнуть хотите?F.A.Q. → Фишки и мелочи PHP

Фишки и мелочи PHP

  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 5 октября 2007 г. 12:59

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

    Индексы массивов
    Локали
    Предотвращение повторной отправки формы
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 5 октября 2007 г. 13:00, спустя 1 минуту 36 секунд

    [size=12]Индексы массивов[/size]

    Как известно из документации, массивы бывают двух видов — порядковые и ассоциативные.

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

    Индексы указываются в виде строк:

    $A = Array('a' => 1, 'b' => 2);
    $A['c'] => 3;
    $A['%^&*#)'] = 4; // Строк абсолютно любого формата


    Либо в виде выражений, приводимых к строкам:

    $i = 15;
    $A['prefix'.intVal($i / 4)] = 10; // A[3] = 10


    Нельзя делать, как любят многие:

    $A = Array(a => 1, b => 2);
    $A
     = 3;


    Почему нельзя, скажет сам PHP, если включен вывод ошибок уровня E_NOTICE:

    Notice: Use of undefined constant a - assumed 'a'
    Notice: Use of undefined constant b - assumed 'b'
    Notice: Use of undefined constant c - assumed 'c'


    Встретив $A
     PHP пытается определить, что это за выражение такое и привести его к строке. Это не строка, не число, не вызов функции и не ключевое слово. Остается одно — это константа. Не найдя определение этой константы, PHP делает единственное что может — использует имя константы в качестве его значения. 

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

    define('a', 5);
    $A[a] = 6; // A[5] = 6

    $A[if] = 7; // if - ключевое слово, Parse error


    Так что не ленимся писать кавычки.


    Порядковые массивы

    Вернемся к первой фразе: "Как известно из документации, массивы бывают двух видов — порядковые и ассоциативные…". Вранье. Нет никаких порядковых массивов — только ассоциативные. И индексы везде указываются в виде строк.


    $A = Array(1, 2, 3); // Упрощенная запись для как бы "порядковых массивов".
    print $A[0]; // В качестве индекса указываем число. На самом деле оно просто приводится к строке.
    print $A['0']; // Тот же элемент

    $A['a'] = 4; // Безо всяких проблем сделали "порядковый" массив "ассоциативным".

    $A[] = 5; // Кажется, это операция только для "порядковых". Однако, спокойно работает и с ассоциативными.
  • Patrick

    Сообщения: 506 Репутация: N Группа: Кто попало

    Spritz 5 октября 2007 г. 14:10, спустя 1 час 9 минут 46 секунд


    $A[] = 5; // Кажется, это операция только для "порядковых". Однако, спокойно работает и с ассоциативными.

    а что тебя смущает????

    $A = Array(1, 2, 3);
    $A['a'] = 4;
    $A[30] = 5;
    $A[] ='a';

    С каким индексом последний элемент массива? Только не запуская)))
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 5 октября 2007 г. 14:17, спустя 6 минут 30 секунд

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

    В массивах есть счетчик, сохраняющий максимальный индекс из тех, что приводятся к числам. Соответственно, здесь будет 30, а следующий индекс будет 31.
    Если сделать $A['30'] = 5; результат будет тот же.
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 5 октября 2007 г. 21:37, спустя 7 часов 19 минут 53 секунды

    [size=12]Локали[/size]

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

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

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

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

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

    Для установки нужной локали, используется функция setLocale. Если у вас сайт в кодировке windows-1251, то обычно срабатывает:
    setLocale(LC_ALL, 'ru_RU.CP1251');

    А иногда не срабатывает. Читайте документацию по функции, спрашивайте у службы поддержки вашего хостинга наименование локалей на сервере.
  • Dagdamor

    Сообщения: 47 Репутация: N Группа: Кто попало

    Spritz 6 октября 2007 г. 6:33, спустя 8 часов 56 минут 53 секунды

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

    1. вызывается setlocale(win1251) для вашего проекта;
    2. вызывается setlocale(utf8) для другого проекта;
    3. вызывается функция strtoupper() для вашего проекта;
    4. функция возвращает ерунду, т.к. в пункте 2 системная локаль переключилась обратно в УТФ.

    Единственный выход - писать свои безопасные функции для работы с русской кодировкой.
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 6 октября 2007 г. 10:33, спустя 3 часа 59 минут 44 секунды

    Или использовать нормальный хостинг )
  • vasa_c

    Сообщения: 3131 Репутация: N Группа: в ухо

    Spritz 27 октября 2007 г. 18:51, спустя 21 день 8 часов 18 минут

    [size=12]Предотвращение повторной отправки формы[/size]


    Суть проблемы

    Пользователь заполняет на сайте форму и отправляет её. После отправки отображается страница: "спасибо, за ваше сообщение, бла-бла-бла, все дела…".
    По какой-то причине пользователь хочет обновить страницу — нажимает F5. Либо он переходит на другую, после чего хочет вернуться назад с помощью кнопки "back". Нажимает он, значит, кнопку, а ему внезапно выскакивает окошко: "Страница содержит введенные данные формы… Для повторной отправки нажмите OK…".

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


    Причины

    Когда вы перемещаетесь по страницам сайта, ваш браузер запрашивает у сервера нужные страницы (а так же картинки, стили и т.п). Запрашивает по протоколу HTTP. Запросы могут быть сделаны различными методами, но наиболее распространены два — GET и POST.

    Когда вы просто перемещаетесь по ссылкам или напрямую вводите URL в адресной строке, используется метод GET. Браузер просто говорит серверу: "а дай-ка мне друг сервер, вон тот документ с таким-то адресом". И сервер безропотно отдает. А иногда не отдает. Нет у меня, грит, такой страницы. Или, дескать, не дам, у вас документов нету.

    Браузер запоминает, как была запрошена страница и, когда вы нажимаете "обновить", он просто повторяет тот же GET-запрос, на что сервер, обычно, возвращает тот же документ.

    Чуть сложнее с отправкой формы. Отправляются формы в большинстве случаев методом POST. Это почти, как GET, но кроме всего прочего, браузер пересылает серверу еще и введенные в форму данные. Те самые данные, которые можно достать в php-сценарии из массива $_POST или $_REQUEST. Сценарий на сервере, смотрит — пришли данные формы, то есть нужно добавить в гесту комментарий пользователя. И добавляет.

    С виду, загруженная методом POST страница, выглядит, как обычная. И в адресной строке ничего не изменилось, и в остальном всё в порядке. Однако, браузер не проведешь, он всё помнит. И когда вы обновляете страницу, он отправляет всё тот же POST-запрос, с всё теми же данными формы. А сценарий на сервере понятия не имеет, пользователь ли опять форму заполнил или просто обновил страницу. Он видит — пришел POST. И добавляет всё снова.


    Решение

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

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

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

    Наиболее распространенный способ — использование HTTP-редиректа.
    Сервер, в ответ на запрос документа, может не только просто выдать его или сказать, что его нет (404-я ошибка). Он так же может ответить "а документ здесь больше не живет, иди-ка посмотри по тому адресу". И браузер идет по другому адресу и ищет его там. То есть снова запрашивает. Причем запрашивает методом GET. Что важно, переадресовать браузер можно и на тот же самый документ. Только если сначала он был запрошен методом POST, то второй раз он будет запрошен методом GET без всяких данных формы.

    На PHP такой ответ сервера можно реализовать отправкой заголовка ответа Location с указанием адреса переадресации. Заголовки в PHP отправляются функцией header().

    В большинстве случаев простой сценарий обработки формы будет иметь вид:


    <?php
    /**
    * Добавление комментариев
    */

    if (isSet($_POST['comment'])) {

    /* Сценарий запрошен методом POST - обрабатываем данные формы */
    if (empty($_POST['comment'])) {
    /* Комментарий пустой - выведем сообщение об ошибке */
    print '<p style="color:red">Введите, пожалуйста, текст комментария</p>';
    } else {
    /*
    Здесь производим действия по добавлению комментария в базу данных
    */
    $addres = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; // Вычисляем полный адрес нашего сценария
    header('Location: '.$addres); // Переадресовываем на себя же
    exit(); // Больше делать нечего - завершаем сценарий
    }
    }

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

    ?>
    <p>Здесь идет список комментариев…</p>
    <form method="post">
    <h2>Ваш комментарий:</h2>
    <div><textarea name="comment" rows="10" cols="50"></textarea></div>
    <input type="submit" value="Отправить" />
    </form>


    При этом не забывайте про проблему с "Cannot add header information - headers already sent".


    Еще варианты

    Можно и не держать саму форму и обработчик в одном файле. Например, форму выводить в одном файле, а action формы направить на другой. Во втором производить обработку (ветвь if в примере), после чего переадресовывать назад на первый сценарий.

    Так же вместо http-редиректа можно сделать редирект с помощью html-тега мета:
    <meta http-equiv="refresh" content="5" />

    Здесь, через 5 секунд произойдет перезагрузка страницы.
    Этот способ может подойти, когда нужно вывести какое-то сообщение, например:

    /*
    Здесь производим действия по добавлению комментария в базу данных
    */
    print '<meta http-equiv="refresh" content="5" />';
    print '<p>Ваш комментарий записан. Спасибо.</p>';
    exit();

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

    PS. Обсуждение здесь
  • md5

    Сообщения: 11960 Репутация: N Группа: в ухо

    Spritz 11 февраля 2009 г. 0:37, спустя 472 дня 6 часов 45 минут

    $addres = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];

    дарагой, а если у нас используется якорь?

    http://site.ru/contacts/#feedback-form
    ?

    ps. да оно ваще, впринципе, хер работает.. хм
    все умрут, а я изумруд
  • Mr.Pihto

    Сообщения: 1386 Репутация: N Группа: Адекваты

    Spritz 16 апреля 2009 г. 21:28, спустя 64 дня 19 часов 51 минуту

    Предотвращение повторной отправки формы…
    тоже стлкивался с такой барадой.. сам допёр..
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 16 апреля 2009 г. 22:04, спустя 35 минут 9 секунд

    у мну каждая форма имеет идентификатор, и когда форму сабмитишь он стирается. иначе никаг :)))))))))))) поэтому двойная отправка формы просто тупо невозможна.
    Сапожник без сапог
  • adw0rd

    Сообщения: 22959 Репутация: N Группа: в ухо

    Spritz 16 апреля 2009 г. 23:08, спустя 1 час 4 минуты 21 секунду

    phpdude, какой идентификатор, где?
    https://smappi.org/ - платформа по созданию API на все случаи жизни
  • Givi

    Сообщения: 2284 Репутация: N Группа: Адекваты

    Spritz 16 апреля 2009 г. 23:23, спустя 15 минут 29 секунд

    adw0rd, Дуд как всегда, хуйни накой-то нахимичил сипе ))))
  • adw0rd

    Сообщения: 22959 Репутация: N Группа: в ухо

    Spritz 16 апреля 2009 г. 23:25, спустя 2 минуты

    Givi, что такое сипе? )
    https://smappi.org/ - платформа по созданию API на все случаи жизни
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 16 апреля 2009 г. 23:50, спустя 24 минуты 43 секунды


    phpdude, какой идентификатор, где?
    hidden поле в форме + сессионную переменную о полях формы. хак устойчивость повышается + офрмы и обработчики даже географически в разных файлах могут быт ь:)
    Сапожник без сапог

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