Подгрузка контента при прокрутке (бесконечная лента)

Пример реализации «Бесконечной ленты» с применением PHP, БД MySQL, и JQuery Ajax.

Формирование списка
Первый PHP-скрипт выводит только первые 12 записей из таблицы `prods` (записей может быть и больше, главное чтобы высота блока была больше высоты окна браузера).

Ниже, списка товаров выводится
<div id="showmore-triger">, который выполняет роль индикатора загрузки с гифкой ajax-loader.gif и того что пользователь уже доскроллил до этого элемента.

<?php
// Кол-во элементов
$limit = 12; 
 
// Подключение к БД
$dbh = new PDO('mysql:dbname=db_name;host=localhost', 'логин', 'пароль');
 
// Получение записей для первой страницы
$sth = $dbh->prepare("SELECT * FROM `prods` LIMIT {$limit}");
$sth->execute();	
$items = $sth->fetchAll(PDO::FETCH_ASSOC);	
 
// Кол-во страниц 
$sth = $dbh->prepare("SELECT COUNT(`id`) FROM `prods`");
$sth->execute();
$total = $sth->fetch(PDO::FETCH_COLUMN);
$amt = ceil($total / $limit);
?>
	
<div id="showmore-list">
	<div class="prod-list">
		<?php foreach ($items as $row): ?>
		<div class="prod-item">
			<div class="prod-item-img">
				<img src="/images/<?php echo $row['img']; ?>" alt="">	
			</div>
			<div class="prod-item-name">
				<?php echo $row['name']; ?>		
			</div>
		</div>
		<?php endforeach; ?>
	</div>
</div>
 
<div id="showmore-triger" data-page="1" data-max="<?php echo $amt; ?>">
	<img src="ajax-loader.gif" alt="">
</div>


CSS-стили:
#showmore-triger {
	text-align: center;
	padding: 10px;
	background: #ffdfdf;
}
 
/* Вывод товаров */
.prod-list {
	overflow: hidden;
	margin: 0 0 20px 0;
}
.prod-item {
	width: 174px;
	height: 230px;
	float: left;
	border: 1px solid #ddd;
	padding: 20px;
	margin: 0 20px 20px 0;
	text-align: center;
	border-radius: 6px;
}
.prod-item-img {
	width: 100%;
}
.prod-item-name {
	font-size: 13px;
	line-height: 16px;
}
 
.prod-list .prod-item:nth-child(3n) {
	margin-right: 0
}


JS-скрипт подгрузки контента
В нём отслеживается скроллинг страницы и в нужный момент отправляется AJAX-запрос на /ajax.php?page=x с номером следующей страницы, который возвращает следующую страницу записей, далее они вставляются в общий список.

При выводе всех страниц из БД, элемент с id="showmore-triger" удаляется методом JQuery remove() – он удаляет элемент из дерева DOM, а также все его JS-события.

<script src="/jquery/2.1.1/jquery.min.js"></script>
 
<script>
var block_show = false;
 
function scrollMore(){
	var $target = $('#showmore-triger');
	
	if (block_show) {
		return false;
	}
 
	var wt = $(window).scrollTop();
	var wh = $(window).height();
	var et = $target.offset().top;
	var eh = $target.outerHeight();
	var dh = $(document).height();   
 
	if (wt + wh >= et || wh + wt == dh || eh + et < wh){
		var page = $target.attr('data-page');	
		page++;
		block_show = true;
 
		$.ajax({ 
			url: '/ajax.php?page=' + page,  
			dataType: 'html',
			success: function(data){
				$('#showmore-list .prod-list').append(data);
				block_show = false;
			}
		});
 
		$target.attr('data-page', page);
		if (page ==  $target.attr('data-max')) {
			$target.remove();
		}
	}
}
 
$(window).scroll(function(){
	scrollMore();
});
	
$(document).ready(function(){ 
	scrollMore();
});
</script>

Код файла ajax.php
Скрип получает обрабатывает переменную $_GET['page'] и нужны записи из БД.

<?php
// Кол-во элементов
$limit = 12; 
 
// Подключение к БД
$dbh = new PDO('mysql:dbname=db_name;host=localhost', 'логин', 'пароль');
 
// Получение записей для текущей страницы
$page = intval(@$_GET['page']);
$page = (empty($page)) ? 1 : $page;				
$start = ($page != 1) ? $page * $limit - $limit : 0;
$sth = $dbh->prepare("SELECT * FROM `prods` LIMIT {$start}, {$limit}");
$sth->execute();	
$items = $sth->fetchAll(PDO::FETCH_ASSOC);				
 
foreach ($items as $row) {
	?>
	<div class="prod-item">
		<div class="prod-item-img">
			<img src="/images/<?php echo $row['img']; ?>" alt="">	
		</div>
		<div class="prod-item-name">
			<?php echo $row['name']; ?>		
		</div>
	</div>
	<?php
}

Добавлено: 10 Января 2022 07:34:19 Добавил: Андрей Ковальчук

Фильтрация и проверка данных PHP. Частые ошибки

Материал предназначен в основном для начинающих веб-программистов.

Введение.
Часто ко мне обращаются клиенты, у которых установлены самописные CMS или модули, написанные начинающими веб-программистами, которые не понимают, что нужно для защиты данных и зачастую копируют функции фильтрации, не задумываясь о том как они работают и что именно нужно с ними делать.

Здесь я постараюсь описать как можно подробнее частые ошибки при фильтрации данных в PHP скрипте и дать простые советы как правильно выполнить фильтрацию данных.

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

Разбор полетов.

Фильтрация. Ошибка №1

Для числовых переменных используется такая проверка:

$number = $_GET['input_number'];
if (intval($number))
{
... выполняем SQL запрос ...
}


Почему она приведет к SQL инъекции? Дело в том, что пользователь может указать в переменной input_number значение:
1'+UNION+SELECT


В таком случаи проверка будет успешно пройдена, т.к. функция intval получает целочисленное значение переменной, т.е. 1, но в самой переменной $number ничего не изменилось, поэтому весь вредоносный код будет передан в SQL запрос.
Правильная фильтрация:
$number = intval($_GET['input_number']);
if ($number)
{
... выполняем SQL запрос ...
}


Конечно, условие может меняться, например если вам нужно получить только определенный диапазон:
if ($number >= 32 AND $number <= 65)



Если вы используете чекбоксы или мультиселекты с числовыми значениями, выполните такую проверку:
$checkbox_arr = array_map('intval', $_POST['checkbox']);


array_map
Так же встречаю фильтрацию в виде:
$number = htmlspecialchars(intval($_GET['input_number']));

htmlspecialchars
Или:
$number = mysql_escape_string(intval($_GET['input_number']));


mysql_escape_string

Ничего кроме улыбки это не может вызвать :)

Фильтрация. Ошибка №2.

Для стринг-переменных используется такая фильтрация:
$input_text = addslashes($_GET['input_text']);


Функция addslashes экранирует спец. символы, но она не учитывает кодировку БД и возможен обход фильтрации. Не стану копировать текст автора, который описал данную уязвимость и дам просто ссылку Chris Shiflett (перевод можно поискать в рунете).

Используйте функцию mysql_escape_string или mysql_real_escape_string, пример:
$input_text = mysql_escape_string($_GET['input_text']);


Если вы не предполагаете вхождение html тегов, то лучше всего сделать такую фильтрацию:
$input_text = strip_tags($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);

strip_tags — убирает html теги.
htmlspecialchars — преобразует спец. символы в html сущности.
Так вы защитите себя от XSS атаки, помимо SQL инъекции.
Если же вам нужны html теги, но только как для вывода исходного кода, то достаточно использовать:
$input_text = htmlspecialchars($_GET['input_text']);
$input_text = mysql_escape_string($input_text);



Если вам важно, чтобы значение переменной не было пустой, то используйте функцию trim, пример:
$input_text = trim($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);



Фильтрация. Ошибка №3.

Она касается поиска в БД.
Для поиска по числам используйте фильтрацию, описанную в первой ошибке.
Для поиска по тексту используйте фильтрацию, описанную во второй ошибке, но с оговорками.
Для того, чтобы пользователь не смог выполнить логическую ошибку, нужно удалять или экранировать спец. символы SQL.
Пример без доп. обработки строки:
$input_text = htmlspecialchars($_GET['input_text']); // Поиск: "%"
$input_text = mysql_escape_string($input_text);


На выходе у нас получится запрос вида:
... WHERE text_row LIKE '%".$input_text."%' ... // WHERE text_row LIKE '%%%'


Это значительно увеличит нагрузку на базу.
В своём скрипте я использую функцию, которая удаляет нежелательные мне символы из поиска:
function strip_data($text)
{
    $quotes = array ("\x27", "\x22", "\x60", "\t", "\n", "\r", "*", "%", "<", ">", "?", "!" );
    $goodquotes = array ("-", "+", "#" );
    $repquotes = array ("\-", "\+", "\#" );
    $text = trim( strip_tags( $text ) );
    $text = str_replace( $quotes, '', $text );
    $text = str_replace( $goodquotes, $repquotes, $text );
    $text = ereg_replace(" +", " ", $text);
            
    return $text;
}


Конечно, не все из выше перечисленных символов представляют опасность, но в моём случаи они не нужны, поэтому выполняю поиск и замену.
Пример использования фильтрации:
$input_text = strip_data($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);


Также советую сделать ограничение по количеству символов в поиске, хотя бы не меньше 3-х, т.к. если у вас будет большое количество записей в базе, то поиск по 1-2 символам будет значительно увеличивать нагрузку на БД.

Фильтрация. Ошибка №4.

Не фильтруются значения в переменной $_COOKIE. Некоторые думаю, что раз эту переменную нельзя передать через форму, то это гарантия безопасности.
Данную переменную очень легко подделать любым браузером, отредактировав куки сайта.
Например, в одной известной CMS была проверка, используемого шаблона сайта:
if (@is_dir ( MAIN_DIR . '/template/' . $_COOKIE['skin'] )){
	$config['skin'] = $_COOKIE['skin'];
}
$tpl->dir = MAIN_DIR . '/template/' . $config['skin'];


В данном случаи можно подменить значение переменной $_COOKIE['skin'] и вызвать ошибку, в результате которой вы увидите абсолютный путь до папки сайта.
Если вы используете значение куков для сохранения в базу, то используйте одну из выше описанных фильтраций, тоже касается и переменной $_SERVER.

Фильтрация. Ошибка №5.

Включена директива register_globals. Обязательно выключите её, если она включена.
В некоторых ситуациях можно передать значение переменной, которая не должна была передаваться, например, если на сайте есть группы, то группе 2 переменная $group должна быть пустой или равняться 0, но достаточно подделать форму, добавив код:
<input type="text" name="group" value="5" />


В PHP скрипте переменная $group будет равна 5, если в скрипте она не была объявлена со значением по умолчанию.

Фильтрация. Ошибка №6.

Проверяйте загружаемые файлы.
Выполняйте проверку по следующим пунктам:
1. Расширение файла. Желательно запретить загрузку файлов с расширениями: php, php3, php4, php5 и т.п.
2. Загружен ли файл на сервер move_uploaded_file
3. Размер файла

Проверка. Ошибка №1.

Сталкивался со случаями, когда для AJAX запроса (например: повышение репутации) передавалось имя пользователя или его ID (кому повышается репутация), но в самом PHP не было проверки на существование такого пользователя.
Например:
$user_id = intval($_REQUEST['user_id']);
... INSERT INTO REPLOG SET uid = '{$user_id}', plus = '1' ...
... UPDATE Users SET reputation = reputation+1 WHERE user_id = '{$user_id}' ...


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

Проверка. Ошибка №2.

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

Давно исправлял в одном модуле форума подобную ошибку, когда любой пользователь мог отредактировать сообщение администрации.

Проверка. Ошибка №3.

При использовании нескольких php файлов сделайте простую проверку.
В файле index.php (или в любом другом главном файле) напишите такую строчку перед подключением других php файлов:
define ( 'READFILE', true );


В начале других php файлов напишите:
if (! defined ( 'READFILE' ))
{
	exit ( "Error, wrong way to file.<br><a href=\"/\">Go to main</a>." );
}


Так вы ограничите доступ к файлам.

Проверка. Ошибка №4.

Используйте хеши для пользователей. Это поможет предотвратить вызов той или иной функции путём XSS.
Пример составления хеша для пользователей:
$secret_key = md5( strtolower( "http://site.ru/" . $member['name'] . sha1($password) . date( "Ymd" ) ) ); // $secret_key - это наш хеш


Далее во все важные формы подставляйте инпут со значением текущего хеша пользователя:
<input type="hidden" name="secret_key" value="$secret_key" />


Во время выполнения скрипта осуществляйте проверку:
if ($_POST['secret_key'] !== $secret_key)
{
exit ('Error: secret_key!');
}



Проверка. Ошибка №5.

При выводе SQL ошибок сделайте простое ограничение к доступу информации. Например задайте пароль для GET переменной:
if ($_GET['passsql'] == "password")
{
... вывод SQL ошибки ...
}
else
{
... Просто информация об ошибке, без подробностей ...
}


Это позволит скрыть от хакера информацию, которая может ему помочь во взломе сайта.

Проверка. Ошибка №5.
Старайтесь не подключать файлы, получая имена файлов извне.
Например:
if (isset($_GET['file_name']))
{
include $_GET['file_name'] .'.php';
}


Используйте переключатель switch:
switch($_GET['file_name'])
{         
         case 'file_1':
         include 'file_1.php';    
         break;     
         
         default:
         include 'file_0.php';    
         break;
}


В таком случаи вы предотвратите подключение файлов, которые не были вами предусмотрены.

Совет.

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

UPD: Поправил пост. Перенес все советы по поводу функций и переменных, которые были в комментариях.

Добавлено: 19 Ноября 2021 07:27:05 Добавил: Андрей Ковальчук

Поиск пустых папок. Идеи и реализации

function RemoveEmptySubFolders($path)
{
  $empty=true;
  foreach (glob($path.DIRECTORY_SEPARATOR."*") as $file)
  {
     if (is_dir($file))
     {
        if (!RemoveEmptySubFolders($file)) $empty=false;
     }
     else
     {
        $empty=false;
     }
  }
  if ($empty) rmdir($path);
  return $empty;
}

Добавлено: 21 Октября 2021 06:55:44 Добавил: Андрей Ковальчук

Поблочная прокрутка на странице landing page

Если вам нравится такая реализация, то дальше давайте рассмотрим как сделать так же на своем сайте.

HTML разметка
С разметкой все просто. Основным блокам на странице нужно присвоить класс - anchor. Данный класс задан в скрипте, о котором я напишу позже. так что если решите его поменять не забудьте сделать это в скрипте. В итоге на вашей странице должна получится такая сетка

<div class="anchor">
// ТУТ СОДЕРЖАНИЕ БЛОКА
</div>
<div class="anchor">
// ТУТ СОДЕРЖАНИЕ БЛОКА
</div>
<div class="anchor">
// ТУТ СОДЕРЖАНИЕ БЛОКА
</div>
<div class="anchor">
// ТУТ СОДЕРЖАНИЕ БЛОКА
</div>

Если вы разместите что-то вне блоков с классом anchor, то оно может быть вне видимой зоны при прокрутке. Так что строить структуру страницы нужно именно внутри блоков anchor.

CSS стили
По сути, можно вообще стили не добавлять никакие, все зависит от ваших нужд. В примере я сделал так, чтобы на странице был только один блок в зоне видимости. Сделал я это благодаря тому, что присвоил параметр высоты для блока - 100vh.
.anchor{
height:100vh;
}

jQuery скрипт
Чтобы все заработало нужно добавить скрипт, так как он работает на основе jQuery, то нужно сначала убедится что подключена библиотека. Посмотрите в коде своего сайта или страницы. Если нет схоже строки с этой, то добавьте ее:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>

Только после нее нужно добавлять скрипт. Кстати, версия библиотеки мжет быть другой, так что, не обязательно - 3.1.0.

Скрипт выглядит следующим образом:
<script>
var anchors = [];
var currentAnchor = -1;
var isAnimating  = false;
$(function(){
    function updateAnchors() {
        anchors = [];
        $('.anchor').each(function(i, element){
            anchors.push( $(element).offset().top );
        });
    }
    $('body').on('mousewheel', function(e){
        e.preventDefault();
        e.stopPropagation();
        if( isAnimating ) {
            return false;
        }
        isAnimating  = true;
        if( e.originalEvent.wheelDelta >= 0 ) {
            currentAnchor--;
        }else{
            currentAnchor++;
        }
        if( currentAnchor > (anchors.length - 1) 
           || currentAnchor < 0 ) {
            currentAnchor = 0;
        }
        isAnimating  = true;
        $('html, body').animate({
            scrollTop: parseInt( anchors[currentAnchor] )
        }, 500, 'swing', function(){
            isAnimating  = false;
        });
    });
    updateAnchors();   
});
</script>

Особо разбирать скрипт не имеет смысла. Обратить внимание нужно на пару элементов.

8 строка - $('.anchor') - тут указан класс блоков которые нужно перелистывать. То о чем я говорил в начале, если решите изменить его название, менять нужно тут тоже.
31 строка - значение 500 - это скорость анимации в миллисекундах. Прокрутка между блоками будет происходить за пол секунды. Можете изменить значение, чтобы ускорить или замедлить.
Вот на сколько прост данный способ для необычного скроллинга. Сложностей здесь нет, но есть важные моменты, о которых следует знать!

Скроллинг ведется только между блоков с классом anchor. Когда скролл доходит к последнему блоку, скрипт возвращает вас к первому. Следовательно, если вы разместите что-то вне этих блоков, оно будет пролистываться.
Обычный скролл не работает. Если ваш блок будет выходить за пределы экрана, видимой его части - то вы не сможете прокрутить его немножко. чтобы увидеть. Вас перебросит на следующий блок. Поэтом блоки должны быть небольшими и учитывать этот момент. Landing page - отлично подходит для этого. Потому как там размещение блоков с минимальной информацией приветствуется.
Учитывая выше сказанное, могу предложить совет. В паре с этим скриптом можно найти еще какой-то для определения устройства. Чтобы на мобильных устройствах данный скрипт отключался.

Добавлено: 21 Апреля 2021 06:51:40 Добавил: Андрей Ковальчук

PHP: Curl

Итак, Curl - это проект, целью которого является интерпретация URL и выборка из них данных, за проект Curl отвечает Дениел Стенберг (Daniel Stenberg). Почти все сценарии, использующие Curl - схожи по своему принципу:

1. создание ресурса Curl функцией curl_init
2. установку параметров с помощью функции curl_setopt
3. выполнение запроса с помощью curl_exec.

Недавно в проект Curl был добавлен так называемый мультиинтерфейс. Теперь РНР поддерживает и эти функции, но они ещё сыроваты.


void curl_close(resource curl)
Функция curl_close освобождает память, занятую ресурсом Curl,
string curlerror(resource curl)
Функция curlerror возвращает описание последней ошибки для заданного ресурса Curl.
integer curl_errno(resource curl)
Функция curl_errno возвращает номер последней ошибки, сгенерированной заданным ресурсом Curl.В таблице ниже приведены константы РНР, представляющие коды ошибок, возвращаемые функцией curl_errno()
Коды ошибок, возвращаемые CURL функциями
Константа Описание
CURLE_ABORTED_BY_CALLBACK Функция callback прервала операцию
CURLE_BAD_CALLING_ORDER Неверный порядок вызова функций
CURLE_BAD_FUNCTION_ARGUMENT Неверный параметр функции
CURLE_BAD_PASSWORD_ENTERED Введен неправильный пароль
CURLE_COULDNT_CONNECT Невозможно подключиться к узлу
CURLE_COULDNT_RESOLVE_HOST Невозможно обнаружить узел
CURLE_COULDNT_RESOLVE_PROXY Невозможно обнаружить прокси-сервер
CURLE_FAILED_INIT Ошибка инициализации
CURLE_FILE_COULDNT_READ_FILE Нельзя прочитать файл
CURLE_FTP_ACCESS_DENIED Во время FTP-операции доступ невозможен
CURLE_FTP_BAD_DOWNLOAD_RESUME Неудачное продолжение FTP-загрузки
CURLE_FTP_CANT_GET_HOST Невозможно обнаружить FTP-узел
CURLE_FTP_CANT_RECONNECT Невозможно повторно подключиться к FTP-серверу
CURLE_FTP_COULDNT_GET_SIZE Ошибка FTP-команды SIZE
CURLE_FTP_COULDNT_RETR_FILE Невозможно найти файл по FTP-соединению
CURLE_FTP_COULDNT_SET_ASCII Невозможно установить режим ASCII
CURLE_FTP_COULDNT_SET_BINARY Невозможно установить режим BINARY
CURLE_FTP_COULDNT_STOR_FILE Ошибка FTP-команды STOR
CURLE_FTP_COULDNT_USE_REST Ошибка FTP-команды REST
CURLE_FTP_PORT_FAILED Ошибка FTP-команды PORT
CURLE_FTP_QUOTE_ERROR Ошибка FTP-команды QUOTE
CURLE_FTP_USER_PASSWORD_INCORRECT Неправильная пара пользователь-пароль для FTP-соединения
CURLE_FTP_WEIRD_227_FORMAT Нераспознанный ответ на FTP-команду 227
CURLE_FTP_WEIRD_PASS_REPLY Нераспознанный ответ на FTP-команду PASS
CURLE_FTP_WEIRD_PASV_REPLY Нераспознанный ответ на FTP-команду PASV
CURLE_FTP_WEIRD_SERVER_REPLY Нераспознанный ответ FTPrcepeepa
CURLE_FTP_WEIRD_USER_REPLY Нераспознанный ответ USER FTP-протокола
CURLE_FTP_WRITE_ERROR FTP-сервер сообщает о проблемах при выполнении операций записи
CURLE_FUNCTION_NOT_FOUND Функция LDAP не найдена
CURLE_HTTP_NOT_FOUND HTTP-страница не найдена
CURLE_HTTP_POST_ERROR Ошибка HTTP-команды post
CURLE_HTTP_RANGE_ERROR Ошибка диапазона HTTP
CURLE_LDAP_CANNOT_BIND Ошибка связи библиотеки LDAP
CURLE_LDAP_SEARCH_FAILED Ошибка поиска библиотеки LDAP
CURLE_LIBRARY_NOT_FOUND Библиотека LDAP не найдена
CURLE_MALFORMAT_USER Неправильно задано имя пользователя
CURLE_OK Ошибок нет
CURLE_OPERATION_TIMEOUTED Тайм-аут операции
CURLE_OUT_OF_MEMORY Не хватает памяти
CURLE_PARTIAL_FILE Была передана только часть файла
CURLE_READ_ERROR Ошибка чтения на локальном устройстве
CURLE_SSL_CONNECT_ERROR Ошибка SSL-соединения
CURLE_SSL_PEER_CERTIFICATE Непроверенный сертификат SSL
CURLE_TOO_MANY_REDIRECTS Слишком много переадресаций
CURLE_UNKNOWN_TELNET_OPTION Задан неизвестный параметр утилиты TELNET
CURLE_UNSUPPORTED_PROTOCOL Протокол не поддерживается
CURLE_UNSUPPORTED_PROTOCOL Протокол не поддерживается
CURLE_URL_MALFORMAT Ошибочный URL
CURLE_URL_MALFORMAT_USER Ошибочный URL пользователя
CURLE_WRITE_ERROR Ошибка записи на локальном устройстве
boolean/string curl_ехес(resource curl)
Функция curl_ехес предназначена для выполнения запроса. В зависимости от параметра CURLOPT_RETURNTRANSFER функция curl_ехес возвращает логическое значение или запрашиваемые данные см. пример ниже.
CURL отправка POST запроса:
<?php
  if(!($curl = curl_init()))
  {
    exit ('Невозможно инициализировать ресурс Curl!');
  }
   
  //Настроить процессор поиска php.net для выполнения запроса post 
  curl_setopt($curl, CURLOPT_URL, 'http://www.php.net/search.php');
  curl_setopt($curl, CURLOPT_RETURNTRANSFER,  TRUE);
  curl_setopt($curl, CURLOPT_POST, TRUE);
  curl_setopt($curl, CURLOPT_POSTFIELDS, 'lang=en_US&Zend API&show=nosource');
   
  //сделать запрос
  $results = curl_exec($curl);
   
  print('<pre>');
  print(htmlentities($results));
  print ('</pre>');
?>

string curl_getinfo(resource curl, integer info)
Функция curl_getinfо применяется для поиска информации о Curl-запросе. В таблице ниже приведен перечень констант, используемых в аргументе info.
Заметьте!

Авторы Curl не стали плодить множество функций, для работы, они пошли другим путём. В скрипте используются несколько функций, а вот их поведение, и скрипта в целом, настраивается с помощью функции curl_setopt и её констант - их то как раз хренова куча! Так что Ctrl + F вам в помощь - я специально не стал разбивать материал на несколько страниц, что бы пользуясь встроенной функцией поиска, в вашем браузере, вы могли найти описание нужной константы на одной странице.

Перечень констант, используемых в аргументе curl_getinfo
Константа Описание
CURLINFO_CONNECT_TIME Время, затраченное на соединение
CURLINFO_CONTENT_LENGTH_DOWNLOAD Значение заголовка HTTP-заголовка Content-length
CURLINFO_CONTENT_LENGTH_UPLOAD Размер выгруженного файла
CURLINFO_CONTENT_TYPE Значение HTTP-заголовка Content - type
CURLINFO_EFFECTIVE_URL URL, использованный во время последнего запроса
CURLINFO_FILETIME Если Curl может определять время изменения запрошенного файла, оно может устанавливаться по временной метке Unix. Curl возвращает значение -1, если не представляется возможным получить время модификации
CURLINFO_HEADER_SIZE Длина всех HTTP-запросов в байтах
CURLINFO_HTTP_CODE HTTP-код, который возвращает сервер
CURLINFO_NAMELOOKUP_TIME Вещественное значение, отражающее время в секундах, необходимое для определения имени
CURLINFO_PRETRANSFER_TIME Вещественное значение, отражающее время в секундах, прошедшее до начала передачи
CURLINFO_REDIRECT_COUNT Количество переадресаций
CURLINFO_REDIRECT_TIME Вещественное значение, отражающее время в секундах, необходимое для всех шагов переадресации
CURLINFO_REQUEST_SIZE Размер HTTP - запроса
CURLINFO_SIZE_DOWNLOAD Общий размер загруженных данных в байтах
CURLINFO_SIZE_UPLOAD Всего выгружено байт
CURLINFO_SPEED_DOWNLOAD Скорость всех загрузок в байтах в секунду
CURLINFO_SPEED_UPLOAD Скорость всех выгрузок в байтах в секунду
CURLINFO_SSL_VERIFYRESULT Результат проверки соединения при SSL-запросе
CURLINFO_STARTTRANSFER_TIME Время, затраченное на начало передачи
CURLINFO_TOTAL_TIME Вещественное значение, отражающее время в секундах, необходимое для завершения передачи за исключением времени, необходимого для подключения
<?php
 
  //принять стартовую страницу узла Zend
  $curl = curl_init('http://www.zend.com/');
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
  curl_exec($curl);
    
  //получить различную информацию
  echo 'CURLINFO_CONNECT_TIME: '.curl_getinfo($curl, CURLINFO_CONNECT_TIME).'<br />';
  echo 'CURLINFO_CONTENT_LENGTH_DOWNLOAD: '.curl_getinfo($curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD).'<br />';
  echo 'CURLINFO_CONTENT_LENGTH_UPLOAD: '.curl_getinfo($curl, CURLINFO_CONTENT_LENGTH_UPLOAD).'<br />';
  echo 'CURLINFO_CONTENT_TYPE: '.curl_getinfo($curl, CURLINFO_CONTENT_TYPE).'<br />';
  echo 'CURLINFO_EFFECTIVE_URL: '.curl_getinfo($curl,  CURLINFO_EFFECTIVE_URL).'<br />';
  echo 'CURLINFO_FILETIME: '.curl_getinfo($curl, CURLINFO_FILETIME).'<br />';
  echo 'CURLINFO_HEADER_SIZE: '.curl_getinfo($curl,  CURLINFO_HEADER_SIZE).'<br />';
  echo 'CURLINFO_HTTP_CODE: '.curl_getinfo($curl,  CURLINFO_HTTP_CODE).'<br />';
  echo 'CURLINFO_NAMELOOKUP_TIME: '.curl_getinfo($curl, CURLINFO_NAMELOOKUP_TIME).'<br />';
  echo 'CURLINFO_PRETRANSFER_TIME: '.curl_getinfo($curl,  CURLINFO_PRETRANSFER_TIME).'<br />';
  echo 'CURLINFO_REDIRECT_COUNT: '.curl_getinfo($curl,  CURLINFO_REDIRECT_COUNT).'<br />';
  echo 'CURLINFO_REDIRECT_TIME: '.curl_getinfo($curl,  CURLINFO_REDIRECT_TIME).'<br />';
  echo 'CURLINFO_REQUEST_SIZE: '.curl_getinfo($curl, CURLINFO_REQUEST_SIZE).'<br />';
  echo 'CURLINFO_SIZE_DOWNLOAD: '.curl_getinfo($curl,  CURLINFO_SIZE_DOWNLOAD).'<br />';
  echo 'CURLINFO_SIZE_UPLOAD: '.curl_getinfo($curl,  CURLINFO_SIZE_UPLOAD).'<br />';
  echo 'CURLINFO_SPEED_DOWNLOAD: '.curl_getinfo($curl,  CURLINFO_SPEED_DOWNLOAD).'<br />';
  echo 'CURLINFO_SPEED_UPLOAD: '.curl_getinfo($curl,  CURLINFO_SPEED_UPLOAD).'<br />';
  echo 'CURLINFO_SSL_VERIFYRESULT: '.curl_getinfo($curl,  CURLINFO_SSL_VERIFYRESULT).'<br />';
  echo 'CURLINFO_STARTTRANSFER_TIME: '.curl_getinfo($curl, CURLINFO_STARTTRANSFER_TIME).'<br />';
  echo 'CURLINFO_TOTAL_TIME: '.curl_getinfo($curl, CURLINFO_TOTAL_TIME).'<br />';
 
?>

resource curl_init(string url)
Функция curl_init используется для создания дескриптора ресурса Curl. Необязательный аргумент url устанавливает параметр CURLOPT_URL.
integer curl_multi_add_handle(resource multi, resource curl)
Функция curl_milti_add_handle добавляет обычный ресурс Curl в мульти ресурсный стек и возвращает код состояния.
curl_multi_close(resource multi)
Функция curl_milti_close закрывает мультиресурс Curl и вызывает функцию curl_multi_cleanup.
integer curl_multi_exec (resource multi)
Функция curl_milti_exec читает и записывает данные на все сокеты мультиресурсного стека. Вызывает функцию curl_milti_perform.
string curl_multigetcontent(resource multi)
Функция curl_multi_getcontent возвращает содержимое из мультиресурса.
array curl_multi_lnfo_read(resource multi)
Функция curl_multi_lnfo_read возвращает массив, содержащий информацию о мультиресурсе.
resource curl_milti_init()
Функция curl_milti_init возвращает ресурс, указывающий на мульти интерфейс.
integer curl_multi_remove_handle(resource multi, resource curl)
Функция curl_multi_remove_handle удаляет обычный ресурс Curl из мульти ресурсного стека. Возвращает код состояния.
curl_multi_select(resource multi, integer timeout)
Функция multi_select выполняет выбор из библиотеки языка С по набору ресурсов Curl в мульти ресурсном стеке. Передается необязательный аргумент timeout.
string curl_version()
Функция curl_version предназначена для определения версии работающей библиотеки Curl.
boolean curl_setopt(resource curl, string option, value setting)
Функция curl_setopt позволяет сделать настройку соединения Curl до выполнения функции curl_exec. Необходимо передавать дескриптор ресурса Curl, созданного функцией curl_init. В таблице ниже, перечислены константы для этой функции
Константа Описание
CURLOPT_BINARYTRANSFER Используется с параметром, CURLOPT_RETURNTRANSFER. Это гарантирует возвращение двоичного значения
CURLOPT_CAINFO Указывает путь к файлу, содержащему один и более сертификатов, необходимых для проверки другого узла. Этот параметр действует только вместе с параметром CURLOPT_SSL_VERIFYPEER
CURLOPT_CAPATH Указывает путь к файлу, содержащему один или более сертификатов, необходимых для проверки другого узла. Этот параметр действует только вместе с параметром CURLOPT_SSL_VERIFYPEER
CURLOPT_CLOSEPOLICY Используется для установки политики закрытия соединения при его заполнении. Может иметь значение CURLCLOSEPOLICY_LEAST_RECENTLY_USED или CURLCLOSEPOLICY_OLDEST
CURLOPT_CONNECTTIMEOUT Задает максимальное время ожидания соединения в секундах
CURLOPT_COOKIE Используется для задания файлов cookie в запросе. Он задается в виде строки со знаком равенства между именем файла cookie и значением value. Имена файлов cookie разделяются точкой с запятой. Например, cookiel=valueA; cookiel=valueB задает два файла cookie: cookiel и cookie2
CURLOPT_COOKIEFILE Путь, по которому размещаются файлы cookie по запросам. Этот файл может соответствовать формату Netscape Navigator или обычному формату HTTP-заголовка
CURLOPT_COOKIEJAR Задает путь к файлу, в котором сохраняются файлы cookie. Curl сохраняет любые файлы cookie, которые он получает во время запроса к этому файлу. Затем этот файл можно указывать в параметре CURLOPT_COOKIEFILE
CURLOPT_CRLF При значении TRUE Curl преобразует символы новой строки, характерные для ОС Unix, в пары символов "возврат каретки перевод строки"
CURLOPT_CUSTOMREQUEST Используется для отсылки альтернативной команды при HTTP-запросе. В нем указывается только команда, а не вся строка запроса
CURLOPT_DNS_CACHE_TIMEOUT Параметр работы с кэшем поиска имен узлов. В нем задается время хранения имени в кэше (в секундах)
CURLOPT_DNS_USE_GLOBAL_CACHE При значении TRUE Curl разделяет кэш поиска имен узлов. Этот параметр не является защищенным с точки зрения потоков
CURLOPT_EGDSOCKET Задает путь к сокету Entropy Gathering Daemon. Curl использует его в качестве генератора случайных чисел, который используется для SSL
CURLOPT_FAILONERROR При значении TRUE коды HTTP-ответов больше 300 не приводят к возвращению страниц, обычно возвращаемых сервером
CURLOPT_FILE Предназначен для вывода сообщений в файл, а не в браузер
CURLOPT_FILETIME При значении TRUE Curl делает попытку получить время модификации запрошенного файла
CURLOPT_FOLLOWLOCATION При значении TRUE Curl просматривает заголовки переадресации, возвращаемые HTTP-серверами. Установите в TRUE, что бы Curl следовал редиректам.
CURLOPT_FORBID_REUSE При значении TRUE Curl закрывает соединение после завершения обработки запроса
CURLOPT_FRESH_CONNECT При значении TRUE Curl независимо от наличия соответствующего соединения в кэше создает новое соединение
CURLOPT_FTPAPPEND При значении TRUE Curl при операции FTP выгрузки, вместо перезаписи выполняет добавление
CURLOPT_FTPLISTONLY При значении TRUE Curl возвращает перечень файлов, хранящихся в FTP-каталоге
CURLOPT_FTPPORT Задает настройки FTP-команды PORT, при этом запрашивается соединение с сервером. В этом параметре указываются IP-адрес, имя узла, имя сетевого интерфейса. Для использования адреса, используемого по умолчанию, указывается -
CURLOPT_FTP_USE_EPSV По умолчанию, при FTP-передачах в пассивном режиме, Curl использует команду EPSV. Для того чтобы запретить использование этой команды, данный параметр должен иметь значение FALSE
CURLOPT_HEADER При значении TRUE Curl включает в вывод заголовки
CURLOPT_HEADERFUNCTION Задает имя функции, которую вызывает Curl для каждого вызванного HTTP-заголовка. Эта функция должна принимать два аргумента: ресурс Curl и строку, содержащую полный заголовок
CURLOPT_HTTPGET При значении TRUE Curl в HTTP-передачах использует метод GET. Это может понадобиться только при повторном использовании ресурса Curl
CURLOPT_HTTPHEADER Позволяет отправить HTTP-заголовки, прописанные вручную. Заголовки должны быть помещены в массив
CURLOPT_HTTPPROXYTUNNEL При значении TRUE Curl передает все запросы, пользуясь туннелем на прокси-сервере
CURLOPT_HTTP_VERSION Используется для того, чтобы вынудить Curl использовать соответствующую версию протокола HTTP. Значение CURL_HTTP_VERSION_NONE задается, если право выбора предоставляется Curl. Для того чтобы использовалась версия HTTP/1.0, необходимо задать значение CURL_HTTP_VERSION_1_0. Для того чтобы использовалась версия HTTP/1.1, необходимо задать значение CURL_HTTP_VERSION_l_l
CURLOPT_INFILE Если в этом параметре указан открытый файловый поток, то Curl будет считывать туда ввод из файла
CURLOPT_INFILESIZE Используется для определения размера выгружаемого файла
CURLOPT_INTERFACE Задает имя используемого интерфейса. Можно задавать имя интерфейса, имя узла или IP-адрес
CURLOPT_KRB4LEVEL Для FTP-передач с помощью этого параметра можно указать уровень безопасности Kerberos. Значением этого параметра могут быть такие строки: clear, safe, confidential и private. Установка значения FALSE отключит безопасность Kerberos
CURLOPT_LOW_SPEED_LIMIT Используется для снижения ограничения скорости передачи, которая задается в байтах в секунду. Если скорость передачи упадет ниже предела, заданного параметром CURLOPT_LOW_SPEED_TIME, Curl прервет передачу
CURLOPT_LOW_SPEED_TIME Предназначен для снижения ограничения скорости передачи и используется с параметром CURLOPT_LOW_SPEED_LIMIT
CURLOPT_MAXCONNECTS Задает размер кэша соединения
CURLOPT_MAXREDIRS Используется для задания максимального количества переадресаций
CURLOPT_MUTE При значении TRUE PHP не генерирует вывод на браузер при выполнении функций Curl
CURLOPT_NETRC При значении TRUE Curl при аутентификации пользователей просматривает путь ~/.netrc
CURLOPT_NOBODY При значении TRUE Curl исключает из вывода основное тело HTTP-страницы
CURLOPT_NOPROGRESS При значении FALSE Curl отображает индикатор работы. По умолчанию этот параметр имеет значение TRUE
CURLOPT_PASSWDFUNCTION Используется для указания имени функции обработки запросов паролей. Эта функция должна принимать три аргумента: ресурс Curl, подсказку пароля, отправленную сервером, и ссылку, куда необходимо поместить пароль. Эта функция возвращает нуль в случае успешного завершения работы и значение, отличное от нуля, в случае возникновения ошибки. Для восстановления стандартной функциональности для этого параметра устанавливается значение FALSE
CURLOPT_PORT Используется для задания номера порта, применяемого в запросах
CURLOPT_POST При значении TRUE Curl делает HTTP-запрос POST с помощью кодировки application/x-www-form-urlencoded
CURLOPT_POSTFIELDS Предназначен для передачи всех данных операции post. Поля форматируются строго в соответствии с порядком приема. Например, строка apple=l&ball=red&cat=45.56 будет соответствовать трем полям: apple, ball и cat
CURLOPT_POSTQUOTE Передает массив FTP-команд, выполняемых после главного запроса
CURLOPT_PROXY Устанавливается на прокси-сервере
CURLOPT_PROXYUSERPWD Используется для установки имени пользователя и его пароля, необходимых для прокси-сервера. При этом используется формат username; password
CURLOPT_PUT При значении TRUE Curl выполняет HTTP-запрос PUT. При этом необходимо задавать параметры CURLOPT INFILE и CURLOPT INFILESIZE
CURLOPT_QUOTE Задает массив FTP-команд, выполняемых до главного запроса
CURLOPT_RANDOM_FILE Задает путь к файлу, который будет читать Curl для задания начальных значений генератора случайных величин SSL
CURLOPT_RANGE Используется для задания диапазона заголовка, отправленного HTTP-сервером. Передается строка, содержащая смещение начала и конца строки в байтах, разделенные тире. При указании нескольких диапазонов они разделяются запятыми, например 100-150,233-502. О диапазонах можно узнать подробнее в спецификации протокола HTTP 1.1
CURLOPT_READFUNCTION Задает имя функции передачи данных другой стороне соединения. Эта функция должна принимать два аргумента: ресурс Curl и строку ссылки. Копирует данные в строку ссылки и возвращает количество байт. Возвращение нулевого значения свидетельствует о достижении конца файла
CURLOPT_REFERER Используется для задания значения поля Ref erer, передаваемого в HTTP-запросах
CURLOPT_RESUME_FROM Предназначен для возобновления передачи. Смещение задается в байтах
CURLOPT_RETURNTRANSFER Обычно Curl отправляет результаты команд непосредственно в браузер. Для получения результатов в виде возвращаемого значения утилиты curl_exec для этого параметра устанавливается значение TRUE
CURLOPT_SSLCERT Задает путь к сертификату SSL в формате РЕМ (Privacy Enhanced Mail)
CURLOPT_SSLCERTPASSWD Задает пароль, необходимый для чтения сертификата SSL, заданного параметром CURLOPT_SSLCERT
CURLOPT_SSLENGINE Задает имя процессора SSL, который будет использоваться для частных ключей
CURLOPT_SSLENGINE_DEFAULT Задает имя процессора SSL, который будет использоваться в большинстве случаев, исключая частные ключи
CURLOPT_SSLKEY Задает путь к частному ключу. По умолчанию задается тип РЕМ, который может быть изменен параметром CURLOPT_SSLKEYTYPE
CURLOPT_SSLKEYPASSWD Задает пароль, необходимый для использования с частным ключом, заданным параметром CURLOPT_SSLKEY
CURLOPT_SSLKEYTYPE Задает тип частного ключа, заданного параметром CURLOPT_SSLKEY. Тип определяется одной из следующих строк: РЕМ, DER или ENG
CURLOPT_SSLVERSION Предназначен для активизации второй или третьей версии SSL. Обычно Curl "угадывает" соответствующую версию протокола
CURLOPT_SSL_CIPHER_LIST Используется для определения списка шифров, которые будут использоваться при SSL-соединениях. Названия шифров разделяются точкой с запятой. Этот список задается в процессе компиляции OpenSSL
CURLOPT_SSL_VERIFYHOST Для этого параметра устанавливается значение 1, если Curl проверяет имя сертификата SSL, или значение 2, для того чтобы было достаточно простого совпадения с именем узла
CURLOPT_SSL_VERIFYPEER При значении TRUE Curl делает попытку проверить идентичность соединения с помощью сертификатов, заданных параметром CURLOPT_CAINFO
CURLOPT_STDERR Задает открытый файловый поток, куда будут перенаправлены сообщения об ошибках
CURLOPT_TIMECONDITION Предназначен для активизации условий передачи на основании анализа времени последней модификации удаленного файла. Для задания временного значения, которое будет использоваться для проверки этого условия, используется параметр CURLOPT_TIMEVALUE. Для соблюдения требования модификации файла с определенного момента времени используется параметр TIMECOND_IFMODSINCE. Для соблюдения требования отсутствия изменений файла с определенного момента времени используется параметр TIMECOND_ISUNMODSINCE
CURLOPT_TIMEOUT Задает максимальное время в секундах, на протяжении которого может выполняться Curl-операция
CURLOPT_TIMEVALUE Устанавливает время в стандартном формате временной метки Unix, который используется параметром CURLOPT_TIMECONDITION
CURLOPT_TRANSFERTEXT При значении TRUE Curl осуществляет FTP-передачи в формате ASCII и в тексте в формате LDAP вместо HTML
CURLOPT_UPLOAD При значении TRUE Curl выполняет НТТР-выгрузку. Необходимо установить CURLOPT_INFILE и CURLOPT_INFILESIZE
CURLOPT_URL Задает исполняемый URL. Этот параметр можно установить и с помощью утилиты curl_init
CURLOPT_USERAGENT Устанавливает значение поля User-agent, переданное в HTTP-запросах
CURLOPT_USERPWD Задает имя пользователя и его пароль, необходимый для соединения. При этом используется формат username:password
CURLOPT_VERBOSE При значении TRUE Curl выводит сообщение о состоянии
CURLOPT_WRITEFUNCTION Задает имя функции, которая будет получать данные при указанном соединении. Эта функция принимает два аргумента: ресурс Curl и строку данных. Она возвращает количество обработанных байтов данных. Если возвращаемое значение не соответствует количеству переданных данных, Curl сообщает об ошибке
CURLOPT_WRITEHEADER Задает открытый файловый поток, который будет получать заголовки. Значением option задается ресурс, возвращаемый функцией fopen

Добавлено: 14 Июня 2019 20:12:48 Добавил: Андрей Ковальчук

Простые правила работы с UTF-8 кодировкой в PHP

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

Я думаю, не надо рассказывать, что UTF-8 уже является стандартом для большинства web-приложений и пока не планируется особых замен. Но на текущий момент множество начинающих "познавателей" PHP сталкиваются с проблемами в этом месте и постят кучу топиков в связи с тем, что PHP пока не поддерживает кодировку UTF-8 "из коробки" и приносит вышеуказанным людям кучу непонимания и проблем. Кракозяблики, кракозяблики, кракозяблики... Проблема с UTF-8... Что делать? Помогите - слышно тут и там... На самом деле всё очень просто. Ниже я опишу несколько правил, которые позволяют избавиться от "проблем" с UTF-8 при разработке приложений на PHP.

Для начала, когда, пользуясь здравым смыслом, мы решаем работать с UTF-8 для простоты дела мы утверждаем, что от ныне абсолютно всё у нас будет храниться, писаться, выводиться в UTF-8. Второе, что нам будет нужно, это наличие php_mbstring расширения на сервере, которое предоставляет функции для работы со строками в многобайтном режиме. Для тех, кто в танке это функции, которые начинаются на mb_. Если ваш хостинг провайдер не потрудился установить у себя это расширение - смело выбирайте другого. Итак, если если ваш php поддерживает mb_ функции - пол дела сделано. Остальные пол дела для работы с UTF-8 в PHP заключаются в следующем:

1. В начале работы скрипта необходимо указать кодировку входа и выхода с помощью функции mb_internal_encoding:

Listing №1 (PHP)

mb_internal_encoding('UTF-8');

Также это позволит определить кодировку по умолчанию для всех mb-функций, которым нужно её указывать.

2. При работе со строками, преобразованиями символов, необходимо использовать функции работы со строками из расширения php_mbstring. Это, например, такие как mb_substr, mb_strpos, mb_strlen и т.п. или же те функции, которые безопасны для обработки данных в двоичной форме, такие как explode, str_replace и т.д. Эта особенность указана в мануале по этим функциям. Иногда нельзя просто заменить функцию на её mb-аналог, потому как такой там просто нет. Например, ucfirst, которая преобразует первый символ строки в верхний регистр. Решение тут очень простое - изящной комбинацией mb_substr и mb_strtoupper получаем необходимый результат. Попробуйте проделать это сами )). Также в этот пункт стоит отнести такие функции как htmlspecialchars, htmlentities и т.п., которые производят различные преобразования строк. Пожалуйста, передавайте в них третий аргумент $charset в виде строки 'UTF-8'.

3. Так как мы используем строки символов в самих скриптах, то строго необходимо сохранять их в UTF-8 кодировке. Это также относиться ко всем остальным файлам, которые вы используете - HTML шаблоны, различные подключаемые файлы, текстовые и т.д. Это всё должен делать ваш редактор кода. Согласуйте с ним эти вещи. Но смотрите внимательно, что бы он кодировал или преобразовывал файлы в UTF-8 без особой метки, называемой BOM. Так будет намного лучше.

4. Немаловажным фактором, является работа браузера с кодировкой UTF-8, а точнее с данными, которые вы посылаете ему, и теми данными, которые он отсылает вам. Для того, что бы браузер правильно понимал в какой кодировке вы отправили ему данные нужно отсылать заголовок с указанием кодировки. Это можно сделать так:

Listing №2 (PHP)
header('Content-Type: text/html; charset=UTF-8');

Только не забудьте отправлять этот заголовок до какого либо вывода с помощью echo, print или просто пробелов в начале скрипта до тега <?php или где то между парой этих тегов ?> <?php. Ну, а для того, чтобы браузер отсылал вам данные в нужной кодировке, нужно помечать сам html документ соответствующим мета-тегом. Например таким:

Listing №3 (HTML)
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

5. Наверное больше всего трудностей вызывает работа с базой данных. Обычно используется MySQL СУРБД. Тут всё тоже очень просто. Перед тем как послать и принимать данные нужно сказать серверу базы данных, что вы будете отсылать ей данные в кодировке UTF-8. Не вдаваясь в особые подробности, скажу лишь, что перед любым обращением к базе лишь единожды, т.е. один раз, нужно выполнить два запроса. Вот они:

Listing №4 (SQL)
SET NAMES "utf8"
-- и
SET CHARACTER SET "utf8"

Поверьте, она с радостью воспримет ваш призыв. Но не только обмениваться, а и хранить данные нужно соответственно. Посему изначально создавайте поля, хранящие строки в UTF-8 кодировке. Я думаю, вам не составит труда указать это при создании таблицы ;)


Ну вот и всё. Придерживаясь этих правил, и, я думаю, вы больше не будете встречать каких-то проблем с UTF-8 в PHP, по крайней мере до выхода шестой его версии. Хотя я знаю некоторые ситуации, но эта заметка не про них.

Добавлено: 08 Августа 2018 07:58:54 Добавил: Андрей Ковальчук

Мой родной PHP шаблонизатор

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

Итак, какой функционал я хочу? Для начала, это должно быть довольно простое решение без всяких рюшиков типа "куча хелперов". Конечно, кому-то это нужно, но у меня обычно всё проще, поэтому обойдёмся. То есть, должно быть независимое решение в виде одного класса. Сразу скажу, что кеширование результатов тоже упустим, дабы не отвлекаться от сути, да и задача эта в большинстве случаев принадлежит другим. Кому надо, легко может его прикрутить как внутри так и снаружи, используя этот материал. Далее я также хочу спокойно выполнять такие вещи как:

Удобное извлечение переменных в шаблоне
Вставка шаблона в шаблон
Корректная работа с HTML кодом
Режим отладки
Этим списком, думаю, я буду полностью удовлетворен.

Начнем с переменных. Тут, как и везде далее все просто - магический метод __get нам поможет обращаться к переменной по ее имени. Всё начинает выглядеть примерно так:


Listing №1 (PHP)

class Template {
  /**
   * @var array
   */
  private $_Vars;
  /**
   * @var string
   */
  private $_Name;
  /**
   * @var string
   */
  private $_Path;
 
  public function __construct()
  {
    $this->_Name = '';
    $this->_Vars = array();
    $this->_Path = '';
  }
  /**
   * @param string $VarName имя переменной
   * @param string $VarValue значение переменной
   * @return Template
   */
  public function AddVar($VarName, $VarValue)
  {
    if ($VarName !== '') {
      $this->_Vars[$VarName] = $VarValue;
    }
    return $this;
  }
  /**
   * @param string $Varname имя переменной
   * @return mixed
   */
  public function __get($VarName)
  {
    if (array_key_exists($VarName, $this->_Vars)) {
      if ($this->_Vars[$VarName] instanceof Template) {
        return $this->_Vars[$VarName]->Prepare();
      }
      else{
        return $this->_Vars[$VarName];
      }
    }
    return NULL;
  }
  /**
   * @var string $Name имя шаблона
   */
  public function SetName($Name)
  {
    $this->_Name = $Name;
  }
  /**
   * @var string $Path путь к шаблону(ам)
   */
  public function SetPath($Path)
  {
    $this->_Path = $Path;
  }
}


Теперь двумя следующими методами обеспечим корректность выводимых данных. Первый заботиться об спецсимволах HTML, а второй о валидности ccылок:


Listing №2 (PHP)
  /**
   * @param mixed $Var
   * @return mixed
   */
  protected function EscapeHtml($Var)
  {
    if(is_array($Var)) {
      foreach($Var as &$VarItem) {
        $VarItem = $this->EscapeHtml($VarItem);
      }
    }
    else {
      $Var = htmlspecialchars($Var, ENT_QUOTES);
    }
    return $Var;
  }
  /**
   * @param mixed $Var
   * @return mixed
   */
  protected function EscapeUrl($Var)
  {
    if(is_array($Var)) {
      foreach($Var as &$VarItem) {
        $VarItem = $this->EscapeUrl($Var);
      }
    }
    else {
      $Var = htmlentities($Var, ENT_QUOTES);
    }
    return $Var;
  }


Идем дальше.

Что я подразумеваю под словами "режим отладки"? Это, скорее всего, больше похоже на режим удобного чтения. В таком режиме на выходе мы получим код шаблона в том же виде, что и до его парсинга, то есть со всеми табуляциями, переводами строк и прочей нечистью. Это довольно удобно, потому что native шаблоны могут сложно восприниматься и то и дело приходится всё табулировать и переносить. В противоположность этому режиму будет обычный рабочий режим, где ненужные символы будут удалены за ненадобностью. За это будет отвечать метод _Zip, а за переключение между режимами статический метод SetDebug:


Listing №3 (PHP)
  /**
   * Свойство хранящее текущий режим
   * @static
   * @var bool
   */
  private static $_Debug = FALSE;
  /**
   * Вкл/выкл дебаг режим
   * @static
   * @param bool $TrueOrFalse TRUE
   */
  public static function SetDebug($TrueOrFalse = TRUE)
  {
    self::$_Debug = $TrueOrFalse;
  }
  /**
   * Возвращает $Text с удаленными "\t", "\n" и "\r"
   * @param  string $Text
   * @return string
   */
  private function _Zip($Text)
  {
    return (empty($Text)) ? $Text : str_replace(array("\t", "\n", "\r"), '', $Text);
  }


Теперь нам нужен метод Prepare, который выполнит всю основную работу, но не выведет данные, а возвратит их строкой. Это нам понадобиться для реализации вставки шаблона в шаблон, которая и происходит при извлечении переменной в шаблоне (метод __get). Думаю, без буферизации вывода тут не обойтись:


Listing №4 (PHP)
  /**
   * @return string
   */
  public function Prepare()
  {
    if (file_exists($this->_Path . $this->_Name)) {
      ob_start();
      ${__CLASS__} = $this;
      include $this->_Path . $this->_Name;
      unset(${__CLASS__});
      return (self::$_Debug) ? ob_get_clean() : $this->_Zip(ob_get_clean());
    }
    else {
      throw new Exception('Template file "' . $this->_Path . $this->_Name . '" does not exists');
    }
  }


Сначала проверяем наличие шаблона. Затем "по родному" парсим шаблон и возвращаем контент из буфера, очистив ненужные символы, если это необходимо. Также, меня очень напрягает писать в шаблонах $this, поэтому я решил писать там $Template. Мне так очень нравится.

Наконец, добавив метод непосредственного вывода, получаем нужный "полный фарш":


Listing №5 (PHP)
class Template {
  /**
   * @var string
   */
  private $_Name;
  /**
   * @var array
   */
  private $_Vars;
  /**
   * @var string
   */
  private $_Path;
  /**
   * @static
   * @var bool
   */
  private static $_Debug = FALSE;
  /**
   * @static
   * @param bool $TrueOrFalse TRUE
   */
  public static function SetDebug($TrueOrFalse = TRUE)
  {
    self::$_Debug = $TrueOrFalse;
  }
 
  public function __construct()
  {
    $this->_Name = '';
    $this->_Vars = array();
    $this->_Path = '';
  }
  /**
   * @param string $VarName
   * @param string $VarValue
   * @return Template
   */
  public function AddVar($VarName, $VarValue)
  {
    if ($VarName !== '') {
      $this->_Vars[$VarName] = $VarValue;
    }
    return $this;
  }
  /**
   * @param string $Varname
   * @return mixed
   */
  public function __get($VarName)
  {
    if (array_key_exists($VarName, $this->_Vars)) {
      if ($this->_Vars[$VarName] instanceof Template) {
        return $this->_Vars[$VarName]->Prepare();
      }
      else{
        return $this->_Vars[$VarName];
      }
    }
    return NULL;
  }
  /**
   * @var string $Name
   */
  public function SetName($Name)
  {
    $this->_Name = $Name;
  }
  /**
   * @var string $Path
   */
  public function SetPath($Path)
  {
    $this->_Path = $Path;
  }
  /**
   * @return string
   */
  public function Prepare()
  {
    if (file_exists($this->_Path . $this->_Name)) {
      ob_start();
      ${__CLASS__} = $this;
      include $this->_Path . $this->_Name;
      unset(${__CLASS__});
      return (self::$_Debug) ? ob_get_clean() : $this->_Zip(ob_get_clean());
    }
    else {
      throw new Exception('Template file "' . $this->_Path . $this->_Name . '" does not exists');
    }
  }
 
  public function Display()
  {
    print ($this->Prepare());
  }
  /**
   * @param mixed $Var
   * @return mixed
   */
  protected function EscapeHtml($Var)
  {
    if(is_array($Var)) {
      foreach($Var as &$VarItem) {
        $VarItem = $this->EscapeHtml($VarItem);
      }
    }
    else {
      $Var = htmlspecialchars($Var, ENT_QUOTES);
    }
    return $Var;
  }
  /**
   * @param mixed $Var
   * @return mixed
   */
  protected function EscapeUrl($Var)
  {
    if(is_array($Var)) {
      foreach($Var as &$VarItem) {
        $VarItem = $this->EscapeUrl($Var);
      }
    }
    else {
      $Var = htmlentities($Var, ENT_QUOTES);
    }
    return $Var;
  }
  /**
   * @param  string $Text
   * @return string
   */
  private function _Zip($Text)
  {
    return (empty($Text)) ? $Text : str_replace(array("\t", "\n", "\r"), '', $Text);
  }
}


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

Допустим, у нас есть два шаблона - главная страница page.php и список пользователей userslist.php. Нужно отобразить этот список вместе с информацией на главной странице. Поскупившись на контент, рисуем главную:


Listing №6 (PHP)
<div style="border: 1px solid #E0E3F3;">
  <?php echo $Template->UserList;?>
</div>

<a href="<?php echo $Template->EscapeUrl($Template->Link)?>">About</a>

А также список пользователей:


Listing №7 (PHP)
<div>
  <?php foreach($Template->EscapeHtml($Template->UserNames) as $UserName) { ?>
  <span style="color: #FF0000;"><?php echo $UserName;?></span><br>
  <?php } ?>
</div>


За логику отвечает некая модель:


Listing №8 (PHP)
//Подключаем шаблонизатор
require 'Template.php';
//Устанавливаем режим отладки включенным для всех шаблонов
Template::SetDebug(TRUE);
//Инициализируем подшаблон списка пользователей
$UsersListView = new Template();
$UsersListView->SetName('userslist.html');
//Список пользователей
$UserNames = array('Den Rights©', 'Dasha Nasha', 'Samuil Kakojto');
$UsersListView->AddVar('UserNames', $UserNames);
//Инициализируем главный шаблон страницы
$PageView = new Template();
$PageView->SetName('page.html');
$PageView->AddVar('UserList', $UsersListView);
$PageView->AddVar('Link', 'http://www.itdumka.com.ua/index.php?cmd=shownode&node=1');
$PageView->Display();



В результате работы с включенным режимом отладки, в отличие от обычного режима, где все будет в одну строку, мы увидим такую картину:


Listing №9 (HTML)
<div style="border: 1px solid #E0E3F3;">
  <div>
    <span style="color: #FF0000;">Den Rights©</span><br>
    <span style="color: #FF0000;">Dasha Nasha</span><br>
    <span style="color: #FF0000;">Samuil Kakojto</span><br>
  </div>
</div>
<a href="http://www.itdumka.com.ua/index.php?cmd=shownode&node=1">About</a>


Вот так.

Добавлено: 08 Августа 2018 07:45:41 Добавил: Андрей Ковальчук

Определение конструкторов объектов

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

Решение
Определите метод с тем же самым именем, что и имя класса:

class user {
function user($username, $password) {
...
}
}


Обсуждение
Если функция имеет то же имя, что и класс, она действует как конструктор:

class user {
var $username;
function user($username, $password) {
if ($this->validate_user($username, $password)) {
$this->username = $username;
}
}
}
$user = new user('Grif', 'Mistoffelees'); // используем встроенный конструтор


PHP не всегда поддерживал конструкторы. Поэтому программисты создавали псевдоконструкторы, следуя соглашению об именах и вызывая эти функции после создания объекта:

class user {
...
init($username, $password) { ... }
}
$user = new user();
$user->init($username, $password);


Увидев что-нибудь подобное, знайте, что это, скорее всего, унаследованный код.

Однако стандартное наименование всех конструкторов облегчает вызов родительских конструкторов (поскольку нет необходимости знать имя родительского класса), а также не требует модификации конструктора, если изменяется имя класса. В Zend Engine 2 соглашения об именах конструкторов были изменены, и новое имя конструктора теперь __construct(). Однако в целях обратной совместимости, если такой метод не найден, PHP пытается вызвать конструктор с тем же именем, что и имя класса.

Добавлено: 21 Июля 2018 13:37:52 Добавил: Андрей Ковальчук

Создание динамических функций

Задача
Необходимо создавать и определять функцию во время выполнения программы.

Решение
Это делается при помощи функции create_function():

$add = create_function('$i,$j', 'return $i+$j;');
$add(1, 1); // возвращает 2


Обсуждение
Первый параметр функции create_function() представляет собой строку, содержащую аргументы функции, а второй параметр – тело функции. Функция create_function() работает крайне медленно, поэтому, если возможно заранее определить функцию, лучше так и сделать.

Чаще всего функция create_function() используется при разработке пользовательских вариантов функций сортировки usort() или array_ walk():

// сортирует файлы в порядке, обратном обычному
usort($files, create_function('$a, $b', 'return strnatcmp($b, $a);'));

Добавлено: 21 Июля 2018 13:37:13 Добавил: Андрей Ковальчук

Доступ к глобальной переменной внутри функции

Задача
Необходимо получить доступ к глобальной переменной внутри функции.

Решение
Поместите глобальную переменную в локальную область видимости с помощью ключевого слова global:

function eat_fruit($fruit) {
global $chew_count;
for ($i = $chew_count; $i > 0; $i--) {
...
}
}


Или сошлитесь на нее непосредственно в массиве $GLOBALS:

function eat_fruit($fruit) {
for ($i = $GLOBALS['chew_count']; $i > 0; $i--) {
...
}
}


Обсуждение
Если внутри функции используется некоторое количество глобальных переменных, то ключевое слово global может сделать синтаксис функции более легким для понимания, особенно если глобальные переменные размещены в строках.

Глобальные переменные можно поместить в локальную область видимости, указав ключевое слово global со списком переменных, разделенных запятыми:

global $age,$gender,shoe_size;


Можно также задавать имена глобальных переменных с помощью переменных переменных:

$which_var = 'age';
global $$which_var; // ссылается на глобальную переменную $age


Однако если функция unset() вызывается для переменной, помещенной в локальную область видимости с помощью ключевого слова global, то переменная становится не установленной только внутри функции. Для того чтобы сбросить переменную в глобальной области, надо вызвать функцию unset() для элемента массива $GLOBALS:

$food = 'pizza';
$drink = 'beer';
function party() {
global $food, $drink;
unset($food); // едим пиццу
unset($GLOBALS['drink']); // пьем пиво
}
print "$food: $drink\n";
party();
print "$food: $drink\n";
pizza: beer
pizza:


Видно, что переменная $food остается той же самой, в то время как переменная $drink стала неустановленной. Объявление переменной глобальной внутри функции подобно присваиванию адреса глобальной переменной локальной переменной:

$food = &GLOBALS['food'];

Добавлено: 21 Июля 2018 13:36:38 Добавил: Андрей Ковальчук

Вызов переменных функций

Задача
Необходимо вызывать различные функции в зависимости от значения переменной.

Решение
Используйте переменные переменные:

function eat_fruit($fruit) { print "chewing $fruit."; }
$function = 'eat_fruit';
$fruit = 'kiwi';
$function($fruit); // вызов функции eat_fruit()


Обсуждение
При наличии нескольких вариантов вызова следует обратиться к ассоциативному массиву имен функций:

$dispatch = array(
'add' => 'do_add',
'commit' => 'do_commit',
'checkout' => 'do_checkout',
'update' => 'do_update'
);
$cmd = (isset($_REQUEST['command']) ? $_REQUEST['command'] : '');
if (array_key_exists($cmd, $dispatch)) {
$function = $dispatch[$cmd];
$function(); // вызываем функцию
} else {
error_log("Unknown command $cmd");
}


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

Есть и еще одно преимущество – появляется возможность связать различные команды с одной и той же функцией, так что имя может быть и длинным, и коротким:

$dispatch = array(
'add' => 'do_add',
'commit' => 'do_commit', 'ci' => 'do_commit',
'checkout' => 'do_checkout', 'co' => 'do_checkout',
'update' => 'do_update', 'up' => 'do_update'
);

Добавлено: 21 Июля 2018 13:35:51 Добавил: Андрей Ковальчук

Возвращение информации об ошибке

Задача
Необходимо показать ошибку, произошедшую в результате работы функции.

Решение
Возвращаем значение false:

function lookup($name) {
if (empty($name)) { return false; }
...
}
if (false !== lookup($name)) { /* реакция на результат поиска */ }


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

Другие возможности – это '' или 0. Однако, хотя все три значения оцениваются в операторе if как неистинные, между ними есть существенная разница. Кроме того, иногда возвращаемое значение 0 представляет значащий результат, а требуется еще возвратить сообщение об ошибке.

Например, функция strpos() возвращает позицию в строке первого вхождения подстроки. Если подстрока не найдена, то strpos() возвращает значение false, а если найдена, – позицию в виде целого числа. Итак, определить расположение подстроки можно следующим образом:

if (strpos($string, $substring)) { /* нашли! */ }


Однако если $substring обнаружена точно в начале строки $string, то возвращается значение 0. К сожалению, внутри оператора if это значение оценивается как false, поэтому условие не выполняется. Ниже показан корректный способ обработки значения, возвращаемого функцией strpos():

if (false !== strpos($string, $substring)) { /* нашли! */ }


Кроме того, значение false всегда будет ложным – в текущей версии PHP и во всех последующих. Для других значений это не гарантируется. Например, в PHP 3 функция empty('0') возвращала значение true, но оно было заменено на false в PHP 4.

Добавлено: 21 Июля 2018 13:35:07 Добавил: Андрей Ковальчук

Пропуск определенных возвращаемых значений

Задача
Функция возвращает несколько значений, но нам нужны лишь некоторые из них.

Решение
Пропустить переменные позволяет функция list():

// Интересуют только минуты
function time_parts($time) {
return explode(':', $time);
}
list(, $minute,) = time_parts('12:34:56');


Обсуждение
Это похоже на ошибку в программе, но фрагмент кода из раздела «Решение» имеет в PHP полное право на существование. Чаще всего это встречается, когда программист выполняет цикл по массиву с помощью функции each(), но нужны ему только значения массива:

while (list(,$value) = each($array)) {
process($value);
}


Однако оператор foreach позволяет написать более понятный код:

foreach ($array as $value) {
process($value);
}


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

while ($fields = fgetcsv($fh, 4096)) {
print $fields . "\n"; // третье поле
}


Если это описанная в коде функция, а не встроенная, то можно определить ключи возвращаемого массива в виде строк, поскольку трудно запомнить, например, что элементу 2 соответствует значение 'rank':

while ($fields = read_fields($filename)) {
$rank = $fields['rank']; // третье поле теперь называется rank
print "$rank\n";
}


Однако ниже показан более эффективный метод:

while (list(,,$rank,,) = fgetcsv($fh, 4096)) {
print "$rank\n"; // непосредственно присваиваем $rank
}


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

Добавлено: 21 Июля 2018 13:34:27 Добавил: Андрей Ковальчук

Возвращение более одного значения

Задача
Необходимо вернуть из функции более одного значения.

Решение
Верните массив и используйте функцию list() для разделения элементов:

function averages($stats) {
...
return array($median, $mean, $mode);
}
list($median, $mean, $mode) = averages($stats);


Обсуждение
С точки зрения производительности это не очень хорошая идея. Здесь мы имеем некоторые дополнительные накладные расходы, поскольку PHP должен сначала создать массив, а затем разобрать его. Вот что происходит в этом примере:

function time_parts($time) {
return explode(':', $time);
}
list($hour, $minute, $second) = time_parts('12:34:56');


Передается строка времени в том виде, как она выглядит на экране цифровых часов, и вызывается функция explode(), чтобы разобрать строку на части в виде элементов массива. К значению, возвращенному функцией time_parts(), применяется функция list(), чтобы извлечь каждый элемент и занести его в скалярную переменную. Это не очень эффективно, но другие возможные решения еще хуже, т. к. в результате код получится запутанным.Есть и еще одна возможность – передача значения по ссылке. Однако это несколько неуклюже и делает логически неочевидной передачу необходимых переменных функции. Например:

function time_parts($time, &$hour, &$minute, &$second) {
list($hour, $minute, $second) = explode(':', $time);
}
time_parts('12:34:56', $hour, $minute, $second);


Не имея прототипа функции, невозможно, взглянув на этот фрагмент, определить, чем же, по существу, являются переменные $hour, $minute и $second, т. е. значения, возвращаемые функцией time_parts().

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

function time_parts($time) {
global $hour, $minute, $second;
list($hour, $minute, $second) = explode(':', $time);
}
time_parts('12:34:56');


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

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

Добавлено: 21 Июля 2018 13:33:21 Добавил: Андрей Ковальчук

Возвращение значений по ссылке

Задача
Необходимо вернуть значение по ссылке, а не по значению. Это позволяет избежать создания еще одной копии переменной.

Решение
Синтаксис возвращения переменной по ссылке подобен синтаксису передачи ее по ссылке. Однако вместо размещения символа & перед параметром располагаем его перед именем функции:

function &wrap_html_tag($string, $tag = 'b') {
return "<$tag>$string</$tag>";
}


Кроме того, при вызове функции нужно использовать оператор присваивания =&, а не обычный оператор =:

$html =& wrap_html_tag($string);


Обсуждение
В отличие от передачи значения в функцию, когда аргумент передается либо по значению, либо по ссылке, в данном случае не обязательно выбирать присваивание ссылки, а можно просто взять возвращенноеb значение. Достаточно заменить обычным оператором = оператор =&, и PHP присвоит значение вместо ссылки.

Добавлено: 21 Июля 2018 12:52:26 Добавил: Андрей Ковальчук