Greetings, habra people.

OnPHP is a framework, as it is not difficult to guess, written in PHP. Like any such tool, it has its advantages and disadvantages and, as I believe, it has more of the first ones.
Oddly enough, on Habré I did not find a single topic dedicated to this frame. Apparently the reason is that the framework has no documentation at all, which is why it resembles a “framework in itself” and “for its own”. However, this is not entirely true, but more on that later.
')
The purpose of this note is to draw the attention of the professional community to a truly worthwhile development, as well as to combine the existing knowledge about this framework and the experience of using it. Perhaps in the future, it will turn out to compile at least some documentation from the collected material, so I will be very grateful to you for the information that you will share.
I note that I am not one of the creators of the framework, but I am a user of it and want to learn more about it, and also, perhaps, lay out my work on its basis. So, let's begin.
Introduction
The framework was born and grew up in a team of people who created it and developed it for their own needs, as far as I understood, since 2005. The earliest publicly available version is version 0.2, dated September 3, 2005. Since then, the framework has changed dramatically, new features have been added to it, and bugs have been fixed. Now onPHP is a worthy candidate for the role of the foundation for a web project of any scale. The latest stable version is 1.0.
Advantages and disadvantages.
As I have already said, any tool (or rather simply any tool) has its advantages and disadvantages, and onPHP is no exception. Among the
advantages I could highlight the following:
1. Rapid application development.
The onPHP tools allows you to quickly create the necessary classes and connections between them, build a database structure and check dependencies. All this is done using an XML schema that describes the project entities. More on this below. In addition, there are convenient tools for accessing data: Criteria (well thought out port from Hibernate) and OSQL.
2. Horizontal scalability.
Deeply worked out the issue of caching. As I understand it, this is the pride of the framework. You can cache in different ways and very cleverly (file system, memcached, etc). In addition, the database work layer supports work with several connections, and each managed entity can be specified which connection to use, i.e. in fact, transparently store data in different databases.
3. Flexibility.
It lies in the fact that in the overwhelming majority of cases, the framework does not impose specific action patterns to create any functionality in your project. At the same time, the framework provides all the necessary features to create what you want. In the architecture of the framework, many well-known design patterns are used, and the framework itself provides opportunities for the “template” of your own classes (StaticFactory, Singleton).
4. Speed.
As I understand it, it is achieved mainly due to very flexible caching. But there is another factor - the module for php, which comes in the kit. Some of the classes that are required constantly were designed as a module, which undoubtedly accelerated their work.
5. Quality and reliability.
The code is covered by unit tests. Each new functionality that appears in the framework is covered by tests. From the experience of use, I was pleasantly surprised by the stability and predictability of the behavior of the constructed system.
6. High-quality, clean and clear code.
Perhaps this is subjective, but this is my opinion and, apparently, the creators, too. Strict
coding slyle , which you quickly get used to, contributes to the rapid perception of the code and even to some kind of aesthetic pleasure that occurs every time you look at the code.
Perhaps of the merits is all.
Disadvantages :
1. Lack of documentation.
The creators of the framework believe that the API is the best documentation. Personally, I disagree with them, so I decided to write this note with the hope that during the discussion and subsequent notes, at least some semblance of documentation will be born.
2. Small community.
This deficiency is a consequence of the first, because, due to the lack of documentation, the threshold of entry greatly increases, and the desire of many disappears. Discussion of issues and proposals is carried out mainly through
the mailing list , in which there are discussions once a month or two.
3. Conflict classes.
The framework classes do not have a prefix, which creates problems when migrating projects to this framework. Therefore, it is sometimes necessary to invent other names for those classes whose names are occupied in onPHP.
Where to begin?
In the git master, there is a
very short tutorial that contains recommendations on the typical structure of the project, the entry point (index.php), the use of the database, the use of forms (a powerful tool for accepting and validating input data) and an unfinished article about ACL, since it is still in the incubator.
Incubator.
This is the part of onPHP, in which there are those parts of the framework that have not yet officially acquired the final form and in which changes are possible, non-working functionality and other disorders. Here is the development, debugging and testing. New parts of the framework pass through the development in the incubator, and after stabilization they fall into the main section.
For example, Acl, Tsearch (full-text search for PostgreSQL), FormRendering (centralized display of forms — the Form object and its primitives), Application (an application and a set of its settings for easy customization), and several other packages are currently in the
incubator .
Meta.
This part of the framework, located in the
meta folder, provides the ability to describe entities and their properties, as well as create the necessary classes for your future application. The scheme is in XML format, which can be validated by the attached
meta.dtd .
Example schema:
<? 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>
What is interesting here:
- For each entity, it indicates what properties it possesses and what their type is. Based on this, meta-builder will recommend what changes to the database schema should be made, and also this can be used to validate these objects according to the form (Form).
- You can immediately specify the relationship between entities, which will then be reflected in the generated classes. OneToOne, OneToMany and ManyToMany are available. It so happened that I always used the OneToOne connection, but I would like to know about the other types of connections (meaning not the theory, everything is in order, but the implementation in onPHP).
- Pattern - is a pattern of the future class. Unfortunately, I don’t know the features of all the templates, but here are some of them:
- StraightMapping - from the name it is clear that this is a direct object mapping for one record in the database table. This is nothing but M. Fowler's ActiveRecord.
- EnumerationClass is an enumeration. A class that does not have a table in the database, but is a linear list (id => name). Used for data that never or almost never changes (for example, the month, zodiac signs, etc.)
- AbstractClass is an abstract class. Used to define abstract classes in the schema, from which other classes will be inherited from the same schema.
The rest of the patterns (
DictionaryClass, SpookedClass, SpookedEnumeration, ValueObject and InternalClass ) are still unknown to me, I hope those who know will tell, and I will write it down.
- fetch = "lazy" is an indication that when assembling an object, the Author property does not need to be loaded before accessing it. In the example above, if I unload the Book object by ID:
$book = Book::dao()->getById(1);
$author = $book->getAuthor();
then for the Assembly of the Author object, a query will be made to the database to obtain the values ​​of its parameters. With fetch = "cascade", all data on all connections is retrieved from the database in one request, after which the related objects are collected and available for use without additional requests.
This is not all that can be used in meta, but for a brief overview it is enough. Let's start generating classes.
Code Generation
In the
meta folder there is a script
bin / build.php , which with the help of nearby classes and turns the scheme into classes. Let's see how to use it:
Usage: build.php [options] [project-configuration-file.inc.php] [metaconfiguration.xml]
Meta we have, but the configuration file is not. Create it.
There is a
global.inc.php.tpl file in the
root , which is the template for the configuration file. Copy it and modify it as follows:
a) add the constant
PATH_CLASSES - where you will have generated classes.
b) Add to 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
c) add the parameters of the connection with the database:
DBPool::me()->setDefault(
DB::spawn('PgSQL', 'user', 'password', 'localhost', 'dbname')
->setEncoding('utf-8')
);
Then, run
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: the 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 cut]
What happened: the builder took a configuration file and created classes using the scheme (Building classes section), created a schema file (Building DB schema section), checked for obsolete files, checked syntax in all project files and, checking the correctness of the generated classes, found an error that tables, we have not yet created. Correct. Go to the database and perform 2 requests:
CREATE TABLE authors (id serial PRIMARY KEY);
CREATE TABLE books (id serial PRIMARY KEY);
Run
build.php again:
$ ./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
Pay attention to
Suggested DB-schema changes , run these requests and run
build.php again:
$ ./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.
All classes generated. Let's look at them. In the directory that was specified in the
PATH_CLASSES constant
, now there are 4 subdirectories:
- Auto : classes that contain non-modifiable classes that are regenerated every time you run build.php;
- Business : business objects that can and should be changed, complementing the necessary logic;
- DAOs : Data Access Objects, data access objects, into which frequently used selections can be placed;
- Proto : classes describing the prototypes of objects, their properties.
An example of using the created classes.
Code:
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();
Result:
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 | one
(1 row)
That's all for now, next time I plan to write about MVC in onPHP and a full-fledged example of the application, as well as highlight the questions that will be asked. I hope the onPHP topic will find a positive and constructive response in the form of comments to this note.
Thanks for attention.
UPD :
References:
- onphp.org
-
git-
blog Denis Gabaidulin , one of the creators of onPHP