Найти тему
Владимир Носков

PHP - графическая библиотека pChart

О проблемках роста и развития языка программирования php и некоторых аспектах сайто строения.

Пре:

Есть такая замечательная штуковина для php: pChart - набор php-классов для построения различных графиков, диаграмм и много чего, что ещё иногда называют (почему-то) бизнес графикой (хотя я бы назвал научной или математическо-статистической), но у нас ведь всем (типа)бизнес рулит. Кстати заметьте во всех учебниках "оттуда" все примеры практически сводятся к программированию различных бизнес-систем: либо банки, либо торговые, либо биржевые и тип того приложения. У нас в русской практике вот как-то всё больше про науку и типа того. Ладно... Обещал (кому - знают) выполняю: адаптировал работу сего на php 8.3.6 заодно и спичь для моего друга и его учеников по курсам php.

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

Библиотека может строить графики и различные диаграммы. И, что самое хорошее это то, что на любом варианте "графика" имеет полноценный пример того, как это всё вмонтировать на страницу вашего сайта:

Полярные график
Полярные график

Ну и так же имеется генератор (к сожалению не на все виды графиков) для экспресс рисования чего-нибудь (Sandbox). Так же все графики сопровождаются кодом для встраивания в ваши страницы сайта.

Библиотека очень легко устанавливается, как часть любого сайта или, как самостоятельный сайт - там всё для этого есть. Устанавливаете Apache, создаёте сайт и в один из его каталогов (или корень) помещаете эту библитеку - она самодостаточна - естественно, надо предварительно установить php 8.3.6 или более новую с необходимым набором модулей. Распаковываете в нужный вам каталог и всё вызываете по ссылке.

Very good work/Очень хорошая работа. Спасибо/Thanks Jean-Damien POGOLOTTI.

Амбула:

К огромному сожалению, автор этой замечательной во всех отношениях, всё-таки наверное правильнее сказать - библиотеки, последнее обновление делал в далёком уже 2014 году, хотя и сайт проекта и какое никакое движение на форуме сайта есть - все не очень понимают почему так. С тех пор и php изменился и прочее. Я этот набор использовал довольно давно, наверное ещё с 2012 года, когда надо было всякое в графиках на одном сайте показывать. Так и оставалось, где надо было работало. Но дизайнеры-прогеры самого языка php решили, что хватит и пора всё сделать по взрослому - хватит уже играться с глюками динамической типизации (как будто сразу было непонятно, что рано или поздно будут проблемы). По прошествии лет любой язык с динамической типизацией всегда вводит в том или ином виде эту самую типизацию. Хотя когда-то казалось, что автоматическая типизация это очень удобно. Типизация вообще - это присвоение типа переменных в зависимости от данных, что они хранят. Классика: int - целое - 1, 2, 3, 4... float - вещественное или числа с плавающей точкой(запятой) - 0.5, 37.5, 3.14 ..., char - символьные - то, что хранит (в виде последовательности из многих char), например слова или целые многие строки слов - как например этот текст. И куча их разновидностей, в зависимости от количества бит на каждый тип данных. В общем, как и многие интерпретируемые (или ещё их называют скриптовыми) языки - php использует динамическую (и автоматическую) типизацию, что во многом удобно, особенно после C/C++, где всегда надо указывать тип при объявлении переменных - лепи как хочешь - интерпретатор языка сам разберётся куда чего и зачем (хотя, опять же строго говоря, в С/С++, типизация не такая уж и строгая).

Например, складывать переменные с разными типамим.

В С/С++ :

int J = 1;        // целое

float F = 1.65;   // вещественное

char T = 'a';     // символ

char D = 33;      // числовое значение символа

int AA = J + D;    // и AA будет иметь тип int и значение 34 - число

char BB = J + D;  // и BB будет иметь тип char и значение 34 - символ кавычка "

int DD = J + F; // сложение целого и вещественного

Вывод на экран:

J = 1

F = 1.65

T = a

D = !

AA = 34

BB = "

JF = 2 // int+float часть нецелого исчезла

На php (символ денежной единицы $ - признак переменной в php):

$J = 1; // целое

$F = 1.65; // вещественное

$T = 'a'; // символ

$D = 33; // числовое значение символа ??? откуда это целое

$AA = $J + $D; // AA будет иметь тип int и значение 34 - число - здесь сложение

$BB = $J + $D; // BB будет иметь тип int и значение 34 - символ кавычка " ?

$JF = $J + $F; // сложение целого и вещественного

Вывод на экран:

J = 1

F = 1.65

T = a

D = 33 // где '!' - нет его потому, что откуда он про него знает? тут целое

AA = 34

BB = 34 // кавычки нет - тут целое

JF = 2.65 // а тут произошло преобразование в float и остаточек не пропал

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

В общем в php задаём переменную и присваеваем значение и интерпретатор сам разбирается какой у ней тип - удобно? Да, удобно. Но такой подход в сложных программах со сложными расчётами и взаимосвязями сущностей может давать трудно объяснимые и, что самое плохое, трудно выявляемые проблемы - даже при использовании отладчика, когда автоматическое преобразование типов внезапно превратит (в самом простом случае) вашу переменную со значением 2.5, то есть "два с половиной", которую надо сложить с 3.5 и получить 6.0, а именно: 2.5 + 3.5 = 6.0 - что верно, но внезапно бы получаете такое "2.5" + "3.5" = "2.53.5", что есть то самое, что вы не ожидали. В php это не совсем так: для сложения (склеивания) строк в php есть специальный оператор (неудачный на мой взгляд) "точка" и сложение строк выглядит так: "2.5"."3.5", что и даст "2.53.5"... но, например в python вполне на такое можно нарваться при невнимательном использовании оператора сложения - там как раз именно "+".

Для ещё более простого понимания проблемы (ага). В php типичное (хотя это характерно для любого языка программирования с динамической типизацией) - это переход от целых к вещественным и наоборот (о проблемах строковых переменных и прочих STL не будем, там - своё не менее интересное бывает) - например, переменной "А" присвоено значение "$А=123.56789012345678903" и переменная "$B=321" - то есть в конце вы не поставили точку и не дописали 0, если бы значение было 321.0 (формальный признак float), то по мнению (и реально) php "В" было бы, как и "A" автоматически типа float, но значение присвоено без точки, а значит оно int. Сложение данных чисел в общем никакого криминала не имеет - к целой части "А" прибавится "B" и "хвост" от А так же прибавится к новой переменной, например "С": $С = $А + $B или вы "А" решили увеличить на величину B: $А = $А + $B, будет $С = 444.56789012345678903 (или A, во втором случае). Всё нормально. Но, что будет, если, например, вы захотите увеличить "B" на величину "А"? В языках с жёсткой типизацией получите ошибку при присвоении значений с разным типом или в С/С++ предупреждение о такой операции (зависит от настроек компилятора - можно настроить и на генерацию ошибки и останов компиляции). Ведь $B = $B + $A при присвоении и увеличении значения "В" на величину "А", то на целую часть "А" увеличит "В", а "хвост" от "А" куда? Тут есть варианты: он либо просто пропадёт - в типе int просто нет места для его хранения, а вот в php "B" автоматически изменит тип и станет float, казалось бы всё прекрасно и удобно - ничего не потеряется и всё красиво.

Но хорошо это или плохо? С одной стороны мы не теряем "хвост", с другой получаем явную неоднозначность поведения данных, которую вы в общем не контролируете и это вполне может повлечь явные и неявные проблемы. Вот представьте, что вычисляется нечто, где тысячи таких сложений и, например, при сложении float 2.5 c int 2 будет либо (в зависимости от умолчаний округления) 4 - то есть вам, грубо говоря, (мы ж тут про бизнес) не довесили полкило или наоборот к примеру будет 5, то есть перевесили полкило - кому плохо от такого поведения думаю понятно. А если будет тысячи таких сложений? Пусть 1000 таких операций и будет уже недо- или пере- вес на 500кг... Как бы совсем нехорошо. Кстати, в случае старых версий php никаких препятствий не было для такого сложения - ваши проблемы, где вы будете искать вашу недо- или пере- веску... Ещё случай: вы оперируете с массивом ABC и обращение к "ячейкам" массива идёт по индексам $ABC[$i], где индекс это всегда целое число (про случай ассоциативных массивов не говорим) - здесь в массиве, как в случае с посадочными местами в автобусе - число сидений строго определено - вы можете сесть на 1, 2, 3, 4 ... место, но не можете сесть между 3 и 4, например на 3.5 - его просто в автобусе нет и быть не может. Но вот, как в php бывает, индекс массива $i может изменить тип с int на float при невнимательном программировании без учёта поведения интерпретатора, а, как правило, программист, привыкнув к автоматической типизации, рано или поздно так или иначе попадаёт на такие проблемы. К примеру, индекс вычисляется в зависимости от какого-то условия, каким-либо математическим выражением, то вполне можно получить индекс 3.5 - и понадеявшись на автоматическую типизацию получить ... А, вот, что получить? Значение (данные) ячейки массива целое и как автоматическое приведение типов поступит? Брать с ячейки 3.5 невозможно - она не существует, тогда "автоматизируем" на уровне интерпретатора делая приведение не целого к целому: и, что будет? $i = 3 или

$i = 4? Ведь нецелое надо привести к целому, а каким образом? Полученный индекс нужно округлить - для этого есть автоматические варианты от интерпретатора или специальные функции из библиотеки языка: функция floor это сделает в меньшую, а функция ceil в большую сторону или round - она может и так и так, как попросите, но согасно принятым математическим правилам. Так вот версия php c версией больше 8.0, а версия 8.3+ строго (это всё-таки, как настроен сам php) трактует такое, как предупреждение или, если вы принципиальны до жути, то, как (я за этот вариант) ошибку исполнения. Все библиотечные функции php 8.3+ переписаны со "строгой" типизацией (насколько, всё-таки, вообще можно говорить о строгости типизации в php) - параметры функций, если не объявлен их тип, принимают любые значения, но если объявлены типы, то потоните в количестве предупреждений и, возможно, потом фатальных ошибок, при различных ситуациях - проблема с индексами самая очевидная, а остальные даже трудно вообразить. Так, что правильное по-сути дело - упорядочивание и наведение порядка и контроля объявляемых переменных в php, добавляет изрядное количество трудодней на исправление кода согласно новым принятым правилам для всяких движков сайтов и прочих приложений на php, которые рассчитаны на старое поведение ранних версий php. Представляю их веселые лица.

В общем совсем всё просто. Да.

Зачем этот экзерсис и, как это относится pChart? А результатом всех правок в этой библиотеке стало такое информационное табло для полярной станции (и сам сайт):

Информацинное табло о погоде - по нему кстати можно сдать СИН (стандартное сообщение о погоде).
Информацинное табло о погоде - по нему кстати можно сдать СИН (стандартное сообщение о погоде).

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

Предыдущая версия работала примерно так:

-11

И собственно суть:

Библиотека pChart была написана для ранних версий php - установив же интерпретатор и библиотеки более новой версии получаем то самое адово количество ошибок и предупреждений, что ведёт к неработоспособности того или иного в движке сайта.

Для адаптации данной библиотеки (прямо скажем лукаво не мудствовал) добавил класс pWraps - класс-враппер (обёртка) - кстати вот она некоторая польза от ООП (и классов, как таковых, в принципе) - для всех вызовов pChart к библиотекам языка, где ввели строгую типизацию параметров. Весь pChart на новые стандарты языка не переписывал (может и сделаю, но пока не требуется - разве, что только для ублажения внутреннего перфекциониста - там правда около 20000+ строк кода - не просто будет). Класс обёртка pWraps подменяет прямые вызовы библиотечных функций на функции класса враппера с простой и необходимой обработкой передаваемых параметров - работоспособность библиотеки обеспечивается, а дальше будем смотреть - ошибок нет - будут поправим.

Сделаны так же правки самого кода библиотеки на перевод вызовов с библиотечных на враперные и правки некоторых явных ошибок в самом коде pChart (коих надо сказать было не много да и те нефатальные) и приведение оформления классов к современным требованиям языка в версии 8.3.6.

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

Проверено всё на

  • FreeBSD 13.2
  • Apache-2.4.58
  • php 8.3.6

и так же соответствующие библиотеки и расширения для Аpache и php. В других связках не проверял, но так, как для нормальной работы с Аpache потребовалась правка кода pChart, то работоспособность например для nginx так же будет. Для Linux думаю проблем тоже не должно быть - ПО там такое же, как и в Windows с соответствующим случаю проинсталлированном ПО.

В работе над так же помогало:

  • Eclipse Version: 2022-06 (4.24) Build id: I20240310-0620
  • Java-runtime version: 20.0.2+9-1
  • PHP Eclipse PDT 8.2.0 + Xdebug 3.3.1

Исправленный pChart-2.1.4-p8.3.6.tar.7z. на яндекс диске:

https://disk.yandex.ru/d/ZVHfiCvlofB9xg

Оригинал тут: http://www.pchart.net

Удачи и наилучшие пожелания.

-12

Update: Было сообщение, что "SandBox" в браузере Chromium не выдаёт код примера (в остальных случаях нормально). В Firefox полностью нормально. Буду проверять. Проверьте и сообщите, если это так.