1
0
Fork 0
mirror of synced 2024-06-28 03:01:15 +12:00

Merge branch '0.7.x' of github.com:appwrite/appwrite into swoole-and-functions

This commit is contained in:
Eldad Fux 2020-10-27 02:22:16 +02:00
commit 67ddfbed34
58 changed files with 1800 additions and 270 deletions

View file

@ -281,6 +281,12 @@ php-cs-fixer fix app/controllers --rules='{"braces": {"allow_single_line_closure
php-cs-fixer fix src --rules='{"braces": {"allow_single_line_closure": true}}' php-cs-fixer fix src --rules='{"braces": {"allow_single_line_closure": true}}'
``` ```
Static Code Analysis:
```bash
docker-compose exec appwrite /usr/src/code/vendor/bin/psalm
```
## Tutorials ## Tutorials
From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials: From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials:

View file

@ -115,6 +115,24 @@ return [
'required' => false, 'required' => false,
'question' => '', 'question' => '',
], ],
[
'name' => '_APP_SMTP_SECURE',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_USERNAME',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_SMTP_PASSWORD',
'default' => '',
'required' => false,
'question' => '',
],
[ [
'name' => '_APP_STORAGE_LIMIT', 'name' => '_APP_STORAGE_LIMIT',
'default' => '100000000', 'default' => '100000000',

View file

@ -690,8 +690,7 @@ App::get('/v1/account/sessions')
if ($record) { if ($record) {
$sessions[$index]['geo']['isoCode'] = \strtolower($record['country']['iso_code']); $sessions[$index]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$sessions[$index]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown'); $sessions[$index]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} } else {
else {
$sessions[$index]['geo']['isoCode'] = '--'; $sessions[$index]['geo']['isoCode'] = '--';
$sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown'); $sessions[$index]['geo']['country'] = $locale->getText('locale.country.unknown');
} }
@ -795,11 +794,10 @@ App::get('/v1/account/logs')
try { try {
$record = $geodb->get($log['ip']); $record = $geodb->get($log['ip']);
if(isset($record)){ if ($record) {
$output[$i]['geo']['isoCode'] = \strtolower($record['country']['iso_code']); $output[$i]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$output[$i]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown'); $output[$i]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} } else {
else{
$output[$i]['geo']['isoCode'] = '--'; $output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown'); $output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
} }

View file

@ -363,11 +363,12 @@ App::get('/v1/avatars/qr')
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true); $download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$qropts = new QROptions([ $options = new QROptions([
'quietzone' => $size, 'quietzone' => $size,
'outputType' => QRCode::OUTPUT_IMAGICK 'outputType' => QRCode::OUTPUT_IMAGICK
]); ]);
$qrcode = new QRCode($qropts);
$qrcode = new QRCode($options);
if ($download) { if ($download) {
$response->addHeader('Content-Disposition', 'attachment; filename="qr.png"'); $response->addHeader('Content-Disposition', 'attachment; filename="qr.png"');

View file

@ -33,7 +33,7 @@ App::get('/v1/locale')
$record = $geodb->get($ip); $record = $geodb->get($ip);
if($record) { if ($record) {
$output['countryCode'] = $record['country']['iso_code']; $output['countryCode'] = $record['country']['iso_code'];
$output['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown'); $output['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
$output['continent'] = (isset($continents[$record['continent']['code']])) ? $continents[$record['continent']['code']] : $locale->getText('locale.country.unknown'); $output['continent'] = (isset($continents[$record['continent']['code']])) ? $continents[$record['continent']['code']] : $locale->getText('locale.country.unknown');

View file

@ -315,11 +315,10 @@ App::get('/v1/users/:userId/logs')
try { try {
$record = $geodb->get($log['ip']); $record = $geodb->get($log['ip']);
if(isset($record)){ if($record){
$output[$i]['geo']['isoCode'] = \strtolower($record['country']['iso_code']); $output[$i]['geo']['isoCode'] = \strtolower($record['country']['iso_code']);
$output[$i]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown'); $output[$i]['geo']['country'] = (isset($countries[$record['country']['iso_code']])) ? $countries[$record['country']['iso_code']] : $locale->getText('locale.country.unknown');
} } else{
else{
$output[$i]['geo']['isoCode'] = '--'; $output[$i]['geo']['isoCode'] = '--';
$output[$i]['geo']['country'] = $locale->getText('locale.country.unknown'); $output[$i]['geo']['country'] = $locale->getText('locale.country.unknown');
} }

View file

@ -66,7 +66,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
? $origin : 'localhost') . (!empty($port) ? ':'.$port : ''); ? $origin : 'localhost') . (!empty($port) ? ':'.$port : '');
$selfDomain = new Domain($request->getHostname()); $selfDomain = new Domain($request->getHostname());
$endDomain = new Domain($origin); $endDomain = new Domain((string)$origin);
// var_dump('referer', $referrer); // var_dump('referer', $referrer);
// var_dump('origin', $origin); // var_dump('origin', $origin);

View file

@ -23,6 +23,9 @@ include __DIR__.'/controllers/general.php';
$preloader = new Preloader(); $preloader = new Preloader();
foreach ([ foreach ([
realpath(__DIR__ . '/../vendor/composer'),
realpath(__DIR__ . '/../vendor/amphp'),
realpath(__DIR__ . '/../vendor/felixfbecker'),
realpath(__DIR__ . '/../vendor/twig/twig'), realpath(__DIR__ . '/../vendor/twig/twig'),
realpath(__DIR__ . '/../vendor/guzzlehttp/guzzle'), realpath(__DIR__ . '/../vendor/guzzlehttp/guzzle'),
realpath(__DIR__ . '/../vendor/domnikl'), realpath(__DIR__ . '/../vendor/domnikl'),

View file

@ -142,10 +142,10 @@ $cli
$stdout = ''; $stdout = '';
$stderr = ''; $stderr = '';
Console::log("Running \"docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans\""); Console::log("Running \"docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans\"");
$exit = Console::execute("docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans", null, $stdout, $stderr); $exit = Console::execute("docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans", '', $stdout, $stderr);
if ($exit !== 0) { if ($exit !== 0) {
Console::error("Failed to install Appwrite dockers"); Console::error("Failed to install Appwrite dockers");

View file

@ -32,7 +32,7 @@ Co\run(function() use ($environments) { // Warmup: make sure images are ready t
Console::info('Warming up '.$environment['name'].' environment'); Console::info('Warming up '.$environment['name'].' environment');
Console::execute('docker pull '.$environment['image'], null, $stdout, $stderr); Console::execute('docker pull '.$environment['image'], '', $stdout, $stderr);
if(!empty($stdout)) { if(!empty($stdout)) {
Console::log($stdout); Console::log($stdout);
@ -306,7 +306,7 @@ class FunctionsV1
$executionStart = \microtime(true); $executionStart = \microtime(true);
$exitCode = Console::execute('docker ps --all --format "name={{.Names}}&status={{.Status}}&labels={{.Labels}}" --filter label=appwrite-type=function' $exitCode = Console::execute('docker ps --all --format "name={{.Names}}&status={{.Status}}&labels={{.Labels}}" --filter label=appwrite-type=function'
, null, $stdout, $stderr, 30); , '', $stdout, $stderr, 30);
$executionEnd = \microtime(true); $executionEnd = \microtime(true);
@ -344,7 +344,7 @@ class FunctionsV1
$stdout = ''; $stdout = '';
$stderr = ''; $stderr = '';
if(Console::execute("docker rm {$container}", null, $stdout, $stderr, 30) !== 0) { if(Console::execute("docker rm {$container}", '', $stdout, $stderr, 30) !== 0) {
throw new Exception('Failed to remove offline container: '.$stderr); throw new Exception('Failed to remove offline container: '.$stderr);
} }
@ -372,7 +372,7 @@ class FunctionsV1
".\implode("\n", $vars)." ".\implode("\n", $vars)."
{$environment['image']} \ {$environment['image']} \
sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null'" sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null'"
, null, $stdout, $stderr, 30); , '', $stdout, $stderr, 30);
$executionEnd = \microtime(true); $executionEnd = \microtime(true);
@ -395,7 +395,7 @@ class FunctionsV1
".\implode("\n", $vars)." ".\implode("\n", $vars)."
{$container} \ {$container} \
{$command}" {$command}"
, null, $stdout, $stderr, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900))); , '', $stdout, $stderr, $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)));
$executionEnd = \microtime(true); $executionEnd = \microtime(true);
$executionTime = ($executionEnd - $executionStart); $executionTime = ($executionEnd - $executionStart);
@ -459,7 +459,7 @@ class FunctionsV1
$stdout = ''; $stdout = '';
$stderr = ''; $stderr = '';
if(Console::execute("docker stop {$first['name']}", null, $stdout, $stderr, 30) !== 0) { if(Console::execute("docker stop {$first['name']}", '', $stdout, $stderr, 30) !== 0) {
Console::error('Failed to remove container: '.$stderr); Console::error('Failed to remove container: '.$stderr);
} }

View file

@ -34,11 +34,11 @@
"appwrite/php-clamav": "1.0.*", "appwrite/php-clamav": "1.0.*",
"utopia-php/framework": "0.9.1", "utopia-php/framework": "0.9.6",
"utopia-php/abuse": "0.2.*", "utopia-php/abuse": "0.2.*",
"utopia-php/audit": "0.3.*", "utopia-php/audit": "0.3.*",
"utopia-php/cache": "0.2.*", "utopia-php/cache": "0.2.*",
"utopia-php/cli": "0.7.1", "utopia-php/cli": "0.7.2",
"utopia-php/config": "0.2.*", "utopia-php/config": "0.2.*",
"utopia-php/locale": "0.3.*", "utopia-php/locale": "0.3.*",
"utopia-php/registry": "0.2.*", "utopia-php/registry": "0.2.*",
@ -57,7 +57,8 @@
"require-dev": { "require-dev": {
"swoole/ide-helper": "4.5.5", "swoole/ide-helper": "4.5.5",
"appwrite/sdk-generator": "master", "appwrite/sdk-generator": "master",
"phpunit/phpunit": "^9.3" "phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
}, },
"repositories": [ "repositories": [
{ {

1619
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -52,6 +52,7 @@ services:
- appwrite-certificates:/storage/certificates:rw - appwrite-certificates:/storage/certificates:rw
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- ./phpunit.xml:/usr/src/code/phpunit.xml - ./phpunit.xml:/usr/src/code/phpunit.xml
- ./psalm.xml:/usr/src/code/psalm.xml
- ./tests:/usr/src/code/tests - ./tests:/usr/src/code/tests
- ./app:/usr/src/code/app - ./app:/usr/src/code/app
# - ./vendor:/usr/src/code/vendor # - ./vendor:/usr/src/code/vendor

15
psalm.xml Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<psalm
errorLevel="3"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

View file

@ -118,7 +118,7 @@ class Auth
* *
* @return string * @return string
*/ */
public static function hash($string) public static function hash(string $string)
{ {
return \hash('sha256', $string); return \hash('sha256', $string);
} }
@ -130,7 +130,7 @@ class Auth
* *
* @param $string * @param $string
* *
* @return bool|string * @return bool|string|null
*/ */
public static function passwordHash($string) public static function passwordHash($string)
{ {
@ -193,14 +193,14 @@ class Auth
*/ */
public static function tokenVerify(array $tokens, int $type, string $secret) public static function tokenVerify(array $tokens, int $type, string $secret)
{ {
foreach ($tokens as $token) { /* @var $token Document */ foreach ($tokens as $token) { /** @var Document $token */
if (isset($token['type']) && if ($token->isSet('type') &&
isset($token['secret']) && $token->isSet('secret') &&
isset($token['expire']) && $token->isSet('expire') &&
$token['type'] == $type && $token->getAttribute('type') == $type &&
$token['secret'] === self::hash($secret) && $token->getAttribute('secret') === self::hash($secret) &&
$token['expire'] >= \time()) { $token->getAttribute('expire') >= \time()) {
return $token->getId(); return (string)$token->getId();
} }
} }

View file

@ -20,7 +20,7 @@ abstract class OAuth2
protected $callback; protected $callback;
/** /**
* @var string * @var array
*/ */
protected $state; protected $state;
@ -38,7 +38,7 @@ abstract class OAuth2
* @param array $state * @param array $state
* @param array $scopes * @param array $scopes
*/ */
public function __construct(string $appId, string $appSecret, string $callback, $state = [], $scopes = []) public function __construct(string $appId, string $appSecret, string $callback, array $state = [], array $scopes = [])
{ {
$this->appID = $appId; $this->appID = $appId;
$this->appSecret = $appSecret; $this->appSecret = $appSecret;
@ -116,7 +116,7 @@ abstract class OAuth2
/** /**
* @param $state * @param $state
* *
* @return string * @return array
*/ */
public function parseState(string $state) public function parseState(string $state)
{ {
@ -152,6 +152,6 @@ abstract class OAuth2
\curl_close($ch); \curl_close($ch);
return $response; return (string)$response;
} }
} }

View file

@ -34,7 +34,7 @@ class Amazon extends OAuth2
/** /**
* @param $state * @param $state
* *
* @return json * @return array
*/ */
public function parseState(string $state) public function parseState(string $state)
{ {
@ -63,7 +63,7 @@ class Amazon extends OAuth2
*/ */
public function getAccessToken(string $code): string public function getAccessToken(string $code): string
{ {
$headers[] = 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'; $headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
$accessToken = $this->request( $accessToken = $this->request(
'POST', 'POST',
'https://api.amazon.com/auth/o2/token', 'https://api.amazon.com/auth/o2/token',
@ -76,6 +76,7 @@ class Amazon extends OAuth2
'grant_type' => 'authorization_code' 'grant_type' => 'authorization_code'
]) ])
); );
$accessToken = \json_decode($accessToken, true); $accessToken = \json_decode($accessToken, true);
if (isset($accessToken['access_token'])) { if (isset($accessToken['access_token'])) {

View file

@ -58,7 +58,7 @@ class Apple extends OAuth2
*/ */
public function getAccessToken(string $code): string public function getAccessToken(string $code): string
{ {
$headers[] = 'Content-Type: application/x-www-form-urlencoded'; $headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request( $accessToken = $this->request(
'POST', 'POST',
'https://appleid.apple.com/auth/token', 'https://appleid.apple.com/auth/token',
@ -175,8 +175,10 @@ class Apple extends OAuth2
/** /**
* @param string $data * @param string $data
*
* @return string
*/ */
protected function encode($data) protected function encode($data): string
{ {
return \str_replace(['+', '/', '='], ['-', '_', ''], \base64_encode($data)); return \str_replace(['+', '/', '='], ['-', '_', ''], \base64_encode($data));
} }

View file

@ -48,7 +48,7 @@ class Bitbucket extends OAuth2
public function getAccessToken(string $code): string public function getAccessToken(string $code): string
{ {
// Required as per Bitbucket Spec. // Required as per Bitbucket Spec.
$headers[] = 'Content-Type: application/x-www-form-urlencoded'; $headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request( $accessToken = $this->request(
'POST', 'POST',

View file

@ -69,7 +69,7 @@ class Discord extends OAuth2
'redirect_uri' => $this->callback, 'redirect_uri' => $this->callback,
'client_id' => $this->appID, 'client_id' => $this->appID,
'client_secret' => $this->appSecret, 'client_secret' => $this->appSecret,
'scope' => \implode(' ', $this->scope) 'scope' => \implode(' ', $this->getScopes())
]) ])
); );

View file

@ -48,7 +48,7 @@ class Dropbox extends OAuth2
*/ */
public function getAccessToken(string $code): string public function getAccessToken(string $code): string
{ {
$headers[] = 'Content-Type: application/x-www-form-urlencoded'; $headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request( $accessToken = $this->request(
'POST', 'POST',
'https://api.dropboxapi.com/oauth2/token', 'https://api.dropboxapi.com/oauth2/token',
@ -127,7 +127,7 @@ class Dropbox extends OAuth2
protected function getUser(string $accessToken): array protected function getUser(string $accessToken): array
{ {
if (empty($this->user)) { if (empty($this->user)) {
$headers[] = 'Authorization: Bearer '. \urlencode($accessToken); $headers = ['Authorization: Bearer '. \urlencode($accessToken)];
$user = $this->request('POST', 'https://api.dropboxapi.com/2/users/get_current_account', $headers); $user = $this->request('POST', 'https://api.dropboxapi.com/2/users/get_current_account', $headers);
$this->user = \json_decode($user, true); $this->user = \json_decode($user, true);
} }

View file

@ -53,7 +53,7 @@ class Microsoft extends OAuth2
*/ */
public function getAccessToken(string $code): string public function getAccessToken(string $code): string
{ {
$headers[] = 'Content-Type: application/x-www-form-urlencoded'; $headers = ['Content-Type: application/x-www-form-urlencoded'];
$accessToken = $this->request( $accessToken = $this->request(
'POST', 'POST',
@ -134,7 +134,7 @@ class Microsoft extends OAuth2
protected function getUser(string $accessToken): array protected function getUser(string $accessToken): array
{ {
if (empty($this->user)) { if (empty($this->user)) {
$headers[] = 'Authorization: Bearer '. \urlencode($accessToken); $headers = ['Authorization: Bearer '. \urlencode($accessToken)];
$user = $this->request('GET', 'https://graph.microsoft.com/v1.0/me', $headers); $user = $this->request('GET', 'https://graph.microsoft.com/v1.0/me', $headers);
$this->user = \json_decode($user, true); $this->user = \json_decode($user, true);
} }

View file

@ -10,18 +10,24 @@ use Appwrite\Auth\OAuth2;
class Paypal extends OAuth2 class Paypal extends OAuth2
{ {
/** /**
* @var string * @var array
*/ */
private $endpoint= [ private $endpoint = [
'sandbox' => 'https://www.sandbox.paypal.com/', 'sandbox' => 'https://www.sandbox.paypal.com/',
'live' => 'https://www.paypal.com/', 'live' => 'https://www.paypal.com/',
]; ];
/**
* @var array
*/
private $resourceEndpoint = [ private $resourceEndpoint = [
'sandbox' => 'https://api.sandbox.paypal.com/v1/', 'sandbox' => 'https://api.sandbox.paypal.com/v1/',
'live' => 'https://api.paypal.com/v1/', 'live' => 'https://api.paypal.com/v1/',
]; ];
/**
* @var string
*/
protected $environment = 'live'; protected $environment = 'live';
/** /**
@ -29,11 +35,13 @@ class Paypal extends OAuth2
*/ */
protected $user = []; protected $user = [];
/**
* @var array
*/
protected $scopes = [ protected $scopes = [
'openid', 'openid',
'profile', 'profile',
'email' 'email'
]; ];
/** /**

View file

@ -34,7 +34,7 @@ class Salesforce extends OAuth2
/** /**
* @param $state * @param $state
* *
* @return json * @return array
*/ */
public function parseState(string $state) public function parseState(string $state)
{ {

View file

@ -61,7 +61,7 @@ class Vk extends OAuth2
*/ */
public function getAccessToken(string $code): string public function getAccessToken(string $code): string
{ {
$headers[] = 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'; $headers = ['Content-Type: application/x-www-form-urlencoded;charset=UTF-8'];
$accessToken = $this->request( $accessToken = $this->request(
'POST', 'POST',
'https://oauth.vk.com/access_token?', 'https://oauth.vk.com/access_token?',

View file

@ -45,7 +45,7 @@ class Yahoo extends OAuth2
/** /**
* @param $state * @param $state
* *
* @return json * @return array
*/ */
public function parseState(string $state) public function parseState(string $state)
{ {

View file

@ -32,7 +32,7 @@ class Yandex extends OAuth2
/** /**
* @param $state * @param $state
* *
* @return json * @return array
*/ */
public function parseState(string $state) public function parseState(string $state)
{ {

View file

@ -54,7 +54,7 @@ abstract class Adapter
/** /**
* Get Document. * Get Document.
* *
* @param int $id * @param string $id
* *
* @return array * @return array
*/ */
@ -82,11 +82,11 @@ abstract class Adapter
/** /**
* Delete Node. * Delete Node.
* *
* @param int $id * @param string $id
* *
* @return array * @return array
*/ */
abstract public function deleteDocument($id); abstract public function deleteDocument(string $id);
/** /**
* Delete Unique Key. * Delete Unique Key.

View file

@ -139,7 +139,7 @@ class MySQL extends Adapter
{ {
$order = 0; $order = 0;
$data = \array_merge(['$id' => null, '$permissions' => []], $data); // Merge data with default params $data = \array_merge(['$id' => null, '$permissions' => []], $data); // Merge data with default params
$signature = \md5(\json_encode($data, true)); $signature = \md5(\json_encode($data));
$revision = \uniqid('', true); $revision = \uniqid('', true);
$data['$id'] = (empty($data['$id'])) ? null : $data['$id']; $data['$id'] = (empty($data['$id'])) ? null : $data['$id'];
@ -232,6 +232,10 @@ class MySQL extends Adapter
// Handle array of relations // Handle array of relations
if (self::DATA_TYPE_ARRAY === $type) { if (self::DATA_TYPE_ARRAY === $type) {
if(!is_array($value)) { // Property should be of type array, if not = skip
continue;
}
foreach ($value as $i => $child) { foreach ($value as $i => $child) {
if (self::DATA_TYPE_DICTIONARY !== $this->getDataType($child)) { // not dictionary if (self::DATA_TYPE_DICTIONARY !== $this->getDataType($child)) { // not dictionary
@ -315,13 +319,13 @@ class MySQL extends Adapter
/** /**
* Delete Document. * Delete Document.
* *
* @param int $id * @param string $id
* *
* @return array * @return array
* *
* @throws Exception * @throws Exception
*/ */
public function deleteDocument($id) public function deleteDocument(string $id)
{ {
$st1 = $this->getPDO()->prepare('DELETE FROM `'.$this->getNamespace().'.database.documents` $st1 = $this->getPDO()->prepare('DELETE FROM `'.$this->getNamespace().'.database.documents`
WHERE uid = :id WHERE uid = :id
@ -765,8 +769,10 @@ class MySQL extends Adapter
/** /**
* Get Unique Document ID. * Get Unique Document ID.
*
* @return string
*/ */
public function getId() public function getId(): string
{ {
$unique = \uniqid(); $unique = \uniqid();
$attempts = 5; $attempts = 5;
@ -884,12 +890,12 @@ class MySQL extends Adapter
} }
/** /**
* @param $key * @param string $key
* @param $value * @param mixed $value
* *
* @return $this * @return $this
*/ */
public function setDebug($key, $value) public function setDebug(string $key, $value): self
{ {
$this->debug[$key] = $value; $this->debug[$key] = $value;
@ -899,15 +905,17 @@ class MySQL extends Adapter
/** /**
* @return array * @return array
*/ */
public function getDebug() public function getDebug(): array
{ {
return $this->debug; return $this->debug;
} }
/** /**
* return $this;. * return $this;.
*
* @return void
*/ */
public function resetDebug() public function resetDebug(): void
{ {
$this->debug = []; $this->debug = [];
} }
@ -917,7 +925,7 @@ class MySQL extends Adapter
* *
* @throws Exception * @throws Exception
*/ */
protected function getPDO() protected function getPDO(): PDO
{ {
return $this->register->get('db'); return $this->register->get('db');
} }
@ -927,7 +935,7 @@ class MySQL extends Adapter
* *
* @return Client * @return Client
*/ */
protected function getRedis():Client protected function getRedis(): Client
{ {
return $this->register->get('cache'); return $this->register->get('cache');
} }

View file

@ -137,13 +137,13 @@ class Redis extends Adapter
/** /**
* Delete Document. * Delete Document.
* *
* @param $id * @param string $id
* *
* @return array * @return array
* *
* @throws Exception * @throws Exception
*/ */
public function deleteDocument($id) public function deleteDocument(string $id)
{ {
$data = $this->adapter->deleteDocument($id); $data = $this->adapter->deleteDocument($id);
@ -243,7 +243,7 @@ class Redis extends Adapter
*/ */
public function lastModified() public function lastModified()
{ {
return; return 0;
} }
/** /**
@ -259,7 +259,7 @@ class Redis extends Adapter
* *
* @return Client * @return Client
*/ */
protected function getRedis():Client protected function getRedis(): Client
{ {
return $this->register->get('cache'); return $this->register->get('cache');
} }

View file

@ -116,7 +116,7 @@ class Database
/** /**
* Create Namespace. * Create Namespace.
* *
* @param int $namespace * @param string $namespace
* *
* @return bool * @return bool
*/ */
@ -128,7 +128,7 @@ class Database
/** /**
* Delete Namespace. * Delete Namespace.
* *
* @param int $namespace * @param string $namespace
* *
* @return bool * @return bool
*/ */
@ -187,22 +187,23 @@ class Database
} }
/** /**
* @param int $id * @param string $id
* @param bool $mock is mocked data allowed? * @param bool $mock is mocked data allowed?
* @param bool $decode enable decoding?
* *
* @return Document * @return Document
*/ */
public function getDocument($id, $mock = true, $decode = true) public function getDocument($id, bool $mock = true, bool $decode = true)
{ {
if (\is_null($id)) { if (\is_null($id)) {
return new Document([]); return new Document();
} }
$document = new Document((isset($this->mocks[$id]) && $mock) ? $this->mocks[$id] : $this->adapter->getDocument($id)); $document = new Document((isset($this->mocks[$id]) && $mock) ? $this->mocks[$id] : $this->adapter->getDocument($id));
$validator = new Authorization($document, 'read'); $validator = new Authorization($document, 'read');
if (!$validator->isValid($document->getPermissions())) { // Check if user has read access to this document if (!$validator->isValid($document->getPermissions())) { // Check if user has read access to this document
return new Document([]); return new Document();
} }
$document = ($decode) ? $this->decode($document) : $document; $document = ($decode) ? $this->decode($document) : $document;
@ -332,13 +333,13 @@ class Database
} }
/** /**
* @param int $id * @param string $id
* *
* @return Document|false * @return Document|false
* *
* @throws AuthorizationException * @throws AuthorizationException
*/ */
public function deleteDocument($id) public function deleteDocument(string $id)
{ {
$document = $this->getDocument($id); $document = $this->getDocument($id);
@ -401,9 +402,9 @@ class Database
* @param string $key * @param string $key
* @param string $value * @param string $value
* *
* @return array * @return self
*/ */
public function setMock($key, $value) public function setMock($key, $value): self
{ {
$this->mocks[$key] = $value; $this->mocks[$key] = $value;
@ -411,11 +412,11 @@ class Database
} }
/** /**
* @param string $mocks * @param array $mocks
* *
* @return array * @return self
*/ */
public function setMocks(array $mocks) public function setMocks(array $mocks): self
{ {
$this->mocks = $mocks; $this->mocks = $mocks;
@ -432,14 +433,14 @@ class Database
/** /**
* Add Attribute Filter * Add Attribute Filter
* *
* @param string $name * @param string $name
* @param callable $encode * @param callable $encode
* @param callable $decode * @param callable $decode
* *
* return $this * @return void
*/ */
static public function addFilter(string $name, callable $encode, callable $decode) static public function addFilter(string $name, callable $encode, callable $decode): void
{ {
self::$filters[$name] = [ self::$filters[$name] = [
'encode' => $encode, 'encode' => $encode,

View file

@ -17,7 +17,7 @@ class Document extends ArrayObject
* *
* @see ArrayObject::__construct * @see ArrayObject::__construct
* *
* @param null $input * @param array $input
* @param int $flags * @param int $flags
* @param string $iterator_class * @param string $iterator_class
*/ */
@ -49,7 +49,7 @@ class Document extends ArrayObject
} }
/** /**
* @return int|null * @return string
*/ */
public function getCollection() public function getCollection()
{ {
@ -196,8 +196,8 @@ class Document extends ArrayObject
/** /**
* Checks if a document key is set. * Checks if a document key is set.
* *
* @param $key * @param string $key
* *
* @return bool * @return bool
*/ */
public function isSet($key) public function isSet($key)

View file

@ -15,7 +15,7 @@ class Authorization extends Validator
/** /**
* @var Document * @var Document
*/ */
protected $document = null; protected $document;
/** /**
* @var string * @var string
@ -56,7 +56,7 @@ class Authorization extends Validator
* *
* Returns true if valid or false if not. * Returns true if valid or false if not.
* *
* @param array $permissions * @param mixed $permissions
* *
* @return bool * @return bool
*/ */
@ -89,8 +89,10 @@ class Authorization extends Validator
/** /**
* @param string $role * @param string $role
*
* @return void
*/ */
public static function setRole($role) public static function setRole($role): void
{ {
self::$roles[] = $role; self::$roles[] = $role;
} }
@ -120,33 +122,41 @@ class Authorization extends Validator
* Change default status. * Change default status.
* This will be used for the * This will be used for the
* value set on the self::reset() method * value set on the self::reset() method
*
* @return void
*/ */
public static function setDefaultStatus($status) public static function setDefaultStatus($status): void
{ {
self::$statusDefault = $status; self::$statusDefault = $status;
self::$status = $status; self::$status = $status;
} }
/** /**
* Enable Authorization checks * Enable Authorization checks
*
* @return void
*/ */
public static function enable() public static function enable(): void
{ {
self::$status = true; self::$status = true;
} }
/** /**
* Disable Authorization checks * Disable Authorization checks
*
* @return void
*/ */
public static function disable() public static function disable(): void
{ {
self::$status = false; self::$status = false;
} }
/** /**
* Disable Authorization checks * Disable Authorization checks
*
* @return void
*/ */
public static function reset() public static function reset(): void
{ {
self::$status = self::$statusDefault; self::$status = self::$statusDefault;
} }

View file

@ -31,7 +31,11 @@ class Collection extends Structure
} }
/** /**
* @param Document $document * Is valid.
*
* Returns true if valid or false if not.
*
* @param mixed $document
* *
* @return bool * @return bool
*/ */

View file

@ -16,7 +16,7 @@ class DocumentId extends Validator
/** /**
* @var Database * @var Database
*/ */
protected $database = null; protected $database;
/** /**
* @var string * @var string

View file

@ -15,7 +15,7 @@ class Permissions extends Validator
/** /**
* @var Document * @var Document
*/ */
protected $document = null; protected $document;
/** /**
* Structure constructor. * Structure constructor.
@ -44,7 +44,7 @@ class Permissions extends Validator
* *
* Returns true if valid or false if not. * Returns true if valid or false if not.
* *
* @param array $value * @param mixed $value
* *
* @return bool * @return bool
*/ */

View file

@ -29,9 +29,9 @@ class Structure extends Validator
protected $database; protected $database;
/** /**
* @var int * @var string
*/ */
protected $id = null; protected $id = '';
/** /**
* Basic rules to apply on all documents. * Basic rules to apply on all documents.
@ -118,7 +118,7 @@ class Structure extends Validator
* *
* Returns true if valid or false if not. * Returns true if valid or false if not.
* *
* @param Document $document * @param mixed $document
* *
* @return bool * @return bool
*/ */
@ -156,9 +156,14 @@ class Structure extends Validator
foreach ($array as $key => $value) { foreach ($array as $key => $value) {
$rule = $collection->search('key', $key, $rules); $rule = $collection->search('key', $key, $rules);
$ruleType = (isset($rule['type'])) ? $rule['type'] : '';
$ruleRequired = (isset($rule['required'])) ? $rule['required'] : true; if(!$rule) {
$ruleArray = (isset($rule['array'])) ? $rule['array'] : false; continue;
}
$ruleType = $rule['type'] ?? '';
$ruleRequired = $rule['required'] ?? true;
$ruleArray = $rule['array'] ?? false;
$validator = null; $validator = null;
switch ($ruleType) { switch ($ruleType) {
@ -269,7 +274,14 @@ class Structure extends Validator
return true; return true;
} }
protected function getCollection($id) /**
* Get Collection
*
* Get Collection by unique ID
*
* @return Document
*/
protected function getCollection($id): Document
{ {
return $this->database->getDocument($id); return $this->database->getDocument($id);
} }

View file

@ -15,7 +15,7 @@ class UID extends Validator
*/ */
public function getDescription() public function getDescription()
{ {
return 'Validate UUID format'; return 'Invalid UID format';
} }
/** /**
@ -23,12 +23,20 @@ class UID extends Validator
* *
* Returns true if valid or false if not. * Returns true if valid or false if not.
* *
* @param string $value * @param mixed $value
* *
* @return bool * @return bool
*/ */
public function isValid($value) public function isValid($value)
{ {
if ($value === 0) { // TODO Deprecate confition when we get the chance.
return true;
}
if (!is_string($value)) {
return false;
}
if(mb_strlen($value) > 32) { if(mb_strlen($value) > 32) {
return false; return false;
} }

View file

@ -28,7 +28,7 @@ class Compose
} }
/** /**
* @return array * @return string
*/ */
public function getVersion(): string public function getVersion(): string
{ {

View file

@ -54,7 +54,7 @@ class Service
public function getImageVersion(): string public function getImageVersion(): string
{ {
$image = $this->getImage(); $image = $this->getImage();
return substr($image, strpos($image, ':')+1); return substr($image, ((int)strpos($image, ':'))+1);
} }
/** /**

View file

@ -46,7 +46,7 @@ class Env
/** /**
* @param string $key * @param string $key
* *
* @return mixed|null * @return string
*/ */
public function getVar(string $key): string public function getVar(string $key): string
{ {

View file

@ -59,7 +59,7 @@ class PDO extends PDONative
return $this->pdo->quote($string, $parameter_type); return $this->pdo->quote($string, $parameter_type);
} }
public function reconnect() public function reconnect(): PDONative
{ {
$this->pdo = new PDONative($this->dsn, $this->username, $this->passwd, $this->options); $this->pdo = new PDONative($this->dsn, $this->username, $this->passwd, $this->options);

View file

@ -7,7 +7,7 @@ use Utopia\Validator;
class CNAME extends Validator class CNAME extends Validator
{ {
/** /**
* @var int * @var string
*/ */
protected $target; protected $target;
@ -19,6 +19,9 @@ class CNAME extends Validator
$this->target = $target; $this->target = $target;
} }
/**
* @return string
*/
public function getDescription() public function getDescription()
{ {
return 'Invalid CNAME record'; return 'Invalid CNAME record';
@ -27,19 +30,19 @@ class CNAME extends Validator
/** /**
* Check if CNAME record target value matches selected target * Check if CNAME record target value matches selected target
* *
* @param string $domain * @param mixed $domain
* *
* @return bool * @return bool
*/ */
public function isValid($domain) public function isValid($domain)
{ {
try { if(!is_string($domain)) {
$records = \dns_get_record($domain, DNS_CNAME);
} catch (\Throwable $th) {
return false; return false;
} }
if (!$records || !\is_array($records)) { try {
$records = \dns_get_record($domain, DNS_CNAME);
} catch (\Throwable $th) {
return false; return false;
} }

View file

@ -93,12 +93,16 @@ class Origin extends Validator
* Check if Origin has been whiltlisted * Check if Origin has been whiltlisted
* for access to the API * for access to the API
* *
* @param string $origin * @param mixed $origin
* *
* @return bool * @return bool
*/ */
public function isValid($origin) public function isValid($origin)
{ {
if(!is_string($origin)) {
return false;
}
$scheme = \parse_url($origin, PHP_URL_SCHEME); $scheme = \parse_url($origin, PHP_URL_SCHEME);
$host = \parse_url($origin, PHP_URL_HOST); $host = \parse_url($origin, PHP_URL_HOST);

View file

@ -53,7 +53,7 @@ class OpenSSL
* @param $length * @param $length
* @param null $crypto_strong * @param null $crypto_strong
* *
* @return int * @return false|string
*/ */
public static function randomPseudoBytes($length, &$crypto_strong = null) public static function randomPseudoBytes($length, &$crypto_strong = null)
{ {

View file

@ -34,7 +34,7 @@ class Resize
* *
* @return Resize * @return Resize
* *
* @throws \ImagickException * @throws \Throwable
*/ */
public function crop(int $width, int $height) public function crop(int $width, int $height)
{ {
@ -73,7 +73,7 @@ class Resize
* *
* @return Resize * @return Resize
* *
* @throws \ImagickException * @throws \Throwable
*/ */
public function setBackground($color) public function setBackground($color)
{ {
@ -133,7 +133,7 @@ class Resize
case 'webp': case 'webp':
try { try {
$this->image->setImageFormat('webp'); $this->image->setImageFormat('webp');
} catch (\ImagickException $th) { } catch (\Throwable $th) {
$signature = $this->image->getImageSignature(); $signature = $this->image->getImageSignature();
$temp = '/tmp/temp-'.$signature.'.'.\strtolower($this->image->getImageFormat()); $temp = '/tmp/temp-'.$signature.'.'.\strtolower($this->image->getImageFormat());
$output = '/tmp/output-'.$signature.'.webp'; $output = '/tmp/output-'.$signature.'.webp';

View file

@ -13,7 +13,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function getName():string; abstract public function getName(): string;
/** /**
* Get Description. * Get Description.
@ -22,7 +22,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function getDescription():string; abstract public function getDescription(): string;
/** /**
* Get Root. * Get Root.
@ -31,7 +31,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function getRoot():string; abstract public function getRoot(): string;
/** /**
* Get Path. * Get Path.
@ -42,7 +42,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function getPath($filename):string; abstract public function getPath($filename): string;
/** /**
* Upload. * Upload.
@ -56,7 +56,7 @@ abstract class Device
* *
* @return bool * @return bool
*/ */
abstract public function upload($source, $path):bool; abstract public function upload($source, $path): bool;
/** /**
* Read file by given path. * Read file by given path.
@ -65,7 +65,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function read(string $path):string; abstract public function read(string $path): string;
/** /**
* Write file by given path. * Write file by given path.
@ -73,9 +73,9 @@ abstract class Device
* @param string $path * @param string $path
* @param string $data * @param string $data
* *
* @return string * @return bool
*/ */
abstract public function write(string $path, string $data):bool; abstract public function write(string $path, string $data): bool;
/** /**
* Move file from given source to given path, return true on success and false on failure. * Move file from given source to given path, return true on success and false on failure.
@ -87,7 +87,7 @@ abstract class Device
* *
* @return bool * @return bool
*/ */
abstract public function move(string $source, string $target):bool; abstract public function move(string $source, string $target): bool;
/** /**
* Delete file in given path return true on success and false on failure. * Delete file in given path return true on success and false on failure.
@ -99,7 +99,7 @@ abstract class Device
* *
* @return bool * @return bool
*/ */
abstract public function delete(string $path, bool $recursive = false):bool; abstract public function delete(string $path, bool $recursive = false): bool;
/** /**
* Returns given file path its size. * Returns given file path its size.
@ -110,7 +110,7 @@ abstract class Device
* *
* @return int * @return int
*/ */
abstract public function getFileSize(string $path):int; abstract public function getFileSize(string $path): int;
/** /**
* Returns given file path its mime type. * Returns given file path its mime type.
@ -121,7 +121,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function getFileMimeType(string $path):string; abstract public function getFileMimeType(string $path): string;
/** /**
* Returns given file path its MD5 hash value. * Returns given file path its MD5 hash value.
@ -132,7 +132,7 @@ abstract class Device
* *
* @return string * @return string
*/ */
abstract public function getFileHash(string $path):string; abstract public function getFileHash(string $path): string;
/** /**
* Get directory size in bytes. * Get directory size in bytes.
@ -145,7 +145,7 @@ abstract class Device
* *
* @return int * @return int
*/ */
abstract public function getDirectorySize(string $path):int; abstract public function getDirectorySize(string $path): int;
/** /**
* Get Partition Free Space. * Get Partition Free Space.
@ -154,7 +154,7 @@ abstract class Device
* *
* @return float * @return float
*/ */
abstract public function getPartitionFreeSpace():float; abstract public function getPartitionFreeSpace(): float;
/** /**
* Get Partition Total Space. * Get Partition Total Space.
@ -163,5 +163,5 @@ abstract class Device
* *
* @return float * @return float
*/ */
abstract public function getPartitionTotalSpace():float; abstract public function getPartitionTotalSpace(): float;
} }

View file

@ -72,7 +72,7 @@ class Local extends Device
* *
* @throws \Exception * @throws \Exception
* *
* @return string|bool saved destination on success or false on failures * @return bool
*/ */
public function upload($source, $path):bool public function upload($source, $path):bool
{ {
@ -109,7 +109,7 @@ class Local extends Device
* *
* @return bool * @return bool
*/ */
public function write(string $path, string $data):bool public function write(string $path, string $data): bool
{ {
if (!\file_exists(\dirname($path))) { // Checks if directory path to file exists if (!\file_exists(\dirname($path))) { // Checks if directory path to file exists
if (!@\mkdir(\dirname($path), 0755, true)) { if (!@\mkdir(\dirname($path), 0755, true)) {
@ -117,7 +117,7 @@ class Local extends Device
} }
} }
return \file_put_contents($path, $data); return (bool)\file_put_contents($path, $data);
} }
/** /**

View file

@ -51,7 +51,7 @@ class S3 extends Device
* *
* @throws \Exception * @throws \Exception
* *
* @return string|bool saved destination on success or false on failures * @return bool
*/ */
public function upload($source, $path):bool public function upload($source, $path):bool
{ {

View file

@ -24,8 +24,10 @@ class Storage
* @param Device $device * @param Device $device
* *
* @throws Exception * @throws Exception
*
* @return void
*/ */
public static function setDevice($name, Device $device) public static function setDevice($name, Device $device): void
{ {
self::$devices[$name] = $device; self::$devices[$name] = $device;
} }
@ -104,7 +106,7 @@ class Storage
), ),
); );
$factor = floor((strlen($bytes) - 1) / 3); $factor = (int)floor((strlen((string)$bytes) - 1) / 3);
return sprintf("%.{$decimals}f%s", $bytes / pow($mod, $factor), $units[$system][$factor]); return sprintf("%.{$decimals}f%s", $bytes / pow($mod, $factor), $units[$system][$factor]);
} }

View file

@ -16,7 +16,7 @@ class File extends Validator
* *
* TODO think what to do here, currently only used for parameter to be present in SDKs * TODO think what to do here, currently only used for parameter to be present in SDKs
* *
* @param string $name * @param mixed $name
* *
* @return bool * @return bool
*/ */

View file

@ -14,7 +14,7 @@ class FileName extends Validator
/** /**
* The file name can only contain "a-z", "A-Z", "0-9" and "-" and not empty. * The file name can only contain "a-z", "A-Z", "0-9" and "-" and not empty.
* *
* @param string $name * @param mixed $name
* *
* @return bool * @return bool
*/ */
@ -24,6 +24,10 @@ class FileName extends Validator
return false; return false;
} }
if(!is_string($name)) {
return false;
}
if (!\preg_match('/^[a-zA-Z0-9.]+$/', $name)) { if (!\preg_match('/^[a-zA-Z0-9.]+$/', $name)) {
return false; return false;
} }

View file

@ -29,12 +29,16 @@ class FileSize extends Validator
/** /**
* Finds whether a file size is smaller than required limit. * Finds whether a file size is smaller than required limit.
* *
* @param int $fileSize * @param mixed $fileSize
* *
* @return bool * @return bool
*/ */
public function isValid($fileSize) public function isValid($fileSize)
{ {
if(!is_int($fileSize)) {
return false;
}
if ($fileSize > $this->max) { if ($fileSize > $this->max) {
return false; return false;
} }

View file

@ -14,12 +14,16 @@ class Upload extends Validator
/** /**
* Check if a file is a valid upload file * Check if a file is a valid upload file
* *
* @param string $path * @param mixed $path
* *
* @return bool * @return bool
*/ */
public function isValid($path) public function isValid($path)
{ {
if(!is_string($path)) {
return false;
}
if (\is_uploaded_file($path)) { if (\is_uploaded_file($path)) {
return true; return true;
} }

View file

@ -97,9 +97,9 @@ class URL
* *
* Convert query string array to string * Convert query string array to string
* *
* @param string $query * @param array $query
* *
* @return array * @return string
*/ */
public static function unparseQuery(array $query):string public static function unparseQuery(array $query):string
{ {

View file

@ -112,6 +112,8 @@ class Response extends SwooleResponse
/** /**
* Response constructor. * Response constructor.
*
* @param float $time
*/ */
public function __construct(SwooleHTTPResponse $response) public function __construct(SwooleHTTPResponse $response)
{ {
@ -275,8 +277,10 @@ class Response extends SwooleResponse
* @see https://en.wikipedia.org/wiki/YAML * @see https://en.wikipedia.org/wiki/YAML
* *
* @param array $data * @param array $data
*
* @return void
*/ */
public function yaml(array $data) public function yaml(array $data): void
{ {
if(!extension_loaded('yaml')) { if(!extension_loaded('yaml')) {
throw new Exception('Missing yaml extension. Learn more at: https://www.php.net/manual/en/book.yaml.php'); throw new Exception('Missing yaml extension. Learn more at: https://www.php.net/manual/en/book.yaml.php');

View file

@ -31,7 +31,7 @@ abstract class Model
/** /**
* Get Rules * Get Rules
* *
* @return string * @return array
*/ */
public function getRules(): array public function getRules(): array
{ {

View file

@ -23,7 +23,7 @@ class Error extends Model
->addRule('version', [ ->addRule('version', [
'type' => 'string', 'type' => 'string',
'description' => 'Server version number.', 'description' => 'Server version number.',
'example' => APP_VERSION_STABLE, 'example' => '1.0',
]) ])
; ;
} }