Одна з найдивніших «багофіч» PHP виражається тим, що при певних умовах стає неможливим запуск одного і того ж скрипта (або навіть різних скриптів) одночасно кілька разів на одному і тому ж сервері.
Все більш-менш досвідчені програмісти знають, що в PHP є вбудований механізм зберігання даних між запитами, який називається «сесіями».
Суть оного (якщо хто не знає) знаходиться під спойлером.
Суть сесій полягає в тому, що після закінчення виконання скрипта весь вміст змінної $ _SESSION автоматично серіалізуются (упаковується в рядок) і зберігається в спеціальний файл в тимчасовій папці. При наступному запиті цей файл автоматично зчитується, десеріалізуется і отримане вміст записується в змінну $ _SESSION.
Таким чином, у скрипта складається враження, що все записане в змінну $ _SESSION живе «вічно», тобто не пропадає безслідно при завершенні скрипта (на відміну від звичайних змінних).
Ім'я тимчасового файлу, в якому буде збережено вміст змінної $ _SESSION, визначається так званим ідентифікатором сесії або session_id, або SID. Щоб при наступному запиті успішно вважати назад ті ж самі дані, ідентифікатор сесії повинен бути десь збережений і переданий в наступному запиті. Зазвичай для цього використовується кука або ж додаткова змінна в запиті GET (це виглядає як додаток на зразок? SID = ab87d9f98e09da в URL), в запиті POST або ж у спеціальній Кука з ім'ям SID.
Баг (а точніше, особливість) стандартних сесій PHP полягає в тому, що файл, в якому зберігаються дані сесії, на час роботи скрипта блокується для доступу. Це робиться не випадково, а навмисно, щоб в результаті перезапису файлу якимось іншим скриптом його дані не загубилися.
А на практиці це виражається тим, що фізично неможливо відправити на одночасну обробку два скрипта з одним і тим же ідентифікатором сесії. Як тільки один скрипт запустився і відкрив сесію (за допомогою session_start () або ж сесія відкрилася автоматично - є опція в php.ini), так відразу ця сесія стає недоступною для інших скриптів (причому це може бути друга копія цього ж скрипта) і при спробі відкрити сесію вони просто зависають на невизначений час, а саме - до того моменту як завершиться перший скрипт або станеться таймаут.
Такий же точно ефект може проявлятися в тому випадку, якщо з одного скрипта ви намагаєтеся робити виклик іншого скрипта цього ж сайту. В цьому випадку викликаний скрипт натикається на блокування доступу до сесії і зависає на рядку session_start (), а перший скрипт, який викликав його, теж зависає, оскільки чекає відповіді від другого, викликаного, скрипта.
І тепер, коли ви знаєте в чому полягає проблема, ви напевно вже зрозуміли, як обходити подібні ситуації. Ну а для тих, хто не здогадався, я привожу ряд рішень, або навіть правильніше сказати, правил, як слід чинити, щоб не потрапити в пастку.
Не використовуйте сесії, якщо в цьому немає необхідності. Наприклад, якщо скрипт призначений для запуску з cron, в ньому зовсім не обов'язково відкривати сесію через session_start (), оскільки ви все одно не зможете передати куку, що містить SID.
Робіть скрипти, що використовують сесію, максимально спритними і простими. Ідея полягає в тому, щоб у користувача не виникло бажання натиснути іншу посилання, поки відкривається перша. Якщо він це встигне зробити, то другий скрипт зависне в очікуванні закінчення виконання першого скрипта і користувачеві доведеться чекати ще довше, поки він, зрештою, не покине сайт.
Намагайтеся не викликати зі своїх скриптів сторінок свого ж сайту. Я маю на увазі речі на зразок
Або ж, якщо це необхідно, закривайте сесію перед викликом, а потім знову її відкривайте. Приблизно ось так:
Підписуйтесь на мій блог і будьте в курсі!