jsend's People
jsend's Issues
Optionen für json_encode konfigurierbar machen
Ansonsten kann es bei binären Daten zu Problem kommen.
Feld "data" optional für Success/Fail?
Das Feld "data" ist laut Spezifikation für Success & Fail required, kann aber null/empty sein. Es bietet sich daher ggf. aus Bequemlichkeitsgründen an, dieses Feld als optional einzustufen.
ResponseFactory: Add successOrFail helper for boolean responses
Quite often we find ourselves writing the following snippet of code whenever we want to return either a success or fail message based on a boolean:
$response = new JSendResponse($success ? Status::success() : Status::fail(), null);
$response->respond();
Proposal
Add a new successOrFail
method to the ResponseFactory
class so it can be used as follows:
ResponseFactory::successOrFail($success)->respond();
ResponseFactory::successOrFail(true)->respond();
ResponseFactory::successOrFail(false)->respond();
The expected output for the data
key is in all cases null
.
Komponente zum senden des Response
Es wäre hilfreich wenn wir eine Komponente hätten wo man das Response-Objekt reingibt und dieses dann als richtiger Response mit entsprechenden Headern etc. gesendet wird.
Dies sollte ggfl. in ein eigenes package
Use Builder-Pattern to build a JSend-Response
Example:
JSend::build()->status(Status::success())->data([...])->get();
and/or
JSend::build()->status('success')->data([...])->get();
Überarbeitung der API
JSend 2.0
Status: Draft
JSend in der derzeitigen Implementierung hat inzwischen viel Spielraum für Verbesserungen:
-
Soll-Zustand Ein JSendResponse sollte das Psr\ResponseInterface direkt implementieren
Ist-Zustand Ein JSendResponse hat eine Funktion
asResponse
(https://github.com/demvsystems/jsend/blob/master/src/AbstractJSendResponse.php#L131) die ein JSendResponse in ein ResponseInterface umwandelt. Allerdings wird dazu die Implementierung von Guzzle verwendet, wodurch es z.B. für den Tarifservice (der illuminate/http verwendet) unbrauchbar ist. -
Soll-Zustand Die Optionen von
json_encode
sollten frei konfigurierbar sein.Ist-Zustand JSend::encode (https://github.com/demvsystems/jsend/blob/master/src/JSend.php#L53) nimmt ein array entgegen und gibt einen string zurück. Die Optionen sind nicht konfigurierbar
-
Soll-Zustand Der Zugriff auf Daten sollte leicht sein und Default-Argumente mit angegeben werden. Ideal wäre
$response->getData(<key>, <default>)
wobei<default>
optional und mitnull
vorbelegt ist.Ist-Zustand Der Zugriff ist schwergängig:
$response->getData()[<key>] ?? <default>
-
Soll-Zustand Es sollte direkt am Response möglich sein abzufragen, ob das JSendResponse ein Success/Fail/Error ist. Ideal wäre
$response->is(Success|Fail|Error)()
Ist-Zustand Es muss immer erst
getStatus()
aufgerufen werden:$response->getStatus()->is(Success|Fail|Error)()
-
Soll-Zustand Es sollte durch die API möglich sein anzugeben, was in einem Erfolgs-/Fail-/Fehler-Fall passieren soll. Ideal wäre
$response->on(Success|Fail|Error)(<closure>)
. Auch ein bool-Return-Wert der angibt, ob es ein Success/Fail/Error war und das Closure daher ausgeführt wurde wäre nützlich.Ist-Zustand Bisher ist so etwas nicht möglich. Es müssen immer if-Bedingungen der Form
if ($response->getStatus()->isFail()) { <logik> }
geschrieben werden -
Soll-Zustand JSendSuccessResponse, JSendFailResponse und JSendErrorResponse sollten klar getrennte Enheiten sein und geteilte/gemeinsame Logik durch traits implementiert werden.
Ist-Zustand Alle drei haben eine gemeinsame Oberklasse und JSendSuccess und JSendFail sind eng gekoppelt. Um allerdings konkret an die JSendErrorResponse-Instanz zu kommen, muss zunächst geprüft werden, ob es ein JSendError ist und dann muss noch
getError
(https://github.com/demvsystems/jsend/blob/master/src/JSendErrorResponse.php#L78) aufgerufen werden.
Neu-Implementierung und Major-Versionssprung
Auf Grund der API-Änderungen (speziell der die durch den 1. Punkt hervorgehen) ist ein Major-Versionssprung notwendig. Außerdem sind wir dadurch nicht mehr an die bisherigen Strukturen und an die bisherige ggf. imkompatible API gebunden. Kompatible und nützliche Features der 2er Versionen können allerdings auch in die 1er Version integriert werden wenn (einfach) möglich & gewünscht. Welche und ob bleibt diskutabel.
Implementierung
Die gemeinsame Oberklasse (AbstractJSendResponse
) fällt weg und die statische JSend Klasse wird stark reduziert. Gemeinsame Logiken werden stattdessen durch Traits implementiert. Die Status-Klasse wird weitgehend so gelassen, das überflüssige StatusInterface wird abgeschafft.
Status
Bleibt weitgehend so wie bisher, Interface fällt weg, Konstanten verlieren das überflüssige STATUS_
-Prefix
JSendStatus - Trait + Interface
Benutzen alle drei Response-Arten (Success / Fail / Error)
interface JSendStatusInterface
{
public function getStatus(): Status;
public function isFail(): bool;
public function isSuccess(): bool;
public function isError(): bool;
public function onSuccess(callable $closure): bool;
public function onFail(callable $closure): bool;
public function onError(callable $closure): bool;
}
trait JSendStatusTrait
{
private $status;
final public function getStatus(): Status
{
return $this->status;
}
final public function isFail(): bool
{
return $this->status->isFail();
}
final public function isSuccess(): bool
{
return $this->status->isSuccess();
}
final public function isError(): bool
{
return $this->status->isError();
}
final public function onSuccess(callable $closure): bool
{
if ($this->isSuccess()) {
$closure($this);
return true;
}
return false;
}
final public function onFail(callable $closure): bool
{
if ($this->isFail()) {
$closure($this);
return true;
}
return false;
}
final public function onError(callable $closure): bool
{
if ($this->isError()) {
$closure($this);
return true;
}
return false;
}
}
JSendData - Trait + Interface
Wird von Success & Fail benutzt. Der Parameter $key
ist ebenfalls optional:
- hasData: Wird der Parameter
$key
nicht angegeben wird überprüft, ob überhaupt Daten vorhanden sind (das data nicht leer ist) - getData: Wird der Parameter
$key
nicht angegeben werden alle Daten in Form eines Arrays zurückgegeben
interface JSendDataInterface
{
public function hasData(string $key = null): bool;
public function getData(string $key = null, $default = null);
}
trait JSendDataTrait
{
private $data = [];
final public function hasData(string $key = null): bool
{
if ($key === null) {
return !empty($this->data);
}
return array_key_exists($key, $this->data ?? []);
}
final public function getData(string $key = null, $default = null)
{
$data = $this->data ?? [];
if ($key === null) {
return $data;
}
return $data[$key] ?? $default;
}
}
JSend
Die Klasse mit den statischen Fabrik-Methoden reduziert sich auf eine Helfer-Klasse, die die Überführung von unbekannten JSend-Eingaben in die entsprechenden Objekte sicherstellt. Mit unbekannt ist gemeint, das zum Zeitpunkt der Angabe nicht bekannt bzw. nicht technisch verifiziert wurde, ob es ein Success, Fail oder Error ist.
final class JSend implements JSendStatusInterface, JSendJsonInterface
{
use JSendStatusTrait;
use JSendJsonTrait;
private $status;
private $decoded = [];
public function __construct(Status $status, array $decoded)
{
$this->status = $status;
$this->decoded = $decoded;
}
public function asArray(): array
{
return $this->decoded;
}
public function onSuccess(callable $closure): bool
{
if ($this->isSuccess()) {
$closure($this->asSuccess());
return true;
}
return false;
}
public function onFail(callable $closure): bool
{
if ($this->isFail()) {
$closure($this->asFail());
return true;
}
return false;
}
public function onError(callable $closure): bool
{
if ($this->isError()) {
$closure($this->asError());
return true;
}
return false;
}
public function asSuccess(): JSendSuccess
{
return JSendSuccess::from($this->decoded);
}
public function asFail(): JSendFail
{
return JSendFail::from($this->decoded);
}
public function asError(): JSendError
{
return JSendError::from($this->decoded);
}
public static function safeDecode(string $json): array
{
$decoded = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidJsonException(json_last_error_msg());
}
return $decoded;
}
public static function decode(string $json): self
{
$decoded = self::safeDecode($json);
return self::from($decoded);
}
public static function from(array $decoded): self
{
['status' => $status] = export('status')->requireAll()->from($decoded);
return new self(Status::from($status), $decoded);
}
}
Abfrage / Identifikation
Empfangenes JSend in Form von Json würde dann wie folgt behandelt werden können:
$jsend = JSend::decode('{...}');
$jsend->onSuccess(function(JSendSuccess $jsend) {
$this->process($jsend->getData());
});
$jsend->onFail(function(JsendFail $jsend) {
$this->recoverOrLog($jsend->getData());
});
$jsend->onError(function(JsendError $jsend) {
$this->throwOrLog($jsend->getMessage());
});
Die Abfrage, ob es sich um ein Success/Fail/Error handelt und die entsprechende Konvertierung ist durch die Methoden is(Success|Fail|Error)
und as(Success|Fail|Error)
problemlos möglich.
Offene Fragen
- Sollte die JSend-Klasse (welche ja nur ein Übergangsobjekt zum konkreten JSend ist) auch ein ResponseInterface nach Definition des PSR-Standards sein?
JSendJson - Trait + Interface
Wird von allen drei Response-Arten verwendet.
Das encode / decode ist nun pro Einheit implementiert anstatt wie bisher in einer statischen Methode der JSend-Klasse:
interface JSendJsonInterface extends JsonSerializable
{
public function asArray(): array;
public function encode(int $options = 0): string;
public static function decode(string $json);
public static function from(array $decoded);
}
trait JSendJsonTrait
{
final public function encode(int $options = 0): string
{
return json_encode($this, $options);
}
final public function jsonSerialize(): array
{
return $this->asArray();
}
}
decode
wird in der jeweiligen konkreten Response-Klasse definiert, um den Rückgabe-Wert & die Überprüfung vorzunehmen.from
wird ebenfalls in der jeweiligen konkreten Response-Klasse definiert und akzeptiert ein Array, dessen Inhalt - nach einer vorhergehenden Validierung - in das Objekt geschaufelt wird.
In JSendSuccess würden die beiden Methoden wie folgt definiert:
public static function decode(string $json): self
{
$decoded = JSend::safeDecode($json);
return self::from($decoded);
}
public static function from(array $decoded): self
{
['status' => $status, 'data' => $data] = export('status', 'data')->requireAll()->from($decoded);
enforce(Status::from($status)->isSuccess())->orThrow('Decode non-success in JSendSuccess');
return new self($data);
}
JSendResponse - Trait + Interface
Die respond
Methode wird in dieses Trait rausgezogen und verbleibt identisch zur bisherigen Implementierung. Selbiges gilt für die Methode getDefaultHttpStatusCode
die bis auf kleine Anpassungen genauso verbleibt wie bisher:
final public function respond(int $code = null): void
{
$code = $this->getHttpStatusCode($code);
ensure($code)->isInt()->isBetween(100, 511);
header('Content-Type: application/json; charset="UTF-8"', true, $code);
print json_encode($this);
exit;
}
final public function getHttpStatusCode(int $code = null): int
{
if ($this->isError()) {
return $this->getCode() ?? $code ?? 500;
}
return $code ?? 200;
}
Außerdem erbt das JSendResponseInterface
vom ResponseInterface
des PSR-Projekts, womit das entsprechende Trait ebenfalls die Response-Funktionalität implementiert.
Instanziierung
So würde bspw. ein neues JSendSuccess instantiiert werden:
new JSendSuccess(); // Ohne $data da optional
new JSendSuccess([...]); // Mit $data
Damit hätte man auch implizit bereits ein Response das jederzeit verarbeitet werden kann.
Offene Fragen
- Fabrik-Methoden wie
JSend::success(...)
anbieten?
Add phpcs and phpstan checks to travis
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.