Сер 282013
 

Практикум PHP. Умная загрузка изображений.

Во время создания сайтов на PHP очень часто приходится работать с изображениями. Информация о работе с изображениями на PHP довольно разрозненная. Существует много громоздких и разноликих примеров.

Но на просторах интернета было найдено очень интересное решение.

Colin VEROT написал замечательный класс, который всего в несколько строк обработает загрузку изображения или выполнит действие над уже существующей фотографией на сервере. Класс основан на gd library и имеет множество настроек. Отлично справляется с php 4 + версии и изображениями png, gif, jpeg форматов.

Вот немногое из того, что класс умеет:

Обработка изображений из массива $_FILES
Работа над изображениями на сервере
Изменение размеров изображений (с сохранением пропорций или с «растягиванием» изображений). При этом доступны такие фильтры, как:
Не изменять/изменять изображения меньше указанной длинны/высоты
Не изменять/изменять изображения больше указанной длинны/высоты
Дополнение изображений указанным цветом до определённых размеров
Преобразования всех входящих изображений к одному типу
Переименование изображений или их перезапись, если указанный путь сохранения уже содержит такой файл
«Обрезка» области изображений по указанным параметрам
Наложение водяного знака watermark а и текста
Даёт управлять именем нового изображения, его расширением, правами, устанавливаемыми на файл
Обладает множеством встроенных эффектов – управляет яркостью и контрастностью, имеет несколько предопределённых эффектов (отражение, замена цветов – инвертирование, оттенки серого, размытие, поворот, рамки, скос и т.п.).
Кроме загрузки изображений, управляется и с загрузкой файлов (может контролировать загружаемые файлы по mime типам).
Скрипт ведёт статистику изменений, которую можно просмотреть в режиме отладки. И множество переменных для контроля.
А теперь давайте рассмотрим примеры обработки изображений из массива $_FILES с помощью данного класса, и почувствуйте, насколько всё просто:

Примечание автора: более подробные примеры и полное описание на английском Вы можете обнаружить прямо вначале файла класса class.upload.php

Для начала скачаем класс и поместим его в web директорию нашего сайта, подключаем его в нужном месте:

require_once(dirname(__FILE__)."/class.upload.php");

Создадим сам объект класса:

$handle = new Upload($file);

Где $file – путь к файлу, может быть так же объектом из массива $_FILES.

После чего укажем, куда сохранять файл:

$savepath = dirname(__FILE__)."/images";

Обработаем и сохраним файл:

$handle->Process($savepath);
$handle->Clean();

Вот и всё! Файл загружен. Осталось только проверить Его наличие по указанному пути $savepath с помощью функции file_exists().

bool file_exists ( string $filename ) – проверяет существование файла, указанного в строковой переменной $filename, если файл существует: возвращает true, иначе – false.

Переменные, которые можно просмотреть до обработки изображения и вызова [имя объекта]->Process([путь сохранения файла]): и до вызова [имя объекта]->clean():

$handle-> file_src_name – имя файла до обработки;
$handle->file_src_name_body – имя файла без расширения до обработки;
$handle->file_src_name_ext – расширение файла до обработки;
$handle->file_src_pathname – полный путь к файлу;
$handle->file_src_size – размер файла;
$handle->file_src_error – ошибка во время обработки/загрузки файла;
$handle->file_is_image – является ли файл изображением?
$handle->image_src_x – ширина изображения?
$handle->image_src_y – высота изображения?
$handle->image_src_pixels – сколько пикселей всего?
$handle->image_src_type – тип изображения.
Переменные, которые можно просмотреть после обработки изображения и вызова [имя объекта]->Process([путь сохранения файла]): и до вызова [имя объекта]->clean():

$handle-> file_dst_path – путь к файлу после обработки;
$handle->dst_name_body – имя файла без расширения после обработки;
$handle->file_dst_name_ext – расширение изображения после обработки;
$handle->file_dst_name – имя файла после обработки;
$handle->file_dst_pathname – полный путь к файлу после обработки;
$handle->image_dst_x – ширина обработанного изображения;
$handle->image_convert – тип преобразованного изображения.
А теперь давайте рассмотрим настройки, которые мы задаём после инициализации объекта класса Upload и до вызова [имя объекта]->Process([путь сохранения файла]):

Загрузить только «изображения»:

$handle->allowed = array("image/*");

Добавим ещё разрешение на загрузку для zip архивов:

$handle->allowed[]   = "application/x-zip";

А что делать, если Файл с таким именем уже существует? Не проблема:

$handle->file_auto_rename = true;

И файл будет переименован.
А как задать имя файла?

$handle->file_new_name_body  =  $image_name;

Преобразовать все загружаемые изображения к типу jpeg?

$handle->image_convert       = "jpg";

А как перезаписать файл, если он уже существует?

$handle->file_overwrite      = true;

Добавить к изображению Watermark слева внизу? Нет проблем!

$handle->image_watermark                 = PATH_SITE."/".$watermark_bigger;
$handle->image_watermark_position            = "LR";

Изменить размеры изображения до 500 на 500 с соблюдением пропорций?

$handle->image_resize        = true;
$handle->image_x             = 500;
$handle->image_y             = 500;
$handle->image_ratio         = true;

«Дополнить» изображения до указанного размера фоновым цветом при изменении размера?

$handle->image_resize            = true;
$handle->image_ratio_fill        = true;
$handle->image_ratio             = true;
$handle->image_background_color  = "#FF00FF";

Запретить изменять размеры изображений меньше 500 пикселей в ширину и длину?

$handle->image_ratio_no_zoom_in  = true;

«Растянуть» изображение без соблюдения пропорций?

$handle->image_ratio             = false;

Сменить права на файл после его обработки?

$handle->dir_chmod = 0777;

Попробуем сменить размер загружаемого файла (в байтах)?

$handle->file_max_size = "1024";

Запретим загрузку только определённых файлов по mime type?

$handle->forbidden = array("application/*");

Изменяем яркость изображения?

$handle->image_brightness = 40;

Отражаем изображение по горизонатли?

$handle->image_flip = "h";

Повернём изображение на 90 градусов?

$handle->image_rotate = 90;

Урежем изображение по заданному образцу на 50 пикселей сверху, 40 срава, 30 снизу и 20 слева?

$handle->image_crop = array(50,40,30,20);

Добавим к изображению белую рамку?

$handle->image_border = "3px";
$handle->image_border_color = "#FFFFFF";

Добавим эффект растворения к чёрному с отражением и 60 процентной прозрачностью отражения?

$handle->image_reflection_height = "25%";
$handle->image_default_color = "#000000";
$handle->image_reflection_opacity = 60;

А в следующей статье мы расширим класс с помощью curl (если поддерживается) или url wrapper ов функции fopen, file_get_contents для загрузки и сохранения изображений, расположенных на других сайтах.

Сер 282013
 

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

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

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

Итак, перейдем к сути вопроса.

Допустим, у нас есть страница, отображающая новости раздела, в контексте которой согласно id страницы $_GET[‘ limitfrom’] категории $_GET[‘id’] мы выбираем количество новостей $_GET[‘limit’] из связанной таблицы материалов `news`.

Обычно (утрировано и упращенно) это выглядит, например, так:

<?php
$id         = $_GET['id'];
$limitfrom  = $_GET['limitfrom'];
$limit      = $_GET['limit'];
$sql        = 'SELECT * FROM news WHERE category_id ='
                  . (int)$id.'  LIMIT '. (int)$limitfrom.','.(int)$limit;
$resource   = mysql_query($sql);
If($resource && is_resource(resource)){
    //обработка данных
} else {
   echo mysql_error();
   die();
}
?>

Пример «взят» не из воздуха, вот так, приблизительно, но в более сложной форме выглядит разбитие на постраничную навигацию в разделах известного новостного движка DLE. А также вот так (echo mysql_error(); die()) выводится, зачастую, в браузер посетителя возникновение mysql ошибки в Joomla 1.5 и более новых версий (чуть сложнее, но смысл остается тот же).

Здесь была осознанно допущена одна оплошность. Дело в том, что данный вариант будет работать отлично! И в повседневной работе программист не увидит выполнение echo mysql_error().

Но почему не следует выводить в браузер отладочную информацию и ошибки исполнения PHP?

Итак, первое, из за того, что мы обходимся без проверки, что же приходит со стороны клиента, например, отрицательные значения $id и $limit, которые допускаются для типа данных integer, или несуществующие значения приведут к выводу отладочной информации mysql_error(). Итак, ясным по белому, мы дадим информацию злоумышленнику о существующих таблицах и пищу для размышления, как же проще осуществить mysql injection в другом месте, где используются эти же таблицы при составлении запросов. Или же в некоторых случаях покажем посетителям техническую информацию, которую видеть они не должны. С одной стороны здесь нам помогут более строгие проверки входных данных. Перепишем этот пример с использованием выше сказанного:

 

<?php
$id         = abs((int)$_GET['id']);
$limit      = abs((int)$_GET['limit']);
$limit      = $limit ? $limit : 10;
$limitfrom  = abs((int)$_GET['limitfrom']);
$sql        = 'SELECT * FROM news '
.($id && !empty($id) ? 'WHERE category_id='. $id
: '')
.( $limitfrom && !empty($limitfrom) ?
'  LIMIT '. $limitfrom.', '. $limit.''
: '  LIMIT '. $limit.'');
$resource   = mysql_query($sql);
If($resource && is_resource(resource)){
    //обработка данных
} else {
   echo mysql_error();
   die();
}
?>

Мы предотвратили, как минимум, возникновение нескольких исключительных ситуаций. Мы знаем, что нам нужны только лишь целые положительные значения $id,$limit – поэтому используем явное приведени типов для этих переменных, оператор приведения к целому (int) и функцию получения целого числа abs(). Также мы знаем, что количество выбранных новостей не должно быть равно нулю, поэтому определяем минимальный «порог» в 10 новостей $limit = $limit ? $limit : 10;, формируя запрос, мы проверяем данные, и если они есть и отличны от 0, то добавляем их в mysql запрос по частям.

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

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

А ведь сбор такой информации бывает полезным для профилирования приложения и дальнейшего его усовершенствования (обеспечения стабильности работы). Для выявления «подводных» камней в стабильной работе сайта.

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

Какие функции и данные для логирования в PHP могут быть полезны?
trigger_error – для генерации исключения.
ini_set , error_reporting – для подавления вывода ошибок
set_error_handler – для установки собственной функции «перехвата» ошибок
register_shutdown_function – для отлова критических ошибок (которая работает, как нужно, начиная с php 5.2)
error_get_last – функция получения последней ошибки, произошедшей в скрипте, очень полезна в случае прерывания скрипта из за возникновения критической ошибки
$_SERVER – суперглобальный массив – источник информации – где и когда и какая ошибка произошла
getenv – функция для получения значения переменных среды окружения сервера, в том случае, если какие-то данные в суперглобальном массиве $_SERVER отсутствуют
Давайте напишем свой перехватичк исключительных ситуаций для дальнейшего логирования данных об ошибках. Суть заключается в следующем, мы переопределим вывод отладочной информации с помощью собственной функции в текстовый файл в определенной папке /errors, чтобы его не смогли прочесть извне, присвоим ему расширение .php (можно и при помощи запрета прямого доступа извне к этой папке, но данный метод более универсален для любых серверов и не требует дополнительной настройки окружения сервера) и добавим первой строчкой , название же зададим ему, совпадающее с текущей датой, чтобы было проще ориентироваться. В итоге один файл ошибок – одна дата.Также мы добавим «перехват» критических ошибок. И при помощи функции trigger_error для логирования непредвиденных ситуаций и генерации собственных ошибок типа E_USER:

Примечание автора: желательно подключить подобный код как можно раньше, еще на этапе инициализации созданного php приложения:

<?php
error_reporting(0);
ini_set('error_reporting','0');
ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
ini_set('ignore_repeated_errors', '1');
define ('ROOT_PATH', dirname(dirname(__FILE__))."/");
//запретить/разрешить вывод ошибок
define('_ERR_HANDLING',true);
//где будем хранить файлы ошибок
define('_ERR_DIR',ROOT_PATH.'errs/');
function error_reporting_log($error_num, $error_var=null,
$error_file=null, $error_line=null) {
$error_desc= '';
$error_desc  = 'Error';
switch ($error_num){
case E_WARNING:
$error_desc = 'E_WARNING';
break;
case E_USER_WARNING:
$error_desc = 'E_USER_WARNING';
break;
case E_NOTICE:
$error_desc = 'E_NOTICE';
break;
case E_USER_NOTICE:
$error_desc = 'E_USER_NOTICE';
break;
case E_USER_ERROR:
$error_desc = 'E_USER_ERROR';
break;
case E_ERROR:
$error_desc = 'E_USER_ERROR';
break;
default:
$error_desc  = 'E_ALL';
break;
}
$date_file = date('y-m-d H:I:S');
$logfile= LOG_FILE;
$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$date_time = date('d.m.y - H:i:s');
$ip= isset($_SERVER['REMOTE_ADDR']) &&
!empty($_SERVER['REMOTE_ADDR']) ?
$_SERVER['REMOTE_ADDR']
: getenv('REMOTE_ADDR ') ;
$from= isset($_SERVER['HTTP_REFERRER'])&&
!empty($_SERVER['HTTP_REFERRER'])?
$_SERVER['HTTP_REFERRER']
:getenv('HTTP_REFERRER');
$errortext = $error_desc.': '.$error_var."\t".'
Line: '.$error_line."\t".'
File: '.$error_file."\t".'
Link: '.$url."\t".'
Date: '.$date_time."\t".'IP: '.$ip."\t".'
FROM:'.$from."\n";
unset($from,$error_desc,
$error_var,$error_line,
$error_file,$url,$date_time,$error_write);
$secuire= '<?php die("Forbidden."); ?>';
if(is_file($logfile)&&is_writeable($logfile)){
$fp = fopen($logfile,'r');
if($fp && is_resource($fp)){
$strings= fgets($fp);
if(isset($strings)&&!empty($strings)
&&strpos($strings,$secuire)===false){
unlink($logfile);
}
fclose($fp);
};
unset($fp);
}
if(!is_file($logfile)){
$dir= dirname($logfile);
if(is_dir($dir)&&is_writable($dir)){
$fp     = fopen($logfile,'w+');
if(is_resource($fp)){
flock($fp,LOCK_EX);
fwrite($fp,$secuire."\n");
flock($fp,LOCK_UN);
fclose($fp);
$fp= null;
}
unset($dir,$fp);
}
}
unset($secuire);
if(is_file($logfile)&&!is_writable($logfile)){
chmod($logfile,0775);
}
if(is_file($logfile)&&is_writeable($logfile)){
$fp     = fopen($logfile,'a+');
if(is_resource($fp)){
flock($fp,LOCK_EX);
fwrite($fp,$errortext);
flock($fp,LOCK_UN);
fclose($fp);
$fp= null;
unset($fp);
}
}
unset($logfile);
                return true;
}
function err_handler(){
if(_ERR_HANDLING){
$error_reporting= '';
$error_reporting= ini_get('error_reporting');
$error_reporting= $error_reporting?$error_reporting:E_ALL;
error_reporting(E_ERROR);
$date_file = date('dmY').'.php';
$dir= _ERR_DIR;
$path= $dir.$date_file;
$logfile= '';
if(!is_dir($dir) || !is_writable($dir)){
if(is_dir($dir)&&!is_writable($dir)){
chmod($dir,0775);
} else if(!is_dir($dir)){
$isdir= false;
$isdir= mkdir($dir,0775);
}
if(!$isdir&&!is_writable($dir)){
$dir= ROOT_PATH;
$path= $date_file;
}
}
if(is_dir($dir) && is_writable($dir)){
if(!is_file($path)){
$fp= fopen($path,'w+');
if($fp && is_resource($fp)){
$secuire= '<?php die("Forbidden."); ?>';
flock($fp,LOCK_EX);
                fwrite($fp,$secuire."\n");
                flock($fp,LOCK_UN);
                fclose($fp);
                $fp= null;
unset($secuire);
}
}
if(is_file($path) && !is_writable($path)){
chmod($path,0775);
}
if(is_file($path) && is_writable($path)){
ini_set('display_errors',0);
set_error_handler('error_reporting_log', (E_ALL & ~E_NOTICE));
$logfile= $path;
define('LOG_FILE',$logfile);
}
unset($date_file,$dir,$path,$logfile);
}
error_reporting($error_reporting);
unset($error_reporting);
}
}
function critical_error(){
$error = error_get_last();
if(is_array($error) && sizeof($error) && isset($error['type'])
&& $error['type'] == E_ERROR && !empty($error['message'])
&& !empty($error['file']) && !empty($error['line'])){
error_reporting_log($error['type'],
$error['message'],$error['file'],$error['line']);
}
}
if(function_exists('register_shutdown_function')
&& function_exists('error_get_last')
&& _ERR_HANDLING){
register_shutdown_function('critical_error');
}
err_handler();

Что здесь происходит? При помощи функции ini_set и error_reporting мы подавляем вывод ошибок в браузер посетителя, константами определяем местоположение нашей директории для хранения логов ошибок define(‘_ERR_DIR’,ROOT_PATH.’errs/’);. Определенная в самом начале константа (bool) _ERR_HANDLING – при значении true разрешит перенаправление всех наших ошибок в файлы, при значении false дальнейшие инструкции выполняться не будут. Для перехвата самих ошибок используется функция error_reporting_log, которая принимает тип ошибки, сообщение об ошибке, имя файла, где произошла ошибка, и строку, в которой она произошла. В ней мы собираем кроме этой информации, и другие полезные данные. А именно:

$_SERVER[‘HTTP_REFERRER’] – откуда пришел посетитель, с какой страницы?
$_SERVER[‘REMOTE_ADDR’] – ip посетителя,
$_SERVER[‘HTTP_HOST’]. $_SERVER[ ‘REQUEST_URI’] – полный адрес страницы вместе со строкой запроса, на которой возникла ошибка
date(‘y-m-d H:I:S’) – текущее время возникновения ошибки.
Зачастую, подобной информации хватит с головой и для логирования и исправления ошибок, которые были не замечены на этапе разработки, так и для отлова злоумышленников. Но есть один тип ошибок, который не будет перехвачен. Это критические ошибки PHP E_ERROR, которые приводят к прерыванию php скрипта . Начиная с версии PHP 5.2 для перехвата критических ошибок можно определить собственную функцию при мопощи register_shutdown_function. Мы создаем собственную функцию для этих целей critical_error, в которой при помощи функции error_get_last получаем информацию о текущей ошибке. Так как данная функция будет всегда выполняться после завершения php скрипта, мы логируем информацию только об ошибках с типом E_ERROR.

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

Давайте на примере все того же скрипта с mysql рассмотрим принцип логирования mysql ошибок (впрочем, по такому же принципу можно построить логирование любых внештатных ситуаций):

<?php
$id         = abs((int)$_GET['id']);
$limit      = abs((int)$_GET['limit']);
$limit      = $limit ? $limit : 10;
$limitfrom  = abs((int)$_GET['limitfrom ']);
$sql        = 'SELECT * FROM news '
.($id && !empty($id) ?
'WHERE category_id='. $id
: '')
.( $limitfrom && !empty($limitfrom)
? '  LIMIT '. $limitfrom.', '. $limit.''
: '  LIMIT '. $limit.'');
$resource   = mysql_query($sql);
If($resource && is_resource(resource)){
    //обработка данных
} else {
  $string                 = print_f(
'Ошибка mysql при получении новостей из категории с ID: %d,'
  . 'на странице:  %d , '
  . 'информация о запросе:  %s,'
  . ' информация об ошибке: %s',
  $id,$limitfrom, $sql, mysql_error());
   trigger_error($string,E_USER_ERROR);
   die();
}
?>

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

Примечание автора: также для генерации и перехвата ошибок Вы можете использовать конструкции try{}catch(Exeption $e){ thrown(throw new Exception("$name contains the word name");}

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

Сер 282013
 

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

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

При заказе данной услуги, клиенту предоставляется полный контроль над выделенным сервером, что дает возможность вносить любые изменения в его настройки, инсталлировать любое программное обеспечение и, даже, операционную систему. Выделенный сервер является лучшим решением для проектов, которым не достаточно ресурсов виртуального хостинга или VPS/VDS.

К преимуществам данной услуги можно отнести то, что клиенту для работы предоставляется полнофункциональный компьютер со всеми правами администратора. Заказчик может осуществлять полный контроль над всеми процессами, файлами и пользователями в системе. У вас будет выделенный IP-адрес (некоторые хостеры предлагают насколько выделенных IP).

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

Выбрав аренду сервера в качестве хостинг площадки вы получаете возможность размещать неограниченное количество сайтов и доменов. При заказе этой услуги вы получаете отдельный компьютер выбранной конфигурации, а соответственно вы становитесь обладателем собственным дисковым пространством, оперативной памятью, процессами, отдельным подключением к сети и другими ресурсами.
Недостатком выделенного сервера является его высокая стоимость. Поэтому для вашего проекта не требуются высокие вычислительные мощности, то лучше обратить свое внимание на более дешевые альтернативы, например виртуальный сервер VPS/VDS. Также, аренда сервера требует наличие опыта администрирования. Многие хостинг-провайдеры предлагаю свои услуги по администрированию, однако это будет стоить дополнительных денег, но все же дешевле, чем нанимать штатного сотрудника.

Сер 282013
 

Итак, продолжаем разбираться с построением магазина на Virtuemart 2.0 и Joomla 2.5. На очереди веселый глюк с русскими инвойсами и pdf в Virtuemart 2.0. Дело в том, что когда происходит формирование заказа, virtuemart выставляет счет в pdf (invoice), только вот все русские буквы заменяются на знаки вопросов. Согласитесь, что это не слишком информативно для Ваших покупателей?

Сделаем маленькое отступление. Если на этапе оформления заказа Ваш Virtuemart 2.0 начинает глючить, то показывая страницу 404, то зависая, то выдавая множество ошибок, переставьте для начала пакет Virtuemart 2.0 all in one (aio), который идет в стандартной поставке с самим магазином.

Virtuemart 2.0 pdf инвойсы и русские буквы.
Итак, оказывается шрифты, которые используются для формирования счетов в самом virtuemart попросту не поддерживают кириллицу (кириллические символы). Программисты Virtuemart здесь кивают в сторону TCPDF, библиотеки, которую использует и Virtuemart для формирования pdf документов (вид для печати в pdf, virtuemart 2.0 invoce pdf и т.п.).

Итак, приведем порядок действий, как же «добавить» поддержку кириллицы в invoice pdf virtuemart 2.0.

Первое, после установки virtuemart 2.0 в папке /libraries создает подпапку /libraries/tcpdf/fonts/

Нам нужно добавить необходимые шрифты, поддерживающие кириллицу, в эту подпапку. Для этого переходим на сайт разработчиков в раздел download, качаем последнюю библиотеку для php 5 (joomla версии 2.5 больше не поддерживает php4). После того, как скачали, открываем архив, в нем папку tcpdf и папку fonts из нее копируем по пути /libraries/tcpdf (только папку шрифтов!), соглашаясь о замене всех существующих файлов. После этого мы сможем использовать следующие кириллические шрифты, например: freesans или times.

Перейдем теперь к изменению файлов virtuemart для добавления поддержки кириллицы в pdf invoice в virtuemart 2.0.

Открываем файл:

/components/com_virtuemart/controllers/invoice.php

Ищем следующее значение:

$pdf->setHeaderFont(Array('helvetica', '', 8));

Изменяем на:

$pdf->setHeaderFont(Array('freesans', '', 8));

Ищем значение:

$pdf->setFooterFont(Array('helvetica', '', 10));

Изменяем на:

$pdf->setFooterFont(Array('freesans', '', 10));

Ищем значение:

$pdf->SetFont('helvetica', '', 8, '', true);

Изменяем на:

$pdf->SetFont('freesans', '', 8, '', true);

Ищем значение:

$this->SetFont('helvetica', 'I', 8);

Изменяем на:

$this->SetFont('freesans', 'I', 8);

Дальше открываем файл

/components/com_virtuemart/helpers/vmpdf.php

Ищем следующее значение:

$this->setHeaderFont(Array('helvetica', '', 8));

Меняем на:

$this->setHeaderFont(Array('freesans', '', 8));

Ищем следующее значение:

$this->setFooterFont(Array('helvetica', '', 10));

Меняем на:

$this->setFooterFont(Array('freesans', '', 10));

Ищем следующее значение:

$this->SetFont('helvetica', '', 8, '', true);

Меняем на:

$this->SetFont('freesans', '', 8, '', true);

Ищем следующее значение:

$this->SetFont('helvetica', 'I', 8);

Меняем на:

$this->SetFont('freesans', 'I', 8);

Данная инструкция верна для virtuemart 2.0.16 + 2.0.18 a по замене кода в указанных файлах. Но что же делать, если код и версия вашего Virtuemart 2.0 отличаются?

Нужно сделать следующее: найти поиском по файлам рекурсивно в папках /components/com_virtuemart все строки, где встречается helvatica, и заменить их на freesans, или другой шрифт в формате tcpdf из папки /libraries/tcpdf/fonts (название шрифта в папке и будет названием шрифта для замены), поддерживающий кириллицу (русские символы). Можно также дополнительно осуществить поиск по методам самого класса tcpdf – SetFont,setHeaderFont,setFooterFont, и изменить шрифт, указанный в этих методах. А еще в этих методах Вы можете кроме начертания шрифтов изменить их размер. Для этого третьим параметром каждого метода можно передать целое число (высоту шрифта). Рекомендую для freesans увеличить на 1-2 пункта это значение по сравнению с helvatica. Шрифт будет крупнее и читабельнее. После всех изменений сделайте заказ и проверьте, как выглядит invoce в pdf.

Сер 282013
 

В продолжение поста об «лучших ресурсах для изучения HTML и CSS» представляем вашему вниманию подборку лучших на наш взгляд ресурсов по изучению PHP и MySQL.
Для начала дадим определения MySQL и PHP.

MySQL – система управления базами данных.

PHP (препроцессор гипертекста) – скриптовый язык, который предназначен для генерирования HTML- страниц на сервере. Широко применяется для создания сайтов, PHP значительно расширяет возможности веб-мастера и упрощает его жизнь. Знание основ PHP желательно для каждого вебмастера.

Куда выгодно вкладывать деньги в России? Неправда ли очень интересный вопрос, есть очень много мнений, куда и как правильно вложить деньги, я нашел хороший блог в котором очень много тем на тему деньги и на тему как выгодно их вложить, поэтому, кому интересно всем советую почитать этот блог.

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

PHPclub.ru/talk – это клуб разработчиков PHP. На их форуме можно найти все – от юмора PHP до теории PHP.

PHP.ru- один из самых популярных форумов PHP программистов. Есть хороший раздел по программированию на PHP для новичков. Также есть разделы форума по HTML, MySQL, CSS, Ajax и Javascript. Еще есть в наличии руководство по PHP и руководство по MySQL на русском.

PHP.su – весьма полезный сайт с кучей информации по PHP. Есть форум по PHP и раздел с учебниками по PHP для свободного скачивания.

SoftTime.ru/forum – сотрудники известной студии SoftTime написали множество полезных книг по MySQL и PHP . Также на их сайте есть форум PHP.

Askdev.ru – замечательный сервис ответов и вопросов IT специалистов, здесь помимо PHP, обсуждаются и другие различные темы.

PHP5.ru – на этом сайте можно найти самоучитель по по PHP, правда он еще в процессе написания, но уже написано несколько глав.

PHPbegun.ru – блог который будет очень полезен для новичков в PHP.

On-line-teaching.com/PHP – учебник PHP для новичков онлайн.

ruseller.com – в разделе PHP есть отличные переводы зарубежных материалов касательно PHP.

Кроме наличия авторских материалов, еще одной отличительной особенностью блогов есть возможность задавать вопросы автору. Безусловно, блоги ориентированы не только на MySQL и PHP, но и на другие смежные тематики, хотя это и к лучшему. В списке указаны только регулярно обновляющиеся блоги.

Zhilinsky.ru – блог интернет-разработчика Владимира Жилинского.

Simplecoding.org- блог о программировании Владимира Стаценко.

Samborsky.com – блог Евгения Самборского.

Англоязычные ресурсы по PHP и MySQL

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

PHP.net – официальный сайт PHP.

MySQL.com – официальный сайт MySQL.

nettuts.com – раздел PHP один из излюбленных ресурсов по PHP. Здесь систематично издаются интересные материалы с детальным описанием и готовыми файлами.

Goodphptutorials.com – здесь есть хороший выбор уроков PHP для новичков и не только.

Если кто-то знает еще какие-нибудь полезные ресурсы по изучению MySQL и PHP – указывайте их в комментариях.