Utility Helpers

8 Aufrufe Markdown

Small static functions the whole codebase leans on. Several exist because the PHP builtin they replace gives the wrong answer here.

Overview

Most of these wrap a PHP builtin that is wrong here. The raw function ignores the configured encoding, breaks on Turkish, or produces JSON the rest of the system cannot read back.

Everything below is static, and a failure is a falsy return. Only the request helper says why, in Utility::$error.

Reference

Calling a Remote Service

Utility, outbound HTTP
public static function HttpRequest($url = '', $params = [], $retry = 0);
two shapes, one method
// Preferred: one options array as the FIRST argument.
$body = Utility::HttpRequest([
    'url'    => 'https://api.example.com/v1/ping',
    'type'   => 'POST',
    'data'   => ['a' => 1],
    'header' => ['Accept: application/json'],
]);

// Legacy: address first, a smaller options array second.
$body = Utility::HttpRequest('https://api.example.com/v1/ping', ['post' => ['a' => 1]]);

Returns the response body as a string, or false when the transfer failed. The third argument is the internal redirect counter, never passed by a caller. Options:

KeyTypeDefaultWhat it does
urlstringrequiredTarget address. A query string belongs here, not in data.
typestringGETMethod, uppercased. Anything but GET goes as a custom request.
dataarray or stringnoneRequest body. An array is form encoded; a string goes verbatim, which is how JSON is sent. Ignored on GET.
headerarrayemptyWhole header lines, for example 'Authorization: Bearer ...'.
timeoutint30Seconds allowed for the whole transfer.
connect_timeoutint10Seconds allowed for the connection phase alone.
ssl_verifybooltrueVerifies the peer and the host name. Turn off only against a service you control.
allow_ipv6boolfalseWhile false, resolution is forced to v4; a host without v6 routing blocks until the system timeout.
HttpRequest() The legacy shape reads five keys only: post, timeout (default 5), connect_timeout, ssl_verify (default false) and allow_ipv6. It sends no headers and switches to POST as soon as post is present.
Utility::$error Public and static. After a false it carries the transport message, a name resolution failure for example. This method is its only writer, so a stale value survives every other failure. Read it before the next call overwrites it.
filter:http.request Fires with both shapes already normalised. A listener can rewrite the address, method, body, headers, timeouts and verification of every outbound call. The options change by reference; the return is not used.

JSON

Utility, JSON
public static function jencode($string = '', $flags = 0): string|false;
public static function jdecode($string = '', $mode = false);
jencode() Your flags join three that are always on: invalid byte sequences dropped, unicode unescaped, slashes unescaped. Answers false for a value that cannot be encoded.
jdecode() Second argument true gives an associative array, false an object tree. An array handed in while true comes back untouched, so a value decoded upstream survives the call. An empty string answers null.

Text

Utility, text
public static function strtoupper($str, $encode = 'utf8');
public static function strtolower($str, $encode = 'UTF-8');
public static function ucfirst($string, $encoding = 'utf8');
public static function strlen($str, $charset = 'utf-8');
public static function substr($str = '', $start = 0, $end = -1, $charset = 'utf-8');
public static function short_text($text = '', $start = false, $end = false, $points = false, $charset = 'UTF-8');
strtoupper() Multibyte uppercase. The dotted capital appears only while the active language is Turkish.
strtolower() The mirror image, mapping both capitals back under the same condition.
ucfirst() First character uppercased, rest untouched.
strlen() Characters, not bytes. Null counts as zero instead of raising a warning.
substr() The third argument is a length, not an end offset; its default of minus one drops the final character. Pass null to read to the end.
short_text() Offset plus length; the fourth argument appends three dots when the original was longer. With no length it returns the whole string.

Addresses

Utility, addresses
public static function AppAdress($prefix = false);
public static function RequestURI();
public static function redirect($url = '', $time = 0): void;
public static function getReferer($internal_only = false);
public static function seo_url(string $url, array $allowed = ['page']): string;
public static function image_link_determiner($arg = '', $prefix = '', $rseparator = true);
AppAdress() The installation's base address, resolved once per request. Pass true to append the routing and language prefix, which a link inside a page needs.
RequestURI() The current path with the installation subfolder removed and a leading slash guaranteed.
redirect() Sends the location header, or a refresh header when the second argument is above zero. It does not stop execution.
getReferer() The referring address with markup stripped, or null. Pass true to reject anything not from this installation.
seo_url() Not a slug maker. It rebuilds an address keeping only the query keys you allow. A canonical or og tag cannot then inherit what a visitor appended. A value with no host comes back unchanged.
image_link_determiner() Prepends the folder and the base address to a stored file path. A value that already carries a scheme is untouched.

The Visitor's Address

Utility, client address
public static function resolve_client_ip(array $headers = []): string;
public static function trusted_proxies(): array;
public static function forwarded_header_ignored(): bool;
public static function ip_in_ranges(string $ip, array $ranges): bool;
public static function cidr_match(string $ip, string $cidr): bool;
resolve_client_ip() The address to record, block or rate-limit. A forwarded header is believed only when the request arrived from a listed proxy range; otherwise the TCP peer wins. No usable peer answers an empty string, so keep your own fallback.
trusted_proxies() The ranges whose forwarded headers may be believed. With no proxy configured, nothing matches and no header is read.
forwarded_header_ignored() True when a header claimed a different address and was refused. Two causes: an unlisted proxy, or something sending the header for no reason.
ip_in_ranges() Exact match or CIDR membership against a list of ranges.
cidr_match() Binary-safe CIDR test for IPv4 and IPv6. A version mismatch answers false, it does not raise.

Data and Files

Utility, data
public static function array_export($array = [], $options = []);
public static function xdecode($xml_string = '', $returnArray = false);
public static function array_to_xml(array $data, string $root = 'data'): string;
public static function generate_hash($length = 9, $add_dashes = false, $available_sets = 'luds');
public static function sizeFormat(int $bytes, string $emptyPlaceholder = "\u{2014}"): string;
array_export() Writes an array as PHP source. One option key, pwith: true wraps the output as a complete file that returns the array, which is how configuration is saved. The result is evaluated first, and a stricter export replaces it if it does not come back as an array.
xdecode() XML to an object tree, or an array when the second argument is true. Invalid input answers false. The root element becomes the single outer key.
array_to_xml() The other direction, indented and escaped. Numeric keys become item elements; an unusable key is rewritten into a legal element name.
generate_hash() The third argument selects character sets by letter: l lowercase, u uppercase, d digits, s symbols; look-alike characters are excluded. The second groups the result with hyphens, which lengthens it.
sizeFormat() A byte count as a person reads it, one decimal above the byte unit. Zero or less answers the placeholder, a dash unless you pass one.

Example

The call and the read back, together.

request, then the read back
public function create_account(string $domain, string $plan): array
{
    $body = Utility::jencode(['domain' => $domain, 'plan' => $plan]);

    $response = Utility::HttpRequest([
        'url'     => 'https://api.example.com/v1/accounts',
        'type'    => 'POST',
        'data'    => $body,                       // a string is sent verbatim
        'header'  => [
            'Authorization: Bearer ' . $this->config['token'],
            'Content-Type: application/json',
        ],
        'timeout' => 20,
    ]);

    // Transport failure: nothing was parsed, the reason is on the class.
    if ($response === false)
        throw new Exception('Provider unreachable: ' . Utility::$error);

    $result = Utility::jdecode((string) $response, true);

    // Protocol failure: we got an answer, it is not the one we need.
    if (!is_array($result) || !isset($result['id']))
        throw new Exception('Unexpected response from the provider.');

    return [
        'id'    => $result['id'],
        'quota' => Utility::sizeFormat((int) ($result['quota_bytes'] ?? 0)),
    ];
}

Pitfalls

The two request shapes do not share defaults

The legacy form turns certificate verification off and drops the transfer timeout from thirty seconds to five. Writing the address as the first argument out of habit disables verification silently. Use the options array.

A redirect retry keeps only the address

When a location line appears in the body the call re-issues itself with that address and the legacy defaults. Method, body and headers are not carried over, so an authenticated POST comes back as an anonymous GET. Follow such a response yourself.

Case conversion follows the session, not the value

The Turkish letter mapping applies only while the active language is Turkish, so the same input uppercases differently in two sessions. Never build a stored key, a comparison key or a file name that way. Use it for what a person will read.

The XML reader loses attributes and changes shape

An element with text inside it drops its attributes entirely, and repeated siblings collapse into one key. A response with one item is a string; the same response with two is a list. Parse a provider whose answer depends on either yourself.

War das hilfreich?

Vielen Dank für Ihre Rückmeldung!

Brauchen Sie weitere Hilfe?

Unser Support-Team ist rund um die Uhr für Sie da, wenn Sie oben nicht fündig werden.