src/Request/Parameters/EncodedParam.php
<?php
declare(strict_types=1);
namespace Core\Request\Parameters;
use Core\Utils\CoreHelper;
use CoreInterfaces\Core\Request\RequestArraySerialization;
use JsonSerializable;
use stdClass;
abstract class EncodedParam extends Parameter
{
protected $format = RequestArraySerialization::INDEXED;
protected function __construct(string $key, $value, string $typeName)
{
parent::__construct($key, $value, $typeName);
}
/**
* Prepare a mixed typed value or array for form/query encoding.
*
* @param mixed $value Any mixed typed value.
*
* @return mixed A valid instance to be sent in form/query.
*/
protected function prepareValue($value)
{
if (is_null($value)) {
return null;
}
if (is_array($value)) {
// recursively calling this function to resolve all types in any array
return array_map([$this, 'prepareValue'], $value);
}
if (is_bool($value)) {
return $this->isMultipart() ? $value : var_export($value, true);
}
if ($value instanceof JsonSerializable) {
$modelArray = $value->jsonSerialize();
// recursively calling this function to resolve all types in any model
return array_map([$this, 'prepareValue'], $modelArray instanceof stdClass ? [] : $modelArray);
}
return $value;
}
/**
* Override this method to provide isMultipart implementation.
*
* @return bool Returning false by default
*/
protected function isMultipart(): bool
{
return false;
}
/**
* Generate URL-encoded query string from the giving list of parameters.
*
* @param array $data Input data to be encoded
* @param string $parent Parent name accessor
*
* @return string Url encoded query string
*/
protected function httpBuildQuery(array $data, string $format, string $parent = ''): string
{
if ($format == RequestArraySerialization::INDEXED) {
return http_build_query($data);
}
$separatorFormat = in_array($format, [
RequestArraySerialization::TSV,
RequestArraySerialization::PSV,
RequestArraySerialization::CSV
], true);
$innerAssociativeArray = !empty($parent) && CoreHelper::isAssociative($data);
$first = true;
$separator = substr($format, strpos($format, ':') + 1);
$result = [];
array_walk($data, function (
$value,
$key
) use (
&$result,
&$first,
$parent,
$format,
$separatorFormat,
$separator,
$innerAssociativeArray
): void {
if (is_null($value)) {
return;
}
$key = $this->generateKeyWithParent($format, $key, $parent, is_scalar($value));
if (is_array($value)) {
$result[] = $this->httpBuildQuery($value, $format, $key);
return;
}
if (!$separatorFormat) {
$result[] = http_build_query([$key => $value]);
return;
}
$associativePartParam = "&" . http_build_query([$key => $value]);
if ($first) {
$result[] = $associativePartParam;
$first = false;
return;
}
if ($innerAssociativeArray) {
$result[] = $associativePartParam;
return;
}
$result[] = urlencode($separator) . urlencode(strval($value));
});
return implode($separatorFormat ? '' : '&', $result);
}
private function generateKeyWithParent(string $format, $key, string $parent, bool $isScalarValue): string
{
if (empty($parent)) {
return $key;
}
$keyForCurrentNonScalarNonAssociativeArray = "{$parent}[$key]";
if (!is_numeric($key)) {
return $keyForCurrentNonScalarNonAssociativeArray;
}
if (!$isScalarValue) {
return $keyForCurrentNonScalarNonAssociativeArray;
}
return $parent . $this->getKeyPostFix($format);
}
private function getKeyPostFix(string $format): string
{
if ($format == RequestArraySerialization::UN_INDEXED) {
return '[]';
}
return '';
}
}