Skip to content

Instantly share code, notes, and snippets.

@gymadarasz
Last active March 16, 2018 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gymadarasz/0dfef1b036ea19ad969dcf4457f6fe2f to your computer and use it in GitHub Desktop.
Save gymadarasz/0dfef1b036ea19ad969dcf4457f6fe2f to your computer and use it in GitHub Desktop.
Exceptions Translates Errors API
<?php
class ErrorMessageException extends Exception {}
class ErrorMessage {
private $error;
private $strict;
private $level;
public function __construct($error = null, $strict = true, $level = 'fatal') {
$this->error = $error;
$this->strict = $strict;
$this->level = $level;
$this->handle();
}
public function handle() {
if($this->strict) { // TODO: "strict" could be separated methods or classes
throw new ErrorMessageException($this->error);
} else {
$log = LoggerManager::getLogger();
$level = $this->level;
$log->$level($this->error);
}
}
}
class LangText {
private $key;
private $args;
public function __construct($key = null, $args = null) {
$this->key = $key;
$this->args = $args;
}
public function getText($key = null, $args = null) {
// TODO: app_strings and mod_strings could be in separated methods
global $app_strings, $mod_strings;
if(!is_null($key)) {
$this->key = $key;
}
if(!is_null($args)) {
$this->args = $args;
}
$text = $mod_strings[$this->key] ? $mod_string[$this->key] : $app_strings[$this->key];
if(!$text) {
new ErrorMessage('A language key does not found: [' . $this->key . ']', false, 'warn');
}
foreach((array)$this->args as $name => $value) {
$text = str_replace('{' . $name . '}', $value, $text);
}
return $text;
}
public function __toString() {
$text = $this->getText();
return $text;
}
}
// implement this interface in any exception to make it translatable
interface LangExceptionInterface {
public function getLangMessage();
}
/**
* @todo should it implement or an interface enough for it?
* (We can use any kind of exception if it's an interface but
* can not guarantee the proper translation in each implementation)
*/
class LangException extends Exception implements LangExceptionInterface {
/**
*
* @var LangText
*/
protected $langMessage;
public function __construct($message = "", $code = 0, Throwable $previous = null, LangText $langMessage = null) {
parent::__construct($message, $code, $previous);
$this->langMessage = $langMessage;
}
public function getLangMessage() {
$message = null;
if(null !== $this->langMessage) {
$message = $this->langMessage->getText();
}
return $message;
}
}
/**
* implementation of http://jsonapi.org/format/#error-objects
*
* @todo incomplete...
*/
class APIErrorMessage {
const STRICT = true;
const DEFAULT_ID = 0;
const DEFAULT_HTTP_STATUS = 200;
const DEFAULT_APP_CODE = 0;
const DEFAULT_LINKS_ABOUT = '';
private $id;
private $links;
private $status;
private $code;
private $title;
private $detail;
private $source;
private $meta;
public function __construct(LangText $title = null, LangText $detail = null, $id = null, $code = null, $status = null, $links = null, $source = null, $meta = null) {
$this->id = $id ? $id : self::DEFAULT_ID;
$this->links = array(
'about' => self::DEFAULT_LINKS_ABOUT,
);
if(null !== $links) {
if(!$this->isValidLinks($this->links)) {
new ErrorMessage('Invalid links format for API Error message');
}
$this->links = $this->links;
}
$this->status = $status ? $status : self::DEFAULT_HTTP_STATUS;
$this->code = $code ? $code : self::DEFAULT_APP_CODE;
$this->title = $title;
$this->detail = $detail ? $detail : new LangText('LBL_DEFAULT_API_ERROR_DETAIL');
$this->source = array(
'pointer' => null,
'parameter' => null,
);
if(null !== $source) {
if(!$this->isValidSource($this->links)) {
new ErrorMessage('Invalid spource format for API Error message');
}
$this->source = $source;
}
$this->meta = $meta;
}
private function isValidLinks($links) {
// TODO
return true;
}
private function isValidSource($source) {
// TODO
return true;
}
public function setTitle(LangText $title) {
$this->title = $title->getText();
}
public function setDetail(LangText $detail) {
$this->detail = $detail->getText();
}
public function setErrorFromLangException(LangExceptionInterface $e) {
$this->code = $e->getCode();
$this->detail = $e->getLangMessage();
// TODO: meta could contains the stack trace in debug mode...
}
public function jsonEncode() {
$json = json_encode(array(
'id' => $this->id,
'links' => $this->links,
'status' => $this->status,
'code' => $this->code,
'title' => (string)$this->title,
'detail' => (string)$this->detail,
'source' => $this->source,
'meta' => $this->meta,
));
// todo: handle json encoding errors maybe
return $json;
}
}
/*
* -------------------- [EXAMPLES] --------------------------
*/
class ExamplesOfUsage {
const E_ASTONISHING_EXCEPTION_CODE = 123; // only for excample
const EXAMPLE_ERROR_ID = 222; // only for excample
const EXAMPLE_ERROR_CODE = 333; // only for excample
const EXAMPLE_ERROR_STATUS = 444; // only for excample
public function exampleForErrorMessageThrowsAnException() {
// it is a system error and we want to throw an error exception:
new ErrorMessage('Example error happend, and throws an exception');
}
public function exampleForErrorMessages() {
// it is a developer error and we want to log it:
new ErrorMessage('Example error happend, and we have to log it', false, 'fatal'); // you can see it in log file
// user error happend, error message to screen (original way):
SugarApplication::appendErrorMessage('This message should be translateable and will shows up on user screen'); // you can see it in screen
}
public function exampleForLangTextUsage() {
// using $app_strings or $mod_strings to translate a text
return new LangText('LBL_LANG_TEXT');
}
public function exampleForExceptionsOriginalWay() {
// throwing simple exception (in original way - it could be any kind of exception)
throw new Exception('Exception message to log (it doesnt have to be translatable) (1)');
}
public function exampleForLangualExceptions() {
// throwing an exception with an extra translatable message (same as above but using LangText)
throw new LangException('Exception message to log (it doesnt have to be translatable) (3)', null, null,
new LangText('LBL_LANG_TEXT'));
}
public function exampleAPIErrorMessage() {
// create an API JSON error object: see the spec at http://jsonapi.org/format/#error-objects
$apiErrorMessage = new APIErrorMessage(); // TODO: which parameter should be optional and which is required??? - no parameter means success message?
// create an API JSON error object with parameters
$apiErrorMessage = new APIErrorMessage(
new LangText('ERR_EXAMPLE_TITLE'),
new LangText('ERR_EXAMPLE_DETAIL'),
self::EXAMPLE_ERROR_ID,
self::EXAMPLE_ERROR_CODE,
self::EXAMPLE_ERROR_STATUS
);
// or use setter functions:
$apiErrorMessage = new APIErrorMessage();
$apiErrorMessage->setTitle(new LangText('LBL_A_TITLE'));
$apiErrorMessage->setDetail(new LangText('LBL_A_DETAIL'));
// or use multilangual strings
// convert it to json for API responses:
$json = $apiErrorMessage->jsonEncode();
return $json;
}
public function complexExampleToCreateAndReturnAnAPI_JSON_ErrorObject() {
$error = new APIErrorMessage(); // TODO: empty error may could means, there is no error by default
try {
// do stuff.. and something happend, we have to throw an exception:
throw new LangException(
'An astonishing exception happend, and this message should going to developers for e.g in log',
self::E_ASTONISHING_EXCEPTION_CODE,
null, new LangText('ERR_ASTONISHING', ['foo' => 'bar'])
);
// it could be a simple exception also (original way)
throw new Exception('An astonishing exception happend, and this message should going to developers for e.g in log',
self::E_ASTONISHING_EXCEPTION_CODE);
} catch (LangExceptionInterface $e) {
// translated message going to the API response error object..
$error->setTitle(new LangText('ERR_ASTONISHING_API_ERROR_TITLE'));
$error->setErrorFromLangException($e);
} catch (Exception $e) {
// We have no translated message for API response, so we have tot take into it:
$error->setTitle(new LangText('ERR_ASTONISHING_API_ERROR_TITLE'));
$error->setDetail(new LangText('ERR_ASTONISHING_API_ERROR_DETAILS'));
}
$jsonError = $error->jsonEncode();
return $jsonError;
}
}
// example lang texts - only for testing
$app_strings = array_merge($app_strings, array(
'ERR_ASTONISHING' => 'A dynamic message for users, going to the output and contains a variable: {foo}.',
'ERR_ASTONISHING_API_ERROR_TITLE' => 'Translatable message into API response',
'ERR_ASTONISHING_API_ERROR_DETAILS' => 'More details in API response about the astonishing thing...',
'LBL_LANG_TEXT' => 'Translation which explane the error to the User',
'LBL_A_TITLE' => 'Some translated title',
'LBL_A_DETAIL' => 'Some translated detail',
));
$examples = new ExamplesOfUsage();
echo "----- [ EXAMPLE 1 ] -----<br>\n";
try {
$examples->exampleForErrorMessageThrowsAnException();
} catch (Exception $e) {
echo $e->getMessage() . "<br>\n";
}
echo "----- [ EXAMPLE 2 ] -----<br>\n";
$examples->exampleForErrorMessages();
echo $examples->exampleForLangTextUsage() . "<br>\n";
try {
$examples->exampleForExceptionsOriginalWay();
} catch (Exception $e) {
echo $e->getMessage() . "<br>\n";
}
echo "----- [ EXAMPLE 3 ] -----<br>\n";
try {
$examples->exampleForLangualExceptions();
} catch (LangExceptionInterface $e) {
echo $e->getMessage() . ' == Translation ==> ' . $e->getLangMessage() . "<br>\n";
}
echo "----- [ EXAMPLE 4 ] -----<br>\n";
$json = $examples->exampleAPIErrorMessage();
echo "<pre>$json</pre>";
echo "----- [ EXAMPLE 5 ] -----<br>\n";
$json = $examples->complexExampleToCreateAndReturnAnAPI_JSON_ErrorObject();
echo "<pre>$json</pre>";
/**
* output:
*
----- [ EXAMPLE 1 ] -----
Example error happend, and throws an exception
----- [ EXAMPLE 2 ] -----
Translation which explane the error to the User
Exception message to log (it doesnt have to be translatable) (1)
----- [ EXAMPLE 3 ] -----
Exception message to log (it doesnt have to be translatable) (3) == Translation ==> Translation which explane the error to the User
----- [ EXAMPLE 4 ] -----
{"id":0,"links":{"about":""},"status":200,"code":0,"title":"Some translated title","detail":"Some translated detail","source":{"pointer":null,"parameter":null},"meta":null}
----- [ EXAMPLE 5 ] -----
{"id":0,"links":{"about":""},"status":200,"code":123,"title":"Translatable message into API response","detail":"A dynamic message for users, going to the output and contains a variable: bar.","source":{"pointer":null,"parameter":null},"meta":null}
*/
/*
* log:
*
==> /var/www/html/SuiteCRM/suitecrm.log <==
Fri Mar 16 11:31:01 2018 [6371][-none-][FATAL] Example error happend, and we have to log it
Fri Mar 16 11:31:47 2018 [6373][-none-][FATAL] Example error happend, and we have to log it
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment