Merge remote-tracking branch 'upstream/develop' into json-ld

This commit is contained in:
Michael 2022-07-16 09:33:24 +00:00
commit 8db0e090d7
328 changed files with 13255 additions and 10224 deletions

View file

@ -119,7 +119,7 @@ abstract class MailBuilder
{
$this->recipientUid = $user['uid'] ?? 0;
try {
$this->l10n = $user['language'] ? $this->l10n->withLang($user['language']) : $this->l10n;
$this->l10n = isset($user['language']) ? $this->l10n->withLang($user['language']) : $this->l10n;
} catch (Exception $e) {
$this->logger->warning('cannot use language.', ['user' => $user, 'exception' => $e]);
}
@ -164,7 +164,7 @@ abstract class MailBuilder
*
* @return string[][]
*/
public function getHeaders()
public function getHeaders(): array
{
return $this->headers;
}
@ -182,7 +182,7 @@ abstract class MailBuilder
* @param string[][] $headers
* @return $this
*/
public function withHeaders(array $headers)
public function withHeaders(array $headers): MailBuilder
{
$this->headers = $headers;

View file

@ -141,7 +141,7 @@ class Emailer
$countMessageId += count($value);
}
}
if ($countMessageId > 0) {
if ($countMessageId > 1) {
$this->logger->warning('More than one Message-ID found - RFC violation', ['email' => $email]);
}

View file

@ -55,7 +55,7 @@ class HTTPSignature
* @return array with verification data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function verifyMagic($key)
public static function verifyMagic(string $key): array
{
$headers = null;
$spoofable = false;
@ -139,7 +139,7 @@ class HTTPSignature
*
* @return array
*/
public static function createSig($head, $prvkey, $keyid = 'Key')
public static function createSig(array $head, string $prvkey, string $keyid = 'Key'): array
{
$return_headers = [];
if (!empty($head)) {
@ -166,7 +166,7 @@ class HTTPSignature
*
* @return array
*/
private static function sign($head, $prvkey, $alg = 'sha256')
private static function sign(array $head, string $prvkey, string $alg = 'sha256'): array
{
$ret = [];
$headers = '';
@ -204,7 +204,7 @@ class HTTPSignature
* - \e string \b signature
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function parseSigheader($header)
public static function parseSigheader(string $header): array
{
// Remove obsolete folds
$header = preg_replace('/\n\s+/', ' ', $header);
@ -251,7 +251,7 @@ class HTTPSignature
* @return string Decrypted signature string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function decryptSigheader(array $headers, string $prvkey)
private static function decryptSigheader(array $headers, string $prvkey): string
{
if (!empty($headers['iv']) && !empty($headers['key']) && !empty($headers['data'])) {
return Crypto::unencapsulate($headers, $prvkey);
@ -341,7 +341,7 @@ class HTTPSignature
* @param boolean $success Transmission status
* @param boolean $shared The inbox is a shared inbox
*/
static public function setInboxStatus($url, $success, $shared = false)
static public function setInboxStatus(string $url, bool $success, bool $shared = false)
{
$now = DateTimeFormat::utcNow();
@ -403,21 +403,21 @@ class HTTPSignature
* @return array JSON array
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function fetch($request, $uid)
public static function fetch(string $request, int $uid): array
{
$curlResult = self::fetchRaw($request, $uid);
if (empty($curlResult)) {
return false;
return [];
}
if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
return false;
return [];
}
$content = json_decode($curlResult->getBody(), true);
if (empty($content) || !is_array($content)) {
return false;
return [];
}
return $content;
@ -438,7 +438,7 @@ class HTTPSignature
* @return \Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses CurlResult
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function fetchRaw($request, $uid = 0, $opts = [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::JSON_AS]])
public static function fetchRaw(string $request, int $uid = 0, array $opts = [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::JSON_AS]])
{
$header = [];
@ -488,13 +488,13 @@ class HTTPSignature
/**
* Gets a signer from a given HTTP request
*
* @param $content
* @param $http_headers
* @param string $content
* @param array $http_headers
*
* @return string Signer
* @return string|null|false Signer
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getSigner($content, $http_headers)
public static function getSigner(string $content, array $http_headers)
{
if (empty($http_headers['HTTP_SIGNATURE'])) {
Logger::debug('No HTTP_SIGNATURE header');
@ -686,13 +686,13 @@ class HTTPSignature
/**
* fetches a key for a given id and actor
*
* @param $id
* @param $actor
* @param string $id
* @param string $actor
*
* @return array with actor url and public key
* @throws \Exception
*/
private static function fetchKey($id, $actor)
private static function fetchKey(string $id, string $actor): array
{
$url = (strpos($id, '#') ? substr($id, 0, strpos($id, '#')) : $id);
@ -709,6 +709,6 @@ class HTTPSignature
}
Logger::notice('Key could not be fetched', ['url' => $url, 'actor' => $actor]);
return false;
return [];
}
}

View file

@ -34,23 +34,23 @@ class Images
/**
* Maps Mime types to Imagick formats
*
* @return array
* @return array Format map
*/
public static function getFormatsMap()
{
$m = [
return [
'image/jpeg' => 'JPG',
'image/jpg' => 'JPG',
'image/png' => 'PNG',
'image/gif' => 'GIF'
'image/gif' => 'GIF',
];
return $m;
}
/**
* Return file extension for mime type
* @param string $mimetype
* @return string
* Return file extension for MIME type
*
* @param string $mimetype MIME type
* @return string File extension for MIME type
*/
public static function getExtensionByMimeType(string $mimetype): string
{
@ -63,9 +63,14 @@ class Images
$imagetype = IMAGETYPE_GIF;
break;
default:
case 'image/jpeg':
case 'image/jpg':
$imagetype = IMAGETYPE_JPEG;
break;
default: // Unknown type must be a blob then
return 'blob';
break;
}
return image_type_to_extension($imagetype);
@ -76,11 +81,13 @@ class Images
*
* @return array
*/
public static function supportedTypes()
public static function supportedTypes(): array
{
$types = [
'image/jpeg' => 'jpg'
'image/jpeg' => 'jpg',
'image/jpg' => 'jpg',
];
if (class_exists('Imagick')) {
// Imagick::queryFormats won't help us a lot there...
// At least, not yet, other parts of friendica uses this array
@ -102,21 +109,20 @@ class Images
*
* @param string $image_data Image data
* @param string $filename File name (for guessing the type via the extension)
* @param string $mime default mime type
*
* @return string
* @param string $default Default MIME type
* @return string MIME type
* @throws \Exception
*/
public static function getMimeTypeByData(string $image_data, string $filename = '', string $mime = '')
public static function getMimeTypeByData(string $image_data, string $filename = '', string $default = ''): string
{
if (substr($mime, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mime]);
return $mime;
if (substr($default, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
return $default;
}
$image = @getimagesizefromstring($image_data);
if (!empty($image['mime'])) {
Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $mime, 'mime' => $image['mime']]);
Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $default, 'mime' => $image['mime']]);
return $image['mime'];
}
@ -128,21 +134,20 @@ class Images
*
* @param string $sourcefile Source file of the image
* @param string $filename File name (for guessing the type via the extension)
* @param string $mime default mime type
*
* @return string
* @param string $default default MIME type
* @return string MIME type
* @throws \Exception
*/
public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $mime = '')
public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $default = ''): string
{
if (substr($mime, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mime]);
return $mime;
if (substr($default, 0, 6) == 'image/') {
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
return $default;
}
$image = @getimagesize($sourcefile);
if (!empty($image['mime'])) {
Logger::info('Mime type detected via file', ['filename' => $filename, 'default' => $mime, 'image' => $image]);
Logger::info('Mime type detected via file', ['filename' => $filename, 'default' => $default, 'image' => $image]);
return $image['mime'];
}
@ -150,14 +155,13 @@ class Images
}
/**
* Guess image mimetype from the filename
* Guess image MIME type from the filename's extension
*
* @param string $filename Image filename
*
* @return string
* @param string $filename Image filename
* @return string Guessed MIME type by extension
* @throws \Exception
*/
public static function guessTypeByExtension(string $filename)
public static function guessTypeByExtension(string $filename): string
{
$ext = pathinfo(parse_url($filename, PHP_URL_PATH), PATHINFO_EXTENSION);
$types = self::supportedTypes();
@ -173,11 +177,13 @@ class Images
}
/**
* Gets info array from given URL, cached data has priority
*
* @param string $url
* @return array
* @return array Info
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getInfoFromURLCached($url)
public static function getInfoFromURLCached(string $url): array
{
$data = [];
@ -195,15 +201,17 @@ class Images
DI::cache()->set($cacheKey, $data);
}
return $data;
return $data ?? [];
}
/**
* Gets info from URL uncached
*
* @param string $url
* @return array
* @return array Info array
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getInfoFromURL($url)
public static function getInfoFromURL(string $url): array
{
$data = [];
@ -239,16 +247,18 @@ class Images
$data['size'] = $filesize;
}
return $data;
return is_array($data) ? $data : [];
}
/**
* @param integer $width
* @param integer $height
* @param integer $max
* @return array
* Returns scaling information
*
* @param integer $width Width
* @param integer $height Height
* @param integer $max Max width/height
* @return array Scaling dimensions
*/
public static function getScalingDimensions($width, $height, $max)
public static function getScalingDimensions(int $width, int $height, int $max): array
{
if ((!$width) || (!$height)) {
return ['width' => 0, 'height' => 0];

View file

@ -31,12 +31,24 @@ use Friendica\Model\APContact;
*/
class LDSignature
{
public static function isSigned($data)
/**
* Checks if element 'signature' is found and not empty
*
* @param array $data
* @return bool
*/
public static function isSigned(array $data): bool
{
return !empty($data['signature']);
}
public static function getSigner($data)
/**
* Returns actor (signer) from given data
*
* @param array $data
* @return mixed Returns actor or false on error
*/
public static function getSigner(array $data)
{
if (!self::isSigned($data)) {
return false;
@ -66,13 +78,20 @@ class LDSignature
}
}
public static function sign($data, $owner)
/**
* Signs given data by owner's signature
*
* @param array $data Data to sign
* @param array $owner Owner information, like URL
* @return array Merged array of $data and signature
*/
public static function sign(array $data, array $owner): array
{
$options = [
'type' => 'RsaSignature2017',
'nonce' => Strings::getRandomHex(64),
'creator' => $owner['url'] . '#main-key',
'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM)
'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
];
$ohash = self::hash(self::signableOptions($options));
@ -82,13 +101,25 @@ class LDSignature
return array_merge($data, ['signature' => $options]);
}
private static function signableData($data)
/**
* Removes element 'signature' from array
*
* @param array $data
* @return array With no element 'signature'
*/
private static function signableData(array $data): array
{
unset($data['signature']);
return $data;
}
private static function signableOptions($options)
/**
* Removes some elements and adds '@context' to it
*
* @param array $options
* @return array With some removed elements and added '@context' element
*/
private static function signableOptions(array $options): array
{
$newopts = ['@context' => 'https://w3id.org/identity/v1'];
@ -99,7 +130,13 @@ class LDSignature
return array_merge($newopts, $options);
}
private static function hash($obj)
/**
* Hashes normalized object
*
* @param ??? $obj
* @return string SHA256 hash
*/
private static function hash($obj): string
{
return hash('sha256', JsonLD::normalize($obj));
}

View file

@ -29,7 +29,7 @@ class Mimetype
* @param string $filename filename
* @return mixed array or string
*/
public static function getContentType($filename)
public static function getContentType(string $filename)
{
$mime_types = [

View file

@ -82,7 +82,7 @@ class Network
* @param string $addr The email address
* @return boolean True if it's a valid email address, false if it's not
*/
public static function isEmailDomainValid(string $addr)
public static function isEmailDomainValid(string $addr): bool
{
if (DI::config()->get('system', 'disable_email_validation')) {
return true;
@ -113,7 +113,7 @@ class Network
* @param string $url URL which get tested
* @return boolean True if url is allowed otherwise return false
*/
public static function isUrlAllowed(string $url)
public static function isUrlAllowed(string $url): bool
{
$h = @parse_url($url);
@ -158,7 +158,7 @@ class Network
*
* @return boolean
*/
public static function isUrlBlocked(string $url)
public static function isUrlBlocked(string $url): bool
{
$host = @parse_url($url, PHP_URL_HOST);
if (!$host) {
@ -187,7 +187,7 @@ class Network
*
* @return boolean
*/
public static function isRedirectBlocked(string $url)
public static function isRedirectBlocked(string $url): bool
{
$host = @parse_url($url, PHP_URL_HOST);
if (!$host) {
@ -218,7 +218,7 @@ class Network
* or if allowed list is not configured
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function isEmailDomainAllowed(string $email)
public static function isEmailDomainAllowed(string $email): bool
{
$domain = strtolower(substr($email, strpos($email, '@') + 1));
if (!$domain) {
@ -242,7 +242,7 @@ class Network
* @param array $domain_list
* @return boolean
*/
public static function isDomainAllowed(string $domain, array $domain_list)
public static function isDomainAllowed(string $domain, array $domain_list): bool
{
$found = false;
@ -257,7 +257,7 @@ class Network
return $found;
}
public static function lookupAvatarByEmail(string $email)
public static function lookupAvatarByEmail(string $email): string
{
$avatar['size'] = 300;
$avatar['email'] = $email;
@ -280,11 +280,12 @@ class Network
* @param string $url Any user-submitted URL that may contain tracking params
* @return string The same URL stripped of tracking parameters
*/
public static function stripTrackingQueryParams(string $url)
public static function stripTrackingQueryParams(string $url): string
{
$urldata = parse_url($url);
if (!empty($urldata["query"])) {
$query = $urldata["query"];
if (!empty($urldata['query'])) {
$query = $urldata['query'];
parse_str($query, $querydata);
if (is_array($querydata)) {
@ -292,30 +293,32 @@ class Network
if (in_array(
$param,
[
"utm_source", "utm_medium", "utm_term", "utm_content", "utm_campaign",
"wt_mc", "pk_campaign", "pk_kwd", "mc_cid", "mc_eid",
"fb_action_ids", "fb_action_types", "fb_ref",
"awesm", "wtrid",
"woo_campaign", "woo_source", "woo_medium", "woo_content", "woo_term"]
'utm_source', 'utm_medium', 'utm_term', 'utm_content', 'utm_campaign',
// As seen from Purism
'mtm_source', 'mtm_medium', 'mtm_term', 'mtm_content', 'mtm_campaign',
'wt_mc', 'pk_campaign', 'pk_kwd', 'mc_cid', 'mc_eid',
'fb_action_ids', 'fb_action_types', 'fb_ref',
'awesm', 'wtrid',
'woo_campaign', 'woo_source', 'woo_medium', 'woo_content', 'woo_term']
)
) {
$pair = $param . "=" . urlencode($value);
$url = str_replace($pair, "", $url);
$pair = $param . '=' . urlencode($value);
$url = str_replace($pair, '', $url);
// Second try: if the url isn't encoded completely
$pair = $param . "=" . str_replace(" ", "+", $value);
$url = str_replace($pair, "", $url);
$pair = $param . '=' . str_replace(' ', '+', $value);
$url = str_replace($pair, '', $url);
// Third try: Maybey the url isn't encoded at all
$pair = $param . "=" . $value;
$url = str_replace($pair, "", $url);
$pair = $param . '=' . $value;
$url = str_replace($pair, '', $url);
$url = str_replace(["?&", "&&"], ["?", ""], $url);
$url = str_replace(['?&', '&&'], ['?', ''], $url);
}
}
}
if (substr($url, -1, 1) == "?") {
if (substr($url, -1, 1) == '?') {
$url = substr($url, 0, -1);
}
}
@ -336,8 +339,10 @@ class Network
return $url;
}
$base = ['scheme' => parse_url($basepath, PHP_URL_SCHEME),
'host' => parse_url($basepath, PHP_URL_HOST)];
$base = [
'scheme' => parse_url($basepath, PHP_URL_SCHEME),
'host' => parse_url($basepath, PHP_URL_HOST),
];
$parts = array_merge($base, parse_url('/' . ltrim($url, '/')));
return self::unparseURL($parts);
@ -348,12 +353,12 @@ class Network
*
* @param string $url1
* @param string $url2
* @return string The matching part
* @return string The matching part or empty string on error
*/
public static function getUrlMatch(string $url1, string $url2)
public static function getUrlMatch(string $url1, string $url2): string
{
if (($url1 == "") || ($url2 == "")) {
return "";
if (($url1 == '') || ($url2 == '')) {
return '';
}
$url1 = Strings::normaliseLink($url1);
@ -362,67 +367,67 @@ class Network
$parts1 = parse_url($url1);
$parts2 = parse_url($url2);
if (!isset($parts1["host"]) || !isset($parts2["host"])) {
return "";
if (!isset($parts1['host']) || !isset($parts2['host'])) {
return '';
}
if (empty($parts1["scheme"])) {
$parts1["scheme"] = '';
if (empty($parts1['scheme'])) {
$parts1['scheme'] = '';
}
if (empty($parts2["scheme"])) {
$parts2["scheme"] = '';
if (empty($parts2['scheme'])) {
$parts2['scheme'] = '';
}
if ($parts1["scheme"] != $parts2["scheme"]) {
return "";
if ($parts1['scheme'] != $parts2['scheme']) {
return '';
}
if (empty($parts1["host"])) {
$parts1["host"] = '';
if (empty($parts1['host'])) {
$parts1['host'] = '';
}
if (empty($parts2["host"])) {
$parts2["host"] = '';
if (empty($parts2['host'])) {
$parts2['host'] = '';
}
if ($parts1["host"] != $parts2["host"]) {
return "";
if ($parts1['host'] != $parts2['host']) {
return '';
}
if (empty($parts1["port"])) {
$parts1["port"] = '';
if (empty($parts1['port'])) {
$parts1['port'] = '';
}
if (empty($parts2["port"])) {
$parts2["port"] = '';
if (empty($parts2['port'])) {
$parts2['port'] = '';
}
if ($parts1["port"] != $parts2["port"]) {
return "";
if ($parts1['port'] != $parts2['port']) {
return '';
}
$match = $parts1["scheme"]."://".$parts1["host"];
$match = $parts1['scheme'] . '://' . $parts1['host'];
if ($parts1["port"]) {
$match .= ":".$parts1["port"];
if ($parts1['port']) {
$match .= ':' . $parts1['port'];
}
if (empty($parts1["path"])) {
$parts1["path"] = '';
if (empty($parts1['path'])) {
$parts1['path'] = '';
}
if (empty($parts2["path"])) {
$parts2["path"] = '';
if (empty($parts2['path'])) {
$parts2['path'] = '';
}
$pathparts1 = explode("/", $parts1["path"]);
$pathparts2 = explode("/", $parts2["path"]);
$pathparts1 = explode('/', $parts1['path']);
$pathparts2 = explode('/', $parts2['path']);
$i = 0;
$path = "";
$path = '';
do {
$path1 = $pathparts1[$i] ?? '';
$path2 = $pathparts2[$i] ?? '';
if ($path1 == $path2) {
$path .= $path1."/";
$path .= $path1 . '/';
}
} while (($path1 == $path2) && ($i++ <= count($pathparts1)));
@ -435,11 +440,10 @@ class Network
* Glue url parts together
*
* @param array $parsed URL parts
*
* @return string The glued URL.
* @return string|null The glued URL or null on error
* @deprecated since version 2021.12, use GuzzleHttp\Psr7\Uri::fromParts($parts) instead
*/
public static function unparseURL(array $parsed)
public static function unparseURL(array $parsed): string
{
$get = function ($key) use ($parsed) {
return isset($parsed[$key]) ? $parsed[$key] : null;
@ -452,15 +456,15 @@ class Network
$scheme = $get('scheme');
$query = $get('query');
$fragment = $get('fragment');
$authority = ($userinfo !== null ? $userinfo."@" : '') .
$authority = ($userinfo !== null ? $userinfo . '@' : '') .
$get('host') .
($port ? ":$port" : '');
return (strlen($scheme) ? $scheme.":" : '') .
(strlen($authority) ? "//".$authority : '') .
return (strlen($scheme) ? $scheme . ':' : '') .
(strlen($authority) ? '//' . $authority : '') .
$get('path') .
(strlen($query) ? "?".$query : '') .
(strlen($fragment) ? "#".$fragment : '');
(strlen($query) ? '?' . $query : '') .
(strlen($fragment) ? '#' . $fragment : '');
}
/**
@ -490,11 +494,10 @@ class Network
/**
* Switch the scheme of an url between http and https
*
* @param string $url URL
*
* @return string switched URL
* @param string $url
* @return string Switched URL
*/
public static function switchScheme(string $url)
public static function switchScheme(string $url): string
{
$scheme = parse_url($url, PHP_URL_SCHEME);
if (empty($scheme)) {
@ -517,7 +520,7 @@ class Network
* @param array $additionalParams Associative array of parameters
* @return string
*/
public static function appendQueryParam(string $path, array $additionalParams)
public static function appendQueryParam(string $path, array $additionalParams): string
{
$parsed = parse_url($path);
@ -541,6 +544,7 @@ class Network
*
* @param string $etag The page etag
* @param string $last_modified The page last modification UTC date
* @return void
* @throws \Exception
*/
public static function checkEtagModified(string $etag, string $last_modified)
@ -577,10 +581,10 @@ class Network
/**
* Check if the given URL is a local link
*
* @param string $url
* @return bool
* @param string $url
* @return bool
*/
public static function isLocalLink(string $url)
public static function isLocalLink(string $url): bool
{
return (strpos(Strings::normaliseLink($url), Strings::normaliseLink(DI::baseUrl())) !== false);
}
@ -588,10 +592,10 @@ class Network
/**
* Check if the given URL is a valid HTTP/HTTPS URL
*
* @param string $url
* @return bool
* @param string $url
* @return bool
*/
public static function isValidHttpUrl(string $url)
public static function isValidHttpUrl(string $url): bool
{
$scheme = parse_url($url, PHP_URL_SCHEME);
return !empty($scheme) && in_array($scheme, ['http', 'https']) && parse_url($url, PHP_URL_HOST);

View file

@ -59,7 +59,7 @@ class ParseUrl
* @param string $accept content-type to accept
* @return array content type
*/
public static function getContentType(string $url, string $accept = HttpClientAccept::DEFAULT)
public static function getContentType(string $url, string $accept = HttpClientAccept::DEFAULT): array
{
$curlResult = DI::httpClient()->head($url, [HttpClientOptions::ACCEPT_CONTENT => $accept]);
@ -101,7 +101,7 @@ class ParseUrl
* @see ParseUrl::getSiteinfo() for more information about scraping
* embeddable content
*/
public static function getSiteinfoCached($url, $do_oembed = true): array
public static function getSiteinfoCached(string $url, bool $do_oembed = true): array
{
if (empty($url)) {
return [
@ -180,7 +180,7 @@ class ParseUrl
* </body>
* @endverbatim
*/
public static function getSiteinfo($url, $do_oembed = true, $count = 1)
public static function getSiteinfo(string $url, bool $do_oembed = true, int $count = 1)
{
if (empty($url)) {
return [
@ -543,12 +543,15 @@ class ParseUrl
{
if (!empty($siteinfo['images'])) {
array_walk($siteinfo['images'], function (&$image) use ($page_url) {
// According to the specifications someone could place a picture url into the content field as well.
// But this doesn't seem to happen in the wild, so we don't cover it here.
/*
* According to the specifications someone could place a picture
* URL into the content field as well. But this doesn't seem to
* happen in the wild, so we don't cover it here.
*/
if (!empty($image['url'])) {
$image['url'] = self::completeUrl($image['url'], $page_url);
$photodata = Images::getInfoFromURLCached($image['url']);
if (!empty($photodata) && ($photodata[0] > 50) && ($photodata[1] > 50)) {
if (($photodata) && ($photodata[0] > 50) && ($photodata[1] > 50)) {
$image['src'] = $image['url'];
$image['width'] = $photodata[0];
$image['height'] = $photodata[1];
@ -633,15 +636,16 @@ class ParseUrl
* @param string $string Tags
* @return array with formatted Hashtags
*/
public static function convertTagsToArray($string)
public static function convertTagsToArray(string $string): array
{
$arr_tags = str_getcsv($string);
if (count($arr_tags)) {
// add the # sign to every tag
array_walk($arr_tags, ["self", "arrAddHashes"]);
array_walk($arr_tags, ['self', 'arrAddHashes']);
return $arr_tags;
}
return [];
}
/**
@ -653,9 +657,9 @@ class ParseUrl
* @param int $k Counter for internal use
* @return void
*/
private static function arrAddHashes(&$tag, $k)
private static function arrAddHashes(string &$tag, int $k)
{
$tag = "#" . $tag;
$tag = '#' . $tag;
}
/**
@ -672,41 +676,41 @@ class ParseUrl
*
* @return string The url with a scheme
*/
private static function completeUrl($url, $scheme)
private static function completeUrl(string $url, string $scheme): string
{
$urlarr = parse_url($url);
// If the url does allready have an scheme
// we can stop the process here
if (isset($urlarr["scheme"])) {
return($url);
if (isset($urlarr['scheme'])) {
return $url;
}
$schemearr = parse_url($scheme);
$complete = $schemearr["scheme"]."://".$schemearr["host"];
$complete = $schemearr['scheme'] . '://' . $schemearr['host'];
if (!empty($schemearr["port"])) {
$complete .= ":".$schemearr["port"];
if (!empty($schemearr['port'])) {
$complete .= ':' . $schemearr['port'];
}
if (!empty($urlarr["path"])) {
if (strpos($urlarr["path"], "/") !== 0) {
$complete .= "/";
if (!empty($urlarr['path'])) {
if (strpos($urlarr['path'], '/') !== 0) {
$complete .= '/';
}
$complete .= $urlarr["path"];
$complete .= $urlarr['path'];
}
if (!empty($urlarr["query"])) {
$complete .= "?".$urlarr["query"];
if (!empty($urlarr['query'])) {
$complete .= '?' . $urlarr['query'];
}
if (!empty($urlarr["fragment"])) {
$complete .= "#".$urlarr["fragment"];
if (!empty($urlarr['fragment'])) {
$complete .= '#' . $urlarr['fragment'];
}
return($complete);
return $complete;
}
/**
@ -716,7 +720,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseParts(array $siteinfo, array $jsonld)
private static function parseParts(array $siteinfo, array $jsonld): array
{
if (!empty($jsonld['@graph']) && is_array($jsonld['@graph'])) {
foreach ($jsonld['@graph'] as $part) {
@ -761,7 +765,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLd(array $siteinfo, array $jsonld)
private static function parseJsonLd(array $siteinfo, array $jsonld): array
{
$type = JsonLD::fetchElement($jsonld, '@type');
if (empty($type)) {
@ -854,7 +858,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdAuthor(array $siteinfo, array $jsonld)
private static function parseJsonLdAuthor(array $siteinfo, array $jsonld): array
{
$jsonldinfo = [];
@ -938,7 +942,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdArticle(array $siteinfo, array $jsonld)
private static function parseJsonLdArticle(array $siteinfo, array $jsonld): array
{
$jsonldinfo = [];
@ -1008,7 +1012,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdWebPage(array $siteinfo, array $jsonld)
private static function parseJsonLdWebPage(array $siteinfo, array $jsonld): array
{
$jsonldinfo = [];
@ -1047,7 +1051,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdWebSite(array $siteinfo, array $jsonld)
private static function parseJsonLdWebSite(array $siteinfo, array $jsonld): array
{
$jsonldinfo = [];
@ -1085,7 +1089,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdWebOrganization(array $siteinfo, array $jsonld)
private static function parseJsonLdWebOrganization(array $siteinfo, array $jsonld): array
{
$jsonldinfo = [];
@ -1131,7 +1135,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdWebPerson(array $siteinfo, array $jsonld)
private static function parseJsonLdWebPerson(array $siteinfo, array $jsonld): array
{
$jsonldinfo = [];
@ -1176,7 +1180,7 @@ class ParseUrl
* @param array $jsonld
* @return array siteinfo
*/
private static function parseJsonLdMediaObject(array $siteinfo, array $jsonld, string $name)
private static function parseJsonLdMediaObject(array $siteinfo, array $jsonld, string $name): array
{
$media = [];

View file

@ -97,7 +97,8 @@ class PidFile
*
* @return boolean|string PID or "false" if not created
*/
static public function create($file) {
static public function create(string $file)
{
$pid = self::pidFromFile($file);
// We have a process id? then we quit
@ -119,7 +120,8 @@ class PidFile
*
* @return boolean Is it running?
*/
static public function delete($file) {
static public function delete(string $file): bool
{
return @unlink($file);
}
}

View file

@ -76,11 +76,10 @@ class Proxy
*
* @param string $url The URL to proxyfy
* @param string $size One of the Proxy::SIZE_* constants
*
* @return string The proxyfied URL or relative path
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function proxifyUrl($url, $size = '')
public static function proxifyUrl(string $url, string $size = ''): string
{
if (!DI::config()->get('system', 'proxify_content')) {
return $url;
@ -133,11 +132,10 @@ class Proxy
* proxy storage directory.
*
* @param string $html Un-proxified HTML code
*
* @return string Proxified HTML code
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function proxifyHtml($html)
public static function proxifyHtml(string $html): string
{
$html = str_replace(Strings::normaliseLink(DI::baseUrl()) . '/', DI::baseUrl() . '/', $html);
@ -151,7 +149,7 @@ class Proxy
* @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function isLocalImage($url)
public static function isLocalImage(string $url): bool
{
if (substr($url, 0, 1) == '/') {
return true;
@ -170,7 +168,7 @@ class Proxy
* @param string $url URL to parse
* @return array Associative array of query string parameters
*/
private static function parseQuery($url)
private static function parseQuery(string $url): array
{
$query = parse_url($url, PHP_URL_QUERY);
$query = html_entity_decode($query);
@ -187,7 +185,7 @@ class Proxy
* @return string Proxified HTML image tag
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function replaceUrl(array $matches)
private static function replaceUrl(array $matches): string
{
// if the picture seems to be from another picture cache then take the original source
$queryvar = self::parseQuery($matches[2]);

View file

@ -55,7 +55,7 @@ class ReversedFileReader implements \Iterator
* @param string $filename File to open
* @return $this
*/
public function open(string $filename)
public function open(string $filename): ReversedFileReader
{
$this->fh = fopen($filename, 'r');
if (!$this->fh) {
@ -73,9 +73,10 @@ class ReversedFileReader implements \Iterator
/**
* Read $size bytes behind last position
*
* @param int $size
* @return string
*/
private function _read($size)
private function _read(int $size): string
{
$this->pos -= $size;
fseek($this->fh, $this->pos);
@ -86,7 +87,7 @@ class ReversedFileReader implements \Iterator
* Read next line from end of file
* Return null if no lines are left to read
*
* @return ?string
* @return string|null Depending on data being buffered
*/
private function _readline()
{
@ -140,7 +141,7 @@ class ReversedFileReader implements \Iterator
* @see Iterator::key()
* @return int
*/
public function key()
public function key(): int
{
return $this->key;
}
@ -151,7 +152,7 @@ class ReversedFileReader implements \Iterator
* @see Iterator::current()
* @return string
*/
public function current()
public function current(): string
{
return $this->value;
}
@ -162,7 +163,7 @@ class ReversedFileReader implements \Iterator
* @see Iterator::valid()
* @return bool
*/
public function valid()
public function valid(): bool
{
return ! is_null($this->value);
}

View file

@ -32,11 +32,11 @@ class Strings
/**
* Generates a pseudo-random string of hexadecimal characters
*
* @param int $size
* @return string
* @param int $size Size of string (default: 64)
* @return string Pseudo-random string
* @throws \Exception
*/
public static function getRandomHex($size = 64)
public static function getRandomHex(int $size = 64): string
{
$byte_size = ceil($size / 2);
@ -51,10 +51,9 @@ class Strings
* Checks, if the given string is a valid hexadecimal code
*
* @param string $hexCode
*
* @return bool
*/
public static function isHex($hexCode)
public static function isHex(string $hexCode): bool
{
return !empty($hexCode) ? @preg_match("/^[a-f0-9]{2,}$/i", $hexCode) && !(strlen($hexCode) & 1) : false;
}
@ -75,10 +74,9 @@ class Strings
* Generate a string that's random, but usually pronounceable. Used to generate initial passwords
*
* @param int $len length
*
* @return string
*/
public static function getRandomName($len)
public static function getRandomName(int $len): string
{
if ($len <= 0) {
return '';
@ -161,11 +159,10 @@ class Strings
*
* @param string $network Network name of the contact (e.g. dfrn, rss and so on)
* @param string $url The contact url
*
* @return string Formatted network name
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function formatNetworkName($network, $url = '')
public static function formatNetworkName(string $network, string $url = ''): string
{
if ($network != '') {
if ($url != '') {
@ -176,6 +173,8 @@ class Strings
return $network_name;
}
return '';
}
/**
@ -187,7 +186,7 @@ class Strings
*
* @return string Transformed string.
*/
public static function deindent($text, $chr = "[\t ]", $count = NULL)
public static function deindent(string $text, string $chr = "[\t ]", int $count = null): string
{
$lines = explode("\n", $text);
@ -216,7 +215,7 @@ class Strings
*
* @return string Size with measured units.
*/
public static function formatBytes($bytes, $precision = 2)
public static function formatBytes(int $bytes, int $precision = 2): string
{
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
@ -231,10 +230,9 @@ class Strings
* Protect percent characters in sprintf calls
*
* @param string $s String to transform.
*
* @return string Transformed string.
*/
public static function protectSprintf($s)
public static function protectSprintf(string $s): string
{
return str_replace('%', '%%', $s);
}
@ -244,10 +242,9 @@ class Strings
*
* @param string $s URL to encode
* @param boolean $strip_padding Optional. Default false
*
* @return string Encoded URL
*/
public static function base64UrlEncode($s, $strip_padding = false)
public static function base64UrlEncode(string $s, bool $strip_padding = false): string
{
$s = strtr(base64_encode($s), '+/', '-_');
@ -260,18 +257,13 @@ class Strings
/**
* Decode Base64 Encoded URL and translate -_ to +/
* @param string $s URL to decode
*
* @param string $s URL to decode
* @return string Decoded URL
* @throws \Exception
*/
public static function base64UrlDecode($s)
public static function base64UrlDecode(string $s): string
{
if (is_array($s)) {
Logger::notice('base64url_decode: illegal input: ', ['backtrace' => debug_backtrace()]);
return $s;
}
/*
* // Placeholder for new rev of salmon which strips base64 padding.
* // PHP base64_decode handles the un-padded input without requiring this step
@ -295,10 +287,9 @@ class Strings
* Normalize url
*
* @param string $url URL to be normalized.
*
* @return string Normalized URL.
*/
public static function normaliseLink($url)
public static function normaliseLink(string $url): string
{
$ret = str_replace(['https:', '//www.'], ['http:', '//'], $url);
return rtrim($ret, '/');
@ -308,10 +299,9 @@ class Strings
* Normalize OpenID identity
*
* @param string $s OpenID Identity
*
* @return string normalized OpenId Identity
*/
public static function normaliseOpenID($s)
public static function normaliseOpenID(string $s): string
{
return trim(str_replace(['http://', 'https://'], ['', ''], $s), '/');
}
@ -327,7 +317,7 @@ class Strings
* @return boolean True if the URLs match, otherwise False
*
*/
public static function compareLink($a, $b)
public static function compareLink(string $a, string $b): bool
{
return (strcasecmp(self::normaliseLink($a), self::normaliseLink($b)) === 0);
}
@ -338,7 +328,7 @@ class Strings
* @param string $uri
* @return string
*/
public static function ensureQueryParameter($uri)
public static function ensureQueryParameter(string $uri): string
{
if (strpos($uri, '?') === false && ($pos = strpos($uri, '&')) !== false) {
$uri = substr($uri, 0, $pos) . '?' . substr($uri, $pos + 1);
@ -354,7 +344,7 @@ class Strings
* @param array $chars
* @return bool
*/
public static function startsWithChars($string, array $chars)
public static function startsWithChars(string $string, array $chars): bool
{
$return = in_array(substr(trim($string), 0, 1), $chars);
@ -369,7 +359,7 @@ class Strings
* @param string $start
* @return bool
*/
public static function startsWith(string $string, string $start)
public static function startsWith(string $string, string $start): bool
{
$return = substr_compare($string, $start, 0, strlen($start)) === 0;
@ -384,7 +374,7 @@ class Strings
* @param string $end
* @return bool
*/
public static function endsWith(string $string, string $end)
public static function endsWith(string $string, string $end): string
{
$return = substr_compare($string, $end, -strlen($end)) === 0;
@ -397,7 +387,7 @@ class Strings
* @return string
* @see https://daringfireball.net/2010/07/improved_regex_for_matching_urls
*/
public static function autoLinkRegEx()
public static function autoLinkRegEx(): string
{
return '@
(?<![=\'\]"/]) # Not preceded by [, =, \', ], ", /
@ -427,7 +417,7 @@ class Strings
* @param string $pathItem
* @return string
*/
public static function sanitizeFilePathItem($pathItem)
public static function sanitizeFilePathItem(string $pathItem): string
{
$pathItem = str_replace('/', '_', $pathItem);
$pathItem = str_replace('\\', '_', $pathItem);
@ -449,7 +439,7 @@ class Strings
* @return string
* @see substr_replace()
*/
public static function substringReplace(string $string, string $replacement, int $start, int $length = null)
public static function substringReplace(string $string, string $replacement, int $start, int $length = null): string
{
$string_length = mb_strlen($string);

View file

@ -0,0 +1,301 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Util\Writer;
use Exception;
use Friendica\Database\Definition\DbaDefinition;
/**
* SQL writer utility for the database definition
*/
class DbaDefinitionSqlWriter
{
/**
* Creates a complete SQL definition bases on a give DBA Definition class
*
* @param DbaDefinition $definition The DBA definition class
*
* @return string The SQL definition as a string
*
* @throws Exception in case of parameter failures
*/
public static function create(DbaDefinition $definition): string
{
$sqlString = "-- ------------------------------------------\n";
$sqlString .= "-- " . FRIENDICA_PLATFORM . " " . FRIENDICA_VERSION . " (" . FRIENDICA_CODENAME . ")\n";
$sqlString .= "-- DB_UPDATE_VERSION " . DB_UPDATE_VERSION . "\n";
$sqlString .= "-- ------------------------------------------\n\n\n";
foreach ($definition->getAll() as $tableName => $tableStructure) {
$sqlString .= "--\n";
$sqlString .= "-- TABLE $tableName\n";
$sqlString .= "--\n";
$sqlString .= static::createTable($tableName, $tableStructure);
}
return $sqlString;
}
/**
* Creates the SQL definition of one table
*
* @param string $tableName The table name
* @param array $tableStructure The table structure
*
* @return string The SQL definition
*
* @throws Exception in cases of structure failures
*/
public static function createTable(string $tableName, array $tableStructure): string
{
$engine = '';
$comment = '';
$sql_rows = [];
$primary_keys = [];
$foreign_keys = [];
foreach ($tableStructure['fields'] as $fieldName => $field) {
$sql_rows[] = '`' . static::escape($fieldName) . '` ' . self::fieldCommand($field);
if (!empty($field['primary'])) {
$primary_keys[] = $fieldName;
}
if (!empty($field['foreign'])) {
$foreign_keys[$fieldName] = $field;
}
}
if (!empty($tableStructure['indexes'])) {
foreach ($tableStructure['indexes'] as $indexName => $fieldNames) {
$sql_index = self::createIndex($indexName, $fieldNames, '');
if (!is_null($sql_index)) {
$sql_rows[] = $sql_index;
}
}
}
foreach ($foreign_keys as $fieldName => $parameters) {
$sql_rows[] = self::foreignCommand($fieldName, $parameters);
}
if (isset($tableStructure['engine'])) {
$engine = ' ENGINE=' . $tableStructure['engine'];
}
if (isset($tableStructure['comment'])) {
$comment = " COMMENT='" . static::escape($tableStructure['comment']) . "'";
}
$sql = implode(",\n\t", $sql_rows);
$sql = sprintf("CREATE TABLE IF NOT EXISTS `%s` (\n\t", static::escape($tableName)) . $sql .
"\n)" . $engine . " DEFAULT COLLATE utf8mb4_general_ci" . $comment;
return $sql . ";\n\n";
}
/**
* Standard escaping for SQL definitions
*
* @param string $sqlString the SQL string to escape
*
* @return string escaped SQL string
*/
public static function escape(string $sqlString): string
{
return str_replace("'", "\\'", $sqlString);
}
/**
* Creates the SQL definition to add a foreign key
*
* @param string $keyName The foreign key name
* @param array $parameters The given parameters of the foreign key
*
* @return string The SQL definition
*/
public static function addForeignKey(string $keyName, array $parameters): string
{
return sprintf("ADD %s", static::foreignCommand($keyName, $parameters));
}
/**
* Creates the SQL definition to drop a foreign key
*
* @param string $keyName The foreign key name
*
* @return string The SQL definition
*/
public static function dropForeignKey(string $keyName): string
{
return sprintf("DROP FOREIGN KEY `%s`", $keyName);
}
/**
* Creates the SQL definition to drop an index
*
* @param string $indexName The index name
*
* @return string The SQL definition
*/
public static function dropIndex(string $indexName): string
{
return sprintf("DROP INDEX `%s`", static::escape($indexName));
}
/**
* Creates the SQL definition to add a table field
*
* @param string $fieldName The table field name
* @param array $parameters The parameters of the table field
*
* @return string The SQL definition
*/
public static function addTableField(string $fieldName, array $parameters): string
{
return sprintf("ADD `%s` %s", static::escape($fieldName), static::fieldCommand($parameters));
}
/**
* Creates the SQL definition to modify a table field
*
* @param string $fieldName The table field name
* @param array $parameters The paramters to modify
*
* @return string The SQL definition
*/
public static function modifyTableField(string $fieldName, array $parameters): string
{
return sprintf("MODIFY `%s` %s", static::escape($fieldName), self::fieldCommand($parameters, false));
}
/**
* Returns SQL statement for field
*
* @param array $parameters Parameters for SQL statement
* @param boolean $create Whether to include PRIMARY KEY statement (unused)
* @return string SQL statement part
*/
public static function fieldCommand(array $parameters, bool $create = true): string
{
$fieldstruct = $parameters['type'];
if (isset($parameters['Collation'])) {
$fieldstruct .= ' COLLATE ' . $parameters['Collation'];
}
if (isset($parameters['not null'])) {
$fieldstruct .= ' NOT NULL';
}
if (isset($parameters['default'])) {
if (strpos(strtolower($parameters['type']), 'int') !== false) {
$fieldstruct .= ' DEFAULT ' . $parameters['default'];
} else {
$fieldstruct .= " DEFAULT '" . $parameters['default'] . "'";
}
}
if (isset($parameters['extra'])) {
$fieldstruct .= ' ' . $parameters['extra'];
}
if (isset($parameters['comment'])) {
$fieldstruct .= " COMMENT '" . static::escape($parameters['comment']) . "'";
}
/*if (($parameters['primary'] != '') && $create)
$fieldstruct .= ' PRIMARY KEY';*/
return $fieldstruct;
}
/**
* Creates the SQL definition to create an index
*
* @param string $indexName The index name
* @param array $fieldNames The field names of this index
* @param string $method The method to create the index (default is ADD)
*
* @return string The SQL definition
* @throws Exception in cases the paramter contains invalid content
*/
public static function createIndex(string $indexName, array $fieldNames, string $method = 'ADD'): string
{
$method = strtoupper(trim($method));
if ($method != '' && $method != 'ADD') {
throw new Exception("Invalid parameter 'method' in self::createIndex(): '$method'");
}
if (in_array($fieldNames[0], ['UNIQUE', 'FULLTEXT'])) {
$index_type = array_shift($fieldNames);
$method .= " " . $index_type;
}
$names = "";
foreach ($fieldNames as $fieldName) {
if ($names != '') {
$names .= ',';
}
if (preg_match('|(.+)\((\d+)\)|', $fieldName, $matches)) {
$names .= "`" . static::escape($matches[1]) . "`(" . intval($matches[2]) . ")";
} else {
$names .= "`" . static::escape($fieldName) . "`";
}
}
if ($indexName == 'PRIMARY') {
return sprintf("%s PRIMARY KEY(%s)", $method, $names);
}
return sprintf("%s INDEX `%s` (%s)", $method, static::escape($indexName), $names);
}
/**
* Creates the SQL definition for foreign keys
*
* @param string $foreignKeyName The foreign key name
* @param array $parameters The parameters of the foreign key
*
* @return string The SQL definition
*/
public static function foreignCommand(string $foreignKeyName, array $parameters): string
{
$foreign_table = array_keys($parameters['foreign'])[0];
$foreign_field = array_values($parameters['foreign'])[0];
$sql = "FOREIGN KEY (`" . $foreignKeyName . "`) REFERENCES `" . $foreign_table . "` (`" . $foreign_field . "`)";
if (!empty($parameters['foreign']['on update'])) {
$sql .= " ON UPDATE " . strtoupper($parameters['foreign']['on update']);
} else {
$sql .= " ON UPDATE RESTRICT";
}
if (!empty($parameters['foreign']['on delete'])) {
$sql .= " ON DELETE " . strtoupper($parameters['foreign']['on delete']);
} else {
$sql .= " ON DELETE CASCADE";
}
return $sql;
}
}

View file

@ -0,0 +1,145 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Util\Writer;
use Friendica\Core\Renderer;
use Friendica\Database\Definition\DbaDefinition;
use Friendica\Network\HTTPException\ServiceUnavailableException;
/**
* Utility class to write content into the '/doc' directory
*/
class DocWriter
{
/**
* Creates all database definitions as Markdown fields and create the mkdoc config file.
*
* @param DbaDefinition $definition The Database definition class
* @param string $basePath The basepath of Friendica
*
* @return void
* @throws ServiceUnavailableException in really unexpected cases!
*/
public static function writeDbDefinition(DbaDefinition $definition, string $basePath)
{
$tables = [];
foreach ($definition->getAll() as $name => $definition) {
$indexes = [
[
'name' => 'Name',
'fields' => 'Fields',
],
[
'name' => '-',
'fields' => '-',
]
];
$lengths = ['name' => 4, 'fields' => 6];
foreach ($definition['indexes'] as $key => $value) {
$fieldlist = implode(', ', $value);
$indexes[] = ['name' => $key, 'fields' => $fieldlist];
$lengths['name'] = max($lengths['name'], strlen($key));
$lengths['fields'] = max($lengths['fields'], strlen($fieldlist));
}
array_walk_recursive($indexes, function (&$value, $key) use ($lengths) {
$value = str_pad($value, $lengths[$key], $value === '-' ? '-' : ' ');
});
$foreign = [];
$fields = [
[
'name' => 'Field',
'comment' => 'Description',
'type' => 'Type',
'null' => 'Null',
'primary' => 'Key',
'default' => 'Default',
'extra' => 'Extra',
],
[
'name' => '-',
'comment' => '-',
'type' => '-',
'null' => '-',
'primary' => '-',
'default' => '-',
'extra' => '-',
]
];
$lengths = [
'name' => 5,
'comment' => 11,
'type' => 4,
'null' => 4,
'primary' => 3,
'default' => 7,
'extra' => 5,
];
foreach ($definition['fields'] as $key => $value) {
$field = [];
$field['name'] = $key;
$field['comment'] = $value['comment'] ?? '';
$field['type'] = $value['type'];
$field['null'] = ($value['not null'] ?? false) ? 'NO' : 'YES';
$field['primary'] = ($value['primary'] ?? false) ? 'PRI' : '';
$field['default'] = $value['default'] ?? 'NULL';
$field['extra'] = $value['extra'] ?? '';
foreach ($field as $fieldName => $fieldvalue) {
$lengths[$fieldName] = max($lengths[$fieldName] ?? 0, strlen($fieldvalue));
}
$fields[] = $field;
if (!empty($value['foreign'])) {
$foreign[] = [
'field' => $key,
'targettable' => array_keys($value['foreign'])[0],
'targetfield' => array_values($value['foreign'])[0]
];
}
}
array_walk_recursive($fields, function (&$value, $key) use ($lengths) {
$value = str_pad($value, $lengths[$key], $value === '-' ? '-' : ' ');
});
$tables[] = ['name' => $name, 'comment' => $definition['comment']];
$content = Renderer::replaceMacros(Renderer::getMarkupTemplate('structure.tpl'), [
'$name' => $name,
'$comment' => $definition['comment'],
'$fields' => $fields,
'$indexes' => $indexes,
'$foreign' => $foreign,
]);
$filename = $basePath . '/doc/database/db_' . $name . '.md';
file_put_contents($filename, $content);
}
asort($tables);
$content = Renderer::replaceMacros(Renderer::getMarkupTemplate('tables.tpl'), [
'$tables' => $tables,
]);
$filename = $basePath . '/doc/database.md';
file_put_contents($filename, $content);
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Util\Writer;
use Friendica\Database\Definition\ViewDefinition;
/**
* SQL writer utility for the db view definition
*/
class ViewDefinitionSqlWriter
{
/**
* Creates a complete SQL definition bases on a give View Definition class
*
* @param ViewDefinition $definition The View definition class
*
* @return string The SQL definition as a string
*/
public static function create(ViewDefinition $definition): string
{
$sqlString = '';
foreach ($definition->getAll() as $viewName => $viewStructure) {
$sqlString .= "--\n";
$sqlString .= "-- VIEW $viewName\n";
$sqlString .= "--\n";
$sqlString .= static::dropView($viewName);
$sqlString .= static::createView($viewName, $viewStructure);
}
return $sqlString;
}
/**
* Creates the SQL definition to drop a view
*
* @param string $viewName the view name
*
* @return string The SQL definition
*/
public static function dropView(string $viewName): string
{
return sprintf("DROP VIEW IF EXISTS `%s`", static::escape($viewName)) . ";\n";
}
/**
* Creates the SQL definition to create a new view
*
* @param string $viewName The view name
* @param array $viewStructure The structure information of the view
*
* @return string The SQL definition
*/
public static function createView(string $viewName, array $viewStructure): string
{
$sql_rows = [];
foreach ($viewStructure['fields'] as $fieldname => $origin) {
if (is_string($origin)) {
$sql_rows[] = $origin . " AS `" . static::escape($fieldname) . "`";
} elseif (is_array($origin) && (sizeof($origin) == 2)) {
$sql_rows[] = "`" . static::escape($origin[0]) . "`.`" . static::escape($origin[1]) . "` AS `" . static::escape($fieldname) . "`";
}
}
return sprintf("CREATE VIEW `%s` AS SELECT \n\t", static::escape($viewName)) .
implode(",\n\t", $sql_rows) . "\n\t" . $viewStructure['query'] . ";\n\n";
}
/**
* Standard escaping for SQL definitions
*
* @param string $sqlString the SQL string to escape
*
* @return string escaped SQL string
*/
public static function escape(string $sqlString): string
{
return str_replace("'", "\\'", $sqlString);
}
}

View file

@ -21,6 +21,9 @@
namespace Friendica\Util;
use DOMDocument;
use DOMElement;
use DOMNode;
use DOMXPath;
use Friendica\Core\Logger;
use Friendica\Core\System;
@ -35,26 +38,25 @@ class XML
* Creates an XML structure out of a given array
*
* @param array $array The array of the XML structure that will be generated
* @param object $xml The createdXML will be returned by reference
* @param object $xml The created XML will be returned by reference
* @param bool $remove_header Should the XML header be removed or not?
* @param array $namespaces List of namespaces
* @param bool $root interally used parameter. Mustn't be used from outside.
*
* @return string The created XML
* @return void
*/
public static function fromArray($array, &$xml, $remove_header = false, $namespaces = [], $root = true)
public static function fromArray(array $array, &$xml, bool $remove_header = false, array $namespaces = [], bool $root = true)
{
if ($root) {
foreach ($array as $key => $value) {
foreach ($namespaces as $nskey => $nsvalue) {
$key .= " xmlns".($nskey == "" ? "":":").$nskey.'="'.$nsvalue.'"';
$key .= ' xmlns' . ($nskey == '' ? '' : ':') . $nskey . '="' . $nsvalue . '"';
}
if (is_array($value)) {
$root = new SimpleXMLElement("<".$key."/>");
$root = new SimpleXMLElement('<' . $key . '/>');
self::fromArray($value, $root, $remove_header, $namespaces, false);
} else {
$root = new SimpleXMLElement("<".$key.">".self::escape($value)."</".$key.">");
$root = new SimpleXMLElement('<' . $key . '>' . self::escape($value ?? '') . '</' . $key . '>');
}
$dom = dom_import_simplexml($root)->ownerDocument;
@ -88,11 +90,11 @@ class XML
continue;
}
$element_parts = explode(":", $key);
$element_parts = explode(':', $key);
if ((count($element_parts) > 1) && isset($namespaces[$element_parts[0]])) {
$namespace = $namespaces[$element_parts[0]];
} elseif (isset($namespaces[""])) {
$namespace = $namespaces[""];
} elseif (isset($namespaces[''])) {
$namespace = $namespaces[''];
} else {
$namespace = null;
}
@ -102,13 +104,13 @@ class XML
$key = $element_parts[1];
}
if (substr($key, 0, 11) == "@attributes") {
if (substr($key, 0, 11) == '@attributes') {
if (!isset($element) || !is_array($value)) {
continue;
}
foreach ($value as $attr_key => $attr_value) {
$element_parts = explode(":", $attr_key);
$element_parts = explode(':', $attr_key);
if ((count($element_parts) > 1) && isset($namespaces[$element_parts[0]])) {
$namespace = $namespaces[$element_parts[0]];
} else {
@ -122,7 +124,7 @@ class XML
}
if (!is_array($value)) {
$element = $xml->addChild($key, self::escape($value), $namespace);
$element = $xml->addChild($key, self::escape($value ?? ''), $namespace);
} elseif (is_array($value)) {
$element = $xml->addChild($key, null, $namespace);
self::fromArray($value, $element, $remove_header, $namespaces, false);
@ -138,7 +140,7 @@ class XML
* @param string $elementname Name of the XML element of the target
* @return void
*/
public static function copy(&$source, &$target, $elementname)
public static function copy(&$source, &$target, string $elementname)
{
if (count($source->children()) == 0) {
$target->addChild($elementname, self::escape($source));
@ -153,20 +155,20 @@ class XML
/**
* Create an XML element
*
* @param \DOMDocument $doc XML root
* @param DOMDocument $doc XML root
* @param string $element XML element name
* @param string $value XML value
* @param array $attributes array containing the attributes
*
* @return \DOMElement XML element object
*/
public static function createElement(\DOMDocument $doc, $element, $value = "", $attributes = [])
public static function createElement(DOMDocument $doc, string $element, string $value = '', array $attributes = []): DOMElement
{
$element = $doc->createElement($element, self::escape($value));
foreach ($attributes as $key => $value) {
$attribute = $doc->createAttribute($key);
$attribute->value = self::escape($value);
$attribute->value = self::escape($value ?? '');
$element->appendChild($attribute);
}
return $element;
@ -175,22 +177,21 @@ class XML
/**
* Create an XML and append it to the parent object
*
* @param \DOMDocument $doc XML root
* @param object $parent parent object
* @param string $element XML element name
* @param string $value XML value
* @param array $attributes array containing the attributes
* @param DOMDocument $doc XML root
* @param DOMElement $parent parent object
* @param string $element XML element name
* @param string $value XML value
* @param array $attributes Array containing the attributes
* @return void
*/
public static function addElement(\DOMDocument $doc, $parent, $element, $value = "", $attributes = [])
public static function addElement(DOMDocument $doc, DOMElement &$parent, string $element, string $value = '', array $attributes = [])
{
$element = self::createElement($doc, $element, $value, $attributes);
$parent->appendChild($element);
}
/**
* Convert an XML document to a normalised, case-corrected array
* used by webfinger
* Convert an XML document to a normalised, case-corrected array used by webfinger
*
* @param object $xml_element The XML document
* @param integer $recursion_depth recursion counter for internal use - default 0
@ -198,11 +199,11 @@ class XML
*
* @return array | string The array from the xml element or the string
*/
public static function elementToArray($xml_element, &$recursion_depth = 0)
public static function elementToArray($xml_element, int &$recursion_depth = 0)
{
// If we're getting too deep, bail out
if ($recursion_depth > 512) {
return(null);
return null;
}
$xml_element_copy = '';
@ -217,7 +218,7 @@ class XML
if (is_array($xml_element)) {
$result_array = [];
if (count($xml_element) <= 0) {
return (trim(strval($xml_element_copy)));
return trim(strval($xml_element_copy));
}
foreach ($xml_element as $key => $value) {
@ -233,9 +234,9 @@ class XML
];
}
return ($result_array);
return $result_array;
} else {
return (trim(strval($xml_element)));
return trim(strval($xml_element));
}
}
@ -261,7 +262,7 @@ class XML
* @return array The parsed XML in an array form. Use print_r() to see the resulting array structure.
* @throws \Exception
*/
public static function toArray($contents, $namespaces = true, $get_attributes = 1, $priority = 'attribute')
public static function toArray(string $contents, bool $namespaces = true, int $get_attributes = 1, string $priority = 'attribute'): array
{
if (!$contents) {
return [];
@ -300,7 +301,7 @@ class XML
Logger::debug('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message);
}
libxml_clear_errors();
return;
return [];
}
//Initializations
@ -414,20 +415,20 @@ class XML
}
}
return($xml_array);
return $xml_array;
}
/**
* Delete a node in a XML object
*
* @param \DOMDocument $doc XML document
* @param DOMDocument $doc XML document
* @param string $node Node name
* @return void
*/
public static function deleteNode(\DOMDocument $doc, $node)
public static function deleteNode(DOMDocument $doc, string $node)
{
$xpath = new DOMXPath($doc);
$list = $xpath->query("//".$node);
$list = $xpath->query('//' . $node);
foreach ($list as $child) {
$child->parentNode->removeChild($child);
}
@ -436,9 +437,9 @@ class XML
/**
* Parse XML string
*
* @param string $s
* @param boolean $suppress_log
* @return Object
* @param string $s XML string to parse into object
* @param boolean $suppress_log Whether to supressing logging
* @return SimpleXMLElement|bool SimpleXMLElement or false on failure
*/
public static function parseString(string $s, bool $suppress_log = false)
{
@ -458,7 +459,15 @@ class XML
return $x;
}
public static function getFirstNodeValue(DOMXPath $xpath, $element, $context = null)
/**
* Gets first node value
*
* @param DOMXPath $xpath XPath object
* @param string $element Element name
* @param DOMNode $context Context object or NULL
* @return string XML node value or empty string on failure
*/
public static function getFirstNodeValue(DOMXPath $xpath, string $element, DOMNode $context = null)
{
$result = @$xpath->evaluate($element, $context);
if (!is_object($result)) {
@ -473,7 +482,15 @@ class XML
return $first_item->nodeValue;
}
public static function getFirstAttributes(DOMXPath $xpath, $element, $context = null)
/**
* Gets first attributes
*
* @param DOMXPath $xpath XPath object
* @param string $element Element name
* @param DOMNode $context Context object or NULL
* @return ???|bool First element's attributes field or false on failure
*/
public static function getFirstAttributes(DOMXPath $xpath, string $element, DOMNode $context = null)
{
$result = @$xpath->query($element, $context);
if (!is_object($result)) {
@ -488,9 +505,17 @@ class XML
return $first_item->attributes;
}
public static function getFirstValue($xpath, $search, $context)
/**
* Gets first node's value
*
* @param DOMXPath $xpath XPath object
* @param string $element Element name
* @param DOMNode $context Context object or NULL
* @return string First value or empty string on failure
*/
public static function getFirstValue(DOMXPath $xpath, string $element, DOMNode $context = null): string
{
$result = @$xpath->query($search, $context);
$result = @$xpath->query($element, $context);
if (!is_object($result)) {
return '';
}
@ -508,32 +533,31 @@ class XML
*
* @param string $str
* @return string Escaped text.
* @todo Move this generic method to Util\Strings and also rewrite all other findingd
*/
public static function escape($str)
public static function escape(string $str): string
{
$buffer = htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
$buffer = trim($buffer);
return $buffer;
return trim(htmlspecialchars($str, ENT_QUOTES, 'UTF-8'));
}
/**
* undo an escape
* Undo an escape
*
* @param string $s xml escaped text
* @return string unescaped text
* @todo Move this generic method to Util\Strings and also rewrite all other findingd
*/
public static function unescape($s)
public static function unescape(string $s): string
{
$ret = htmlspecialchars_decode($s, ENT_QUOTES);
return $ret;
return htmlspecialchars_decode($s, ENT_QUOTES);
}
/**
* apply escape() to all values of array $val, recursively
* Apply escape() to all values of array $val, recursively
*
* @param array $val
* @return array|string
* @param array|bool|string $val Value of type bool, array or string
* @return array|string Returns array if array provided or string in other cases
* @todo Move this generic method to Util\Strings
*/
public static function arrayEscape($val)
{