Приветствую, хабралюди.
OnPHP — это фреймворк, как не трудно догадаться, написанный на PHP. Как и любой подобный инструмент он обладает своими преимуществами и недостатками и, как я считаю, первых у него больше.
Как ни странно, на хабре я не нашел ни одного топика, посвященного этому фреймоврку. Видимо причина в том, что у фрейморка напрочь отсутствует документация, отчего он напоминает «фреймворк в себе» и «для своих». Однако это не совсем так, но об этом чуть позже.
Целью этой заметки является обращения внимания профессионального сообщества к действительно стоящей разработке, а также объединение имеющихся знаний об этом фреймворке и опыта его использования. Возможно в будущем, из собранного материала получиться составить хоть какую-нибудь документацию, поэтому буду очень признателен вам за информацию, которой поделитесь.
Замечу, что я не являюсь одним из создателей фреймворка, но являюсь его пользователем и хочу побольше узнать о нем, а также, возможно, выложить свои наработки на его основе. Итак, начнем.
Фреймворк родился и вырос в команде людей, которая создавала его и развивала для собственных нужд, насколько я понял, с 2005-го года. Самая ранняя версия, доступная публично, это версия 0.2, датированная 3 сентября 2005 года. С тех пор фреймворк сильно изменялся, в него добавлялись новые возможности и исправлялись ошибки. Сейчас onPHP — это достойная кандидатура на роль фундамента для веб-проекта любого масштаба. Последняя стабильная версия — 1.0.
Как я уже говорил, у любого инструмента (а скорее просто — у любого) есть свои достоинства и недостатки, и onPHP не исключение. Среди достоинств я смог выделить следующие:
Инструменты onPHP позволяет очень быстро создать нужные классы и связи между ними, построить структуру БД и проверить зависимости. Все это делается с помощью схемы в формате XML, описывающей сущности проекта. Подробнее об этом ниже. Кроме того, имеется удобные инструменты для доступа к данным: Criteria (обдуманный порт из Hibernate) и OSQL.
Глубоко проработан вопрос кэширования. Как я понимаю, это гордость фрейморка. Кэшировать можно по разному и очень умно (файловая система, memcached, etc). Кроме того, слой работы с базами данных поддерживает работу с несколькими соединениями, а каждой заведенной сущности можно указать какое соединение использовать, т.е. фактически прозрачно хранить данные в разных БД.
Она заключается в том, что фрейморк в подавляющем большинстве случаев, не навязывает конкретных шаблонов действия для создания какой-либо функциональности в вашем проекте. В то же самое время, фреймворк предоставляет все необходимые возможности для создания того, что вы хотите. В архитектуре фреймворка используютсся многие общеизвестные шаблоны проектирования, а сам фрейморк предоставляет возможности для «ошаблонивания» ваших собственных классов (StaticFactory, Singleton).
Как я понимаю, она достигается в основном за счет очень гибкого кэширования. Но есть и еще один фактор — модуль для php, который идет в комплекте. Часть классов, которые требуются постоянно, были оформлены в виде модуля, что несомненно ускорило их работу.
Код покрыт unit-тестами. Каждая новая функциональность, которая появляется во фрейморке, покрывается тестами. По опыту использования, я был приятно удивлен стабильностью и предсказуемостью поведения построенной системы.
Возможно, это субъективно, но это мое мнение и, видимо, создателей тоже. Строгий coding slyle, к которому быстро привыкаешь, способствует быстрому восприятию кода и даже некоему эстетическому наслаждению, которое возникает всякий раз, кода заглядываешь в код.
Пожалуй из достоинств это все. Недостатки:
Создатели фрейморка считают, что API — лучшая документация. Лично я с ними не согласен, поэтому и решил написать эту заметку с надеждой, что в ходе обсуждения и последующих заметок, родится хотя бы какое-то подобие документации.
Этот недостаток является следствием первого, потому что из-за отсутствия документации порог вхождения сильно увеличивается, да и желание у многих пропадает. Обсуждение вопросов и предложений ведется преимущественно через список рассылки, в которой обсуждения бывают раз в месяц-два.
Классы фреймворка не имеют префикса, что создает проблемы при миграции проектов на этот фреймворк. Поэтому приходится иногда выдумывать другие имена тем классам, имена которых заняты в onPHP.
В мастер-ветке git'а, есть очень краткий tutorial, в котором есть рекомендации по типовой структуре проекта, точке входа (index.php), использованию базы данных, использованию форм (мощный инструмент для приема и валидации входных данных) и недописанная заметка про ACL, поскольку он еще в инкубаторе.
Эта та часть onPHP, в которой находятся те части фрейморка, которые официально еще не приобрели окончательной формы и в которых возможны изменения, неработающий функционал и прочие беспорядки. Здесь ведется разработка, отладка и тестирование. Новые части фрейморвка проходят через разработку в инкубаторе, а после стабилизации попадают в основной раздел.
Так, в инкубаторе на текущий момент находятся Acl, Tsearch (полнотекстовый поиск для PostgreSQL), FormRendering (централизованное отображение форм — объекта Form и его примитивов), Application (приложение и совокупность его настроек для удобной настройки) и еще несколько пакетов.
Эта часть фрейморка, расположенная в папке meta предоставляет возможность описать сущности и их свойства, а также создать необходимые классы вашего будущего приложения. Схема оформляется в формате XML, которую можно валидировать по приложенной meta.dtd.
Пример схемы:
Что здесь интересного:
— Для каждой сущности указывается какими свойствами она обладает и какой их тип. На основе этого, meta-builder порекомендует какие изменения в схеме БД нужно сделать, а также этим можно воспользоваться для валидации данных объектов по форме (Form).
— Можно сразу указать связи между сущностями, которые затем будут отражены в сгенерированных классах. Доступны к использованию OneToOne, OneToMany и ManyToMany. Так получилось, что я всегда использовал связь OneToOne, но хотелось бы узнать про остальные типы связей (имеется ввиду не теория, с этим все в порядке, а именно реализация в onPHP).
— Pattern — представляет собой шаблон будущего класса. Особенностей всех шаблонов я, к сожалению, не знаю, однако вот некоторые из них:
— fetch=«lazy» — это указание на то, что при сборке объекта, свойство Author загружать не нужно до обращения к нему. В приведенном примере, если я выгружу объект Book по ID:
Это далеко не все, что можно использовать в meta, но для краткого обзора достаточно. Начнем генерировать классы.
В папке meta есть скрипт bin/build.php, который с помощью рядом находящихся классов и превращает схему в классы. Посмотрим как его использовать:
Meta у нас есть, а вот файла конфигурации — нет. Создадим его.
В корне есть файл global.inc.php.tpl, который является шаблоном для файла конфигурации. Копируем его и изменяем следующим образом:
а) добаляем константу PATH_CLASSES — там, где у вас будут лежать сгенерированные классы.
б) Добавляем в set_include_path():
Затем, запускаем build.php:
Что произошло: builder взял конфигурационный файл и по схеме создал классы (раздел Building classes), создал файл схемы (раздел Building DB schema), проверил наличие устаревших файлов, проверил синтаксис во всех файлах проекта и, про��еряя правильность сгенерированных классов, нашел ошибку, что таблицы-то мы еще не создали. Исправимся. Заходим на базу и выполняем 2 запроса:
Запускаем еще раз build.php:
Обратите внимание на Suggested DB-schema changes, выполним эти запросы и снова запустим build.php:
Всё, классы сгенерированы. Давайте посмотрим на них. В каталоге, который был указан в константе PATH_CLASSES сейчас находятся 4 подкаталога:
Код:
Результат:
На этом пока все, в следующий раз планирую написать про MVC в onPHP и полноценный пример приложения, а также осветить вопросы, которые будут заданы. Надеюсь тема onPHP найдет позитивный и конструктивный отклик в виде комментариев к этой заметке.
Спасибо за внимание.
UPD:
Ссылки:
— onphp.org
— GIT
— блог Дениса Габайдулина, одного из создателей onPHP

Как ни странно, на хабре я не нашел ни одного топика, посвященного этому фреймоврку. Видимо причина в том, что у фрейморка напрочь отсутствует документация, отчего он напоминает «фреймворк в себе» и «для своих». Однако это не совсем так, но об этом чуть позже.
Целью этой заметки является обращения внимания профессионального сообщества к действительно стоящей разработке, а также объединение имеющихся знаний об этом фреймворке и опыта его использования. Возможно в будущем, из собранного материала получиться составить хоть какую-нибудь документацию, поэтому буду очень признателен вам за информацию, которой поделитесь.
Замечу, что я не являюсь одним из создателей фреймворка, но являюсь его пользователем и хочу побольше узнать о нем, а также, возможно, выложить свои наработки на его основе. Итак, начнем.
Введение.
Фреймворк родился и вырос в команде людей, которая создавала его и развивала для собственных нужд, насколько я понял, с 2005-го года. Самая ранняя версия, доступная публично, это версия 0.2, датированная 3 сентября 2005 года. С тех пор фреймворк сильно изменялся, в него добавлялись новые возможности и исправлялись ошибки. Сейчас onPHP — это достойная кандидатура на роль фундамента для веб-проекта любого масштаба. Последняя стабильная версия — 1.0.
Достоинства и недостатки.
Как я уже говорил, у любого инструмента (а скорее просто — у любого) есть свои достоинства и недостатки, и onPHP не исключение. Среди достоинств я смог выделить следующие:
1. Быстрая разработка приложений.
Инструменты onPHP позволяет очень быстро создать нужные классы и связи между ними, построить структуру БД и проверить зависимости. Все это делается с помощью схемы в формате XML, описывающей сущности проекта. Подробнее об этом ниже. Кроме того, имеется удобные инструменты для доступа к данным: Criteria (обдуманный порт из Hibernate) и OSQL.
2. Горизонтальная масштабируемость.
Глубоко проработан вопрос кэширования. Как я понимаю, это гордость фрейморка. Кэшировать можно по разному и очень умно (файловая система, memcached, etc). Кроме того, слой работы с базами данных поддерживает работу с несколькими соединениями, а каждой заведенной сущности можно указать какое соединение использовать, т.е. фактически прозрачно хранить данные в разных БД.
3. Гибкость.
Она заключается в том, что фрейморк в подавляющем большинстве случаев, не навязывает конкретных шаблонов действия для создания какой-либо функциональности в вашем проекте. В то же самое время, фреймворк предоставляет все необходимые возможности для создания того, что вы хотите. В архитектуре фреймворка используютсся многие общеизвестные шаблоны проектирования, а сам фрейморк предоставляет возможности для «ошаблонивания» ваших собственных классов (StaticFactory, Singleton).
4. Скорость.
Как я понимаю, она достигается в основном за счет очень гибкого кэширования. Но есть и еще один фактор — модуль для php, который идет в комплекте. Часть классов, которые требуются постоянно, были оформлены в виде модуля, что несомненно ускорило их работу.
5. Качество и надежность.
Код покрыт unit-тестами. Каждая новая функциональность, которая появляется во фрейморке, покрывается тестами. По опыту использования, я был приятно удивлен стабильностью и предсказуемостью поведения построенной системы.
6. Качественный, чистый и понятный код.
Возможно, это субъективно, но это мое мнение и, видимо, создателей тоже. Строгий coding slyle, к которому быстро привыкаешь, способствует быстрому восприятию кода и даже некоему эстетическому наслаждению, которое возникает всякий раз, кода заглядываешь в код.
Пожалуй из достоинств это все. Недостатки:
1. Отсутствие документации.
Создатели фрейморка считают, что API — лучшая документация. Лично я с ними не согласен, поэтому и решил написать эту заметку с надеждой, что в ходе обсуждения и последующих заметок, родится хотя бы какое-то подобие документации.
2. Небольшое сообщество.
Этот недостаток является следствием первого, потому что из-за отсутствия документации порог вхождения сильно увеличивается, да и желание у многих пропадает. Обсуждение вопросов и предложений ведется преимущественно через список рассылки, в которой обсуждения бывают раз в месяц-два.
3. Конфликт классов.
Классы фреймворка не имеют префикса, что создает проблемы при миграции проектов на этот фреймворк. Поэтому приходится иногда выдумывать другие имена тем классам, имена которых заняты в onPHP.
С чего начать?
В мастер-ветке git'а, есть очень краткий tutorial, в котором есть рекомендации по типовой структуре проекта, точке входа (index.php), использованию базы данных, использованию форм (мощный инструмент для приема и валидации входных данных) и недописанная заметка про ACL, поскольку он еще в инкубаторе.
Инкубатор.
Эта та часть onPHP, в которой находятся те части фрейморка, которые официально еще не приобрели окончательной формы и в которых возможны изменения, неработающий функционал и прочие беспорядки. Здесь ведется разработка, отладка и тестирование. Новые части фрейморвка проходят через разработку в инкубаторе, а после стабилизации попадают в основной раздел.
Так, в инкубаторе на текущий момент находятся Acl, Tsearch (полнотекстовый поиск для PostgreSQL), FormRendering (централизованное отображение форм — объекта Form и его примитивов), Application (приложение и совокупность его настроек для удобной настройки) и еще несколько пакетов.
Meta.
Эта часть фрейморка, расположенная в папке meta предоставляет возможность описать сущности и их свойства, а также создать необходимые классы вашего будущего приложения. Схема оформляется в формате XML, которую можно валидировать по приложенной meta.dtd.
Пример схемы:
<?xml version="1.0"?> <!DOCTYPE metaconfiguration SYSTEM "meta.dtd"> <metaconfiguration> <classes> <class name="Author" table="authors"> <properties> <identifier/> <property name="name" type="String" size="50" required="true"/> </properties> <pattern name="StraightMapping"/> </class> <class name="Book" table="books"> <properties> <identifier/> <property name="title" type="String" size="50" required="true"/> <property name="author" type="Author" relation="OneToOne" required="true" fetch="lazy"/> </properties> <pattern name="StraightMapping"/> </class> </classes> </metaconfiguration>
Что здесь интересного:
— Для каждой сущности указывается какими свойствами она обладает и какой их тип. На основе этого, meta-builder порекомендует какие изменения в схеме БД нужно сделать, а также этим можно воспользоваться для валидации данных объектов по форме (Form).
— Можно сразу указать связи между сущностями, которые затем будут отражены в сгенерированных классах. Доступны к использованию OneToOne, OneToMany и ManyToMany. Так получилось, что я всегда использовал связь OneToOne, но хотелось бы узнать про остальные типы связей (имеется ввиду не теория, с этим все в порядке, а именно реализация в onPHP).
— Pattern — представляет собой шаблон будущего класса. Особенностей всех шаблонов я, к сожалению, не знаю, однако вот некоторые из них:
- StraightMapping — из названия понятно, что это прямой маппинг объекта на одну запись в таблице БД. Это ничто иное, как ActiveRecord М.Фаулера.
- EnumerationClass — перечисление. Класс, который не имеет таблицы в БД, а является линейным списком (id => name). Используется для тех данных, которые никогда или практически никогда не меняются (например, месяца, знаки зодиака, и т. д.)
- AbstractClass — абстрактный класс. Используется для определения в схеме абстрактных классов, от которых в этой же схеме другие классы будет наследоваться.
— fetch=«lazy» — это указание на то, что при сборке объекта, свойство Author загружать не нужно до обращения к нему. В приведенном примере, если я выгружу объект Book по ID:
$book = Book::dao()->getById(1);
$author = $book->getAuthor();
то для сборки объекта Author будет сделан запрос в БД на получение значений его параметров. При fetch=«cascade» все данные по всем связям выгребаются из БД одним запросом, после чего связанные объекты собираются и доступны для использования без дополнительных запросов.Это далеко не все, что можно использовать в meta, но для краткого обзора достаточно. Начнем генерировать классы.
Кодогенерация.
В папке meta есть скрипт bin/build.php, который с помощью рядом находящихся классов и превращает схему в классы. Посмотрим как его использовать:
Usage: build.php [options] [project-configuration-file.inc.php] [metaconfiguration.xml]
Meta у нас есть, а вот файла конфигурации — нет. Создадим его.
В корне есть файл global.inc.php.tpl, который является шаблоном для файла конфигурации. Копируем его и изменяем следующим образом:
а) добаляем константу PATH_CLASSES — там, где у вас будут лежать сгенерированные классы.
б) Добавляем в set_include_path():
.PATH_CLASSES.PATH_SEPARATOR
.PATH_CLASSES.'Auto'.PATH_SEPARATOR
.PATH_CLASSES.'Auto'.DIRECTORY_SEPARATOR.'Business'.PATH_SEPARATOR
.PATH_CLASSES.'Auto'.DIRECTORY_SEPARATOR.'DAOs'.PATH_SEPARATOR
.PATH_CLASSES.'Auto'.DIRECTORY_SEPARATOR.'Proto'.PATH_SEPARATOR
.PATH_CLASSES.'Business'.PATH_SEPARATOR
.PATH_CLASSES.'DAOs'.PATH_SEPARATOR
.PATH_CLASSES.'Proto'.PATH_SEPARATOR
в) добавляем параметры соединения с БД:DBPool::me()->setDefault(
DB::spawn('PgSQL', 'user', 'password', 'localhost', 'dbname')
->setEncoding('utf-8')
);
Затем, запускаем build.php:
$ ./meta/bin/build.php ./global.inc.php ./schema/project.xml onPHP-1.1: MetaConfiguration builder. Known internal classes: Range, DateRange, TimestampRange, IdentifiableObject, IdentifiableTree, NamedObject, NamedTree, that's all. Building classes: Author: AutoProtoAuthor (classes/Auto/Proto/AutoProtoAuthor.class.php) ProtoAuthor (classes/Proto/ProtoAuthor.class.php) AutoAuthor (classes/Auto/Business/AutoAuthor.class.php) Author (classes/Business/Author.class.php) AutoAuthorDAO (classes/Auto/DAOs/AutoAuthorDAO.class.php) AuthorDAO (classes/DAOs/AuthorDAO.class.php) Book: AutoProtoBook (classes/Auto/Proto/AutoProtoBook.class.php) ProtoBook (classes/Proto/ProtoBook.class.php) AutoBook (classes/Auto/Business/AutoBook.class.php) Book (classes/Business/Book.class.php) AutoBookDAO (classes/Auto/DAOs/AutoBookDAO.class.php) BookDAO (classes/DAOs/BookDAO.class.php) Building containers: Building DB schema: schema.php (classes/Auto/schema.php) Suggested DB-schema changes: table 'authors' not found, skipping. table 'books' not found, skipping. Checking for stale files: Trying to compile all known classes... done. Checking sanity of generated files: Author (2/1/ ERROR: 42P01: relation "authors" does not exist - SELECT "authors"."id", "authors"."name" FROM "authors" WHERE ("authors"."id" IS NOT NULL) ORDER BY "authors"."id" LIMIT 1 [exception trace вырезан]
Что произошло: builder взял конфигурационный файл и по схеме создал классы (раздел Building classes), создал файл схемы (раздел Building DB schema), проверил наличие устаревших файлов, проверил синтаксис во всех файлах проекта и, про��еряя правильность сгенерированных классов, нашел ошибку, что таблицы-то мы еще не создали. Исправимся. Заходим на базу и выполняем 2 запроса:
CREATE TABLE authors (id serial PRIMARY KEY);
CREATE TABLE books (id serial PRIMARY KEY);
Запускаем еще раз build.php:
$ ./meta/bin/build.php ./global.inc.php ./schema/project.xml onPHP-1.1: MetaConfiguration builder. Known internal classes: Range, DateRange, TimestampRange, IdentifiableObject, IdentifiableTree, NamedObject, NamedTree, that's all. Building classes: Author: AutoProtoAuthor AutoAuthor AutoAuthorDAO Book: AutoProtoBook AutoBook AutoBookDAO Building containers: Building DB schema: schema.php Suggested DB-schema changes: ALTER TABLE "authors" ALTER COLUMN "id" TYPE BIGINT; ALTER TABLE "authors" ADD COLUMN "name" CHARACTER VARYING(50) NOT NULL; ALTER TABLE "books" ALTER COLUMN "id" TYPE BIGINT; ALTER TABLE "books" ADD COLUMN "title" CHARACTER VARYING(50) NOT NULL; ALTER TABLE "books" ADD COLUMN "author_id" BIGINT NOT NULL REFERENCES "authors"("id") ON DELETE RESTRICT ON UPDATE CASCADE; CREATE INDEX "author_id_idx" ON "books"("author_id"); Checking for stale files: Trying to compile all known classes... done. Checking sanity of generated files: Author (2/1/F/C/H/T), Book (3/1/ ERROR: 42703: column books.title does not exist - SELECT "books"."id", "books"."title", "books"."author_id" FROM "books" WHERE ("books"."id" IS NOT NULL) ORDER BY "books"."id" LIMIT 1
Обратите внимание на Suggested DB-schema changes, выполним эти запросы и снова запустим build.php:
$ ./meta/bin/build.php ./global.inc.php ./schema/project.xml onPHP-1.1: MetaConfiguration builder. Known internal classes: Range, DateRange, TimestampRange, IdentifiableObject, IdentifiableTree, NamedObject, NamedTree, that's all. Building classes: Author: AutoProtoAuthor AutoAuthor AutoAuthorDAO Book: AutoProtoBook AutoBook AutoBookDAO Building containers: Building DB schema: schema.php Suggested DB-schema changes: Checking for stale files: Trying to compile all known classes... done. Checking sanity of generated files: Author (2/1/F/C/H/T), Book (3/1/F/C/H/T), done.
Всё, классы сгенерированы. Давайте посмотрим на них. В каталоге, который был указан в константе PATH_CLASSES сейчас находятся 4 подкаталога:
- Auto: классы, которые содержат не подлежащие изменению классы, которые перегенерируются всякий раз при запуске build.php;
- Business: бизнес-объекты, которые можно и нужно изменять, дополняя необходимой логикой;
- DAOs: Data Access Objects, объекты доступа к данным, в которые можно поместить часто используемые выборки;
- Proto: классы, описывающие прототипы объектов, их свойства.
Пример использования созданных классов.
Код:
require "global.inc.php";
DBPool::me()->getLink()->begin();
$author = Author::create()
->setName('Martin Fowler');
Author::dao()->add($author);
$book = Book::create()
->setTitle('Patterns of Enterprise Application Architecture')
->setAuthor($author);
Book::dao()->add($book);
DBPool::me()->getLink()->commit();
Результат:
dbname=> select * from authors; id | name ----+--------------- 1 | Martin Fowler (1 row) dbname=> select * from books; id | title | author_id ----+-------------------------------------------------+----------- 1 | Patterns of Enterprise Application Architecture | 1 (1 row)
На этом пока все, в следующий раз планирую написать про MVC в onPHP и полноценный пример приложения, а также осветить вопросы, которые будут заданы. Надеюсь тема onPHP найдет позитивный и конструктивный отклик в виде комментариев к этой заметке.
Спасибо за внимание.
UPD:
Ссылки:
— onphp.org
— GIT
— блог Дениса Габайдулина, одного из создателей onPHP