Як створити тимчасовий файл на PHP, коли функція tmpfile () не підходить +12
- такий же як Forbes, тільки краще.
Коли PHP-програмісту необхідно створити тимчасовий файл він в мануалі знаходить функцію tmpfile () і після вивчення прикладів починає думати як її краще застосувати. Так було і зі мною, коли мені треба було вивантажити дані відразу в тимчасовий файл, а не працювати з ними через змінну. Але з файлом, створеним таким чином, надалі незручно працювати в силу того, що tmpfile () повертає дескриптор, а не посилання на локальний файл. Давайте трохи заглибимося в анатомію тимчасового файлу і розглянемо підводні камені, з якими мені довелося зіткнутися.
Функція tmpfile () створює ресурс, так як це робить fopen (). і працює з потоками введення-виведення STDIO. Це еквівалентно тому, якби ми відкрили потік php: // temp для подальшої роботи з тимчасовим файлом. В обох випадках файл з'явиться в тимчасовій папці, яка прописана в php.ini. і буде автоматично видалений після завершення скрипта або достроково за допомогою fclose ().
При роботі з php: // temp файл буде створений в тимчасовій папці коли розмір даних перевалить за 2 Мбайт. До цього всі записані дані будуть зберігатися в php: // memory. Це обмеження можна обійти, якщо відразу увійти в потік php: // temp / maxmemory: 0. - PHP
Оскільки tmpfile () і fopen () при створенні тимчасового файлу працюють з потоками, ми можемо за допомогою stream_get_meta_data () витягти метадані і дізнатися реальний шлях до файлу для подальших маніпуляцій:
Які значення повертає stream_get_meta_data () добре описано в документації. але нас більше цікавить ім'я файлу, пов'язане з потоком. Його можна витягти по ключу uri в масиві.
У випадку з php: // temp ми ніяк не зможемо отримати URI з метаданих, хоча файл за фактом буде створений в тимчасовій папці, якщо його вага перевищить 2 Мбайт. Іншого способу дізнатися де фізично зберігається тимчасовий файл і під яким ім'ям при роботі з потоками не існує.
Перекидати ресурс з одного об'єкта в інший теж не дуже зручно, тому що для даної реалізації знадобиться інтерфейс. У моєму випадку потрібно було передати ім'я тимчасового файлу з класом File з пакета Symfony HttpFoundation в об'єкт, у якого прописана сувора залежність від класу File в конструкторі. Бізнес-логіка програми передбачала валідацію файлу на іншому рівні і тут важливо було подбати про видалення файлу на самому початку його шляху, якщо перевірка буде провалена. На цьому етапі стало зрозуміло, що функція tmpfile () для створення тимчасового файлу не підходить.
Для альтернативного рішення я написав свій механізм, який працює за наступною схемою: створення файлу в тимчасовій папці> будь-які маніпуляції з файлом> автоматичне видалення. Створити файл з унікальним ім'ям в тимчасовій папці PHP дозволяє за допомогою функції tempnam ().
Першим аргументом вказується розташування тимчасової папки через sys_get_temp_dir (). а другим - префікс в імені файлу. Такий файл доступний для читання і запису тільки власнику, т. К. Створюється з правами 0600 (rw-). Для реалізації автоматичного видалення файлу пропоную перенести подальшу логіку в клас, де за допомогою __destruct () спробуємо видалити файл.
Об'єкт поверне посилання на файл, який створила функція tempnam (). т. к. в класі прописаний __toString (). Таким чином ми позбулися роботи з ресурсом. Сам файл буде видалений при звільненні всіх посилань на об'єкт або по завершенню скрипта, але до того випадку, поки не буде викликана фатальна помилка або кинуто виключення.
Деструкція викликається при знищенні об'єкта. У разі критичних помилок __destruct () може не викликатися в PHP7 і нижче. Деструкція не повинен залишати об'єкт в нестабільному стані. Тому в PHP обробники знищення і звільнення об'єкта відокремлені один від одного. Оброблювач звільнення викликається, коли движок повністю впевнений в тому, що об'єкт більше ніде не застосовується. - Об'єкти в PHP7
Видаляти файл через деструкцію не найкраща практика, яка, до речі, застосовується в багатьох доступних рішеннях. Для гарантованого видалення файлу ми можемо зареєструвати свою функцію, яка виконається в будь-якому випадку після завершення скрипта. Робиться це за допомогою register_shutdown_function () в конструкторі нашого класу:
Такий підхід дозволяє створити тимчасовий файл без використання tmpfile () або php: // temp. що в ООП дуже зручно. Стандартні способи краще для вирішення локальних завдань, де вся логіка Інкапсульована в одному методі або класі.
В результаті вийшов клас для роботи з тимчасовим файлом. Вихідні тексти я виклав в репозиторії на Гітхабе denisyukphp / tmpfile і додав в клас підтримку CRUD-операцій. Методи для запису і читання є обгорткою для file_put_contents () і file_get_contents (). Підключити до свого проект можна через Composer.