Аутичный onPHP: обзор

    Приветствую, хабралюди.

    onPHP OnPHP — это фреймворк, как не трудно догадаться, написанный на PHP. Как и любой подобный инструмент он обладает своими преимуществами и недостатками и, как я считаю, первых у него больше.

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



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

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

    Введение.


    Фреймворк родился и вырос в команде людей, которая создавала его и развивала для собственных нужд, насколько я понял, с 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 — представляет собой шаблон будущего класса. Особенностей всех шаблонов я, к сожалению, не знаю, однако вот некоторые из них:
    1. StraightMapping — из названия понятно, что это прямой маппинг объекта на одну запись в таблице БД. Это ничто иное, как ActiveRecord М.Фаулера.
    2. EnumerationClass — перечисление. Класс, который не имеет таблицы в БД, а является линейным списком (id => name). Используется для тех данных, которые никогда или практически никогда не меняются (например, месяца, знаки зодиака, и т. д.)
    3. AbstractClass — абстрактный класс. Используется для определения в схеме абстрактных классов, от которых в этой же схеме другие классы будет наследоваться.
    Остальные паттерны (DictionaryClass, SpookedClass, SpookedEnumeration, ValueObject и InternalClass) мне пока неведомы, надеюсь знающие расскажут, а я запишу.

    — 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
    Поделиться публикацией
    Комментарии 84
      +27
      >у фрейморка напрочь отсутствует документация
      совсем, совсем не стыкуется с
      >Сейчас onPHP — это достойная кандидатура на роль фундамента для веб-проекта любого масштаба.
      сразу можно ставить жирный минус
        0
        В целом вполне согласен. Однако для тех, кто с ним знаком этого минуса не существует. Надеюсь в скором будущем минус обратиться в плюс.
          +1
          Если хочешь развивать OpenSource проект — пиши документацию, поддерживай ее в актуальном состоянии, привлекай сторонних разработчиков.

          Если что-то из этого не делается — проект будет топтаться на том же месте.
            +1
            Документация нужна всегда, и к системе, с которой работаешь двадцать лет. Даже к той, которую сам написал. Невозможно держать все в голове.
            Это конечно при условии, что эта система развивается и действительно делает что-то нетривиальное.
            +1
            api — это тоже документация. :)
            +3
            Я провтыкал или ссылки на фреймворк нет?
              +2
              И хотелось бы увидеть бенчмарки с другими FW (если таковые имеются).
                +2
                По ссылкам исправился.
                По бенчмаркам — как будет время, сделаю, очень интересный момент.
                  +3
                  Если будешь делать, то можно кинуть код для onPHP в рассылку, там помогут с оптимизацией. писькомерки такие писькомерки :-)
                +2
                +1
                Не совсем понятно для чего создавать а затем «альтерить» таблички приходится вручную — у фрейворка есть скрипт — генератор проекта, перед созданием проекта прописываются все настройки конекта к БД,
                поэтому вполне можно автоматом создавать таблички, как оно реализовано в той же Симфони
                  0
                  Да, я тоже об этом думал. Раз этого нет в стандартной поставке, есть возможность сделать патч и отправить его в рассылку =)
                    0
                    Мало того, он же еще и подсказывает какие запросы надо сделать 0_o
                    Видимо сделано чтобы человеки сперва все вручную перепроверили. А возможно просто нужно выбрать какую-то опцию
                      –1
                      Нет там никакой такой опции. Можно только запустить генерацию без валидации БД.

                      По поводу того, что не обновляет базу автоматом — у меня есть некие предположения.
                      1) Никто не запускает генерацию меты на продакшене.
                      2) В MySQL вместо boolean используется тип tinyint. Так вот когда запускаешь генерацию меты, в которой объекты содержат булевые поля, скрипт предлагает накатить альтеры для смены типа с tinyint на bool. Это видимо недоработка в самом фреймворке, потому, что он явно развивается с прицелом на Postgres в ккачестве основной базы. MySQL там идет «что бы было». Получается, если бы база автоматически апдейтилась, то скрипт дергал бы её каждый раз по таким пустякам.

                      Хотя определенно есть смысл в том, что бы ввести новый флаг для генерации таблиц. Собсна это фопрос к Sherman-у )))
                        0
                        Почему же сразу к Денису. Уверен, что если Вы напишите патч к билдеру и он выдержит критику, то его добавят.
                      +1
                      одно дело изменение в коде, которые покажет svn di, другое дело непрозрачные для программиста изменения в БД, которые непонятно сколько времени займут, что откатывать и т.п.
                      0
                      Как я понимаю, она достигается в основном за счет очень гибкого кэширования. Но есть и еще один фактор — модуль для php, который идет в комплекте. Часть классов, которые требуются постоянно, были оформлены в виде модуля, что несомненно ускорило их работу.

                      А классы, которые вынесены в модуль могут дублироваться в php-виде? Просто не на всех хостах, с которыми сталкиваешься при разработке есть возможность ставить редкие модули.
                        0
                        Конечно, все необходимые классы присутствуют в виде обычных php-классов, и использование модуля в общем-то не обязательно, если у проекта нет проблем с нагрузкой.
                        –6
                        еще один фреймворк [x]
                          –2
                          А что делать, если у меня на хостинге нет шела? Ну разные бывают тарифные планы, ведь так? Вот в чём прикол таких фреймворков???
                            +2
                            Шел нужен только в процессе разработки, на хостинге нет смысла запускать генератор проекта.
                              –2
                              хм. ну так у вас значит и запросы функционала небольшие :) зачем вам фреймворк вообще тогда? Вы много фреймворков без шелла установить сможете?
                                0
                                Возможно, что небольшие требования к функционалу… Но вот те же CI или Kohana + Doctrine установлю без шела. Где я могу прочитать, что данный фреймворк на голову быстрее, функциональнее, удобнее хотя бы приведённых выше?
                                  –3
                                  Я вообще эту штуку не защищаю. Код без документации это дешевая поделка. Будь он трижды крут пользоваться им смысла нет.
                                    +2
                                    >Будь он трижды крут пользоваться им смысла нет.
                                    Голословно.
                                    Я пользуюсь и вижу в этом смысл.

                                    А вообще, Денис правильно сказал.
                                      0
                                      Высокий порог вхождения отчасти вызван отсутствием документации, вы так не думаете?
                                        0
                                        Да, этим в том числе.
                                        Но это даже хорошо, ИМХО. Когда человек уже «вошел», то он знает фремворк уже хорошо, а не на уровне «почитал документацию, но не понимаю как оно внутри работает».

                                        У onphp открыт код (и этот код прекрасен :), всегда можно посмотреть как оно внутри устроено и что делает. Документации на уровне api должно хватать для беглого поиска.
                                        Фреймворк — это инструмент. Чем лучше владение инструментом, тем лучше получается продукт.

                                        ps. кстати, последний год я вкладку с api-docs не закрываю в опере :)
                                    0
                                    Ага, не знал про CI :) Хотя кстати я наверное и ZF поставлю без шелла, подумалось сейчас. Короче, я был неправ :)
                                +2
                                Я бы добавил в статью, что на этом фреймворке работает несколько крупных проектов:
                                rabota.ru
                                ucheba.ru
                                pravda.ru

                                P.S. Что он быстрый — это бабушка на двое сказала.
                                +10
                                  +4
                                  сомневаюсь что эта аналогия отражает действительность
                                    0
                                    Коля, ты не прав)
                                    –2
                                    пардон, но «Аутичный» это из серии Аутизм?
                                      +1
                                      Не совсем, имелось ввиду аутичность:
                                      неуверенность в себе, ранимость, предпочтение творческого уединения шуму внешней деятельности, склонность к мечтательности
                                      +3
                                      Забыли упомянуть один из важных плюсов — прозрачное кеширование.

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

                                      Т.е. вы знаете, что Author::dao()->getById(42); вернет вам объект из кеша если он есть, либо из базы и сама положит в кеш.
                                        0
                                        Для таких случаев есть кеш в sql базе, который кеширует все запросы. Делать ещё одну прослойку кеша, не обязательно.
                                          +2
                                          при чем здесь кеш базы? Author::dao()->getById(42); вернет кешированную запись из того кеша который наcтроен, например memcache. Кеш в базе нам вообщем то не интересен, мы на него слабо влияем, да и не такой быстрый.
                                            –1
                                            1) Это всё предварительная оптимизация, что достаточно бессмысленно.
                                            2) Если есть уникальный ключ кешировать почти бесполезно, в силу пункта 3.
                                            3) Кеш нужен только в разрезе всяких крупных десятиэтажных запросов (А особенно с подзапросами без точных условий) и тут не совсем понятно когда этот кеш должен скидыватся.
                                            Если взять ваш пример, то допустим я вывожу 10 последних книг, в которых были коментарии.
                                            TTL поменьше, и весь вопрос? Ну-ну.
                                            4) Вообще не вижу особых технических проблем, по которым это нельзя сделать в любой ORM очень небольшими усилиями.
                                              0
                                              1) Если пишете личный блог то да, бессмысленно, а если работаете над крупным проектом, то будет нехорошо, если он после запуска ляжет.
                                              2-3) Если кэш продержится (прежде чем устареет) хотя бы 5 минут, когда страничку посетит сотня, другая человек, то получается что есть резон кэшировать. К тому же, в реальной жизни редко возникает необходимость показывать стопроцентно актуальную информацию и лезть за ней в базу при каждом рефреше.
                                              4) А вы попробуйте)
                                                –1
                                                1) Потому что это уже не будет преждевременной оптимизацией. Я к тому, что нужно смотреть где и что в конкретном проекте кешируем. Всё кешировать фреймворком без ведома программиста — ничего хорошего…
                                                2-3) Да это выход, но не панацея. Вот есть проект с большой нагрузкой. Пусть тот же каталог с авторами и их произведениями. Я хочу в кабинете автора выложить список его десяти последних произведений с последним комментарием в отзывах. Итого: текст + избыточность серилизации, да помноженное на количество авторов. То есть в данном случае будет просто так жраться оператива, потомучто даже если на главной у нас 200 человек в секунду, то где-то на авторе явно меньше.
                                                Всё-таки таких штук на автокасте делать не стоит.
                                                4) if( $var = $memcache_obj->get(md5( $result_sql_query ))) return $var;
                                                  +1
                                                  Вы просто не до конца представляете себе как работает кеширование в onPHP. Там никто за вас ничего не делает. Начнем с того, что по дефолту все вообще работает без кэша. Далее, если есть потребность, вы можете выбрать одну из нескольких стратегий кэширования и выбрать куда кэшировать — файлы, мемкэш…

                                                  Кроме того, вы в состоянии не только определять стратегию кэширпования для конкретного DAO (класса объектов), но и для конкретного запроса.

                                                  Например:
                                                  $query = OSQL::select()->...;
                                                  Book::dao()->getByQuery($query, Cache::DO_NOT_CACHE); Собственно второй параметр может быть временем кеширования в секундах.

                                                  Чтобы не жралась оперативка, там где это действительно нужно, можно кешировать список id-шников например, а потом по id так же доставать объекты из кеша.
                                                    0
                                                    Спасибо!
                                                    То что вы говорите, в разрез расходится с «Author::dao()->getById(42); вернет кешированную запись из того кеша который настроен»…
                                                      +1
                                                      Тут ключевое «из которого настроен», можно вообще не настраивать кеш :)
                                                        0
                                                        Ну я так и понял — настроил кеш, он везде где можно стал использоваться.
                                        0
                                        жалко, что он не имеет своего неймспейса
                                          0
                                          Как это было?

                                          «Нас не устроили все фреймворки на php и поэтому мы написали свой. Он умеет то чего не умеют другие»

                                          или

                                          «Мы делали какой то проект и его архитектура нам так понравилась, что мы решили что это будет фреймворк»
                                            +3
                                            Насколько мне известно (работал около полугода с разработчиками, активно использовал фреймворк, даже добавлен в контрибьютеры), идеалогия частично позаимствована у spring ffamework. Вообще, впечатления двоякие. С одной стороны, мощный, продуманный, учит правильно писать и проектировать. А с другой, фреймворк «для своих». Достаточно высокий порог вхождения. Поэтому сейчас сижу на symfony, но именно работа с onPHP направила мои стопы в эту сторону, за что бесконечно признателен.
                                            Возможно, желающим разобраться поможет воскурение документации к spring-у.
                                              +1
                                              Только не к современному, а версии эдак 2-2.5 :-)
                                                0
                                                Привет, Денис! =)
                                                А можешь привести сравнение onPHP и Symfony? Было бы весьма любопытно.
                                                  0
                                                  Привет. Нет, потому что я не видел Symfony года 4 и не интересуюсь сейчас этим :-)
                                            –3
                                            Невозможно в таком FW разрабатывать быстро.

                                            Хотя-бы потому что вместо простого ясного SQL, предлагают ещё одну прослойку. Может даже на простом примере кажется и гибко, но…

                                            Объясню на примере другого FW:

                                            Files/Db/File/get.php — mysql класс
                                            class Files_Db_File_get extends zzMysql{
                                              function onRun(){
                                                //prepare tags array
                                                $tags = preg_split('/\s*,\s*/', $this->tags);

                                                $this->tags = array_unique($tags);
                                                $this->countTags = count($this->tags);

                                                return parent::onRun();
                                              }
                                            }

                                            SQL файл Files/Db/File/get.zzMysql — mysql sql файл
                                            SELECT
                                                fb.id
                                            FROM `tag_file_rel` as tfr
                                            JOIN `file_body` as fb ON fb.id = tfr.file_id
                                            WHERE
                                                (tfr.`tag` in ~$tags~)
                                            GROUP by tfr.`file_id`
                                            HAVING count(tfr.`file_id`) = ~$countTags~
                                            ORDER BY fb.`date`, fb.`id`

                                            И пример использования:
                                                //get tags from input
                                                $search = array('tags' => 'php, onphp, framework, open source');

                                                //get data from SQL (ZZ is factory)
                                                $files = ZZ()->Files_Db_File_get($search)->toAssign('id');


                                            * This source code was highlighted with Source Code Highlighter.

                                            Если не ясно, объясню.
                                            1) Берем массив $search (с 'tags') и передаем классу Files_Db_File_get, вызывая метод ->toAssign('id') — который возвращает результат запроса.
                                            2) Переменная ->tags берется из массива. В процессе запроса, идет её преобразование из string в array.
                                            3) Дальше происходит запрос в SQL, а переменные ~$tags~ и ~$countTags~ беруться прямиком из класса (само-собою в инкапсулированном виде).
                                            3') ~$tags~ превращается из array в (…,…, ...), ~$countTags~ остается int.

                                            При этом, таблицы tag_file_rel и file_body можно использовать как душе угодно (без связи 1 к многим fb.id = tfr.file_id).

                                            Интерестно посмотреть подобный код на onPHP.
                                              +1
                                              на примере постов с тегами будет что-то типа этого:
                                              $postList =
                                                Criteria::create(Post::dao())->
                                                add(
                                                  Expression::in('tagList.word', $tagList)
                                                )->
                                                getList();


                                              * This source code was highlighted with Source Code Highlighter.

                                              в $postList будет лежать массив объктов Post
                                                0
                                                Да, только сам запрос откуда формироваться будет? :)

                                                Мне интерестно не как это использовать потом, а как разрабатывать.
                                                  +2
                                                  Если не нужна оптимизация, то запроса вы даже не увидите, он соберется сам по схеме объекта, вы просто указали что получить, лезть на уровень raw sql вам абсолютно не нужно.
                                                    0
                                                    Я про схему такого объекта и спрашиваю.
                                                      0
                                                      Перечитайте статью, пожалуйста.
                                                      Там как раз пишут, что в onphp присутсвует кодогенерация.
                                                      Чтобы поиметь такой объект, достаточно написать несколько строк xml, а бизнес-, прото- и дао-классы сгенерируются сами.

                                                      Код, который вам привели достаточен для вывода списка постов. Ну еще шаблон нарисовать.
                                                        –2
                                                        Я задаю простые вопросы.

                                                        1) Несколько строк xml — это сколько? Напишите, мне интерестно. Я понял основной концепт. Но интерестно применение на реальных задачах, а не в вакууме.

                                                        2) После написания xml, интерестно, на сколько будет понятно, что именно делает код?

                                                        Вы привели лишь пример, который состоит в моем случае из 2х строк, говоря, что внутри модели (которая автогенерится) может быть что угодно. В моем примере — модель тоже может быть чем угодно.
                                                          0
                                                          1. В статье, которую мы сейчас обсуждаем есть пример. Его можно посмотреть.
                                                          Для Вашего удобства я скопирую его в свой ответ (переписав под пост и теги):
                                                                  <class name="Post" type="final">
                                                                          <properties>
                                                                                  <identifier/>
                                                                                  <property name="title" type="String" size="50" required="true"/>
                                                                                  <property name="author" type="Author" relation="OneToOne" required="true" fetch="lazy"/>
                                                                                  <property name="tags" type="Tag" relation="OneToMany" required="true" fetch="lazy"/>
                                                                          </properties>
                                                                          <pattern name="StraightMapping"/>
                                                                  </class>
                                                          


                                                          2. А это уже от Вас зависит :) Я не могу сказать, насколько Вам будет понятен код.
                                                            –2
                                                            Ну зачем люди придумывают такой головняк?
                                                            Ведь все это быстро работает только на 10 запросах к коду в минуту(цифра с потолка), а когда 10 в секунду, а если 1000, все?

                                                            И начинаются появлятся какие то костыли, где употреблятся RAW SQL.
                                                              +1
                                                              У нас около 2к запросов к бекендам в секунду. )
                                                    +1
                                                    Критерия и сформирует Вам запрос, и соберет коллекцию объектов как результат. Никакого sql руками писать не надо.
                                                    Всегда можно посмотреть как будет выглядеть запрос вот таким образом:
                                                    echo
                                                     Criteria::create(Post::dao())->
                                                     add(
                                                      Expression::in('tagList.word', $tagList)
                                                     )->
                                                     toDialectString(PostgresDialect::me())."\n";


                                                    * This source code was highlighted with Source Code Highlighter.
                                                  +3
                                                  просто и ясно, это объекты
                                                  SQL код — это не просто, не ясно и к тому же не гибко, мое глубокое ИМХО
                                                    +1
                                                    Часто за внешней простотой, ясностью и гибкостью объектов скрывается такие неудачные SQL запросы… То для отображения таблицы 10х10 вызывается сотня запросов, то для вывода одного числового столбца этой же таблицы вызывается десятиэтажный join, возвращающий три десятка полей. Не случайно во всех (наверное) ORM есть возможность напрямую указать SQL запрос, результаты которого ORM уже сама раскидает по объектам./свойствам.
                                                      +1
                                                      В onPHP вам тоже никто не мешает сделать свой запрос и тоже построителем на объектах, а не куском SQL кода с переменными, который почти не поддается изменению. Да и к тому же, даже если запрос не оптимален, приложение не ляжет, т.к. данные в первую очередь возьмутся из кеша, а там они точно будут присутствовать, пока не ляжет кеш, или будут изменены сами данные, что повлечет их сброс из кеша.
                                                        0
                                                        Это-то понятно всё, сам обычно использую «высокоуровневый» ORM, а только перед деплоем перевожу запросы на «объектный SQL» типа $title = Query::create()->select('title')->from('table')->where('id=?', $id)->execute(); Почему не $title = Query::executeSQL('SELECT title FROM table WHERE id=?', $id); сам толком не знаю, результаты идентичны, а «парсится» запрос только в первый раз :), может потому что IDE автоподстановку делает :D
                                                          +2
                                                          Я думаю что вы подсознательно понимаете, что при изменении требований легче изменить первый вариант, в который можно добавить и условную логику, а не снова хардкодить новую версию второго варианта.
                                                            0
                                                            Скорее потому что во всех примерах, мануалах и т. п. только такой стиль используется с объясненинями типа «не зависит от диалекта SQL» и т. д. и не хочется привязываться к, например, MySQL в надежде, что когда-нить сочтешь код достойным выкладывания в паблик и кто-нить захочет попробовать его, например, на PostgreSQL. Да и автоматически сгенерированные контроллеры, например, тоже содержат в таком стиле код, хочется однообразия в проекте. В общем скорее желание переносимости и унификации, чем легкость модификации, хотя она присутсвует, определённо, особенно при работе в IDE, знающей PHP, но не знающей SQK (по карйней мере внутри PHP).

                                                            P.S. А еще проверю завтра работанет ли у меня первый вариант с «виртуальными» полями, закодированными на PHP в классе модели (а-ля public function getCost() { return $this->getPrice()*$this->getCount(); } Если работает, то неоспоримое преимущество будет у «объектного SQL», поскольку закрывает от программиста контроллера детали реализации модели, хотя по хорошему все такие запросы надо в модель тащить и из контроллера вызывать getCostById(), но не всегда по хорошему получается
                                                  +1
                                                  Я начинал писать туториал, но желающих помочь мне, к сожалению, не нашлось.
                                                    0
                                                    Так можно ваш туториал оформить в виде цикла статей на хабре. Я думаю эта идея найдет поддержку среди народных масс ;-)
                                                    +1
                                                    Боюсь получить по шапке, но после «Видимо причина в том, что у фрейморка напрочь отсутствует документация» дальше читать не стал, на мой взгляд это самый существенный недостаток который просто перекрывает все вкусности данного продукта. Любой продукт OpenSource без документации это мертвый продукт
                                                      +2
                                                      В новых версия появился OQL(Object query language). Это аналог hql их hibernate или JPA QL из jpa. Это dsl для извелечения объектов из хранилища.

                                                      Оно медленнее Criteria, поэтому для hl не подходит.

                                                      Также стоит упомянуть, что можно не пользоваться ORM, и работать в терминах чего-то типа RecordSet.

                                                      Пример:

                                                      dao->getCustomList();
                                                      criteria->getCustomList();

                                                      Вернет:

                                                      array(
                                                      array('id' => value, 'name' => value,… ,)
                                                      )

                                                      Не будет сборки объекта.

                                                      Есть возможность запустить вообще голый sql:

                                                      DBPool::getByDao($dao)->queryRaw(«sql here»);

                                                      Вернет голый db-specific курсор(или как там называется то что возвращает mysql_query, например). Но так делать не рекомендуется.

                                                      И последнее, насколько мне известно, в ряде компаний до сих пор активно используется используется onPHP и есть масса наработок, которые могли бы теоретически быть в репозитории(как минимум две версии olap, две версии Application и много-много misc utils), так как код там достаточно обобщенный, но этого почему-то не происходит. Я думаю, что по экономическим причинам(нужно дополнительные телодвижения, что заопенсорсить код).
                                                        0
                                                        Добавляю. Я тут чисто из любопытства решил его на PHP 5.3.x поднять. Так вообще никаких проблем, даже древняя версия onPHP типа 0.2.x работает на 5.3 без особых нареканий.
                                                        Это говорит еще о том что он спроектирован правильно.

                                                        Идеи в onPHP в основном взяты из Java, конкретно это Spring, Hibernate.

                                                        Тут дело не приходится иметь с лапша кодом, что не может не радовать.

                                                        Конечно чтобы разобраться в onPHP недостаточно базовых знаний, как минимум советуем ознакомится с книгой (Архитектура корпоративных программных приложений. Автор Фаулер М.)
                                                          0
                                                          Сколько строк/фактически файлов нужно для того, чтобы сделать ввод комментариев (не вывод списка комментариев, только ввод в базу), с валидацией емейла, или гостевой книги? А для регистрации (имя, логин, емейл без отправки проверки, два поля ввода пароля с проверкой на совпадение)?
                                                            +2
                                                            Приведу пример для регистрации. Для ввода коментария просто будет немного другая форма.

                                                            У нас есть пользователь — User. Мета написана, классы сгенерированы.

                                                            Для начала мы добавим в прото-класс правила для валидации.

                                                            1.     class ProtoUser extends AutoProtoUser 
                                                            2.     {
                                                            3.         public function makeForm($prefix = null)
                                                            4.         {
                                                            5.             $form = parent::makeForm($prefix);
                                                            6.  
                                                            7.             $form->get('mail')->
                                                            8.                 setAllowedPattern(PrimitiveString::MAIL_PATTERN);
                                                            9.  
                                                            10.             // можно также добавить правила/ограничения на логин и т.д.
                                                            11.  
                                                            12.             return $form;
                                                            13.         }
                                                            14.     }

                                                            А теперь проверим данные пришедшие в post, добавив правила для идентичности паролей:
                                                            1. $form =
                                                            2.     User::proto()->makeForm()->
                                                            3.     drop('id')->
                                                            4.     add(
                                                            5.         Primitive::string('passwordConfirm')->
                                                            6.     )->
                                                            7.     addRule(
                                                            8.         'passwordConcistence',
                                                            9.         Expression::eq(
                                                            10.             FormField::create('password'),
                                                            11.             FormField::create('passwordConfirm')
                                                            12.         )
                                                            13.     )->
                                                            14.     import($_POST)->
                                                            15.     checkRules();
                                                            16.  
                                                            17.     if ($form->getErrors()) {
                                                            18.         // данные не корректны
                                                            19.     }

                                                            PROFIT!
                                                              +2
                                                              Также на форму навешиваются сообщения для вывода ошибки:

                                                              $form->addWrongLabel(
                                                              'passwordConcistence',
                                                              _('Пароли должны совпадать')
                                                              );

                                                              Выводим:

                                                              echo $form->getTextualErrorFor('passwordConcistence')

                                                              Также прошедшие валидацию значения сохраняются в форме, к ним можно получить доступ:

                                                              $form->getValue('login');

                                                              Также хочется отметить формы обладают примитивами типа «Бизнес объект» т.е.:

                                                              $form = Form::create();

                                                              $form->add(
                                                              Primitive::integerIdentifier('user_id')->of('User')
                                                              /*
                                                              * Id пользователя
                                                              * $form->getValue('user_id) вернет нам объект типа User
                                                              */
                                                              )->add(
                                                              Primitive::identifierlist('user_ids')->of('User')
                                                              )->add(
                                                              Primitive::clazz('class_name')->of('SomeClass')
                                                              /*
                                                              * Ожидаем от формы название класса с учетом наследования и интерфейсов
                                                              */
                                                              )->add(
                                                              Primitive::enumeration('type_id')->of('ExtendedEnumerationClass')
                                                              /*
                                                              * Ожидаем Id EnumerationClass-а
                                                              */
                                                              )->add(

                                                              /*
                                                              * Прочие вкусности :-)
                                                              */
                                                              );

                                                              $form->importMore(
                                                              $_POST
                                                              );


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

                                                                Form::create()->
                                                                add(
                                                                Primitive::string('pass')->
                                                                setImportFilter(Filter::textImport())->
                                                                setMin(6)->
                                                                isRequired()
                                                                )->
                                                                add(
                                                                Primitive::string('name')->
                                                                optional()
                                                                )->
                                                                add(
                                                                Primitive::string('description')->
                                                                setImportFilter(
                                                                Filter::chain()->
                                                                add(Filter::textImport())->
                                                                addFilter(Filter::stripTags())
                                                                )->
                                                                optional()
                                                                );
                                                              0
                                                              Нейминг вроде ничего, но многие конструктивные решения слегка устарели… На дворе spl_autoload, 5.3.2…

                                                              Не сильно тормозит этакая прикольная конструкция в global.inc.php set_include_path() с прописью всех путей поиска классов?

                                                              Думаю тормоза будут неслабые при переборе путей… Ибо по статистике профилировщиков (kachegrind) самые тормоза после БД, это «инклудинг» классов вместе с поиском и бизнес-логикой в циклах. Особенно касается include_once, require_once. В общем autoload хорошо справляется с этим. Если его нет, можно эмулировать через обработку ошибок… И максимум статики где это возможно используйте.

                                                              Попытка использовать типы-классы и базовые сущности выглядит интересной…
                                                                0
                                                                Нет, там нету тормозов. Там все кешируется при желании и кладтся в один файлик. В файлике асоциативный массив в котором ключик это имя класса, а значение — путь к файлу. Так что все работает довольно быстро.

                                                                Пробовал кстати запускать без кэширования путей и тогда да, сильно тупит. Примерно 0,3 сек на моем буке добавляется толькоза счет поиска и инклюда всех необходимых файлов.
                                                                  0
                                                                  Нужно использовать eAccelerator, xCache либо подобные продукты. Без них как-то туговато будет в любом смысле этого слова.
                                                                    +1
                                                                    Самый быстрый вариант в onPHP, это autoload.classPathCache с хранением кэша классов в памяти.

                                                                    В этом случае все классы(код) вообще объединяются в несколько файлов(в зависимости от путей).

                                                                    Если разместить(указать путь в конфиге) эти файлы в /dev/shm проблема загрузки классов будет решена.
                                                                    0
                                                                    Насчет конфликта классов:

                                                                    почти все знают ведь что делать :)
                                                                      +1
                                                                      Написал свое _личное_ мнение про документацию в oss: gabaidulin.livejournal.com/171431.html
                                                                        0
                                                                        А точно «Аутичный»?

                                                                        Может быть, всё таки, «Аутентичный»?

                                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                        Самое читаемое