The duck says "quack-quack", the cow says "Moo-Moo", "Runn Me!" — tells us the next framework* PHP. Part 1

"Oh no!" exclaims the reader, tired by the different micro-mini-slim-frameworks and QueryBuilder-AMI will be right.

There is nothing more boring than yet another framework for PHP. Is that "fundamentally new" CMS or a new Dating.



So why do I have a tenacity worthy of a better cause, walking around uncomfortable pitfalls and put in the delight of the audience the court of comrades his creation? Knowing that the wrath of critics, as a powerful tsunami will fall upon this post and bury it at the bottom Habra?

I do not know. I did not know at the time of Columbus, why he sailed from the coast of Spain. Did he hope to find the way to India? Of course, Yes. But I did not know will come?

Apparently the programmers in PHP, which I'm now 13 years absented themselves, have the same inner need to put your code and narrow eyes, waiting for the reaction of colleagues.

What awaits you under the cut?

the
    the
  • Open source LGPL
  • the
  • Code that is fully compatible with PHP 7.0-7.2
  • the
  • 100% coverage unit tests
  • the
  • library of time-tested in real projects (and only damn procrastination prevented me to publish them earlier!)

And, of course, the history of the invention of the next spike bike the drive framework*!

* generally speaking this is not a framework, just a set of libraries, framework it will be later



the

a Little history or Where did the idea of "to write another framework?"


Yes, strictly speaking, nowhere. It has always been.

Different interesting projects, which I happened to participate during programming career, put into the personal Treasury of standard solutions for standard problems — I, like any normal programmer, has accumulated its own library of functions, classes, and Biblioteca.

Six years ago, the company where I then worked, has set the task: to develop your own framework. To make a lightweight MVC framework, taking only the most necessary, to add specific libraries subject area (believe me — very specific!) and collect some kind of universal solution. The decision, it should be noted, happened, but the specificity of the subject area did not allow him to become a mass — code not published, sold installation at the customer site. Which is a pity. Some things really ahead of its time: it is enough to say that it's primitive, but it's still pretty similar likeness of the composer with the team we did then quite independently and a bit before there was actually a stable public composer :)

Thanks to this experience I had the opportunity to explore virtually all of the then existing in the ecosystem of PHP frameworks. Along the way, there was another event, another "the transition from quantity to quality" — I began to teach programming. First, in a well-known online school, then focused on the development of your own service. Became "overgrown" with teaching methods, teaching materials and, of course, students. In this get-together came the idea of a "training framework", intentionally simplified for the understanding of beginners, but allows all the same to successfully develop a simple web application in accordance with modern standards and trends.

Three years ago, as the realization of this idea, "training framework", was born on a small MVC-framework called "T4"*. The title is nothing special, just an abbreviation of "Technological layout, version 4". I think it is clear that the previous three versions came out unsuccessful, and only on the fourth attempt we, along with then my students, managed to create something really interesting.
* I later learned that the third Reich was the name of the program of sterilization and euthanize terminally ill people... of course immediately the question arose about the change of name

T4 safely developed and grew, became known as the saying goes, "in the narrow circles" (very narrow), it was made a number of fairly large projects, but growing internal dissatisfaction with this decision.

In the beginning of this year I was finally ready to reformat the accumulated code. Together with a group of like-minded people who are also actively used T4, we made a number of basic principles of the new framework:

    the
  1. Make it loosely coupled set of libraries, so that each libo can be mounted and used separately.
  2. the
  3. Try to maintain a healthy minimalism where possible
  4. the
  5. a framework for web and console applications — is also one of the libraries, thus we avoid solidity.
  6. the
  7. Trying not to reinvent the wheel, and maximally maintain the approaches and the code, which already proved to be in T4.
  8. the
  9. Refuse to support older versions of PHP, I write the code under the most current version.
  10. the
  11. Try to make code as flexible as possible. If you can — instead of classes and inheritance using interfaces, traits, and composition of code, leaving users of the framework the ability to replace a reference implementation of any component of its.
  12. the
  13. Cover the code with tests, achieving 100% coverage.

Thus was born the project which initially called "Running.FM", and then finally renamed to "Runn Me!"

It today I do.

Incidentally, the word "runn" is constructed artificially: on the one hand to be understood and to evoke associations with a "run" on the other — to not coincide with any of the vocabulary words. Actually, I like the combination of letters "run": I'm still in RunCMS at the time, had time to participate :)

At the moment the project "Runn Me!" is in the middle of the road — some libraries already be used in production, some in the process of migrating from old projects and refactoring, and some we have not yet begun to move.

the

In the beginning was Core


To fit in one post the story of each library project "Runn Me!" is impossible: a lot of them, I want to tell in detail about each, and besides this is a live project in which everything changes for the better every day :)

So I decided to break the story about the project a few posts. Today we will talk about the basic library, called "Core".

the

    Purpose: implementation of the base classes of the framework

    GitHub: github.com/RunnMe/Core

    Composer: github.com/RunnMe/Core

    Setting: command composer require runn/core

    Version: like any other library project "Runn Me!" there are three versions corresponding to previous, current and future versions of PHP:
    7.0.*, 7.1.* and 7.2.*


the

Pattern? Object? Or both?


The fertile idea of an object consisting of arbitrary properties that you can create and delete on the fly, as elements in the array, comes to mind every programmer in PHP. And every second this idea implements. No exception and with my team I: your introduction to the library Runn\Core I want to start with a story about the concept ObjectAsArray.

Do time: define an interface that will allow you to chastity your object to an array and back: the array to convert to object, not forgetting in this interface a few useful methods (merge() for merging with external data object and recursively casting the array)

github.com/RunnMe/Core/blob/master/src/Core/ArrayCastingInterface.php
the
namespace Runn\Core;

interface ArrayCastingInterface
{
public function fromArray(iterable $data);
public function merge(iterable $data);
public function toArray(): array;
public function toArrayRecursive(): array;
}

Do: collect magazinehas, which describe the behavior of a future object-as-array as fully as possible, laying there the most useful: serialization, iteration, counting the number of elements get a list of keys and values, search the element in this "object-array".
github.com/RunnMe/Core/blob/master/src/Core/ObjectAsArrayInterface.php
the
namespace Runn\Core;

interface ObjectAsArrayInterface
extends \ArrayAccess, \Countable, \Iterator, ArrayCastingInterface, HasInnerCastingInterface, \Serializable, \JsonSerializable
{
...
}

Do three write a trait that will become the reference implementation of megaintestine. Cm. github.com/RunnMe/Core/blob/master/src/Core/ObjectAsArrayTrait.php

As a result, we received full-fledged implementation of the "object-as-array". The use of ObjectAsArrayInterface and trait ObjectAsArrayTrait allows us to do something like this:

the
class implements someObjAsArray \Runn\Core\ObjectAsArrayInterface 
{
use \Runn\Core\ObjectAsArrayTrait;
}

$obj = (new someObjAsArray)->fromArray([1 => 'foo', 2 => 'bar']);
$obj[] = 'baz';
$obj[4] = 'bla';

assert(4 === count($obj));
assert([1 => 'foo', 2 => 'bar', 3 => 'baz', 4 => 'bla'] === $obj->values());

foreach ($obj as $key => $val) {
// ...
}

assert('{"1":"foo","2":"bar","3":"baz","4":"bla"}' === json_encode($obj));

In addition to the basic options in ObjectAsArrayTrait the possibility of interception of both assignment and read "the elements of the object array" using custom setters-getters, a kind of groundwork for future classes:

the
class implements customObjAsArray \Runn\Core\ObjectAsArrayInterface 
{

use \Runn\Core\ObjectAsArrayTrait;

protected function getFoo() 
{
return 42;
}

protected function setBar($value)
{
echo $value;
}

}

$obj = new customObjAsArray;
assert(42 === $obj['foo']);

$obj['bar'] = 13; // prints 13, the assignment is not happening

the

Important: null is set!


Yes, the element object array whose null value is considered defined.

This decision caused a lot of controversy, but was eventually adopted. Believe me, there are serious reasons, which will be discussed further in the narrative of the ORM library:

the
class implements someObjAsArray \Runn\Core\ObjectAsArrayInterface 
{
use \Runn\Core\ObjectAsArrayTrait;
}

$obj = new someObjAsArray;
assert(false === isset($obj['foo']));
assert(null === $obj['foo']);

$obj['foo'] = null;
assert(true === isset($obj['foo']));
assert(null === $obj['foo']);

the

why?


Well, of course! All I told above is only the beginning. Interface \Runn\Core\ObjectAsArrayInterface inherited by other interfaces and classes implementerat giving birth to two "branches" class: Collection and Std.

the

Collection


Collection to Runn Me! are objects arrays are provided with a large number of additional useful methods:

Here you can see them all
namespace Runn\Core;

interface CollectionInterface
extends ObjectAsArrayInterface
{
public function add($value);
public function prepend($value);
public function append($value);
public function slice(int $offset, int $length = null);
public function first();
public function last();
public function existsElementByAttributes(iterable $attributes);
public function findAllByAttributes(iterable $attributes);
public function findByAttributes(iterable $attributes);
public function asort();
public function ksort();
public function uasort(callable $callback);
public function uksort(callable $callback);
public function natsort();
public function natcasesort();
public function sort(callable $callback);
public function reverse();
public function map(callable $callback);
public function filter(callable $callback);
public function reduce($start, callable $callback);
public function collect($what);
public function group($by);
public function __call(string $method, array $params = []);
}


Of course, once a developer is available as a reference implementation of this interface in the form of a trait CollectionTrait and ready to use (or inherit) a class \Runn\Core\Collection, which extends the implementation methods of the class convenience constructor.

Using collections, it becomes possible to write code like this:

the
$collection = new Collection([1 => 'foo', 2 => 'bar', 3 => 'baz']);
$collection- > prepend('bla');

$collection
->reverse()
->map(function ($x) { 
return $x . '!'; 
})
- >group(function ($x) {
return substr($x, 0, 1);
});

/*
will get something like
[
'b' => new Collection([0 => 'baz!', 1 => 'bar!', 2 => 'bla!']),
'f' => new Collection([0 => 'foo!'),
),
]
*/

What is important to know about collections?

    Most methods do not modify the original collection, and returns a new.

    Most methods do not guarantee the preservation of key elements. the

  1. Better use of collections, storing them in sets of homogeneous or similar objects.

Typed collections


In addition to "regular" collections in the library Runn\Core included an interesting tool that allows you to fully control objects that may be contained in the collection. It is a typed collection.
All very, very simple:

the
class UsersCollection extends \Runn\Core\TypedCollection 
{
public static function getType()
{
return User::class; // here may be the name of a scalar type, by the way
} 
}

$collection = new UsersCollection;

$collection[] = 42; // Exception: Typed collection type mismatch
$collection- > prepend(new stdClass); // Exception: Typed collection type mismatch

$collection->append(new User); // Success!

the

Std


The second "branch" of code, in some ways the opposite collections, called "Standard object". Is step by step:

Do time: define interface for "magic".

the
namespace Runn\Core;

interface StdGetSetInterface
{
public function __isset($key);
public function __unset($key);
public function __get($key);
public function __set($key, $val);
}

Do: add to it the standard implementation (see github.com/RunnMe/Core/blob/master/src/Core/StdGetSetTrait.php )

Do three gather from the "spare parts" class, based on StdGetSetInterface with many additional features. github.com/RunnMe/Core/blob/master/src/Core/Std.php

In the end we get a generic class that is suitable for solving a variety of problems. Here are just a few examples:

the
$obj = new Std(['foo' = > 42, 'bar' => 'bla-bla', 'baz' => [1, 2, 3]]);
assert(3 === count($obj));

assert(42 === $obj- > foo);
assert(42 === $obj['foo']);

assert(Std::class == get_class($obj->baz));
assert([1, 2, 3] === $obj->baz->values());

// Oh, Yes, the realization that this thing is quite monstrous:
$obj = new Std;
$obj->foo->bar = 42;
assert(Std::class === get_class($obj->foo));
assert(42 === $obj->foo->bar);

Of course, the "skills" class Std are not limited to chaining th, access properties as array elements and Vice versa, by casting the class. It does so much more: to validate and clean the data, keep track of mandatory properties, etc. But more about that later, in other articles of the cycle.

the

And then?


It's only the beginning! Ahead of us there are stories about:

the
    the
  • Multicluster
  • the
  • Validators and sanitizers
  • the
  • About repositories, serializers, and configs
  • the
  • On the implementation of Value Objects and Entities
  • the
  • HTML and submission forms on the server side
  • the
  • native library DBAL, including, of course, QueryBuilder!
  • the
  • Library ORM
  • the
  • and as a finale — MVC-framework

But all this in future articles. In the meantime, what with a holiday, comrades! Peace, labor, code! :)



PS a Detailed plan with the time we have, and no desire to get to any next date. So don't ask "when". As the availability of individual library will get articles about them.

P. P. S. gratefully accept information about mistakes or typos in messages.

the

©


KDPW (C) Mart Virkus 2016
The picture in the conclusion of the article from googlevoice images
Article based on information from habrahabr.ru

Популярные сообщения из этого блога

Approval of WSUS updates: import, export, copy

The Hilbert curve vs. Z-order

Kaspersky Security Center — the fight for automation