Вам знакомо понятие «DLL hell»?

Конечно, если они выберут «правильную платформу» для своих приложений. А как же быть тем разработчикам, которые, не попавшись на маркетинговые уловки, продолжают развивать уже апробированные зрелые решения и пользуются фактически теми же технологиями, что и пять-семь лет назад? Да и все ли проблемы могут решить современные «умные» программные сборки? Творческий путь ИТ-специалиста нашего времени настолько непредсказуем, что на ум приходит сравнение с десантником, которого выбрасывают на неведомую территорию с одной-единственной задачей — победить в рекордно короткие сроки. Вот только сражаться наш «айтишник» вынужден практически голыми руками, настраивая программу под конкретные пожелания заказчика и принимая во внимание массу требований, причем как учтенных при создании продукта, так и появившихся много позже.

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

Сегодня на рынке ПО существуют целые программные платформы, гарантирующие прикладному программисту и конечному пользователю мощное, стабильное и универсальное решение. Такие программные комплексы позволяют не тратить время на «изобретение колеса», а изначально обеспечить поддержку современных межпрограммных интерфейсов и соответственно возможность быстро и безболезненно внести в проект изменения, связанные, скажем, со специфическими деталями предметной области, структурой решения или с использованием стороннего ПО. Безусловно, результат применения таких решений будет близок к идеалу, но не каждому разработчику такое оружие по карману, да и не для всяких целей оно сгодится.

Объективная необходимость в адаптируемости возникает в процессе развития и совершенствования любого программного решения, особенно если нужные механизмы не были заложены на стадии проектирования. Чтобы добиться нужной степени гибкости приложения, можно использовать несколько универсальных подходов, например организовать работу с собственными метаданными приложения. Эффективным будет также вынесение критичных к предметной области описания программных алгоритмов в отдельные (но легкодоступные в любое время) фрагменты. Решить подобные задачи можно с помощью встроенного интерпретируемого языка программирования. Грамотная комбинация перечисленных возможностей свидетельствует о зрелости продукта. Яркий пример в подтверждение этого — современная линейка систем MS Windows, и в частности платформа .NET, использующая решения с применением Web Forms . NET-компонентов, нестандартных атрибутов и т.д. Аналогичные подходы реализованы и в таких типах продуктов, как CRM, ERP, MES, а также во всевозможных SCADA-системах различной отраслевой направленности. Характерный представитель ERP-систем — Microsoft Axapta, имеющая собственный компилятор, вернее фактически интерпретатор, проверяющий и исполняющий код. Кроме того, важной составляющей системы Axapta являются так называемые формы, большая часть информации для которых хранится в реляционной базе данных.

Куда закапывать логику?

В данной статье основное внимание уделено программам, создаваемым в среде Delphi для платформы Win32 (она все еще актуальна для разработчиков, судя по перспективным планам компании CodeGear), хотя описываемые подходы вполне могут быть справедливы и для других вариантов.

В каждом серьезном решении есть важные фрагменты кода, уязвимые с точки зрения малейших корректировок в бизнес-процессах.
С учетом требований они могут часто меняться, а значит, не имеет смысла компилировать их «намертво» в основной исполняемый модуль. Правильнее было бы целиком поместить их в какой-нибудь текстовый файл, руководствуясь поговоркой «Подальше положишь — поближе возьмешь». Еще лучше было бы поместить в какое-то отдельное легкодоступное программное окно целый фрагмент кода, состоящего не только из статических данных, но и из описаний реализаций программных методов. Это могут быть участки алгоритма, где описываются правила обмена данными между программными объектами (формы ввода, хранилища данных, серверы приложений, внешние устройства, удаленные машины и т. д.), специфичными для конкретного коммерческого решения. Одновременно упомянутый фрагмент кода должен быть неотъемлемой частью некоего более общего алгоритма. Именно такие условия и определяют идею использования встроенного скриптового языка.

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

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

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

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

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

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

Пожалуй, наиболее популярным сценарным языком сейчас можно считать PHP. И дело не только в том, что он является неотъемлемой частью услуг предоставляемого интернет-хостинга (по крайней мере под Unix/Linux-платформу). В последнее время очевидной стала растущая интеграция PHP с платформой Windows, причем ориентированная на серьезные коммерческие проекты. Не последнюю роль в этих проектах играет использование PHP именно в обычных Windows-приложениях.

На сайте www.php4delphi.net  вниманию программистов предлагается изящное решение, позволяющее тесно интегрировать ваше приложение с уже готовой стандартной быстро развивающейся средой — пакетом PHP. Судя по тому, что ко времени написания статьи самый свежий выложенный пакет датировался сентябрем 2007, php4delphi продолжает развиваться с учетом поддержки последних версий PHP и среды программирования Delphi. Сейчас для свободной загрузки с сайта доступны установочные пакеты для версий Delphi, начиная c Delphi 5 и заканчивая Delphi 2007, а при возникновении проблем с установкой или использованием всегда можно задать вопрос на форуме разработчиков.

Разработчикам предлагаются две возможности интеграции PHP с Delphi — встраиваемый PHP (Embedding PHP) и расширяемый PHP (Extending PHP). Таким образом обеспечивается гибкий подход с двух сторон: во-первых, скрипт связан с основной частью программы непосредственно через компоненты, обеспечивающие (регламентирующие) интерпретацию открытого текста, а во-вторых, php4delphi позволяет быстро и без особых ухищрений создавать дополнительные библиотечные модули (библиотеки dll) для PHP; доступ к ним возможен все из того же открытого текста.

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

Компоненты, входящие в состав пакета, обеспечивают довольно богатую функциональность, предлагая среди прочего настройку режимов и параметров работы пакета PHP. Но наиболее интересные возможности конечно же предоставляют компоненты TPHPLibrary (необходим для создания библиотеки функций, реализуемых на Delphi и предназначенных для применения в скрипте) и TPHPSystemLibrary, позволяющий использовать в скрипте разнообразные функции работы с типами данных. Подробнее о библиотеках см. в приложении к статье на «Мир ПК-диске».

Кроме перечисленных компонентов, в списке шаблонов Delphi-проектов после установки php4delphi можно найти ярлык PHP Extention для упрощения создания на Delphi уже упоминавшихся расширений среды PHP.

После установки пакета php4delphi в самой среде Delphi разработчикам становятся доступными пять компонентов: TPHPEngine, TpsvPHP, TPHPLibrary, TPHPClass и TPHPSystemLibrary.

Теперь попробуем создать простое приложение для пояснения возможностей пакета php4delphi. Оно будет выполнять скрипт, указанный в текстовом поле класса Tmemo, и выводить результат его работы в диалоговое окно. Для этого поместим в режиме дизайна на форму компоненты Memo1, Button1, PHPEngine1, TpsvPHP1 (рис. 1).

Рис. 1. Простейшая форма для проверки работы компонента TpsvPHP

Приятной особенностью компонента класса TPHPEngine является то, что по умолчанию нет необходимости указывать ему путь к папке, в которой находится конфигурационный файл PHP, — он находит ее сам. Но вернемся к примеру. Предполагается, что по нажатию кнопки Button1 будет выведен результат работы скрипта, т. е. того текста, который может быть задан или изменен пользователем в окне программы уже во время ее работы. В нашем примере программа возвращает значение системной переменной окружения. Создадим обработчик для кнопки:

 

Усложним пример, продемон-стрировав работу с внешними переменными и константами, — добавим в коллекцию PHPEngine1.Constants
константу AboutApp и присвоим ей какое-нибудь строковое значение, а в psvPHP1.Variables добавим переменную Param1. При этом для проверки работы механизма работы с константами и переменными изменим сам скрипт:

Получить значение переменной Param1 после работы скрипта можно несколькими способами. Один из вариантов — изменить обработчик кнопки следующим образом:

Но обращаться к переменным, конечно, позволительно и по имени.

Теперь рассмотрим возможность обращения из скрипта к компонентам формы.

Поместим в окне Memo1 следующий скрипт:

 

И не забудем добавить на форму компонент Edit1.

В итоге получаем ожидаемый результат (рис. 2):

Рис. 2. Пример взаимодействия скрипта с VCL Delphi

Изучение работы с компонентами формы — тема отдельных исследований, а «навскидку» можно лишь заметить, что этот механизм очень далек от идеала.

Добавление функций

Добавление встроенных функций в пакете php4delphi упрощено до предела, что значительно облегчает для программиста организацию обработки данных и обмена ими между скриптом и основной частью программы. Разработчик практически освобожден от необходимости вручную описывать в исходных текстах своей программы встроенные функции с помощью различных хитроумных указателей и объявлений. Весь этот уровень от него аккуратно скрыт, что как нельзя лучше соответствует самому духу среды Delphi. Компонент класса TPHPLibrary позволяет быстро «накидать» в программу свои собственные функции, которые будут использоваться внутри скрипта, а сама их реализация станет плотью от плоти Delphi-проекта.

Эти функции окажутся связаны со скриптом через параметры и возвращаемый результат. В качестве примера напишем простую функцию, возвращающую заголовок программы. Для этого добавим в программу функцию phpfunction1, переименовав ее для наглядности в GetAppName. А реализацией функции будет код на базе автоматически сгенерированной заготовки для события OnExecute (рис. 3). Добавим только одну строчку:

Рис. 3. Пример создания простейшей функции на Delphi для расширения возможностей использования РНР-скрипта

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

 

Для этого в новую функцию добавим один параметр с типом tpString. А в скрипте не забудем поставить знак ссылки:

 

При первой попытке выполнить скрипт можно увидеть сообщение о необходимости присвоения параметру allow_call_time_pass_reference в файле php. ini значения «On». После этого изменения функция действительно начинает успешно работать.

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

Создание собственного расширения для PHP

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

Для создания дополнительного модуля с функциями или классами можно воспользоваться имеющейся заготовкой (рис. 4), которая позволяет создать проект библиотеки с отдельной невизуальной формой, чтобы вынести на нее необходимые невидимые компоненты для работы с операционной системой, базами данных и т. д. При этом в модуль все таким же легким способом можно добавлять функции (TPHPFunctions), а при использовании компонента TPHPClass  — описывать методы (TPHPClassMethod), а также и свойства как пассивные открытые члены класса с возможностью чтения/записи значений неопределенного типа.

Рис. 4. Ярлык в PHPExtention в репозитории шаблонов проектов для облегчения создания модулей расширения  

Создадим простой модуль, добавляющий возможность работы с классом PHPClass1. Для простоты ограничимся всего одним методом в нашем классе — classmethod1.

Вот его описание (обработчик для OnExecute):

 

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

Не забудем также добавить на вход метода один параметр строкового типа.

Для того чтобы скомпилированный модуль был виден для PHP-скриптов, помещаем его в директорию /ext/ пакета PHP и объявляем в php. ini

где Project1.dll — библиотека, результат компиляции вашего проекта.

Для простоты не будем в скрипте проверять модуль на загрузку. Из примеров в папке Demos пакета php4delphi вы узнаете, как это сделать правильно.

Работу модуля можно проверить прямо из предыдущего примера, поменяв содержимое скрипта (рис. 5).

Рис. 5. Скрипт для проверки работы новой функции

Пример реализации редактора PHP-скрипта на базе того же пакета php4delphi — это программа Light PHP Edit (http://sourceforge.net/projects/lightphpedit/ ). Она создана как раз с использованием SynEdit для отображения синтаксиса языка.

* * *

Жаль, конечно, что в пакете php4delphi нет еще оптимизированного под Lazarus варианта установки, так же как и варианта для Kylix 3. Тем не менее возможность использования пакета в Delphi способна резко повысить уровень адаптируемости вашего приложения к новым требованиям. Уже того факта, что все изменения, отправляемые разработчиком клиенту, представляют собой маленький фрагмент кода PHP, вполне достаточно, чтобы вызвать в душе разработчика приятные эмоции. В тех условиях, когда четко видны все входные-выходные интерфейсы, необходимость проверок на совместимость может и вовсе отпасть, благодаря чему будет сэкономлена масса времени, сил и нервов.

В статье мы рассмотрели основные моменты, связанные с использованием пакета php4delphi. Подробнее с такими вопросами, как особенности выдачи результатов работы скрипта, интеграция с веб-браузером и возможность работы с PHP из программ, написанных на языках С#, C++ и Delphi.NET, можно ознакомиться, изучив прилагаемое к пакету руководство или приведенные в нем примеры.

Приложение к статье, а также дистрибутивы PHP, php4delphi и SynEdit вы найдете на «Мир ПК-диске».


Наводим красоту

Для визуализации работы с языком PHP в Delphi-приложениях можно воспользоваться типовым решением — набором бесплатных компонентов SynEdit (http://synedit.sourceforge.net/ ).

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

Дистрибутив SynEdit вы найдете на «Мир ПК-диске».


Проблемы с отладкой

При отладке могут возникать различные неясные моменты, а значит, нелишне будет убедиться, что ваш PHP работает и верно воспринимает указанный в ini-файле модуль расширения. Для этого достаточно с системной командной строки запустить php. exe. Если модуль будет прописан неверно, то PHP скорее всего поделится с программистом соответствующей претензией.


Где хранить скрипт?

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

В некоторых случаях нет необходимости во вмешательстве человека непосредственно в содержимое скрипта. Скрипт можно использовать как промежуточное звено между программными модулями; он будет формироваться динамически и существовать очень короткое время. Как промежуточный вариант — вместо самого скрипта постоянно хранится лишь его шаблон.