New Core Features - xaraya/core GitHub Wiki

Table of Contents

Introduction

The Xaraya core has been stable for quite a few years now. But sometimes we have an idea, or an itch to scratch, that just doesn't go away, and we wonder ... what if?

So here are a few of the latest changes and features that explore these ideas. In general they are implemented in a way that doesn't affect any of the existing code base, either in the core or elsewhere. However, if you do discover an unexpected side-effect, please raise an issue here with the specific use case where you hit the problem.

Partial Core Load

Xaraya already supported the concept of loading xarCore::xarInit() up to a certain "run level", from NONE to DATABASE, MODULES or USER etc.

 * NONE                            (00000000)
 * |
 * |- EXCEPTIONS
 * |
 * |- SYSTEMVARS
 * |
 * |- LOG
 * |
 * |- DATABASE                     (00000001)
 *    |
 *    |- AUTOLOAD
 *    |
 *    |- EVENTS
 *    |
 *    |- CONFIGURATION             (00000010)
 *       |
 *       |- LEGACY
 *       |
 *       |- MLS
 *       |
 *       |- MODULES                (00000100)
 *          |
 *          |- SERVER
 *              |
 *              |- TEMPLATE        (00001000)
 *                 |
 *                 |- SESSION      (00010000)
 *                    |
 *                    |- USER      (00100000)
 *                       |                 
 *                       |- BLOCKS (01000000)
 *                       |
 *                       |- HOOKS  (10000000)
 *                        
 * |- ALL                          (11111111)

However, there is still some system configuration done inside xarCore::xarInit() that is passed along by bootstrap.php and the index.php endpoint, and the dependency between "run levels" is fixed.

What if we didn't need to load the whole core and could just pick the system components we were interested in?

As an example, the gql.php endpoint doesn't call xarCore::xarInit() at all, but it picks up the pieces it wants and lets the components initialize themselves:

// initialize bootstrap
sys::init();
// initialize database
xarDatabase::init();

This required cleaning up the system component initialization a bit to support, but it didn't change any of the "normal" xarCore::xarInit() loading that other endpoints use. Of course, this way of loading wouldn't work unless we tackled the next issue...

Composer Autoload

One of the issues we've had with the Xaraya codebase spread across multiple directories (var, lib/xaraya, code/modules, themes/default, ...) was making sure all of the necessary PHP code is loaded before we call certain functions or class methods.

Modern code bases solve this by using namespaces and standard PSR-4 autoloaders, but Xaraya has been around for ages so it wasn't really suited for this approach just yet. So now we call sys::import() to include specific core or module files first whenever we're not sure they will be loaded already.

And of course this didn't solve the issue when complex objects are cached and need to be re-composed, so we added our own flavor of autoload.php - which works in general, but still requires maintaining individual autoload.php files in each module for these hard-to-find functions, classes and constants.

What if we didn't need to manually import files in code and maintain our own autoload, but could re-use a standard autoloader?

That's where composer autoload comes in. Next to the standard PSR-4 and older PSR-0 it also supports generating and loading a classmap by scanning for classes in all specified subdirectories. And in fact generating a class map is a recommended optimization for PSR-4 and PSR-0 in production as well. That doesn't sound too bad, does it?

So in order to start using this, we created a composer.json file that would scan the directories we're most interested in. This will generate a new class map whenever you run any of the following commands in the root directory of Xaraya:

$ composer install  # first time installation
$ composer dump-autoload -o  # when you add new modules or classes in development
$ composer update  # if you want to update the dev libraries too

These will create or update the vendor/autoload.php and vendor/composer/* files (including vendor/composer/autoload_classmap.php) that you'll need to copy in production.

Note: this functionality is not used or needed with any of the standard endpoints or modules today - except the new gql.php and rst.php endpoints

For new developments, this will allow you to get rid of all these numerous sys::import() and custom autoload.php files and simply start your endpoint with the following require line. As a bonus, this will also allow you to make use of any of the composer packages from https://packagist.org/

<?php
/**
 * Entrypoint using composer autoload for partial core loading and/or external composer packages
 */
require dirname(__DIR__).'/vendor/autoload.php';

use Whatever\Something;

// initialize bootstrap
sys::init();
// ...

The gql.php entrypoint shows one example of this approach, combining Xaraya features like Dynamic Data Objects with GraphQL Query capabilities as a proof of concept.

Of course, this story wouldn't have a happy ending if we didn't tackle the next subject as well...

OOP: Replacing core functions & constants with class methods & constants

One of the changes formalized with Xaraya 2.x (back in the old days) was the systematic use of object oriented programming (OOP) with classes for all system components under lib/ (and related code/modules/*/class/). However, to be honest we didn't really finish the switch-over and clean-up job until after 2.4.0, both in the core library and core modules.

As a result, there were still global functions being called all over the place, relying on the full core loading, sprinkling of sys::import() and/or manual inclusion in autoload.php to make sure PHP could actually find them.

What if we could get rid of all these old core functions & constants once and for all, and be ready for the future?

The good news is that most of the clean-up work has been done now, at least for the core library and modules. However, this still leaves a large quantity of xaraya modules & themes unaccounted for...

Clean-up of non-core Xaraya modules

Some of the more "recent" modules from https://github.com/xaraya/modules (i.e. those with any development activity in the past few years) have already gone through a clean-up. The cleaned-up modules have been moved to separate repositories in the new https://github.com/xaraya-modules organization, and submitted as individual packages of type 'xaraya-module' at https://packagist.org/?type=xaraya-module. They haven't necessarily been tested with a recent Xaraya core though, so your mileage may vary - feel free to contribute any changes...

What if we had a tool that could help us clean up old Xaraya modules?

There is a bermuda clean-up tool available under the developer/tools directory that should help clean up these modules, along with an older aruba 2 jamaica conversion script for the really outdated modules.

GraphQL Query Capability

Coming back to the core, one of the continuing trends over the past years is the more prominent use of API's to support mobile apps and integration.

Although Xaraya did support some of the API's like RSS/Atom via themes or XMLRPC, JSONRPC, SOAP, WEBDAV, REMOTING, REST via the ws.php web service endpoint + dedicated modules, it didn't really keep up with more modern API's like GraphQL.

What if we could provide GraphQL Query capability for dynamic data objects out of the box?

See gql.php

Example: https://owncloud.mikespub.net/bermuda/gql.php - use the GraphQL Playground or GraphiQL browser extension to explore

Deferred Properties

One of the key concerns for performance with GraphQL queries is how to solve the so-called N+1 problem, where querying 1 parent item triggers querying N child items (which in turn triggers M related queries and so on). The GraphQL Query library we use in Xaraya solves this nicely by buffering execution of child queries with Deferred fields.

What if we could provide similar deferred properties for Dynamic Data Objects in web views and displays?

See deferitem.php, deferlist.php and defermany.php

Example: https://owncloud.mikespub.net/bermuda/index.php?object=api_people&method=display&itemid=1 - follow some links to see the many-to-many relationships

Use case: Star Wars API (SWAPI) with Xaraya

See https://github.com/xaraya-modules/apischemas

Source: https://github.com/Juriy/swapi and https://github.com/graphql/swapi-graphql

REST APIs Revisited

What if we could provide REST APIs to access Dynamic Data Objects and optional module APIs out of the box?

See rst.php

Example: https://owncloud.mikespub.net/bermuda/rst.php - use the Swagger UI to explore, or use in front-end web pages like DataTables

Use case: Headless CMS with Xaraya

Xaraya has always been positioned as a web application framework rather than a content management system, but all this talk of APIs leads us to the next question.

What if we could use Xaraya as framework for a decoupled or headless CMS?

The presentation layer, user functions and api functions have always been separated from the start. But with the use of GraphQL or REST APIs it becomes much easier to re-use many of the capabilities of Xaraya in combination with any front-end technology.

Anything else?

Introduce namespaces for Xaraya 3.x perhaps? Having replaced global functions all over the place with fewer core classes will make it easier to modernize the code base further if we want to...

What about the future?

You tell us. Do you have a specific need or itch to scratch? Please let us know by raising an issue - or submitting a pull request...