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

Small but useful. Entity class. #1176 #1186

Closed
wants to merge 13 commits into from
32 changes: 32 additions & 0 deletions system/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ public function __set(string $key, $value = null)
$value = serialize($value);
}

// JSON casting requires that we JSONize the value
// when setting it so that it can easily be stored
// back to the database.
if (function_exists('json_encode') && array_key_exists($key, $this->_options['casts']) && ($this->_options['casts'][$key] === 'json' || $this->_options['casts'][$key] === 'json-array'))
{
$value = json_encode($value);
}

// if a set* method exists for this key,

// use that method to insert this value.

$method = 'set' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $key)));
Expand Down Expand Up @@ -388,6 +396,30 @@ protected function castAs($value, string $type)
$value = unserialize($value);
}

$value = (array)$value;
break;
case 'json':
if (function_exists('json_decode') && is_string($value) && (strpos($value, '[') === 0 || strpos($value, '{') === 0))
{
$value = json_decode($value, false);
Copy link
Member

Choose a reason for hiding this comment

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

You should check the json_error_code here, also, to ensure it's valid json, or throw an exception otherwise, so the developer knows what went wrong.

Copy link
Contributor Author

@nowackipawel nowackipawel Aug 30, 2018

Choose a reason for hiding this comment

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

Firstly I am not convinced about throwing exception when case 'array' won't throw anything when serialized array is formatted incorrectly.

Secondly if you think it is really required to throw exception with json (let me say more, I do not see any proper exception in CI that I could throw in this case; should I use "Config Exception" and a new Language/en/Entuty.php file dedicated to it?) then we have to clarify that
json_error_code() will return error if $value is null; what could be very likely case when data is retrieved from database. Do you reckon we should treat NULL value as exception or just ignore it? I bet: ignore it, as we do in the moment.

--
i will add docs and test in tmrw.

Copy link
Member

Choose a reason for hiding this comment

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

That's a fair point about the array - except that PHP itself will throw exceptions if the serialization fails, while I'm pretty sure json_encode doesn't throw an exception, it just returns false, leaving it to us to throw that ourself.

Yes - I do agree that we ignore nulls like we currently do, because that's a possible valid response from a database query.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

exception + language (messages) files added and docs modified


if(json_last_error() !== JSON_ERROR_NONE)
{
throw CastException::forInvalidJsonFormatException(json_last_error());
}
}
$value = (object)$value;
break;
case 'json-array':
if (function_exists('json_decode') && is_string($value) && (strpos($value, '[') === 0 || strpos($value, '{') === 0))
{
$value = json_decode($value, true);

if(json_last_error() !== JSON_ERROR_NONE)
{
throw CastException::forInvalidJsonFormatException(json_last_error());
}
}
$value = (array)$value;
break;
case 'datetime':
Expand Down
40 changes: 40 additions & 0 deletions system/Exceptions/CastException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php namespace CodeIgniter\Exceptions;

/**
* Cast Exceptions.
*/
class CastException extends CriticalError
{

/**
* Error code
* @var int
*/
protected $code = 3;

public static function forInvalidJsonFormatException(int $error)
{
switch($error)
{
case JSON_ERROR_DEPTH:
throw new static(lang('Cast.jsonErrorDepth'));
break;
case JSON_ERROR_STATE_MISMATCH:
throw new static(lang('Cast.jsonErrorStateMismatch'));
break;
case JSON_ERROR_CTRL_CHAR:
throw new static(lang('Cast.jsonErrorCtrlChar'));
break;
case JSON_ERROR_SYNTAX:
throw new static(lang('Cast.jsonErrorSyntax'));
break;
case JSON_ERROR_UTF8:
throw new static(lang('Cast.jsonErrorUtf8'));
break;
default:
throw new static(lang('Cast.jsonErrorUnknown'));
}

}

}
23 changes: 23 additions & 0 deletions system/Language/en/Cast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/**
* Cast language strings.
*
* @package CodeIgniter
* @author CodeIgniter Dev Team
* @copyright 2014-2018 British Columbia Institute of Technology (https://bcit.ca/)
* @license https://opensource.org/licenses/MIT MIT License
* @link https://codeigniter.com
* @since Version 3.0.0
* @filesource
*
* @codeCoverageIgnore
*/

return [
'Cast.jsonErrorDepth' => 'Maximum stack depth exceeded',
nowackipawel marked this conversation as resolved.
Show resolved Hide resolved
'Cast.jsonErrorStateMismatch' => 'Underflow or the modes mismatch',
'Cast.jsonErrorCtrlChar' => 'Unexpected control character found',
'Cast.jsonErrorSyntax' => 'Syntax error, malformed JSON',
'Cast.jsonErrorUtf8' => 'Malformed UTF-8 characters, possibly incorrectly encoded',
'Cast.jsonErrorUnknown' => 'Unknown error'
];
22 changes: 17 additions & 5 deletions user_guide_src/source/database/entities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,22 @@ For example, if you had a User entity with an **is_banned** property, you can ca
];
}

Array Casting
Array/Json Casting
-------------

Array casting is especially useful with fields that store serialized arrays or json in them. When cast as an array,
they will automatically be unserialized when you read the property's value. Unlike the rest of the data types that
you can cast properties into, the **array** cast type will serialize the value whenever the property is set::
Array/Json casting is especially useful with fields that store serialized arrays or json in them. When cast as:

* an **array**, they will automatically be unserialized,
* a **json**, they will automatically be set as an value of json_decode($value, false),
* a **json-array**, they will automatically be set as an value of json_decode($value, true),

when you read the property's value.
Unlike the rest of the data types that you can cast properties into, the:

* **array** cast type will serialize,
* **json** and **json-array** cast will use json_encode function on

the value whenever the property is set::

<?php namespace App\Entities;

Expand All @@ -354,7 +364,9 @@ you can cast properties into, the **array** cast type will serialize the value w

protected $_options = [
'casts' => [
'options' => 'array'
'options' => 'array',
'options_object' => 'json',
'options_array' => 'json-array'
],
'dates' => ['created_at', 'updated_at', 'deleted_at'],
'datamap' => []
Expand Down