Skip to content

Commit

Permalink
Method/ReqRes::getResponseHeaders() was not doing a case-insensitive …
Browse files Browse the repository at this point in the history
…merge-by-key with default-header values

LogRequest - not "parsing" content-type correctly / not displaying $_POST  (ie for multipart/form-data)
HttpMessage\ServerRequest::fromGlobals() now has $parseStrOpts param
HttpMessage\Uri::fromGlobals() is now a public static method
  • Loading branch information
bkdotcom committed Dec 7, 2023
1 parent 9103fb1 commit 9657602
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 170 deletions.
2 changes: 1 addition & 1 deletion src/Debug/Abstraction/Object/MethodParams.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ private function phpDocParamValue($param, $className = null)
// there are no quotes around value
return $defaultValue * 1;
}
if (\preg_match('/^array\(\s*\)|\[\s*\]$/i', $defaultValue)) {
if (\preg_match('/^(array\(\s*\)|\[\s*\])$/i', $defaultValue)) {
// empty array...
// we're not going to eval non-empty arrays...
// non empty array will appear as a string
Expand Down
2 changes: 1 addition & 1 deletion src/Debug/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Config
'methodCollect',
'methodDescOutput',
'methodOutput',
'objAttributeObj',
'objAttributeCollect',
'objAttributeOutput',
'objectsExclude',
'objectSort',
Expand Down
13 changes: 7 additions & 6 deletions src/Debug/Dump/Html/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,11 @@ private function buildArgStringArgs(array $args, array $meta)
*/
private function markupTypePart($type)
{
$isArray = false;
if (\substr($type, -2) === '[]') {
$isArray = true;
$type = \substr($type, 0, -2);
$arrayCount = 0; // how many "[]" at end..
if (\preg_match('/(\[\])+$/', $type, $matches)) {
$strlen = \strlen($matches[0]);
$arrayCount = $strlen / 2;
$type = \substr($type, 0, 0 - $strlen);
}
if (\is_numeric($type)) {
return '<span class="t_type">' . $type . '</span>';
Expand All @@ -230,8 +231,8 @@ private function markupTypePart($type)
if (\in_array($type, $this->debug->phpDoc->types, true) === false) {
$type = $this->dumper->valDumper->markupIdentifier($type);
}
if ($isArray) {
$type .= '<span class="t_punct">[]</span>';
if ($arrayCount > 0) {
$type .= '<span class="t_punct">' . \str_repeat('[]', $arrayCount) . '</span>';
}
return '<span class="t_type">' . $type . '</span>';
}
Expand Down
10 changes: 5 additions & 5 deletions src/Debug/Dump/Html/ObjectMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ protected function dumpItemInner($name, array $info, array $cfg)
*
* @return string html fragment
*/
protected function dumpName($name, $info)
protected function dumpName($name, array $info)
{
return $this->html->buildTag(
'span',
Expand All @@ -100,11 +100,11 @@ protected function dumpName($name, $info)
/**
* Dump method parameters as HTML
*
* @param array $params params as returned from getParams()
* @param array $params Params as returned from getParams()
*
* @return string html fragment
*/
protected function dumpParams($params)
protected function dumpParams(array $params)
{
foreach ($params as $i => $info) {
$params[$i] = $this->dumpParam($info);
Expand All @@ -117,11 +117,11 @@ protected function dumpParams($params)
/**
* Dump a single parameter
*
* @param array $info parameter info
* @param array $info Parameter info
*
* @return string html fragment
*/
protected function dumpParam($info)
protected function dumpParam(array $info)
{
return $this->html->buildTag(
'span',
Expand Down
9 changes: 5 additions & 4 deletions src/Debug/Plugin/AbstractLogReqRes.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,19 @@ protected function assertCorrectContentType($contentTypeDetected, $contentTypeUs
* @param StreamInterface|string $content Reqeust/response body
* @param string $contentTypeUser Content-Type provided with request or being sent with response
*
* @return string|null
* @return string|null The detected content-type. may contain the supplied character encoding / boundary
*/
protected function detectContentType($content, $contentTypeUser)
{
$ctuTrimmed = \preg_replace('/\s*[;,].*$/', '', (string) $contentTypeUser);
$contentTypeDetected = $this->debug->stringUtil->contentType($content);
$xmlTypes = array(ContentType::XML, 'application/xml');
$userIsXml = \in_array($contentTypeUser, $xmlTypes, true)
|| \preg_match('/application\\/\S+\+xml/', $contentTypeUser);
$userIsXml = \in_array($ctuTrimmed, $xmlTypes, true)
|| \preg_match('/application\\/\S+\+xml/', $ctuTrimmed);
if (
\array_filter(array(
\in_array($contentTypeDetected, $xmlTypes, true) && $userIsXml,
$contentTypeDetected === ContentType::TXT && $contentTypeUser === ContentType::FORM,
$contentTypeDetected === ContentType::TXT && \in_array($ctuTrimmed, array(ContentType::FORM, ContentType::FORM_MULTIPART), true),
$contentTypeDetected === 'application/x-empty',
))
) {
Expand Down
3 changes: 2 additions & 1 deletion src/Debug/Plugin/LogRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,12 @@ private function logPostOrInput()
$this->debug->utility->getStreamContents($request->getBody()),
$contentTypeUser
);
$contentTypeTrimmed = \preg_replace('/\s*[;,].*$/', '', (string) $contentType);
$parsedBody = $request->getParsedBody();
$this->assertCorrectContentType($contentType, $contentTypeUser, $method);
if (
$method === 'POST'
&& \in_array($contentType, array(ContentType::FORM, ContentType::FORM_MULTIPART), true)
&& \in_array($contentTypeTrimmed, array(ContentType::FORM, ContentType::FORM_MULTIPART), true)
&& $parsedBody
) {
$this->debug->log('$_POST', $parsedBody, $this->debug->meta('redact'));
Expand Down
15 changes: 10 additions & 5 deletions src/Debug/Plugin/Method/ReqRes.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,20 @@ private static function mergeDefaultHeaders(array $headers)
{
$contentTypeDefault = \ini_get('default_mimetype');
$charset = \ini_get('default_charset');
$headersDefault = array();
if ($contentTypeDefault) {
// By default, PHP will output a Content-Type header if this ini value is non-empty
$contentTypeDefault = $contentTypeDefault . '; charset=' . $charset;
$contentTypeDefault = \preg_replace('/; charset=$/', '', $contentTypeDefault);
$headers = \array_merge(array(
'Content-Type' => array(
$contentTypeDefault,
),
), $headers);
$headersDefault['Content-Type'] = array(
$contentTypeDefault,
);
}
$keysLower = \array_map('strtolower', \array_keys($headers));
foreach ($headersDefault as $k => $v) {
if (\in_array(\strtolower($k), $keysLower, true) === false) {
$headers[$k] = $v;
}
}
return $headers;
}
Expand Down
123 changes: 8 additions & 115 deletions src/HttpMessage/AbstractServerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ abstract class AbstractServerRequest extends Request
* key difference: by default this does not convert root key dots and spaces to '_'
*
* @param string $str input string
* @param array $opts parse options
* @param array $opts parse options (default: {convDot:false, convSpace:false})
*
* @return array
*
Expand All @@ -62,8 +62,8 @@ public static function parseStr($str, $opts = array())
/**
* Set default parseStr option(s)
*
* parseStrOpts('key', 'value')
* parseStrOpts(array('k1'=>'v1', 'k2'=>'v2'))
* parseStrOpts('convDot', true)
* parseStrOpts(array('convDot'=>true, 'convSpace'=>true))
*
* @param array|string $mixed key=>value array or key
* @param mixed $val new value
Expand Down Expand Up @@ -146,12 +146,13 @@ protected function getHeadersViaServer($serverParams)
*
* Note: this will return null if content-type = "multipart/form-data" and input = "php://input"
*
* @param string $contentType Content-Type header value
* @param string $input ('php://input') specify input
* @param string $contentType Content-Type header value
* @param string $input ('php://input') specify input
* @param array $parseStrOpts Parse options (default: {convDot:false, convSpace:false})
*
* @return array|null
*/
protected static function postFromInput($contentType, $input = 'php://input')
protected static function postFromInput($contentType, $input = 'php://input', $parseStrOpts = array())
{
$contentType = \preg_replace('/\s*[;,].*$/', '', $contentType);
$contentType = \strtolower($contentType);
Expand All @@ -163,49 +164,14 @@ protected static function postFromInput($contentType, $input = 'php://input')
return null;
}
if ($contentType !== ContentType::JSON) {
return self::parseStr($rawBody);
return self::parseStr($rawBody, $parseStrOpts);
}
$jsonParsedBody = \json_decode($rawBody, true);
return \json_last_error() === JSON_ERROR_NONE
? $jsonParsedBody
: null;
}

/**
* Get a Uri populated with values from $_SERVER.
*
* @return Uri
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function uriFromGlobals()
{
$uri = new Uri('');
$parts = \array_merge(
array(
'scheme' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'
? 'https'
: 'http',
),
self::uriHostPortFromGlobals(),
self::uriPathQueryFromGlobals()
);
$methods = array(
'host' => 'withHost',
'path' => 'withPath',
'port' => 'withPort',
'query' => 'withQuery',
'scheme' => 'withScheme',
);
foreach ($parts as $name => $value) {
if ($value) {
$method = $methods[$name];
$uri = $uri->{$method}($value);
}
}
return $uri;
}

/**
* Create UploadedFile(s) from $_FILES entry
*
Expand Down Expand Up @@ -346,77 +312,4 @@ static function ($matches) {
}, \array_keys($params));
return \array_combine($keys, $params);
}

/**
* Get host and port from $_SERVER vals
*
* @return array host & port
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
private static function uriHostPortFromGlobals()
{
$hostPort = array(
'host' => null,
'port' => null,
);
if (isset($_SERVER['HTTP_HOST'])) {
$hostPort = self::uriHostPortFromHttpHost($_SERVER['HTTP_HOST']);
} elseif (isset($_SERVER['SERVER_NAME'])) {
$hostPort['host'] = $_SERVER['SERVER_NAME'];
} elseif (isset($_SERVER['SERVER_ADDR'])) {
$hostPort['host'] = $_SERVER['SERVER_ADDR'];
}
if ($hostPort['port'] === null && isset($_SERVER['SERVER_PORT'])) {
$hostPort['port'] = $_SERVER['SERVER_PORT'];
}
return $hostPort;
}

/**
* Get host & port from `$_SERVER['HTTP_HOST']`
*
* @param string $httpHost `$_SERVER['HTTP_HOST']` value
*
* @return array host & port
*/
private static function uriHostPortFromHttpHost($httpHost)
{
$url = 'http://' . $httpHost;
$partsDefault = array(
'host' => null,
'port' => null,
);
$parts = \parse_url($url) ?: array();
$parts = \array_merge($partsDefault, $parts);
return \array_intersect_key($parts, $partsDefault);
}

/**
* Get request uri and query from $_SERVER
*
* @return array path & query
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
private static function uriPathQueryFromGlobals()
{
$path = '/';
$query = null;
if (isset($_SERVER['REQUEST_URI'])) {
$exploded = \explode('?', $_SERVER['REQUEST_URI'], 2);
// exploded is an array of length 1 or 2
// use array_shift to avoid testing if exploded[1] exists
$path = \array_shift($exploded);
$query = \array_shift($exploded); // string|null
} elseif (isset($_SERVER['QUERY_STRING'])) {
$query = $_SERVER['QUERY_STRING'];
}
return array(
'path' => $path,
'query' => $query !== null
? $query
: \http_build_query($_GET),
);
}
}
73 changes: 73 additions & 0 deletions src/HttpMessage/AbstractUri.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,32 @@ protected function filterQueryAndFragment($str)
return $this->regexEncode($regex, $str);
}

/**
* Get host and port from $_SERVER vals
*
* @return array host & port
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function hostPortFromGlobals()
{
$hostPort = array(
'host' => null,
'port' => null,
);
if (isset($_SERVER['HTTP_HOST'])) {
$hostPort = self::hostPortFromHttpHost($_SERVER['HTTP_HOST']);
} elseif (isset($_SERVER['SERVER_NAME'])) {
$hostPort['host'] = $_SERVER['SERVER_NAME'];
} elseif (isset($_SERVER['SERVER_ADDR'])) {
$hostPort['host'] = $_SERVER['SERVER_ADDR'];
}
if ($hostPort['port'] === null && isset($_SERVER['SERVER_PORT'])) {
$hostPort['port'] = $_SERVER['SERVER_PORT'];
}
return $hostPort;
}

/**
* Is a given port standard for the given scheme?
*
Expand Down Expand Up @@ -138,6 +164,53 @@ protected static function lowercase($str)
);
}

/**
* Get request uri and query from $_SERVER
*
* @return array path & query
*
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected static function pathQueryFromGlobals()
{
$path = '/';
$query = null;
if (isset($_SERVER['REQUEST_URI'])) {
$exploded = \explode('?', $_SERVER['REQUEST_URI'], 2);
// exploded is an array of length 1 or 2
// use array_shift to avoid testing if exploded[1] exists
$path = \array_shift($exploded);
$query = \array_shift($exploded); // string|null
} elseif (isset($_SERVER['QUERY_STRING'])) {
$query = $_SERVER['QUERY_STRING'];
}
return array(
'path' => $path,
'query' => $query !== null
? $query
: \http_build_query($_GET),
);
}

/**
* Get host & port from `$_SERVER['HTTP_HOST']`
*
* @param string $httpHost `$_SERVER['HTTP_HOST']` value
*
* @return array host & port
*/
private static function hostPortFromHttpHost($httpHost)
{
$url = 'http://' . $httpHost;
$partsDefault = array(
'host' => null,
'port' => null,
);
$parts = \parse_url($url) ?: array();
$parts = \array_merge($partsDefault, $parts);
return \array_intersect_key($parts, $partsDefault);
}

/**
* Call rawurlencode on on match
*
Expand Down
Loading

0 comments on commit 9657602

Please sign in to comment.