Saturday, August 15, 2020

HTTP routing

http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html

https://github.com/nikic/FastRoute

https://benhoyt.com/writings/go-routing/

You have several options, how to route HTTP requests. My habit is to find the fastest way, but it's not needed here, because the router is called only once on URL change.

Table

Loop through pre-compiled regexes or patterns and pass matches using the request context

  1. router.addRoute(httpMethod, "a/(*.)/c", routeHandler),

Or use directly the HTTP method:

  1. router.get("a/(*.)/c", routeHandler);

Switch

A switch statement with cases that call a regex-based match() helper which scans path parameters into variables.

Similar to regex match, but using a simple pattern matching function instead of regexes. It's not that powerful, but you will mostly need “anything” wildcard for a path part anyway.

  1. case httpMethod == "GET" && routeRegexPattern.match("a/(*.)/c"):
  2. return routeHandler();

Split

Split the path on / and then switch on the contents of the path segments

  1. case httpMethod == "GET" && p[0] == "a" && p[1] == "b":
  2. return routeHandler();

Shift

Basically delegates the route matching to the module, according to the first path part. For a/b/c path the router will call an “a” module, pass him [b,c] and don't care any more.

 

Monday, November 25, 2019

Lite Framework in PHP

I noticed something bad: when I create a small static website, instead of using the Framework, I rather write it from scratch. It's probably so, because I'm a performance freak and that extra weight of the Framework is bothering me.

I tried to create a minimal version in the past, but it still wasn't what I was looking for.

So, I decided to cut the Framework into layers: the base layer will be good for static websites and simple APIs, but lightweight enough to offer mostly benefits of handling modules, forms, CORS, errors and other basic stuff.

The next layer will add sessions, messaging and authentication, and the final layer will add config, internationalization, multi-app and multi-site support and the rest of all the QetriX magic.

Using Converters, Components and DataStores in Lite Framework will be encouraged, but not enforced. I want no dependencies, my goal is to point a web server to a single script, write a single module and voilà.

Friday, July 5, 2019

Saving

There are basically three major approaches:

  • save everything at once using a manual trigger (button),
  • save after every change using an event trigger,
  • save after a time period using a timer.

My approach is a combination of all of the above, with incremental saves prevalence. But, as usual, things aren't as simple as they may seem. In forms you can save data only after all required fields are filled. In editing long text there are no partial changes, so there's no cue when to save.

Therefore I have two ways of auto-save in a text. I define a 5 secs timer, that saves the text after it's not changed, which means e.g. arrow keys do not stop this from firing. The other way is I save after Enter key is pressed.

Before each change of text I create a “backup”, to have an undo option in case I or the editor mess it up. After the text is verified, those backups are deleted.

I also try to show saving in some unobtrusive way, like dimming header color or change it's background.

Saturday, June 1, 2019

Scoping

I started with a single “master” scope for all components and data elements. It went well, until it didn't :-) I needed to separate elems in lists (table columns), because they also got filled by the .data method.

The obvious choice was to create a per-component scope, but problems were single data elements can appear in multiple components and at the same time a single form can be composed of multiple other components.

So I tried keep all elems in the global scope and separate all elems in lists. It worked well and it solved the issue I had.

But I still had one issue, I wasn't able to declare a custom temporary form or a form-in-form. So the ultimate decision (and hopefully the last major rewrite of the whole thing) was to have somewhat hybrid scenario with per-major-component scopes – besides separate scope for lists, all nested forms will inherit scope from the initial parent form.

This way the nested parent form will have separate scope and a custom form, e.g. in a modal window, will also have it's own scope.

And to make it even more complex, I had to add a scope-groups, because I allow to load multiple modules at once (module-in-module) and I know what scopes to remove with closing the nested module. The former way didn't look right anyway :-)

Thursday, August 10, 2017

Min Version

I designed the Framework to be as lightweight as possible, with as few classes as possible. There are many of them, but most of them aren't required for it to work.

The minimal version for PHP Framework contains there files and directories:

  • /libs/qmodule.php
  • /libs/qpage.php
  • /libs/util.php
  • /modules/[module].php
  • /vars/logs/
  • /qetrix.php

In this configuration, the whole framework (not minified) has just 75 kB and 1600 lines of code. All classes in this minimal version are regular, there's no “lite” version. QetriX is designed to be highly extensible, so even such integral parts, like components, are optional.

Saturday, April 22, 2017

Type Helpers

Starting with PHP 7, a lot of things in PHP moved towards more mature programming language. But what probably will remain for a long time is function inconsistency, argument-wise.

String functions are often [haystack,needle] or [string,param], whilst array functions are often the other way around - [needle,haystack] or [param,array]. I wrote “often”, because sometimes it's not the case. What a mess!

So I decided to provide common type classes with unified order and pull some functions from Util class, where they didn't belong in the first place. I agree a need for Util classes often means wrong class architecture.

Those classes are QS, QN, QA, QD and QE for string, number, array, date and enum functions/methods. They will be optional and usage will be encouraged for creating custom modules for custom projects. The core won't need them, so if you're familiar with PHP functions, those classes won't add anything into the compiler.

They are just helpers and you may use them only for what PHP is the best in – hack something working together in an hour :-)

In JS Framework there is one extra class QC, helping with components and elements.

Friday, February 3, 2017

It's all the same!

The evergoing theme in QetriX is to unify things as much as possible. This was the case for components in the first place, notably for QList: everything with repeating pattern is just a list of stuff, no matter if it's a navigation, gallery, table, slider, search results, blog or whatever.

But this approach applies even to whatever – there's not much of a difference between table row and a form. That was another breakthrough idea for QetriX, allowing me to merge QFormControl and QListItem into QElem, so-called “data element”.

QElem contains value and it's specification, notably data type. In QForm it's a form control, in QList it's mostly a table column (because table is the most used implementation of QList with variable data model).

Monday, April 4, 2016

Page Path Parts

QetriX Application consists of pages. If a page has path property, it's accessible via URL and should provide some kind of output.

Path also define other aspects of the page, like what app or site it belongs to. The Framework supports multiple apps running on the same core and domain, as well as multiple sites. Of course, apps and sites are optional.

“Site” is a standalone part of an App, like user space. For blogging service it would be individual blogs, where content and data is separated from other sites (blogs). The difference between App and Site is that Sites share the same resources, while Apps don't.

Multi-app multi-site path example: /blogs/qetrix/2016/04/hello. “Blogs” is an App, “qetrix” is a Site and the rest is a path of a page. App and Site names always corresponds to a directory structure on server's file system, Page path is often a database entry.

Monday, March 21, 2016

Framework Mk. VI released

Another version of the Framework is out and this is a big one, certainly in terms of expected longevity for model and major API specification.

First enhancement is a class Dict, a wrapper around HashMap (associative array in PHP, object in JS). It offers some helper methods with missing item handling and data type conversions.

Second enhancement is I renamed QApp class to QPage. It better suits its purpose, because App is more imaginary, while Page is real (defined by path, which is commonly represented by URL).

Third enhancement is the biggest – I realized form field and table column are technically the same, so instead of QType and QListCol I introduced QElem, so called Data Element, which you can add to either QList or QForm.

Fourth enhancement is I renamed QListRow class to QItem. An “item” generally means a record in a secondary dataset. QItem extends Dict, but it has some extra methods for easy access to basic properties, like value, text, action or image.


General parts of QItem, demonstrated on iOS's Table Cell
Monday, March 14, 2016

CRUD in QetriX

In the big picture, QetriX is just a CRUD (create-read-update-delete) platform. I tried to describe the whole process to find common aspects and repeating patterns among it's parts, leading to simplification and therefore to minimal code.

From this emerged components. There are four component types in QetriX:

  • *QElem* is (r)evolution of Data Particle from QPDB. Besides value it could also specify it's data structure.
  • *QForm* is like view, but contains form controls (textboxes, comboboxes, checkboxes, buttons...) to modify data.
  • *QList* is a listing of entites and their “base” attributes.
  • *QView* is partly like a QForm, partly a “glue” holding other components together.

And that’s it. I didn’t find any use case, where I’d need to use anything like this.

Wednesday, July 10, 2013

Reengineering AGAIN?

And again :) I don’t have to worry about anything right now, as everything is doing just fine with the previous version and nobody rely on the new one.

I wanted to add more functionality to the code and realized I pasted a lot of code in the core without further investigation what it really does and how good it is in sake of performance. And because I did some thinking about the object model, I decided to start over with no old code.

I know it will take a lot of time, but from my point it’s time well wasted. Reengineering is kinda my way of “watching TV” – just relaxation without much thinking – I did all the thinking before, the thinking brought me to the point I decided to make it better.

This time I’ll introduce:

  • Simple module model, along with the current model, which proved itself right.
  • Improved class inherition schema.
  • Better way of module loading.
  • Better way of using Renderers (former Templates) in Components.
  • Improved DataStores.
  • Better way of using DataStores in Components.

I was very cautious about using different renderers in the past, because I knew it might cause a problem. This time I’ll start using multiple renderers right away to make it work immediately without struggling in the future.

Wednesday, May 29, 2013

Less Frame, More Work

When I started to code QetriX in PHP, I didn’t plan to release it as a framework. But with progress of the concept my perspective slightly changed.

First of all, the idea is cross platform, cross environment and cross language. It says just “what”, not “how”. Second of all, I think the idea may be used in a wrong way, so It’s gonna be better, if I set the rules here and let fellow programmers just to follow them and stick with them.

Because all languages I’m focusing on are very similar, I decided to unify the code across all these langs. I’m talking about PHP, Java and C# here.

How it works:

  • Install
  • Profit

I don’t plan to document the framework to the core. I don’t want people to mess with the core code. One day I may release a printed book, as I always wanted, with deeper explanation how stuff works.

I don’t want to look like I’m hiding something there. I know my coding habits are sometimes obsolete, but I’m strictly against today’s beloved bloats, like giant classes in PHP even in situations, when simple function would do the job as well. I want my code quick and sleek, performance tests speaks loudly against OOP in PHP. In latest PHP builds it’s better, but still little slower and more memory consuming.

On the other hand because I decided to unify the code, I have no choice but use objects. Making full OOP in PHP is like mounting wings on a car and trying to fly with that – it takes a lot of time and effort and the result is questionable. So I will try use the OOP wisely, only in situations, that otherwise would be problematic (like no object for “toolbox” or app itself).

Except for that, I don’t want anyone to change anything in the code (PHP, JavaScript), everything is there for a reason. If you need anything to work in a different way, you may consider to look somewhere else. The framework is built to be “just enough” for the purpose it serves. I want full out-of-the-box experience.

During implementation it’s possible to add a new module, but again – with deep respect to the ecosystem. The only part I’m completely OK about changes is CSS, but even there I created everything you may need.

Regardless, I’d love to hear a suggestions or constructive criticism (preferably with refactored code), if it stays in defined boundaries.