GithubHelp home page GithubHelp logo

baileyherbert / horizon Goto Github PK

View Code? Open in Web Editor NEW
0.0 0.0 0.0 2.19 MB

Framework for building distributed web applications that work on all servers.

Home Page: https://docs.bailey.sh/horizon/

License: MIT License

PHP 98.20% HTML 0.76% Twig 0.93% Dockerfile 0.12%

horizon's People

Contributors

baileyherbert avatar dependabot[bot] avatar

Watchers

 avatar  avatar

horizon's Issues

Provider error when deleting translations directory

Uncaught exception "UnexpectedValueException" with message "RecursiveDirectoryIterator::__construct(app\translations,app\translations): The system cannot find the file specified. (code: 2)" in horizon\lib\Provider\Services\TranslationProvider.php on line 36

Add route pipes

Route pipes are similar to middleware, except they apply to all requests under a prefix rather than specific routes. They also execute before the request is matched to a route, allowing them to:

  • Transform or normalize the request before routing
  • Set the error handler for a directory (such as setting JSON errors for an API directory)
  • Set headers like CORS for all requests in a directory

In addition, two official pipes will be added to the framework.

  • Horizon\Http\Pipes\ErrorHandlerPipe -- Used to change the error handler for all matching requests.
  • Horizon\Http\Pipes\HeaderPipe -- Used to set headers for all matching requests.

Example pipe invocations to demonstrate prefixes and attributes:

Route::pipe('/api/', Horizon\Http\Pipes\ErrorHandlerPipe::class)
    ->set('handler', Horizon\Exception\JsonErrorHandler::class);

Route::pipe('/api/', Horizon\Http\Pipes\HeaderPipe::class)
    ->push('headers', 'Access-Control-Allow-Credentials: true')
    ->push('headers', 'Access-Control-Allow-Origin: http://localhost:3000');

With the above example, the pipes are applied for all requests within /api/ before the kernel even checks if the request has a matching route:

  • /api -- Not matched
  • /api/ -- Matched
  • /api/file -- Matched
  • /api/dir/file -- Matched

As a result, even 404 errors generated by the framework will receive the custom headers and use the JSON error handler.

Add tagged components

Currently a component can be rendered within a template like this:

@component('name', args)

This proposal will add tagged components that look like regular HTML tags and accept attributes and slots.

Basic usage

A tagged component must be in the format <@name> which will resolve to a component template named tags/name.

<@name><div>Inner content</div></@name>
<@name />

The corresponding tags/name component template file will need to express the @tagged attribute at the top of the file. This directs the transpiler to treat the component as a tag. It can then use the special @slot directive.

@tagged
@slot

Attributes

Attributes supplied after the declaration of a tagged component are parsed as strings and made available to the component template. Attributes defined without a value will be set to "true".

<@name value="The bool attribute is set!" bool />

The corresponding tags/name component template file can retrieve these values using its class, which receives an associative array of all attributes and their values at construction time. For the default component class, this means all attributes can be retrieved using the standard $this->varname:

@tagged
@if ($this->bool)
  {{ $this->value }}
@end

Attributes can accept variables from the environment like usual, and can be optionally enclosed in quotes:

<@name value="{{ $variableName }}" />

Slots

Attributes can accept inner HTML using slots. Each component has a default slot and can also define named slots. Behind the scenes, tagged components treat slots as twig blocks which can be easily extended from above.

The default slot can be used by simply providing inner HTML:

<@name>Inner HTML</@name>

However, named slots must be defined as their own tags in the format <@slot:name>, where name is the name of the slot as defined within the corresponding component template. As an example, take this tagged component template:

@tagged

<h1><@slot:header /></h1>
<p><@slot /></p>

The usage to implement both slots would look like the following:

<@name>
    <@slot:header>Hello world!</@slot>
    <@slot>This is some paragraph text.</@slot>
</@name>

Note that when specifying slots with the special @slot tag, it is illegal to supply additional inner HTML outside of slot tags. In other words, this must error:

<@name>
    <@slot:header>Hello world!</@slot>
    <@slot>This is some paragraph text.</@slot>
    This content isn't allowed outside of a slot.
</@name>

Exports

The cool thing about normal components is that they have their own environment for state which is managed by the class defined with the @using() directive. Tagged components are no exception, they too must be attached to a class and can specify the @using() directive.

Normally, this state is isolated and cannot be accessed by the templates that invoke the component. However, tagged components can have their state exported from their attributes.

For example, if the component has a variable stored at $this->varname, the caller can export it like below, after which the variable varname will immediately become available in the scope of the calling template:

<@name export:varname />
{{ $varname }}

Dynamic invocation

If the name of the tagged component is unknown until runtime, it can be invoked dynamically with the built-in <@dynamic> tag. This accepts a name argument containing a string.

<@dynamic name={{ $componentName }} />

Allow filters to be called like normal functions in templates

Twig filters that have an equivalent PHP function should be added to the custom template syntax to behave like native PHP functions.

For example, the lower twig filter is normally invoked like this:

{{ 'UPPER'|lower }}

Horizon should accept the following and convert it to the above syntax:

{{ strtolower('UPPER') }}  ->  {{ 'UPPER'|lower }}

For filters with arguments, they will be shifted accordingly:

{{ sprintf("%s", "foo") }}  ->  {{ '%s'|format('foo') }}

Add a database driver for sessions

Add a new session driver that uses session_set_save_handler to save data in the database.

Add Schema::sessions() and Schema::dropSessions() to create the database table. The default name should be @sessions.

Add a flag to invoke `afterExecute` on failed requests

The afterExecute method on middleware only executes for successful requests.

Add a protected flag that will enable invocation of the afterExecute method on failed requests. There should also be a separate option to enable it for fatal errors (null for this option will use the same value as the former flag).

protected $afterErrors = false;
protected $afterFatalErrors = null;

Add task scheduling

There needs to be some sort of managed task scheduling.

  • Execute tasks or commands on a set schedule/frequency
  • Can be triggered by a command (cron job)
  • Can be manually invoked

Configuring tasks

Tasks can be anonymous closures, callables, and commands as strings. They can be registered on a regular schedule using the global Schedule class alias, similar to routes.

First the file(s) containing the tasks must be set in the app configuration.

'tasks' => [
    'app/tasks/schedule.php'
]

Then define tasks within the app/tasks/schedule.php file as follows:

// Run commands
Schedule::command('command:name --args')->daily();
Schedule::command(SendEmailsCommand::class, ['--args'])->daily();

// Run functions and methods
Schedule::run(function() {})->daily();
Schedule::run(new DeleteRecentUsers())->daily();
Schedule::run('App\Tasks\DeleteRecentUsers::methodName')->daily();

// Run shell commands
Schedule::exec('node /path/to/script')->daily();

Scheduling tasks at runtime

First define a task via the schedule configuration file above using Schedule::define(). The first argument requires a unique name for this task, and the second is a callable just like with Schedule::run().

Schedule::define('name', function() {});
Schedule::define('name', 'App\Tasks\DeleteRecentUsers::methodName');

When the application is running, schedule one of the defined tasks for later with the Schedule::later() function.

Schedule::later('name')->immediately();
Schedule::later('name')->inMinutes(5);

Because multiple instances of the same task could be scheduled simultaneously this way, it is important to allow custom "stacking" behavior for each "later" task.

// Append the new scheduled task to the list of existing ones and run them all (default)
Schedule::define('name', function() {})->withStacking(Schedule::STACK_APPEND);

// Replace any existing scheduled tasks with the new one
Schedule::define('name', function() {})->withStacking(Schedule::STACK_REPLACE);

// Ignore the new scheduled task if there is an existing one
Schedule::define('name', function() {})->withStacking(Schedule::STACK_DISABLED);

You can also call Schedule::later()->withStacking() to override the behavior per invocation.

Running tasks

  • Invoke Horizon\Foundation\Kernel::scheduler()->run()
  • Run the schedule:run ace command
* * * * * cd /path-to-your-project && ace schedule:run >> /dev/null 2>&1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.