Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Facades #35

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions proposed/facades-meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Facades Meta Document

## 1. Summary

The facade pattern (also spelled façade) is a software-design pattern commonly used in object-oriented programming.
Analogous to a facade in architecture, a facade is an object that serves as a front-facing interface masking more
complex underlying or structural code. A facade can:

* improve the readability and usability of a software library by masking interaction with more complex components behind
a single (and often simplified) API
* provide a context-specific interface to more generic functionality (complete with context-specific input validation)
* serve as a launching point for a broader refactor of monolithic or tightly-coupled systems in favor of more
loosely-coupled code

Developers often use the facade design pattern when a system is very complex or difficult to understand because the
system has many interdependent classes or because its source code is unavailable. This pattern hides the complexities of
the larger system and provides a simpler interface to the client. It typically involves a single wrapper class that
contains a set of members required by the client. These members access the system on behalf of the facade client and
hide the implementation details. [[Wikipedia: Facade Pattern](https://en.wikipedia.org/wiki/Facade_pattern)]

## 2. Why Bother?

In Joomla, we have lots of places with code like this:

```php
$this->app->getIdentity()->authorise(...)
```

or

```php
Factory::getUser()->authorise(...);
```

While `Factory::getUser()` is deprecated, `$this->app->getIdentity()` relies on knowledge about the internal structure.
Being able to use

```php
User::authorise(...);
```

instead, would lower the cognitive load a lot. Additionally, it would make it easier for us to make changes in the
underlying code without breaking extensions that use the feature in question.
`authorise()` is just a single example here; there are several other places, where the facade pattern would simplify our lives:

* App(lication)
* Auth(orisation)
* Config
* Session
* Lang(uage)
* Doc(ument)
* Database
* Mail(er)
* Event
* Log(ger)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also Layout? Currently LayoutHelper::render('blabla.layout')
And Toolbar :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list was not ment to be exhaustive.


## 3. Scope

### 3.1 Goals

### 3.2 Non-Goals

## 4. Approaches

### 4.1 Composite Pattern

### 4.2 Chosen Approach

## 5. Design Decisions

## 6. People

### 6.1 Editor(s)

* Niels Braczek, <[email protected]>

### 6.2 Sponsors

* N/A

### 6.3 Contributors

* N/A

## 7. Votes

* **Entrance Vote:** _(not yet taken)_
* **Acceptance Vote:** _(not yet taken)_

## 8. Relevant Links

_**Note:** Order descending chronologically._

## 9. Errata

...
121 changes: 121 additions & 0 deletions proposed/facades.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Content Elements


The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][].

[RFC 2119]: http://tools.ietf.org/html/rfc2119

### References

- [RFC 2119][]: Key words for use in RFCs to Indicate Requirement Levels

## 1. Specification

### 1.1 Facade

### 1.2 Application

```php
<?php

namespace Joomla\Facade;

use \Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Factory;

class App extends Facade
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a good idea to add a requirement for typehint?

/**
 *  @method Documeny getDocument()
 *  @method Potato getPotato()
 *  ....
 * /
class App extends Facade

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, absolutely makes sense.

{
/**
* @var \Joomla\CMS\Application\CMSApplicationInterface
*/
static private $instance;

use Mockable;

/**
* Get the root object behind the facade.
*
* @return \Joomla\CMS\Application\CMSApplicationInterface
*/
public static function getFacadeRoot(): CMSApplicationInterface
{
if (self::$isMock) {
return self::$mock;
}

if (self::$instance === null) {
self::$instance = Factory::getApplication();
}

return self::$instance;
}
}
```

### 1.3 Content Visitor

## 2. Interfaces, Traits and Classes

### 2.1 Joomla\Facade\Facade

```php
<?php

namespace Joomla\Facade;

use RuntimeException;

abstract class Facade
{
/**
* Get the root object behind the facade.
*/
abstract public static function getFacadeRoot();

/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
*
* @return mixed
*
* @throws RuntimeException
*/
public static function __callStatic(string $method, array $args)
{
$instance = static::getFacadeRoot();
if (!$instance) {
throw new RuntimeException('A facade root has not been set.');
}

return $instance->$method(...$args);
}
}
```

### 2.2 Joomla\Facade\Mockable

```php
<?php

namespace Joomla\Facade;

trait Mockable
{
protected static $isMock = false;

protected static $mock;

/**
* @param $mock
*/
public static function setMock($mock): void
{
self::$mock = $mock;
self::$isMock = true;
}
}
```