Просматривая различные архивы скриптов и читая популярные статьи об использовании PHP я обнаружил, что такая полезная вещь как объектно-ориентриванное программирование практически не используется в повседневной жизни, хотя дает несомненные преимущества как в части моделирования и постановки задачи, так и в реализации.
Здесь я хотел бы показать как с помощью объектно-ориентированного подхода можно создать удобный в применении и дальнейшем усовершенствовании скрипт для работы с графикой.
Необходимые требования: установленный модуль PHP для поддержки графической библиотеки GD.
Итак, нам надо создать универсальный класс для работы с графикой. Назовем наш корневой класс Graph. Его задача - создать картинку определенного формата, напечатать HTML-код с заголовком и телом картинки, возвратить ошибку в случае неудачи. Картинки используют различные цвета для отображения графики, поэтому создадим класс Color. Больше никаких корневых классов не требуется. Теперь для каждого типа графики создадим свой класс-наследник от Graph, т.е. конкретизируем, что за картинку мы хотим создать. В нашей ситуации мы хотим создать графики столбцовой, круговой и линейной диаграмм. Для этого создадим классы Barchart, Piechart и Linechart соответственно. В дальнейшем для создания новых типов графиков следует просто создать соответсвующий новый класс.
Далее представлен листинг класса Graph:
<?php
class Graph {
var $format=\'gif\';
var $error = \'\';
var $image = \'\';
var $filename = \'\';
// Возвращает изображение в формате GD-графики или выводит его в файл
function image($filename=\'\') {
$this->error = \'\';
if (empty($this->image)) { $this->image = $this->createImage(); }
if (!empty($filename)) {
switch ($this->format) {
case \'gif\':
imageGif($this->image,$filename);
break;
case \'jpeg\':
imageJpeg($this->image,$filename);
break;
case \'png\':
imagePng($this->image,$filename);
break;
}
}
return $this->image;
}
// Устанавливает формат вывода изображения
function setFormat($format=\'gif\') {
$this->error = \'\';
if (preg_match(\'/gif|jpeg|png/\',$format)) {
$this->format = $format;
}
else {
$this->error = \'Invalid image format. Use gif,jpeg or png instead.\';
}
}
// Печатает изображение с заголовком на стандартный вывод
function printHTML() {
$this->error = \'\';
if (empty($this->image)) { $this->image = $this->createImage(); }
Header(\'Content-type: image/\'.$this->format);
switch ($this->format) {
case \'gif\':
imageGif($this->image);
break;
case \'jpeg\':
imageJpeg($this->image);
break;
case \'png\':
imagePng($this->image);
break;
}
}
// Создание изображения
function createImage() {
$im = \'\';
return $im;
}
// Возвращает последнюю ошибку
function error() {
return $this->error;
}
}
?>
Класс Color:
<?php
class Color {
var $R=255;
var $G=255;
var $B=255;
function Color($R=255,$G=255,$B=255) {
$this->R = $R;
$this->G = $G;
$this->B = $B;
}
}
?>
Круговая диаграмма (Piechart) работает только с одним набором чисел, по которым в дальнейшем вычисляется соотношение каждого числа к общей сумме. Для ввода чисел создадим метод addSegment(value:double,color:Color) . Созданием и отображением класса занимаются унаследованные от класса Graph методы createImage() и printHTML() .
Далее представлен листинг класса Piechart:
<?php
// Наследуем класс Piechart от класса Graph
class Piechart extends Graph {
var $segments = array();
var $img_w = 100;
var $img_h = 100;
var $radius = 50;
var $bgcolor;
// Конструктор класса
// На вход подаются радиус окружности и цвет фона
function Piechart($rad,$bgcolor=\'\') {
$this->img_w = $rad*2;
$this->img_h = $rad*2;
$this->radius = $rad;
if (!empty($bgcolor)) {
$this->bgcolor = $bgcolor;
}
else {
$this->bgcolor = new Color(0,0,255);
}
}
// Добавляет сегмент диаграммы и цвет его отрисовки
function addSegment($val=0,$color) {
$this->segments[$val]=$color;
}
// Переопределенный метод создания изображения
function createImage() {
$total = 0;
reset($this->segments);
while(list($key,$val)=each($this->segments)) {
$total += $key;
}
$cx = floor($this->img_w/2);
$cy = floor($this->img_h/2);
// Создаем изображение с помощью библиотеки GD
$im = ImageCreate($this->img_w,$this->img_h);
$blue = ImageColorAllocate($im,0,0,255);
$white = ImageColorAllocate($im,255,255,255);
$bgc = ImageColorAllocate($im,$this->bgcolor->R,$this->bgcolor->G,$this->bgcolor->B);
ImageColorTransparent($im,$bgc);
ImageFill($im,$cx,$cy,$bgc);
ImageArc($im,$cx,$cy,$this->img_w,$this->img_h,0,360,$blue);
ImageLine($im,$cx,$cy,$cx+$this->img_w,$cy,$blue);
// Отрисовываем саму круговую диаграмму
reset($this->segments);
$prev = 0;
$sm = 0;
while(list($key,$val)=each($this->segments)) {
// процентное соотношение
$percent = $key/$total*100;
// угол
$angle = (360/100)*$percent;
$sm += $angle;
// угол в радианах
$rad = deg2rad($sm);
$dx = ($this->radius) * cos($rad);
$dy = ($this->radius) * sin($rad);
ImageLine($im,$cx,$cy,$cx+$dx,$cy-$dy,$blue);
$gamma = $rad - $prev;
$delta = $prev + $gamma/2;
$dxp = ($this->radius/2) * cos($delta);
$dyp = ($this->radius/2) * sin($delta);
$color = ImageColorAllocate($im,$val->R,$val->G,$val->B);
ImageFill($im,$cx+$dxp,$cy-$dyp,$color);
$prev = $rad;
}
$this->image = $im;
return $im;
}
}
?>
Линейная и столбцовая диаграммы, в отличие от круговой, работают с массивами данных, поэтому на вход должны принимать массив чисел и цвет для отображения. Для этого создадим метод addArray(values:array of double,color:Color) . Так же как и в круговой диаграмме методы для создания и отображения наследуются из класса Graph.
Далее представлен листинг класса Linechart:
<?php
class Linechart extends Graph {
var $arrays = array();
var $colors = array();
var $img_w = 100;
var $img_h = 100;
var $bgcolor=\'\';
var $arr_counter = 0;
// Конструктор класса
// на вход подаются размеры изображения и цвет фона
function Linechart($img_w=100,$img_h=100,$bgcolor=\'\') {
$this->img_w = $img_w;
$this->img_h = $img_h;
if (!empty($bgcolor)) {
$this->bgcolor = $bgcolor;
}
else {
$this->bgcolor = new Color(0,0,255);
}
}
// Добавляет массив данных и цвет его отрисовки
function addArray($val=array(),$color) {
$this->arr_counter++;
$this->arrays[$this->arr_counter] = array();
$this->arrays[$this->arr_counter] = $val;
$this->colors[$this->arr_counter] = $color;
}
// Переопределяет создание изображения
function createImage() {
reset($this->arrays);
error_reporting(63);
$cnmax = 0;
$ar = $this->arrays[$this->arr_counter];
$min = $ar[0];
$max = $ar[0];
while(list($key,$val)=each($this->arrays)) {
$tt = count($val);
if ($tt > $cnmax) { $cnmax = $tt; }
$armax = $val[0];
$armin = $val[0];
while (list($key1,$val1)=each($val)) {
if ($val1 > $armax) { $armax = $val1; }
if ($val1 < $armin) { $armin = $val1; }
}
if ($armax > $max) { $max = $armax; }
if ($armin < $min) { $min = $armin; }
}
$kx = $this->img_w/$cnmax;
$ky = $this->img_h/(abs($max)+abs($min));
$cx = floor($this->img_w/2);
$cy = floor($this->img_h/2);
$im = ImageCreate($this->img_w,$this->img_h);
$blue = ImageColorAllocate($im,0,0,255);
$white = ImageColorAllocate($im,255,255,255);
$bgc = ImageColorAllocate($im,$this->bgcolor->R,$this->bgcolor->G,$this->bgcolor->B);
ImageColorTransparent($im,$bgc);
ImageFill($im,$cx,$cy,$bgc);
ImageRectangle($im,0,0,$this->img_w-1,$this->img_h-1,$blue);
$ay = abs($max);
// ось x
ImageLine($im,0,$ay*$ky,$this->img_w-1,$ay*$ky,$blue);
//рисуем точки
reset($this->arrays);
while(list($key,$val)=each($this->arrays)) {
$i = 0;
reset($val);
$prx = 0;
$pry = $ay*$ky;
$val_cl = $this->colors[$key];
while (list($key1,$val1)=each($val)) {
$i++;
$px = floor($i*$kx);
$py = floor($ky*($ay-$val1));
$color = ImageColorAllocate($im,$val_cl->R,$val_cl->G,$val_cl->B);
ImageSetPixel($im,$px,$py,$blue);
ImageLine($im,$prx,$pry,$px,$py,$color);
$prx = $px;
$pry = $py;
}
}
$this->image = $im;
return $im;
}
}
?>
Далее представлен листинг класса Barchart:
<?php
class Barchart extends Graph {
var $arrays = array();
var $colors = array();
var $img_w = 100;
var $img_h = 100;
var $bgcolor = \'\';
var $arr_counter = 0;
// Конструктор класса
// на вход подаются размеры изображения и цвет фона
function Barchart($img_w=100,$img_h=100,$bgcolor=\'\') {
$this->img_w = $img_w;
$this->img_h = $img_h;
if (!empty($bgcolor)) {
$this->bgcolor = $bgcolor;
}
else {
$this->bgcolor = new Color(0,0,255);
}
}
// Добавляет массив данных и цвет его отрисовки
function addArray($val=array(),$color) {
$this->arr_counter++;
$this->arrays[$this->arr_counter] = array();
$this->arrays[$this->arr_counter] = $val;
$this->colors[$this->arr_counter] = $color;
}
// Переопределяет создание изображения
function createImage() {
reset($this->arrays);
error_reporting(63);
// коеффициент растяга
$k = 3;
// расстояние между столбцами
$dbs = 4;
$cnmax = 0;
$ar = $this->arrays[$this->arr_counter];
$min = $ar[0];
$max = $ar[0];
while(list($key,$val)=each($this->arrays)) {
$tt = count($val);
if ($tt > $cnmax) { $cnmax = $tt; }
$armax = $val[0];
$armin = $val[0];
while (list($key1,$val1)=each($val)) {
if ($val1 > $armax) { $armax = $val1; }
if ($val1 < $armin) { $armin = $val1; }
}
if ($armax > $max) { $max = $armax; }
if ($armin < $min) { $min = $armin; }
}
// ширина столбца
$bw = ($this->img_w-$dbs*($this->arr_counter-1)*$cnmax)/($k*($cnmax+1)+$this->arr_counter*$cnmax);
$kx = ($this->img_w-$this->arr_counter*($dbs+$k*$bw))/($cnmax);
$ky = $this->img_h/(abs($max)+abs($min));
$cx = floor($this->img_w/2);
$cy = floor($this->img_h/2);
$im = ImageCreate($this->img_w,$this->img_h);
$blue = ImageColorAllocate($im,0,0,255);
$white = ImageColorAllocate($im,255,255,255);
$bgc = ImageColorAllocate($im,$this->bgcolor->R,$this->bgcolor->G,$this->bgcolor->B);
ImageColorTransparent($im,$bgc);
ImageFill($im,$cx,$cy,$bgc);
ImageRectangle($im,0,0,$this->img_w-1,$this->img_h-1,$blue);
$ay = abs($max);
// ось x
ImageLine($im,0,$ay*$ky,$this->img_w-1,$ay*$ky,$blue);
//рисуем точки
reset($this->arrays);
$db=0;
while(list($key,$val)=each($this->arrays)) {
$i = 0;
reset($val);
$val_cl = $this->colors[$key];
while (list($key1,$val1)=each($val)) {
$i++;
$px = floor($i*$kx);
$py = floor($ky*($ay-$val1));
$color = ImageColorAllocate($im,$val_cl->R,$val_cl->G,$val_cl->B);
ImageRectangle($im,$px+$db,$ay*$ky,$px+$bw+$db,$py,$color);
ImageFill($im,floor($px+$db+$bw/2),floor($ky*($ay-$val1/2)),$color);
}
$db += ($bw+$dbs);
}
$this->image = $im;
return $im;
}
}
?>
Приведем тестовые примеры использования каждого типа графиков:
test_pie.php
<?php
require \"Color.php\";
require \"Graph.php\";
require \"Piechart.php\";
$white = new Color(255,255,255);
$blue = new Color(0,0,255);
$red = new Color(255,0,0);
$green = new Color(0,255,0);
$pie = new Piechart(100,$white);
$pie->addSegment(23,$white);
$pie->addSegment(120,$blue);
$pie->addSegment(60,$red);
$pie->addSegment(30,$green);
$pie->setFormat(\'gif\');
$pie->printHTML();
?>
test_line.php
<?php
require \"Color.php\";
require \"Graph.php\";
require \"Linechart.php\";
$white = new Color(255,255,255);
$blue = new Color(0,0,255);
$red = new Color(255,0,0);
$green = new Color(0,255,0);
$line = new Linechart(200,200,$white);
$ar1 = array(12,13,16,2,5,10,0,1,2,8);
$ar2 = array(20,13,-1,2,23,11,1,3,4,9);
$ar3 = array(11,15,20,12,6,-10,28,30,10,0);
$line->addArray($ar1, $white);
$line->addArray($ar2, $green);
$line->addArray($ar3, $red);
$line->printHTML();
?>
test_bar.php
<?php
require \"Color.php\";
require \"Graph.php\";
require \"Barchart.php\";
$white = new Color(255,255,255);
$blue = new Color(0,0,255);
$red = new Color(255,0,0);
$green = new Color(0,255,0);
$bar = new Barchart(500,500,$white);
$ar1 = array(12,13,16,2,5,10,0,1,2,8);
$ar2 = array(20,13,-1,2,23,11,1,3,4,9);
$ar3 = array(11,15,20,12,6,-10,28,30,10,0);
$bar->addArray($ar1, $white);
$bar->addArray($ar2, $green);
$bar->addArray($ar3, $red);
$bar->printHTML();
?>
Чего же мы добились создав эту библиотеку классов: во-первых, простота использования, ведь согласитесь, что рисовать с ней графики гораздо проще, чем набив весь текст на одной странице, кроме того понять текст Вашей программы гораздо легче; во-вторых, независимость и повторное использование кода, что означает непересекаемость методов и свойств класса с вашими процедурами и, в-третьих, масштабируемость и совместимость, т.е. возможность введения дополнительных видов графов и единая терминология их создания. Итак, мы разработали удобную для использования и модификации универсальную библиотеку классов для работы с графикой. Надеюсь, на этом примере премущества объектно-ориентированного подхода стали Вам понятнее.