Merge remote-tracking branch 'upstream/develop' into server-detection

This commit is contained in:
Michael 2022-07-16 12:44:21 +00:00
commit 8b7cb5d9ef
328 changed files with 13242 additions and 10211 deletions

8
.gitignore vendored
View file

@ -90,3 +90,11 @@ venv/
#ignore avatar picture cache path #ignore avatar picture cache path
/avatar /avatar
#Ignore autotest results
autotest-results.xml
#ignore phpunit result cache
tests/.phpunit.result.cache
#ignore .php_cs (local copy)
.php_cs

View file

@ -9,7 +9,10 @@ depends_on:
- database_checks - database_checks
- messages.po_check - messages.po_check
platform: releaser/release # This prevents executing this pipeline at other servers than ci.friendi.ca # This prevents executing this pipeline at other servers than ci.friendi.ca
labels:
location: friendica
type: releaser
skip_clone: true skip_clone: true

View file

@ -7,7 +7,10 @@ depends_on:
- phpunit - phpunit
- code_standards_check - code_standards_check
platform: releaser/release # This prevents executing this pipeline at other servers than ci.friendi.ca # This prevents executing this pipeline at other servers than ci.friendi.ca
labels:
location: friendica
type: releaser
skip_clone: true skip_clone: true

Binary file not shown.

View file

@ -26,7 +26,7 @@
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php * This script was taken from http://php.net/manual/en/function.pcntl-fork.php
*/ */
if (php_sapi_name() !== 'cli') { if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden'); header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
exit(); exit();
} }
@ -45,13 +45,13 @@ $longopts = ['foreground'];
$options = getopt($shortopts, $longopts); $options = getopt($shortopts, $longopts);
// Ensure that daemon.php is executed from the base path of the installation // Ensure that daemon.php is executed from the base path of the installation
if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) { if (!file_exists('boot.php') && (sizeof($_SERVER['argv']) != 0)) {
$directory = dirname($_SERVER["argv"][0]); $directory = dirname($_SERVER['argv'][0]);
if (substr($directory, 0, 1) != "/") { if (substr($directory, 0, 1) != '/') {
$directory = $_SERVER["PWD"] . "/" . $directory; $directory = $_SERVER['PWD'] . '/' . $directory;
} }
$directory = realpath($directory . "/.."); $directory = realpath($directory . '/..');
chdir($directory); chdir($directory);
} }
@ -86,16 +86,16 @@ TXT
$pidfile = DI::config()->get('system', 'pidfile'); $pidfile = DI::config()->get('system', 'pidfile');
if (in_array("start", $_SERVER["argv"])) { if (in_array('start', $_SERVER['argv'])) {
$mode = "start"; $mode = 'start';
} }
if (in_array("stop", $_SERVER["argv"])) { if (in_array('stop', $_SERVER['argv'])) {
$mode = "stop"; $mode = 'stop';
} }
if (in_array("status", $_SERVER["argv"])) { if (in_array('status', $_SERVER['argv'])) {
$mode = "status"; $mode = 'status';
} }
$foreground = array_key_exists('f', $options) || array_key_exists('foreground', $options); $foreground = array_key_exists('f', $options) || array_key_exists('foreground', $options);
@ -104,7 +104,7 @@ if (!isset($mode)) {
die("Please use either 'start', 'stop' or 'status'.\n"); die("Please use either 'start', 'stop' or 'status'.\n");
} }
if (empty($_SERVER["argv"][0])) { if (empty($_SERVER['argv'][0])) {
die("Unexpected script behaviour. This message should never occur.\n"); die("Unexpected script behaviour. This message should never occur.\n");
} }
@ -114,12 +114,12 @@ if (is_readable($pidfile)) {
$pid = intval(file_get_contents($pidfile)); $pid = intval(file_get_contents($pidfile));
} }
if (empty($pid) && in_array($mode, ["stop", "status"])) { if (empty($pid) && in_array($mode, ['stop', 'status'])) {
DI::config()->set('system', 'worker_daemon_mode', false); DI::config()->set('system', 'worker_daemon_mode', false);
die("Pidfile wasn't found. Is the daemon running?\n"); die("Pidfile wasn't found. Is the daemon running?\n");
} }
if ($mode == "status") { if ($mode == 'status') {
if (posix_kill($pid, 0)) { if (posix_kill($pid, 0)) {
die("Daemon process $pid is running.\n"); die("Daemon process $pid is running.\n");
} }
@ -130,12 +130,12 @@ if ($mode == "status") {
die("Daemon process $pid isn't running.\n"); die("Daemon process $pid isn't running.\n");
} }
if ($mode == "stop") { if ($mode == 'stop') {
posix_kill($pid, SIGTERM); posix_kill($pid, SIGTERM);
unlink($pidfile); unlink($pidfile);
Logger::notice("Worker daemon process was killed", ["pid" => $pid]); Logger::notice('Worker daemon process was killed', ['pid' => $pid]);
DI::config()->set('system', 'worker_daemon_mode', false); DI::config()->set('system', 'worker_daemon_mode', false);
die("Worker daemon process $pid was killed.\n"); die("Worker daemon process $pid was killed.\n");
@ -145,7 +145,7 @@ if (!empty($pid) && posix_kill($pid, 0)) {
die("Daemon process $pid is already running.\n"); die("Daemon process $pid is already running.\n");
} }
Logger::notice('Starting worker daemon.', ["pid" => $pid]); Logger::notice('Starting worker daemon.', ['pid' => $pid]);
if (!$foreground) { if (!$foreground) {
echo "Starting worker daemon.\n"; echo "Starting worker daemon.\n";
@ -194,7 +194,7 @@ $last_cron = 0;
// Now running as a daemon. // Now running as a daemon.
while (true) { while (true) {
if (!$do_cron && ($last_cron + $wait_interval) < time()) { if (!$do_cron && ($last_cron + $wait_interval) < time()) {
Logger::info('Forcing cron worker call.', ["pid" => $pid]); Logger::info('Forcing cron worker call.', ['pid' => $pid]);
$do_cron = true; $do_cron = true;
} }
@ -214,7 +214,7 @@ while (true) {
} }
$start = time(); $start = time();
Logger::info("Sleeping", ["pid" => $pid, 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]); Logger::info('Sleeping', ['pid' => $pid, 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]);
do { do {
$seconds = (time() - $start); $seconds = (time() - $start);
@ -236,10 +236,10 @@ while (true) {
if ($timeout) { if ($timeout) {
$do_cron = true; $do_cron = true;
Logger::info("Woke up after $wait_interval seconds.", ["pid" => $pid, 'sleep' => $wait_interval]); Logger::info('Woke up after $wait_interval seconds.', ['pid' => $pid, 'sleep' => $wait_interval]);
} else { } else {
$do_cron = false; $do_cron = false;
Logger::info("Worker jobs are calling to be forked.", ["pid" => $pid]); Logger::info('Worker jobs are calling to be forked.', ['pid' => $pid]);
} }
} }

View file

@ -15,33 +15,34 @@
# - TEST_SELECTION= ... Specify which tests are used to run (based on the test-labeling) # - TEST_SELECTION= ... Specify which tests are used to run (based on the test-labeling)
# - XDEBUG_CONFIG= ... Set some XDEBUG specific environment settings for development # - XDEBUG_CONFIG= ... Set some XDEBUG specific environment settings for development
DATABASENAME=${MYSQL_DATABASE:-test} DATABASE_NAME=${FRIENDICA_MYSQL_DATABASE:-test}
DATABASEUSER=${MYSQL_USERNAME:-friendica} DATABASE_USER=${FRIENDICA_MYSQL_USERNAME:-friendica}
DATABASEHOST=${MYSQL_HOST:-localhost} DATABASE_HOST=${FRIENDICA_MYSQL_HOST:-localhost}
BASEDIR=$PWD DATABASE_PASSWORD=${FRIENDICA_MYSQL_PASSWORD:-friendica}
BASEDIR=${PWD}
DBCONFIGS="mysql mariadb" DBCONFIGS="mysql mariadb"
TESTS="REDIS MEMCACHE MEMCACHED APCU NODB" TESTS="REDIS MEMCACHE MEMCACHED APCU NODB"
export MYSQL_DATABASE="$DATABASENAME" export MYSQL_DATABASE="${DATABASE_NAME}"
export MYSQL_USERNAME="$DATABASEUSER" export MYSQL_USERNAME="${DATABASE_USER}"
export MYSQL_PASSWORD="friendica" export MYSQL_PASSWORD="${DATABASE_PASSWORD}"
if [ -z "$PHP_EXE" ]; then if [ -z "${PHP_EXE}" ]; then
PHP_EXE=php PHP_EXE=php
fi fi
PHP=$(which "$PHP_EXE") PHP=$(which "${PHP_EXE}")
# Use the Friendica internal composer # Use the Friendica internal composer
COMPOSER="$BASEDIR/bin/composer.phar" COMPOSER="${BASEDIR}/bin/composer.phar"
set -e set -e
_XDEBUG_CONFIG=$XDEBUG_CONFIG _XDEBUG_CONFIG=${XDEBUG_CONFIG}
unset XDEBUG_CONFIG unset XDEBUG_CONFIG
function show_syntax() { function show_syntax() {
echo -e "Syntax: ./autotest.sh [dbconfigname] [testfile]\n" >&2 echo -e "Syntax: ./autotest.sh [dbconfigname] [testfile]\n" >&2
echo -e "\t\"dbconfigname\" can be one of: $DBCONFIGS" >&2 echo -e "\t\"dbconfigname\" can be one of: ${DBCONFIGS}" >&2
echo -e "\t\"testfile\" is the name of a test file, for example lib/template.php" >&2 echo -e "\t\"testfile\" is the name of a test file, for example lib/template.php" >&2
echo -e "\nDatabase environment variables:\n" >&2 echo -e "\nDatabase environment variables:\n" >&2
echo -e "\t\"MYSQL_HOST\" Mysql Hostname (Default: localhost)" >&2 echo -e "\t\"MYSQL_HOST\" Mysql Hostname (Default: localhost)" >&2
@ -57,22 +58,22 @@ function show_syntax() {
echo -e "\nIf no arguments are specified, all tests will be run with all database configs" >&2 echo -e "\nIf no arguments are specified, all tests will be run with all database configs" >&2
} }
if [ -x "$PHP" ]; then if [ -x "${PHP}" ]; then
echo "Using PHP executable $PHP" echo "Using PHP executable ${PHP}"
else else
echo "Could not find PHP executable $PHP_EXE" >&2 echo "Could not find PHP executable ${PHP_EXE}" >&2
exit 3 exit 3
fi fi
echo "Installing depdendencies" echo "Installing depdendencies"
$PHP "$COMPOSER" install ${PHP} "$COMPOSER" install
PHPUNIT="$BASEDIR/vendor/bin/phpunit" PHPUNIT="${BASEDIR}/vendor/bin/phpunit"
if [ -x "$PHPUNIT" ]; then if [ -x "${PHPUNIT}" ]; then
echo "Using PHPUnit executable $PHPUNIT" echo "Using PHPUnit executable ${PHPUNIT}"
else else
echo "Could not find PHPUnit executable after composer $PHPUNIT" >&2 echo "Could not find PHPUnit executable after composer ${PHPUNIT}" >&2
exit 3 exit 3
fi fi
@ -83,8 +84,8 @@ fi
if [ "$1" ]; then if [ "$1" ]; then
FOUND=0 FOUND=0
for DBCONFIG in $DBCONFIGS; do for DBCONFIG in ${DBCONFIGS}; do
if [ "$1" = "$DBCONFIG" ]; then if [ "$1" = "${DBCONFIG}" ]; then
FOUND=1 FOUND=1
break break
fi fi
@ -103,13 +104,13 @@ fi
function cleanup_config() { function cleanup_config() {
if [ -n "$DOCKER_CONTAINER_ID" ]; then if [ -n "${DOCKER_CONTAINER_ID}" ]; then
echo "Kill the docker $DOCKER_CONTAINER_ID" echo "Kill the docker ${DOCKER_CONTAINER_ID}"
docker stop "$DOCKER_CONTAINER_ID" docker stop "${DOCKER_CONTAINER_ID}"
docker rm -f "$DOCKER_CONTAINER_ID" docker rm -f "${DOCKER_CONTAINER_ID}"
fi fi
cd "$BASEDIR" cd "${BASEDIR}"
# Restore existing config # Restore existing config
if [ -f config/local.config-autotest-backup.php ]; then if [ -f config/local.config-autotest-backup.php ]; then
@ -122,77 +123,77 @@ trap cleanup_config EXIT
function execute_tests() { function execute_tests() {
DB=$1 DB=$1
echo "Setup environment for $DB testing ..." echo "Setup environment for ${DB} testing ..."
# back to root folder # back to root folder
cd "$BASEDIR" cd "${BASEDIR}"
# backup current config # backup current config
if [ -f config/local.config.php ]; then if [ -f config/local.config.php ]; then
mv config/local.config.php config/local.config-autotest-backup.php mv config/local.config.php config/local.config-autotest-backup.php
fi fi
if [ -z "$NOINSTALL" ]; then if [ -z "${NOINSTALL}" ]; then
#drop database #drop database
if [ "$DB" == "mysql" ]; then if [ "${DB}" == "mysql" ]; then
if [ -n "$USEDOCKER" ]; then if [ -n "${USEDOCKER}" ]; then
echo "Fire up the mysql docker" echo "Fire up the mysql docker"
DOCKER_CONTAINER_ID=$(docker run \ DOCKER_CONTAINER_ID=$(docker run \
-e MYSQL_ROOT_PASSWORD=friendica \ -e MYSQL_ROOT_PASSWORD=friendica \
-e MYSQL_USER="$DATABASEUSER" \ -e MYSQL_USER="${DATABASE_USER}" \
-e MYSQL_PASSWORD=friendica \ -e MYSQL_PASSWORD=friendica \
-e MYSQL_DATABASE="$DATABASENAME" \ -e MYSQL_DATABASE="${DATABASE_NAME}" \
-d mysql) -d mysql)
DATABASEHOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "$DOCKER_CONTAINER_ID") DATABASE_HOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "${DOCKER_CONTAINER_ID}")
else else
if [ -z "$DRONE" ]; then # no need to drop the DB when we are on CI if [ -z "${DRONE}" ]; then # no need to drop the DB when we are on CI
if [ "mysql" != "$(mysql --version | grep -o mysql)" ]; then if [ "mysql" != "$(mysql --version | grep -o mysql)" ]; then
echo "Your mysql binary is not provided by mysql" echo "Your mysql binary is not provided by mysql"
echo "To use the docker container set the USEDOCKER environment variable" echo "To use the docker container set the USEDOCKER environment variable"
exit 3 exit 3
fi fi
mysql -u "$DATABASEUSER" -pfriendica -e "DROP DATABASE IF EXISTS $DATABASENAME" -h $DATABASEHOST || true mysql -u "${DATABASE_USER}" -pfriendica -e "DROP DATABASE IF EXISTS ${DATABASE_NAME}" -h ${DATABASE_HOST} || true
mysql -u "$DATABASEUSER" -pfriendica -e "CREATE DATABASE $DATABASENAME DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h $DATABASEHOST mysql -u "${DATABASE_USER}" -pfriendica -e "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h ${DATABASE_HOST}
else else
DATABASEHOST=mysql DATABASE_HOST=mysql
fi fi
fi fi
echo "Waiting for MySQL $DATABASEHOST initialization..." echo "Waiting for MySQL ${DATABASE_HOST} initialization..."
if ! bin/wait-for-connection $DATABASEHOST 3306 300; then if ! bin/wait-for-connection ${DATABASE_HOST} 3306 300; then
echo "[ERROR] Waited 300 seconds, no response" >&2 echo "[ERROR] Waited 300 seconds, no response" >&2
exit 1 exit 1
fi fi
echo "MySQL is up." echo "MySQL is up."
fi fi
if [ "$DB" == "mariadb" ]; then if [ "${DB}" == "mariadb" ]; then
if [ -n "$USEDOCKER" ]; then if [ -n "${USEDOCKER}" ]; then
echo "Fire up the mariadb docker" echo "Fire up the mariadb docker"
DOCKER_CONTAINER_ID=$(docker run \ DOCKER_CONTAINER_ID=$(docker run \
-e MYSQL_ROOT_PASSWORD=friendica \ -e MYSQL_ROOT_PASSWORD=friendica \
-e MYSQL_USER="$DATABASEUSER" \ -e MYSQL_USER="${DATABASE_USER}" \
-e MYSQL_PASSWORD=friendica \ -e MYSQL_PASSWORD=friendica \
-e MYSQL_DATABASE="$DATABASENAME" \ -e MYSQL_DATABASE="${DATABASE_NAME}" \
-d mariadb) -d mariadb)
DATABASEHOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "$DOCKER_CONTAINER_ID") DATABASE_HOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "${DOCKER_CONTAINER_ID}")
else else
if [ -z "$DRONE" ]; then # no need to drop the DB when we are on CI if [ -z "${DRONE}" ]; then # no need to drop the DB when we are on CI
if [ "MariaDB" != "$(mysql --version | grep -o MariaDB)" ]; then if [ "MariaDB" != "$(mysql --version | grep -o MariaDB)" ]; then
echo "Your mysql binary is not provided by mysql" echo "Your mysql binary is not provided by mysql"
echo "To use the docker container set the USEDOCKER environment variable" echo "To use the docker container set the USEDOCKER environment variable"
exit 3 exit 3
fi fi
mysql -u "$DATABASEUSER" -pfriendica -e "DROP DATABASE IF EXISTS $DATABASENAME" -h $DATABASEHOST || true mysql -u "${DATABASE_USER}" -pfriendica -e "DROP DATABASE IF EXISTS ${DATABASE_NAME}" -h ${DATABASE_HOST} || true
mysql -u "$DATABASEUSER" -pfriendica -e "CREATE DATABASE $DATABASENAME DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h $DATABASEHOST mysql -u "${DATABASE_USER}" -pfriendica -e "CREATE DATABASE ${DATABASE_NAME} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" -h ${DATABASE_HOST}
else else
DATABASEHOST=mariadb DATABASE_HOST=mariadb
fi fi
fi fi
echo "Waiting for MariaDB $DATABASEHOST initialization..." echo "Waiting for MariaDB ${DATABASE_HOST} initialization..."
if ! bin/wait-for-connection $DATABASEHOST 3306 300; then if ! bin/wait-for-connection ${DATABASE_HOST} 3306 300; then
echo "[ERROR] Waited 300 seconds, no response" >&2 echo "[ERROR] Waited 300 seconds, no response" >&2
exit 1 exit 1
fi fi
@ -200,28 +201,28 @@ function execute_tests() {
echo "MariaDB is up." echo "MariaDB is up."
fi fi
if [ -n "$USEDOCKER" ]; then if [ -n "${USEDOCKER}" ]; then
echo "Initialize database..." echo "Initialize database..."
docker exec $DOCKER_CONTAINER_ID mysql -u root -pfriendica -e 'CREATE DATABASE IF NOT EXISTS $DATABASENAME;' docker exec ${DOCKER_CONTAINER_ID} mysql -u root -pfriendica -e "CREATE DATABASE IF NOT EXISTS ${DATABASE_NAME};"
fi fi
export MYSQL_HOST="$DATABASEHOST" export MYSQL_HOST="${DATABASE_HOST}"
#call installer #call installer
echo "Installing Friendica..." echo "Installing Friendica..."
"$PHP" ./bin/console.php autoinstall --dbuser="$DATABASEUSER" --dbpass=friendica --dbdata="$DATABASENAME" --dbhost="$DATABASEHOST" --url=https://friendica.local --admin=admin@friendica.local "${PHP}" ./bin/console.php autoinstall --dbuser="${DATABASE_USER}" --dbpass=friendica --dbdata="${DATABASE_NAME}" --dbhost="${DATABASE_HOST}" --url=https://friendica.local --admin=admin@friendica.local
fi fi
#test execution #test execution
echo "Testing..." echo "Testing..."
rm -fr "coverage-html" rm -fr "coverage-html"
mkdir "coverage-html" mkdir "coverage-html"
if [[ "$_XDEBUG_CONFIG" ]]; then if [[ "${_XDEBUG_CONFIG}" ]]; then
export XDEBUG_CONFIG=$_XDEBUG_CONFIG export XDEBUG_CONFIG=${_XDEBUG_CONFIG}
fi fi
COVER='' COVER=''
if [ -z "$NOCOVERAGE" ]; then if [ -z "${NOCOVERAGE}" ]; then
COVER="--coverage-clover tests/autotest-clover.xml" COVER="--coverage-clover tests/autotest-clover.xml"
else else
echo "No coverage" echo "No coverage"
@ -229,51 +230,51 @@ function execute_tests() {
# per default, there is no cache installed # per default, there is no cache installed
GROUP='--exclude-group REDIS,MEMCACHE,MEMCACHED,APCU' GROUP='--exclude-group REDIS,MEMCACHE,MEMCACHED,APCU'
if [ "$TEST_SELECTION" == "REDIS" ]; then if [ "${TEST_SELECTION}" == "REDIS" ]; then
GROUP="--group REDIS" GROUP="--group REDIS"
fi fi
if [ "$TEST_SELECTION" == "MEMCACHE" ]; then if [ "${TEST_SELECTION}" == "MEMCACHE" ]; then
GROUP="--group MEMCACHE" GROUP="--group MEMCACHE"
fi fi
if [ "$TEST_SELECTION" == "MEMCACHED" ]; then if [ "${TEST_SELECTION}" == "MEMCACHED" ]; then
GROUP="--group MEMCACHED" GROUP="--group MEMCACHED"
fi fi
if [ "$TEST_SELECTION" == "APCU" ]; then if [ "${TEST_SELECTION}" == "APCU" ]; then
GROUP="--group APCU" GROUP="--group APCU"
fi fi
if [ "$TEST_SELECTION" == "NODB" ]; then if [ "${TEST_SELECTION}" == "NODB" ]; then
GROUP="--exclude-group DB,SLOWDB" GROUP="--exclude-group DB,SLOWDB"
fi fi
INPUT="$BASEDIR/tests" INPUT="${BASEDIR}/tests"
if [ -n "$2" ]; then if [ -n "$2" ]; then
INPUT="$INPUT/$2" INPUT="${INPUT}/$2"
fi fi
echo "${PHPUNIT[@]}" --configuration tests/phpunit.xml $GROUP $COVER --log-junit "autotest-results.xml" "$INPUT" "$3" echo "${PHPUNIT[@]}" --configuration tests/phpunit.xml ${GROUP} ${COVER} --log-junit "autotest-results.xml" "${INPUT}" "$3"
"${PHPUNIT[@]}" --configuration tests/phpunit.xml $GROUP $COVER --log-junit "autotest-results.xml" "$INPUT" "$3" "${PHPUNIT[@]}" --configuration tests/phpunit.xml ${GROUP} ${COVER} --log-junit "autotest-results.xml" "${INPUT}" "$3"
RESULT=$? RESULT=$?
if [ -n "$DOCKER_CONTAINER_ID" ]; then if [ -n "${DOCKER_CONTAINER_ID}" ]; then
echo "Kill the docker $DOCKER_CONTAINER_ID" echo "Kill the docker ${DOCKER_CONTAINER_ID}"
docker stop $DOCKER_CONTAINER_ID docker stop ${DOCKER_CONTAINER_ID}
docker rm -f $DOCKER_CONTAINER_ID docker rm -f ${DOCKER_CONTAINER_ID}
unset $DOCKER_CONTAINER_ID unset ${DOCKER_CONTAINER_ID}
fi fi
} }
# #
# Start the test execution # Start the test execution
# #
if [ -z "$1" ] && [ -n "$TEST_SELECTION" ]; then if [ -z "$1" ] && [ -n "${TEST_SELECTION}" ]; then
# run all known database configs # run all known database configs
for DBCONFIG in $DBCONFIGS; do for DBCONFIG in ${DBCONFIGS}; do
execute_tests "$DBCONFIG" execute_tests "${DBCONFIG}"
done done
else else
FILENAME="$2" FILENAME="$2"
if [ -n "$2" ] && [ ! -f "tests/$FILENAME" ] && [ "${FILENAME:0:2}" != "--" ]; then if [ -n "$2" ] && [ ! -f "tests/${FILENAME}" ] && [ "${FILENAME:0:2}" != "--" ]; then
FILENAME="../$FILENAME" FILENAME="../${FILENAME}"
fi fi
execute_tests "$1" "$FILENAME" "$3" execute_tests "$1" "${FILENAME}" "$3"
fi fi

View file

@ -87,8 +87,8 @@ define('PRIORITIES', [PRIORITY_CRITICAL, PRIORITY_HIGH, PRIORITY_MEDIUM, PRIORIT
/* @}*/ /* @}*/
// Normally this constant is defined - but not if "pcntl" isn't installed // Normally this constant is defined - but not if "pcntl" isn't installed
if (!defined("SIGTERM")) { if (!defined('SIGTERM')) {
define("SIGTERM", 15); define('SIGTERM', 15);
} }
/** /**
@ -117,6 +117,7 @@ function local_user()
if (!empty($_SESSION['authenticated']) && !empty($_SESSION['uid'])) { if (!empty($_SESSION['authenticated']) && !empty($_SESSION['uid'])) {
return intval($_SESSION['uid']); return intval($_SESSION['uid']);
} }
return false; return false;
} }
@ -169,7 +170,7 @@ function remote_user()
* *
* @param string $s - Text of notice * @param string $s - Text of notice
*/ */
function notice($s) function notice(string $s)
{ {
if (empty($_SESSION)) { if (empty($_SESSION)) {
return; return;
@ -189,7 +190,7 @@ function notice($s)
* *
* @param string $s - Text of notice * @param string $s - Text of notice
*/ */
function info($s) function info(string $s)
{ {
if (empty($_SESSION)) { if (empty($_SESSION)) {
return; return;

204
composer.lock generated
View file

@ -545,12 +545,12 @@
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-4": {
"DivineOmega\\PasswordExposed\\": "src/"
},
"files": [ "files": [
"src/PasswordExposedFunction.php" "src/PasswordExposedFunction.php"
] ],
"psr-4": {
"DivineOmega\\PasswordExposed\\": "src/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -587,12 +587,12 @@
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-0": {
"HTMLPurifier": "library/"
},
"files": [ "files": [
"library/HTMLPurifier.composer.php" "library/HTMLPurifier.composer.php"
], ],
"psr-0": {
"HTMLPurifier": "library/"
},
"exclude-from-classmap": [ "exclude-from-classmap": [
"/library/HTMLPurifier/Language/" "/library/HTMLPurifier/Language/"
] ]
@ -839,24 +839,24 @@
}, },
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
"version": "6.5.5", "version": "6.5.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/guzzle.git", "url": "https://github.com/guzzle/guzzle.git",
"reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a52f0440530b54fa079ce76e8c5d196a42cad981",
"reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-json": "*", "ext-json": "*",
"guzzlehttp/promises": "^1.0", "guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.6.1", "guzzlehttp/psr7": "^1.9",
"php": ">=5.5", "php": ">=5.5",
"symfony/polyfill-intl-idn": "^1.17.0" "symfony/polyfill-intl-idn": "^1.17"
}, },
"require-dev": { "require-dev": {
"ext-curl": "*", "ext-curl": "*",
@ -873,22 +873,52 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
},
"files": [ "files": [
"src/functions_include.php" "src/functions_include.php"
] ],
"psr-4": {
"GuzzleHttp\\": "src/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
"authors": [ "authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{ {
"name": "Michael Dowling", "name": "Michael Dowling",
"email": "mtdowling@gmail.com", "email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling" "homepage": "https://github.com/mtdowling"
},
{
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
} }
], ],
"description": "Guzzle is a PHP HTTP client library", "description": "Guzzle is a PHP HTTP client library",
@ -902,7 +932,21 @@
"rest", "rest",
"web service" "web service"
], ],
"time": "2020-06-16T21:01:06+00:00" "funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
"type": "tidelift"
}
],
"time": "2022-06-20T22:16:07+00:00"
}, },
{ {
"name": "guzzlehttp/promises", "name": "guzzlehttp/promises",
@ -931,12 +975,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [ "files": [
"src/functions_include.php" "src/functions_include.php"
] ],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -986,16 +1030,16 @@
}, },
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "1.8.3", "version": "1.9.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/psr7.git", "url": "https://github.com/guzzle/psr7.git",
"reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", "url": "https://api.github.com/repos/guzzle/psr7/zipball/e98e3e6d4f86621a9b75f623996e6bbdeb4b9318",
"reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", "reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1016,16 +1060,16 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.7-dev" "dev-master": "1.9-dev"
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [ "files": [
"src/functions_include.php" "src/functions_include.php"
] ],
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -1088,7 +1132,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-10-05T13:56:00+00:00" "time": "2022-06-20T21:43:03+00:00"
}, },
{ {
"name": "league/html-to-markdown", "name": "league/html-to-markdown",
@ -1471,12 +1515,12 @@
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
"classmap": [
"Mobile_Detect.php"
],
"psr-0": { "psr-0": {
"Detection": "namespaced/" "Detection": "namespaced/"
} },
"classmap": [
"Mobile_Detect.php"
]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -1611,12 +1655,12 @@
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [ "files": [
"src/functions.php" "src/functions.php"
] ],
"psr-4": {
"FastRoute\\": "src/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -3803,16 +3847,16 @@
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.23.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "65bd267525e82759e7d8c4e8ceea44f398838e65" "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/65bd267525e82759e7d8c4e8ceea44f398838e65", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"reference": "65bd267525e82759e7d8c4e8ceea44f398838e65", "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3826,7 +3870,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3834,12 +3878,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
] ],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -3883,20 +3927,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-05-27T09:27:20+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.23.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" "reference": "219aa369ceff116e673852dce47c3a41794c14bd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "reference": "219aa369ceff116e673852dce47c3a41794c14bd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3908,7 +3952,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -3916,12 +3960,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
], ],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"classmap": [ "classmap": [
"Resources/stubs" "Resources/stubs"
] ]
@ -3964,7 +4008,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-19T12:13:01+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-php56", "name": "symfony/polyfill-php56",
@ -4033,16 +4077,16 @@
}, },
{ {
"name": "symfony/polyfill-php72", "name": "symfony/polyfill-php72",
"version": "v1.23.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php72.git", "url": "https://github.com/symfony/polyfill-php72.git",
"reference": "9a142215a36a3888e30d0a9eeea9766764e96976" "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2",
"reference": "9a142215a36a3888e30d0a9eeea9766764e96976", "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4051,7 +4095,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -4059,12 +4103,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
] ],
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -4102,7 +4146,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-05-27T09:17:38+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "ua-parser/uap-php", "name": "ua-parser/uap-php",
@ -4865,12 +4909,12 @@
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
},
"files": [ "files": [
"src/DeepCopy/deep_copy.php" "src/DeepCopy/deep_copy.php"
] ],
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
@ -5618,11 +5662,11 @@
} }
}, },
"autoload": { "autoload": {
"classmap": [
"src/"
],
"files": [ "files": [
"src/Framework/Assert/Functions.php" "src/Framework/Assert/Functions.php"
],
"classmap": [
"src/"
] ]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
@ -6586,12 +6630,12 @@
} }
}, },
"autoload": { "autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [ "files": [
"bootstrap.php" "bootstrap.php"
] ],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2022.09-dev (Giant Rhubarb) -- Friendica 2022.09-dev (Giant Rhubarb)
-- DB_UPDATE_VERSION 1469 -- DB_UPDATE_VERSION 1473
-- ------------------------------------------ -- ------------------------------------------
@ -297,6 +297,7 @@ CREATE TABLE IF NOT EXISTS `2fa_trusted_browser` (
`cookie_hash` varchar(80) NOT NULL COMMENT 'Trusted cookie hash', `cookie_hash` varchar(80) NOT NULL COMMENT 'Trusted cookie hash',
`uid` mediumint unsigned NOT NULL COMMENT 'User ID', `uid` mediumint unsigned NOT NULL COMMENT 'User ID',
`user_agent` text COMMENT 'User agent string', `user_agent` text COMMENT 'User agent string',
`trusted` boolean NOT NULL DEFAULT '1' COMMENT 'Whenever this browser should be trusted or not',
`created` datetime NOT NULL COMMENT 'Datetime the trusted browser was recorded', `created` datetime NOT NULL COMMENT 'Datetime the trusted browser was recorded',
`last_used` datetime COMMENT 'Datetime the trusted browser was last used', `last_used` datetime COMMENT 'Datetime the trusted browser was last used',
PRIMARY KEY(`cookie_hash`), PRIMARY KEY(`cookie_hash`),
@ -1216,13 +1217,13 @@ CREATE TABLE IF NOT EXISTS `post-link` (
CREATE TABLE IF NOT EXISTS `post-media` ( CREATE TABLE IF NOT EXISTS `post-media` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`url` varbinary(511) NOT NULL COMMENT 'Media URL', `url` varbinary(1024) NOT NULL COMMENT 'Media URL',
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Media type', `type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Media type',
`mimetype` varchar(60) COMMENT '', `mimetype` varchar(60) COMMENT '',
`height` smallint unsigned COMMENT 'Height of the media', `height` smallint unsigned COMMENT 'Height of the media',
`width` smallint unsigned COMMENT 'Width of the media', `width` smallint unsigned COMMENT 'Width of the media',
`size` int unsigned COMMENT 'Media size', `size` bigint unsigned COMMENT 'Media size',
`preview` varbinary(255) COMMENT 'Preview URL', `preview` varbinary(512) COMMENT 'Preview URL',
`preview-height` smallint unsigned COMMENT 'Height of the preview picture', `preview-height` smallint unsigned COMMENT 'Height of the preview picture',
`preview-width` smallint unsigned COMMENT 'Width of the preview picture', `preview-width` smallint unsigned COMMENT 'Width of the preview picture',
`description` text COMMENT '', `description` text COMMENT '',
@ -1234,7 +1235,7 @@ CREATE TABLE IF NOT EXISTS `post-media` (
`publisher-name` varchar(255) COMMENT 'Name of the publisher of the media', `publisher-name` varchar(255) COMMENT 'Name of the publisher of the media',
`publisher-image` varbinary(255) COMMENT 'Image of the publisher of the media', `publisher-image` varbinary(255) COMMENT 'Image of the publisher of the media',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
UNIQUE INDEX `uri-id-url` (`uri-id`,`url`), UNIQUE INDEX `uri-id-url` (`uri-id`,`url`(512)),
INDEX `uri-id-id` (`uri-id`,`id`), INDEX `uri-id-id` (`uri-id`,`id`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media';

View file

@ -82,6 +82,7 @@ General
../settings ../settings
--------- ---------
* o - Account * o - Account
* 2 - Two-factor authentication
* p - Profiles * p - Profiles
* t - Additional features * t - Additional features
* w - Social Networks * w - Social Networks

View file

@ -6,13 +6,14 @@ Two-factor authentication trusted browsers
Fields Fields
------ ------
| Field | Description | Type | Null | Key | Default | Extra | | Field | Description | Type | Null | Key | Default | Extra |
| ----------- | ------------------------------------------ | ------------------ | ---- | --- | ------- | ----- | | ----------- | ---------------------------------------------- | ------------------ | ---- | --- | ------- | ----- |
| cookie_hash | Trusted cookie hash | varchar(80) | NO | PRI | NULL | | | cookie_hash | Trusted cookie hash | varchar(80) | NO | PRI | NULL | |
| uid | User ID | mediumint unsigned | NO | | NULL | | | uid | User ID | mediumint unsigned | NO | | NULL | |
| user_agent | User agent string | text | YES | | NULL | | | user_agent | User agent string | text | YES | | NULL | |
| created | Datetime the trusted browser was recorded | datetime | NO | | NULL | | | trusted | Whenever this browser should be trusted or not | boolean | NO | | 1 | |
| last_used | Datetime the trusted browser was last used | datetime | YES | | NULL | | | created | Datetime the trusted browser was recorded | datetime | NO | | NULL | |
| last_used | Datetime the trusted browser was last used | datetime | YES | | NULL | |
Indexes Indexes
------------ ------------

View file

@ -10,13 +10,13 @@ Fields
| --------------- | --------------------------------------------------------- | ----------------- | ---- | --- | ------- | -------------- | | --------------- | --------------------------------------------------------- | ----------------- | ---- | --- | ------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | | id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | | NULL | | | uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | | NULL | |
| url | Media URL | varbinary(511) | NO | | NULL | | | url | Media URL | varbinary(1024) | NO | | NULL | |
| type | Media type | tinyint unsigned | NO | | 0 | | | type | Media type | tinyint unsigned | NO | | 0 | |
| mimetype | | varchar(60) | YES | | NULL | | | mimetype | | varchar(60) | YES | | NULL | |
| height | Height of the media | smallint unsigned | YES | | NULL | | | height | Height of the media | smallint unsigned | YES | | NULL | |
| width | Width of the media | smallint unsigned | YES | | NULL | | | width | Width of the media | smallint unsigned | YES | | NULL | |
| size | Media size | int unsigned | YES | | NULL | | | size | Media size | bigint unsigned | YES | | NULL | |
| preview | Preview URL | varbinary(255) | YES | | NULL | | | preview | Preview URL | varbinary(512) | YES | | NULL | |
| preview-height | Height of the preview picture | smallint unsigned | YES | | NULL | | | preview-height | Height of the preview picture | smallint unsigned | YES | | NULL | |
| preview-width | Width of the preview picture | smallint unsigned | YES | | NULL | | | preview-width | Width of the preview picture | smallint unsigned | YES | | NULL | |
| description | | text | YES | | NULL | | | description | | text | YES | | NULL | |
@ -31,11 +31,11 @@ Fields
Indexes Indexes
------------ ------------
| Name | Fields | | Name | Fields |
| ---------- | ------------------- | | ---------- | ------------------------ |
| PRIMARY | id | | PRIMARY | id |
| uri-id-url | UNIQUE, uri-id, url | | uri-id-url | UNIQUE, uri-id, url(512) |
| uri-id-id | uri-id, id | | uri-id-id | uri-id, id |
Foreign Keys Foreign Keys
------------ ------------

View file

@ -302,7 +302,7 @@ function display_content(App $a, $update = false, $update_uid = 0)
// Preparing the meta header // Preparing the meta header
$description = trim(BBCode::toPlaintext($item['body'])); $description = trim(BBCode::toPlaintext($item['body']));
$title = trim(BBCode::toPlaintext($item['title'])); $title = trim(BBCode::toPlaintext($item['title'] ?? ''));
$author_name = $item['author-name']; $author_name = $item['author-name'];
$image = DI::baseUrl()->remove($item['author-avatar']); $image = DI::baseUrl()->remove($item['author-avatar']);

View file

@ -83,7 +83,7 @@ function editpost_content(App $a)
Hook::callAll('jot_tool', $jotplugins); Hook::callAll('jot_tool', $jotplugins);
$tpl = Renderer::getMarkupTemplate("jot.tpl"); $tpl = Renderer::getMarkupTemplate('jot.tpl');
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [
'$is_edit' => true, '$is_edit' => true,
'$return_path' => '/display/' . $item['guid'], '$return_path' => '/display/' . $item['guid'],

View file

@ -47,7 +47,7 @@ function fbrowser_content(App $a)
} }
// Needed to match the correct template in a module that uses a different theme than the user/site/default // Needed to match the correct template in a module that uses a different theme than the user/site/default
$theme = Strings::sanitizeFilePathItem($_GET['theme'] ?? null); $theme = Strings::sanitizeFilePathItem($_GET['theme'] ?? '');
if ($theme && is_file("view/theme/$theme/config.php")) { if ($theme && is_file("view/theme/$theme/config.php")) {
$a->setCurrentTheme($theme); $a->setCurrentTheme($theme);
} }

View file

@ -439,8 +439,13 @@ function item_post(App $a) {
// Ensure to only modify attachments that you own // Ensure to only modify attachments that you own
$srch = '<' . intval($contact_id) . '>'; $srch = '<' . intval($contact_id) . '>';
$condition = ['allow_cid' => $srch, 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '', $condition = [
'id' => $attach]; 'allow_cid' => $srch,
'allow_gid' => '',
'deny_cid' => '',
'deny_gid' => '',
'id' => $attach,
];
if (!Attach::exists($condition)) { if (!Attach::exists($condition)) {
continue; continue;
} }
@ -520,7 +525,7 @@ function item_post(App $a) {
$origin = $_REQUEST['origin']; $origin = $_REQUEST['origin'];
} }
$uri = Item::newURI($api_source ? $profile_uid : $uid, $guid); $uri = Item::newURI($guid);
// Fallback so that we alway have a parent uri // Fallback so that we alway have a parent uri
if (!$thr_parent_uri || !$toplevel_item_id) { if (!$thr_parent_uri || !$toplevel_item_id) {

View file

@ -187,7 +187,7 @@ function photos_post(App $a)
} }
if (DI::args()->getArgc() > 3 && DI::args()->getArgv()[2] === 'album') { if (DI::args()->getArgc() > 3 && DI::args()->getArgv()[2] === 'album') {
if (!Strings::isHex(DI::args()->getArgv()[3])) { if (!Strings::isHex(DI::args()->getArgv()[3] ?? '')) {
DI::baseUrl()->redirect('photos/' . $user['nickname'] . '/album'); DI::baseUrl()->redirect('photos/' . $user['nickname'] . '/album');
} }
$album = hex2bin(DI::args()->getArgv()[3]); $album = hex2bin(DI::args()->getArgv()[3]);
@ -360,7 +360,7 @@ function photos_post(App $a)
if (DBA::isResult($photos) && !$item_id) { if (DBA::isResult($photos) && !$item_id) {
// Create item container // Create item container
$title = ''; $title = '';
$uri = Item::newURI($page_owner_uid); $uri = Item::newURI();
$arr = []; $arr = [];
$arr['guid'] = System::createUUID(); $arr['guid'] = System::createUUID();
@ -524,7 +524,7 @@ function photos_post(App $a)
if (count($taginfo)) { if (count($taginfo)) {
foreach ($taginfo as $tagged) { foreach ($taginfo as $tagged) {
$uri = Item::newURI($page_owner_uid); $uri = Item::newURI();
$arr = []; $arr = [];
$arr['guid'] = System::createUUID(); $arr['guid'] = System::createUUID();
@ -728,7 +728,7 @@ function photos_post(App $a)
$smallest = 2; $smallest = 2;
} }
$uri = Item::newURI($page_owner_uid); $uri = Item::newURI();
// Create item container // Create item container
$lat = $lon = null; $lat = $lon = null;
@ -892,7 +892,7 @@ function photos_content(App $a)
return; return;
} }
$selname = Strings::isHex($datum) ? hex2bin($datum) : ''; $selname = (!is_null($datum) && Strings::isHex($datum)) ? hex2bin($datum) : '';
$albumselect = ''; $albumselect = '';
@ -954,7 +954,7 @@ function photos_content(App $a)
// Display a single photo album // Display a single photo album
if ($datatype === 'album') { if ($datatype === 'album') {
// if $datum is not a valid hex, redirect to the default page // if $datum is not a valid hex, redirect to the default page
if (!Strings::isHex($datum)) { if (is_null($datum) || !Strings::isHex($datum)) {
DI::baseUrl()->redirect('photos/' . $user['nickname']. '/album'); DI::baseUrl()->redirect('photos/' . $user['nickname']. '/album');
} }
$album = hex2bin($datum); $album = hex2bin($datum);
@ -977,7 +977,7 @@ function photos_content(App $a)
/// @TODO I have seen this many times, maybe generalize it script-wide and encapsulate it? /// @TODO I have seen this many times, maybe generalize it script-wide and encapsulate it?
$order_field = $_GET['order'] ?? ''; $order_field = $_GET['order'] ?? '';
if ($order_field === 'posted') { if ($order_field === 'created') {
$order = 'ASC'; $order = 'ASC';
} else { } else {
$order = 'DESC'; $order = 'DESC';
@ -1031,10 +1031,10 @@ function photos_content(App $a)
$drop = [DI::l10n()->t('Drop Album'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '/drop']; $drop = [DI::l10n()->t('Drop Album'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '/drop'];
} }
if ($order_field === 'posted') { if ($order_field === 'created') {
$order = [DI::l10n()->t('Show Newest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album), 'oldest']; $order = [DI::l10n()->t('Show Newest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album), 'oldest'];
} else { } else {
$order = [DI::l10n()->t('Show Oldest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '?order=posted', 'newest']; $order = [DI::l10n()->t('Show Oldest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '?order=created', 'newest'];
} }
$photos = []; $photos = [];
@ -1054,7 +1054,7 @@ function photos_content(App $a)
'id' => $rr['id'], 'id' => $rr['id'],
'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2,4), 'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2,4),
'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id'] 'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id']
. ($order_field === 'posted' ? '?order=posted' : ''), . ($order_field === 'created' ? '?order=created' : ''),
'title' => DI::l10n()->t('View Photo'), 'title' => DI::l10n()->t('View Photo'),
'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext, 'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext,
'alt' => $imgalt_e, 'alt' => $imgalt_e,
@ -1122,7 +1122,7 @@ function photos_content(App $a)
if ($cmd === 'view' && !DI::config()->get('system', 'no_count', false)) { if ($cmd === 'view' && !DI::config()->get('system', 'no_count', false)) {
$order_field = $_GET['order'] ?? ''; $order_field = $_GET['order'] ?? '';
if ($order_field === 'posted') { if ($order_field === 'created') {
$params = ['order' => [$order_field]]; $params = ['order' => [$order_field]];
} elseif (!empty($order_field)) { } elseif (!empty($order_field)) {
$params = ['order' => [$order_field => true]]; $params = ['order' => [$order_field => true]];
@ -1150,10 +1150,10 @@ function photos_content(App $a)
} }
if (!is_null($prv)) { if (!is_null($prv)) {
$prevlink = 'photos/' . $user['nickname'] . '/image/' . $prvnxt[$prv]['resource-id'] . ($order_field === 'posted' ? '?order=posted' : ''); $prevlink = 'photos/' . $user['nickname'] . '/image/' . $prvnxt[$prv]['resource-id'] . ($order_field === 'created' ? '?order=created' : '');
} }
if (!is_null($nxt)) { if (!is_null($nxt)) {
$nextlink = 'photos/' . $user['nickname'] . '/image/' . $prvnxt[$nxt]['resource-id'] . ($order_field === 'posted' ? '?order=posted' : ''); $nextlink = 'photos/' . $user['nickname'] . '/image/' . $prvnxt[$nxt]['resource-id'] . ($order_field === 'created' ? '?order=created' : '');
} }
$tpl = Renderer::getMarkupTemplate('photo_edit_head.tpl'); $tpl = Renderer::getMarkupTemplate('photo_edit_head.tpl');

View file

@ -73,7 +73,7 @@ function tagger_content(App $a) {
return; return;
} }
$uri = Item::newURI($owner_uid); $uri = Item::newURI();
$xterm = XML::escape($term); $xterm = XML::escape($term);
$post_type = (($item['resource-id']) ? DI::l10n()->t('photo') : DI::l10n()->t('status')); $post_type = (($item['resource-id']) ? DI::l10n()->t('photo') : DI::l10n()->t('status'));
$targettype = (($item['resource-id']) ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE ); $targettype = (($item['resource-id']) ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE );

View file

@ -157,9 +157,9 @@ function wall_upload_post(App $a, $desktopmode = true)
" - size: " . $filesize . " - type: " . $filetype); " - size: " . $filesize . " - type: " . $filetype);
$imagedata = @file_get_contents($src); $imagedata = @file_get_contents($src);
$Image = new Image($imagedata, $filetype); $image = new Image($imagedata, $filetype);
if (!$Image->isValid()) { if (!$image->isValid()) {
$msg = DI::l10n()->t('Unable to process image.'); $msg = DI::l10n()->t('Unable to process image.');
@unlink($src); @unlink($src);
if ($r_json) { if ($r_json) {
@ -170,18 +170,18 @@ function wall_upload_post(App $a, $desktopmode = true)
System::exit(); System::exit();
} }
$Image->orient($src); $image->orient($src);
@unlink($src); @unlink($src);
$max_length = DI::config()->get('system', 'max_image_length'); $max_length = DI::config()->get('system', 'max_image_length');
if ($max_length > 0) { if ($max_length > 0) {
$Image->scaleDown($max_length); $image->scaleDown($max_length);
$filesize = strlen($Image->asString()); $filesize = strlen($image->asString());
Logger::info("File upload: Scaling picture to new size " . $max_length); Logger::info("File upload: Scaling picture to new size " . $max_length);
} }
$width = $Image->getWidth(); $width = $image->getWidth();
$height = $Image->getHeight(); $height = $image->getHeight();
$maximagesize = DI::config()->get('system', 'maximagesize'); $maximagesize = DI::config()->get('system', 'maximagesize');
@ -190,10 +190,10 @@ function wall_upload_post(App $a, $desktopmode = true)
foreach ([5120, 2560, 1280, 640] as $pixels) { foreach ([5120, 2560, 1280, 640] as $pixels) {
if (($filesize > $maximagesize) && (max($width, $height) > $pixels)) { if (($filesize > $maximagesize) && (max($width, $height) > $pixels)) {
Logger::info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]); Logger::info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
$Image->scaleDown($pixels); $image->scaleDown($pixels);
$filesize = strlen($Image->asString()); $filesize = strlen($image->asString());
$width = $Image->getWidth(); $width = $image->getWidth();
$height = $Image->getHeight(); $height = $image->getHeight();
} }
} }
if ($filesize > $maximagesize) { if ($filesize > $maximagesize) {
@ -220,7 +220,7 @@ function wall_upload_post(App $a, $desktopmode = true)
$defperm = '<' . $default_cid . '>'; $defperm = '<' . $default_cid . '>';
$r = Photo::store($Image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 0, Photo::DEFAULT, $defperm); $r = Photo::store($image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 0, Photo::DEFAULT, $defperm);
if (!$r) { if (!$r) {
$msg = DI::l10n()->t('Image upload failed.'); $msg = DI::l10n()->t('Image upload failed.');
@ -233,16 +233,16 @@ function wall_upload_post(App $a, $desktopmode = true)
} }
if ($width > 640 || $height > 640) { if ($width > 640 || $height > 640) {
$Image->scaleDown(640); $image->scaleDown(640);
$r = Photo::store($Image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 1, Photo::DEFAULT, $defperm); $r = Photo::store($image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 1, Photo::DEFAULT, $defperm);
if ($r) { if ($r) {
$smallest = 1; $smallest = 1;
} }
} }
if ($width > 320 || $height > 320) { if ($width > 320 || $height > 320) {
$Image->scaleDown(320); $image->scaleDown(320);
$r = Photo::store($Image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 2, Photo::DEFAULT, $defperm); $r = Photo::store($image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 2, Photo::DEFAULT, $defperm);
if ($r && ($smallest == 0)) { if ($r && ($smallest == 0)) {
$smallest = 2; $smallest = 2;
} }
@ -264,8 +264,8 @@ function wall_upload_post(App $a, $desktopmode = true)
$picture["height"] = $photo["height"]; $picture["height"] = $photo["height"];
$picture["type"] = $photo["type"]; $picture["type"] = $photo["type"];
$picture["albumpage"] = DI::baseUrl() . '/photos/' . $page_owner_nick . '/image/' . $resource_id; $picture["albumpage"] = DI::baseUrl() . '/photos/' . $page_owner_nick . '/image/' . $resource_id;
$picture["picture"] = DI::baseUrl() . "/photo/{$resource_id}-0." . $Image->getExt(); $picture["picture"] = DI::baseUrl() . "/photo/{$resource_id}-0." . $image->getExt();
$picture["preview"] = DI::baseUrl() . "/photo/{$resource_id}-{$smallest}." . $Image->getExt(); $picture["preview"] = DI::baseUrl() . "/photo/{$resource_id}-{$smallest}." . $image->getExt();
if ($r_json) { if ($r_json) {
System::jsonExit(['picture' => $picture]); System::jsonExit(['picture' => $picture]);
@ -280,7 +280,7 @@ function wall_upload_post(App $a, $desktopmode = true)
System::jsonExit(['ok' => true]); System::jsonExit(['ok' => true]);
} }
echo "\n\n" . '[url=' . DI::baseUrl() . '/photos/' . $page_owner_nick . '/image/' . $resource_id . '][img]' . DI::baseUrl() . "/photo/{$resource_id}-{$smallest}.".$Image->getExt()."[/img][/url]\n\n"; echo "\n\n" . '[url=' . DI::baseUrl() . '/photos/' . $page_owner_nick . '/image/' . $resource_id . '][img]' . DI::baseUrl() . "/photo/{$resource_id}-{$smallest}." . $image->getExt() . "[/img][/url]\n\n";
System::exit(); System::exit();
// NOTREACHED // NOTREACHED
} }

View file

@ -145,7 +145,7 @@ class App
$this->nickname = $nickname; $this->nickname = $nickname;
} }
public function isLoggedIn() public function isLoggedIn(): bool
{ {
return local_user() && $this->user_id && ($this->user_id == local_user()); return local_user() && $this->user_id && ($this->user_id == local_user());
} }
@ -155,7 +155,7 @@ class App
* *
* @return bool true if user is an admin * @return bool true if user is an admin
*/ */
public function isSiteAdmin() public function isSiteAdmin(): bool
{ {
$admin_email = $this->config->get('config', 'admin_email'); $admin_email = $this->config->get('config', 'admin_email');
@ -166,18 +166,18 @@ class App
/** /**
* Fetch the user id * Fetch the user id
* @return int * @return int User id
*/ */
public function getLoggedInUserId() public function getLoggedInUserId(): int
{ {
return $this->user_id; return $this->user_id;
} }
/** /**
* Fetch the user nick name * Fetch the user nick name
* @return string * @return string User's nickname
*/ */
public function getLoggedInUserNickname() public function getLoggedInUserNickname(): string
{ {
return $this->nickname; return $this->nickname;
} }
@ -198,7 +198,7 @@ class App
* *
* @return int * @return int
*/ */
public function getProfileOwner():int public function getProfileOwner(): int
{ {
return $this->profile_owner; return $this->profile_owner;
} }
@ -219,7 +219,7 @@ class App
* *
* @return int * @return int
*/ */
public function getContactId():int public function getContactId(): int
{ {
return $this->contact_id; return $this->contact_id;
} }
@ -241,7 +241,7 @@ class App
* *
* @return int * @return int
*/ */
public function getTimeZone():string public function getTimeZone(): string
{ {
return $this->timezone; return $this->timezone;
} }
@ -260,9 +260,9 @@ class App
/** /**
* Fetch workerqueue information * Fetch workerqueue information
* *
* @return array * @return array Worker queue
*/ */
public function getQueue() public function getQueue(): array
{ {
return $this->queue ?? []; return $this->queue ?? [];
} }
@ -270,8 +270,8 @@ class App
/** /**
* Fetch a specific workerqueue field * Fetch a specific workerqueue field
* *
* @param string $index * @param string $index Work queue record to fetch
* @return mixed * @return mixed Work queue item or NULL if not found
*/ */
public function getQueueValue(string $index) public function getQueueValue(string $index)
{ {
@ -306,9 +306,9 @@ class App
/** /**
* The basepath of this app * The basepath of this app
* *
* @return string * @return string Base path from configuration
*/ */
public function getBasePath() public function getBasePath(): string
{ {
// Don't use the basepath of the config table for basepath (it should always be the config-file one) // Don't use the basepath of the config table for basepath (it should always be the config-file one)
return $this->config->getCache()->get('system', 'basepath'); return $this->config->getCache()->get('system', 'basepath');
@ -396,10 +396,10 @@ class App
/** /**
* Returns the current theme name. May be overriden by the mobile theme name. * Returns the current theme name. May be overriden by the mobile theme name.
* *
* @return string * @return string Current theme name or empty string in installation phase
* @throws Exception * @throws Exception
*/ */
public function getCurrentTheme() public function getCurrentTheme(): string
{ {
if ($this->mode->isInstall()) { if ($this->mode->isInstall()) {
return ''; return '';
@ -425,10 +425,10 @@ class App
/** /**
* Returns the current mobile theme name. * Returns the current mobile theme name.
* *
* @return string * @return string Mobile theme name or empty string if installer
* @throws Exception * @throws Exception
*/ */
public function getCurrentMobileTheme() public function getCurrentMobileTheme(): string
{ {
if ($this->mode->isInstall()) { if ($this->mode->isInstall()) {
return ''; return '';
@ -441,12 +441,22 @@ class App
return $this->currentMobileTheme; return $this->currentMobileTheme;
} }
public function setCurrentTheme($theme) /**
* Setter for current theme name
*
* @param string $theme Name of current theme
*/
public function setCurrentTheme(string $theme)
{ {
$this->currentTheme = $theme; $this->currentTheme = $theme;
} }
public function setCurrentMobileTheme($theme) /**
* Setter for current mobile theme name
*
* @param string $theme Name of current mobile theme
*/
public function setCurrentMobileTheme(string $theme)
{ {
$this->currentMobileTheme = $theme; $this->currentMobileTheme = $theme;
} }
@ -525,10 +535,10 @@ class App
/** /**
* Provide a sane default if nothing is chosen or the specified theme does not exist. * Provide a sane default if nothing is chosen or the specified theme does not exist.
* *
* @return string * @return string Current theme's stylsheet path
* @throws Exception * @throws Exception
*/ */
public function getCurrentThemeStylesheetPath() public function getCurrentThemeStylesheetPath(): string
{ {
return Core\Theme::getStylesheetPath($this->getCurrentTheme()); return Core\Theme::getStylesheetPath($this->getCurrentTheme());
} }
@ -604,9 +614,9 @@ class App
if (!empty($_GET['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !local_user()) { if (!empty($_GET['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !local_user()) {
// Only continue when the given profile link seems valid // Only continue when the given profile link seems valid
// Valid profile links contain a path with "/profile/" and no query parameters // Valid profile links contain a path with "/profile/" and no query parameters
if ((parse_url($_GET['zrl'], PHP_URL_QUERY) == "") && if ((parse_url($_GET['zrl'], PHP_URL_QUERY) == '') &&
strstr(parse_url($_GET['zrl'], PHP_URL_PATH), "/profile/")) { strstr(parse_url($_GET['zrl'], PHP_URL_PATH), '/profile/')) {
if (Core\Session::get('visitor_home') != $_GET["zrl"]) { if (Core\Session::get('visitor_home') != $_GET['zrl']) {
Core\Session::set('my_url', $_GET['zrl']); Core\Session::set('my_url', $_GET['zrl']);
Core\Session::set('authenticated', 0); Core\Session::set('authenticated', 0);
@ -695,7 +705,8 @@ class App
// Initialize module that can set the current theme in the init() method, either directly or via App->setProfileOwner // Initialize module that can set the current theme in the init() method, either directly or via App->setProfileOwner
$page['page_title'] = $moduleName; $page['page_title'] = $moduleName;
if (!$this->mode->isInstall() && !$this->mode->has(App\Mode::MAINTENANCEDISABLED)) { // The "view" module is required to show the theme CSS
if (!$this->mode->isInstall() && !$this->mode->has(App\Mode::MAINTENANCEDISABLED) && $moduleName !== 'view') {
$module = $router->getModule(Maintenance::class); $module = $router->getModule(Maintenance::class);
} else { } else {
// determine the module class and save it to the module instance // determine the module class and save it to the module instance
@ -730,7 +741,7 @@ class App
* *
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function redirect($toUrl) public function redirect(string $toUrl)
{ {
if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) { if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) {
Core\System::externalRedirect($toUrl); Core\System::externalRedirect($toUrl);

View file

@ -78,7 +78,7 @@ class Arguments
/** /**
* @return string The whole command of this call * @return string The whole command of this call
*/ */
public function getCommand() public function getCommand(): string
{ {
return $this->command; return $this->command;
} }
@ -94,7 +94,7 @@ class Arguments
/** /**
* @return array All arguments of this call * @return array All arguments of this call
*/ */
public function getArgv() public function getArgv(): array
{ {
return $this->argv; return $this->argv;
} }
@ -102,7 +102,7 @@ class Arguments
/** /**
* @return string The used HTTP method * @return string The used HTTP method
*/ */
public function getMethod() public function getMethod(): string
{ {
return $this->method; return $this->method;
} }
@ -110,7 +110,7 @@ class Arguments
/** /**
* @return int The count of arguments of this call * @return int The count of arguments of this call
*/ */
public function getArgc() public function getArgc(): int
{ {
return $this->argc; return $this->argc;
} }
@ -145,7 +145,7 @@ class Arguments
* *
* @return bool if the argument position exists * @return bool if the argument position exists
*/ */
public function has(int $position) public function has(int $position): bool
{ {
return array_key_exists($position, $this->argv); return array_key_exists($position, $this->argv);
} }
@ -158,7 +158,7 @@ class Arguments
* *
* @return Arguments The determined arguments * @return Arguments The determined arguments
*/ */
public function determine(array $server, array $get) public function determine(array $server, array $get): Arguments
{ {
// removing leading / - maybe a nginx problem // removing leading / - maybe a nginx problem
$server['QUERY_STRING'] = ltrim($server['QUERY_STRING'] ?? '', '/'); $server['QUERY_STRING'] = ltrim($server['QUERY_STRING'] ?? '', '/');

View file

@ -107,7 +107,7 @@ class BaseURL
* *
* @return string * @return string
*/ */
public function getHostname() public function getHostname(): string
{ {
return $this->hostname; return $this->hostname;
} }
@ -117,7 +117,7 @@ class BaseURL
* *
* @return string * @return string
*/ */
public function getScheme() public function getScheme(): string
{ {
return $this->scheme; return $this->scheme;
} }
@ -127,7 +127,7 @@ class BaseURL
* *
* @return int * @return int
*/ */
public function getSSLPolicy() public function getSSLPolicy(): int
{ {
return $this->sslPolicy; return $this->sslPolicy;
} }
@ -137,7 +137,7 @@ class BaseURL
* *
* @return string * @return string
*/ */
public function getUrlPath() public function getUrlPath(): string
{ {
return $this->urlPath; return $this->urlPath;
} }
@ -151,7 +151,7 @@ class BaseURL
* *
* @return string * @return string
*/ */
public function get($ssl = false) public function get(bool $ssl = false): string
{ {
if ($this->sslPolicy === self::SSL_POLICY_SELFSIGN && $ssl) { if ($this->sslPolicy === self::SSL_POLICY_SELFSIGN && $ssl) {
return Network::switchScheme($this->url); return Network::switchScheme($this->url);
@ -168,8 +168,9 @@ class BaseURL
* @param string? $urlPath * @param string? $urlPath
* *
* @return bool true, if successful * @return bool true, if successful
* @TODO Find proper types
*/ */
public function save($hostname = null, $sslPolicy = null, $urlPath = null) public function save($hostname = null, $sslPolicy = null, $urlPath = null): bool
{ {
$currHostname = $this->hostname; $currHostname = $this->hostname;
$currSSLPolicy = $this->sslPolicy; $currSSLPolicy = $this->sslPolicy;
@ -224,11 +225,11 @@ class BaseURL
/** /**
* Save the current url as base URL * Save the current url as base URL
* *
* @param $url * @param string $url
* *
* @return bool true, if the save was successful * @return bool true, if the save was successful
*/ */
public function saveByURL($url) public function saveByURL(string $url): bool
{ {
$parsed = @parse_url($url); $parsed = @parse_url($url);
@ -421,7 +422,7 @@ class BaseURL
* *
* @return string The cleaned url * @return string The cleaned url
*/ */
public function remove(string $origURL) public function remove(string $origURL): string
{ {
// Remove the hostname from the url if it is an internal link // Remove the hostname from the url if it is an internal link
$nurl = Strings::normaliseLink($origURL); $nurl = Strings::normaliseLink($origURL);
@ -443,9 +444,13 @@ class BaseURL
* @param string $toUrl The destination URL (Default is empty, which is the default page of the Friendica node) * @param string $toUrl The destination URL (Default is empty, which is the default page of the Friendica node)
* @param bool $ssl if true, base URL will try to get called with https:// (works just for relative paths) * @param bool $ssl if true, base URL will try to get called with https:// (works just for relative paths)
* *
* @throws HTTPException\FoundException
* @throws HTTPException\MovedPermanentlyException
* @throws HTTPException\TemporaryRedirectException
*
* @throws HTTPException\InternalServerErrorException In Case the given URL is not relative to the Friendica node * @throws HTTPException\InternalServerErrorException In Case the given URL is not relative to the Friendica node
*/ */
public function redirect($toUrl = '', $ssl = false) public function redirect(string $toUrl = '', bool $ssl = false)
{ {
if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) { if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) {
throw new HTTPException\InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo"); throw new HTTPException\InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo");
@ -458,8 +463,8 @@ class BaseURL
/** /**
* Returns the base url as string * Returns the base url as string
*/ */
public function __toString() public function __toString(): string
{ {
return $this->get(); return (string) $this->get();
} }
} }

View file

@ -130,7 +130,7 @@ class Mode
* *
* @throws \Exception * @throws \Exception
*/ */
public function determine(BasePath $basepath, Database $database, Cache $configCache) public function determine(BasePath $basepath, Database $database, Cache $configCache): Mode
{ {
$mode = 0; $mode = 0;
@ -178,7 +178,7 @@ class Mode
* *
* @return Mode returns the determined mode * @return Mode returns the determined mode
*/ */
public function determineRunMode(bool $isBackend, array $server, Arguments $args, MobileDetect $mobileDetect) public function determineRunMode(bool $isBackend, array $server, Arguments $args, MobileDetect $mobileDetect): Mode
{ {
foreach (self::BACKEND_CONTENT_TYPES as $type) { foreach (self::BACKEND_CONTENT_TYPES as $type) {
if (strpos(strtolower($server['HTTP_ACCEPT'] ?? ''), $type) !== false) { if (strpos(strtolower($server['HTTP_ACCEPT'] ?? ''), $type) !== false) {
@ -201,7 +201,7 @@ class Mode
* *
* @return bool returns true, if the mode is set * @return bool returns true, if the mode is set
*/ */
public function has($mode) public function has(int $mode): bool
{ {
return ($this->mode & $mode) > 0; return ($this->mode & $mode) > 0;
} }
@ -227,7 +227,7 @@ class Mode
* *
* @return int Execution Mode * @return int Execution Mode
*/ */
public function getExecutor() public function getExecutor(): int
{ {
return $this->executor; return $this->executor;
} }
@ -235,9 +235,9 @@ class Mode
/** /**
* Install mode is when the local config file is missing or the DB schema hasn't been installed yet. * Install mode is when the local config file is missing or the DB schema hasn't been installed yet.
* *
* @return bool * @return bool Whether installation mode is active (local/database configuration files present or not)
*/ */
public function isInstall() public function isInstall(): bool
{ {
return !$this->has(Mode::LOCALCONFIGPRESENT) || return !$this->has(Mode::LOCALCONFIGPRESENT) ||
!$this->has(MODE::DBCONFIGAVAILABLE); !$this->has(MODE::DBCONFIGAVAILABLE);
@ -248,7 +248,7 @@ class Mode
* *
* @return bool * @return bool
*/ */
public function isNormal() public function isNormal(): bool
{ {
return $this->has(Mode::LOCALCONFIGPRESENT) && return $this->has(Mode::LOCALCONFIGPRESENT) &&
$this->has(Mode::DBAVAILABLE) && $this->has(Mode::DBAVAILABLE) &&
@ -261,7 +261,7 @@ class Mode
* *
* @return bool Is it a backend call * @return bool Is it a backend call
*/ */
public function isBackend() public function isBackend(): bool
{ {
return $this->isBackend; return $this->isBackend;
} }
@ -271,7 +271,7 @@ class Mode
* *
* @return bool true if it was an AJAX request * @return bool true if it was an AJAX request
*/ */
public function isAjax() public function isAjax(): bool
{ {
return $this->isAjax; return $this->isAjax;
} }
@ -281,7 +281,7 @@ class Mode
* *
* @return bool true if it was an mobile request * @return bool true if it was an mobile request
*/ */
public function isMobile() public function isMobile(): bool
{ {
return $this->isMobile; return $this->isMobile;
} }
@ -291,7 +291,7 @@ class Mode
* *
* @return bool true if it was an tablet request * @return bool true if it was an tablet request
*/ */
public function isTablet() public function isTablet(): bool
{ {
return $this->isTablet; return $this->isTablet;
} }

View file

@ -195,7 +195,7 @@ class Page implements ArrayAccess
* @param string $media * @param string $media
* @see Page::initHead() * @see Page::initHead()
*/ */
public function registerStylesheet($path, string $media = 'screen') public function registerStylesheet(string $path, string $media = 'screen')
{ {
$path = Network::appendQueryParam($path, ['v' => FRIENDICA_VERSION]); $path = Network::appendQueryParam($path, ['v' => FRIENDICA_VERSION]);
@ -288,7 +288,7 @@ class Page implements ArrayAccess
* *
* Taken from http://webcheatsheet.com/php/get_current_page_url.php * Taken from http://webcheatsheet.com/php/get_current_page_url.php
*/ */
private function curPageURL() private function curPageURL(): string
{ {
$pageURL = 'http'; $pageURL = 'http';
if (!empty($_SERVER["HTTPS"]) && ($_SERVER["HTTPS"] == "on")) { if (!empty($_SERVER["HTTPS"]) && ($_SERVER["HTTPS"] == "on")) {

152
src/App/Request.php Normal file
View file

@ -0,0 +1,152 @@
<?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\App;
use Friendica\Core\Config\Capability\IManageConfigValues;
/**
* Container for the whole request
*
* @see https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface
*
* @todo future container class for whole requests, currently it's not :-)
*/
class Request
{
/**
* A comma separated list of default headers that could contain the client IP in a proxy request
*
* @var string
*/
const DEFAULT_FORWARD_FOR_HEADER = 'HTTP_X_FORWARDED_FOR';
/** @var string The remote IP address of the current request */
protected $remoteAddress;
/**
* @return string The remote IP address of the current request
*
* Do always use this instead of $_SERVER['REMOTE_ADDR']
*/
public function getRemoteAddress(): string
{
return $this->remoteAddress;
}
public function __construct(IManageConfigValues $config, array $server = [])
{
$this->remoteAddress = $this->determineRemoteAddress($config, $server);
}
/**
* Checks if given $remoteAddress matches given $trustedProxy.
* If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
* $remoteAddress is an IPv4 address within that IP range.
* Otherwise, $remoteAddress will be compared to $trustedProxy literally and the result
* will be returned.
*
* @param string $trustedProxy The current, trusted proxy to check
* @param string $remoteAddress The current remote IP address
*
*
* @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
*/
protected function matchesTrustedProxy(string $trustedProxy, string $remoteAddress): bool
{
$cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
if (preg_match($cidrre, $trustedProxy, $match)) {
$net = $match[1];
$shiftbits = min(32, max(0, 32 - intval($match[2])));
$netnum = ip2long($net) >> $shiftbits;
$ipnum = ip2long($remoteAddress) >> $shiftbits;
return $ipnum === $netnum;
}
return $trustedProxy === $remoteAddress;
}
/**
* Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
* For details regarding what "match" means, refer to `matchesTrustedProxy`.
*
* @param string[] $trustedProxies A list of the trusted proxies
* @param string $remoteAddress The current remote IP address
*
* @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
*/
protected function isTrustedProxy(array $trustedProxies, string $remoteAddress): bool
{
foreach ($trustedProxies as $tp) {
if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
return true;
}
}
return false;
}
/**
* Determines the remote address, if the connection came from a trusted proxy
* and `forwarded_for_headers` has been configured then the IP address
* specified in this header will be returned instead.
*
* @param IManageConfigValues $config
* @param array $server The $_SERVER array
*
* @return string
*/
protected function determineRemoteAddress(IManageConfigValues $config, array $server): string
{
$remoteAddress = $server['REMOTE_ADDR'] ?? '0.0.0.0';
$trustedProxies = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'trusted_proxies', ''));
if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
$forwardedForHeaders = preg_split('/(\s*,*\s*)*,+(\s*,*\s*)*/', $config->get('proxy', 'forwarded_for_headers', static::DEFAULT_FORWARD_FOR_HEADER));
foreach ($forwardedForHeaders as $header) {
if (isset($server[$header])) {
foreach (explode(',', $server[$header]) as $IP) {
$IP = trim($IP);
// remove brackets from IPv6 addresses
if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
$IP = substr($IP, 1, -1);
}
// skip trusted proxies in the list itself
if ($this->isTrustedProxy($trustedProxies, $IP)) {
continue;
}
if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
return $IP;
}
}
}
}
}
return $remoteAddress;
}
}

View file

@ -152,7 +152,7 @@ class Router
* *
* @throws HTTPException\InternalServerErrorException In case of invalid configs * @throws HTTPException\InternalServerErrorException In case of invalid configs
*/ */
public function loadRoutes(array $routes) public function loadRoutes(array $routes): Router
{ {
$routeCollector = ($this->routeCollector ?? new RouteCollector(new Std(), new GroupCountBased())); $routeCollector = ($this->routeCollector ?? new RouteCollector(new Std(), new GroupCountBased()));
@ -166,6 +166,13 @@ class Router
return $this; return $this;
} }
/**
* Adds multiple routes to a route collector
*
* @param RouteCollector $routeCollector Route collector instance
* @param array $routes Multiple routes to be added
* @throws HTTPException\InternalServerErrorException If route was wrong (somehow)
*/
private function addRoutes(RouteCollector $routeCollector, array $routes) private function addRoutes(RouteCollector $routeCollector, array $routes)
{ {
foreach ($routes as $route => $config) { foreach ($routes as $route => $config) {
@ -221,7 +228,7 @@ class Router
* *
* @return bool * @return bool
*/ */
private function isRoute(array $config) private function isRoute(array $config): bool
{ {
return return
// The config array should at least have one entry // The config array should at least have one entry
@ -253,7 +260,7 @@ class Router
* @throws HTTPException\MethodNotAllowedException If a rule matched but the method didn't * @throws HTTPException\MethodNotAllowedException If a rule matched but the method didn't
* @throws HTTPException\NotFoundException If no rule matched * @throws HTTPException\NotFoundException If no rule matched
*/ */
private function getModuleClass() private function getModuleClass(): string
{ {
$cmd = $this->args->getCommand(); $cmd = $this->args->getCommand();
$cmd = '/' . ltrim($cmd, '/'); $cmd = '/' . ltrim($cmd, '/');

View file

@ -70,9 +70,11 @@ class BaseCollection extends \ArrayIterator
} }
/** /**
* @return int * Getter for total count
*
* @return int Total count
*/ */
public function getTotalCount() public function getTotalCount(): int
{ {
return $this->totalCount; return $this->totalCount;
} }
@ -85,7 +87,7 @@ class BaseCollection extends \ArrayIterator
* @return array * @return array
* @see array_column() * @see array_column()
*/ */
public function column($column, $index_key = null) public function column(string $column, $index_key = null): array
{ {
return array_column($this->getArrayCopy(true), $column, $index_key); return array_column($this->getArrayCopy(true), $column, $index_key);
} }
@ -97,7 +99,7 @@ class BaseCollection extends \ArrayIterator
* @return BaseCollection * @return BaseCollection
* @see array_map() * @see array_map()
*/ */
public function map(callable $callback) public function map(callable $callback): BaseCollection
{ {
return new static(array_map($callback, $this->getArrayCopy()), $this->getTotalCount()); return new static(array_map($callback, $this->getArrayCopy()), $this->getTotalCount());
} }
@ -110,7 +112,7 @@ class BaseCollection extends \ArrayIterator
* @return BaseCollection * @return BaseCollection
* @see array_filter() * @see array_filter()
*/ */
public function filter(callable $callback = null, int $flag = 0) public function filter(callable $callback = null, int $flag = 0): BaseCollection
{ {
return new static(array_filter($this->getArrayCopy(), $callback, $flag)); return new static(array_filter($this->getArrayCopy(), $callback, $flag));
} }

View file

@ -55,14 +55,14 @@ abstract class BaseEntity extends BaseDataTransferObject
} }
/** /**
* @param $name * @param mixed $name
* @return bool * @return bool
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function __isset($name) public function __isset($name): bool
{ {
if (!property_exists($this, $name)) { if (!property_exists($this, $name)) {
throw new HTTPException\InternalServerErrorException('Unknown property ' . $name . ' in Entity ' . static::class); throw new HTTPException\InternalServerErrorException('Unknown property ' . $name . ' of type ' . gettype($name) . ' in Entity ' . static::class);
} }
return !empty($this->$name); return !empty($this->$name);

View file

@ -110,11 +110,11 @@ abstract class BaseModel extends BaseDataTransferObject
* - $model->field (outside of class) * - $model->field (outside of class)
* - $this->field (inside of class) * - $this->field (inside of class)
* *
* @param $name * @param string $name Name of data to fetch
* @return mixed * @return mixed
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function __get($name) public function __get(string $name)
{ {
$this->checkValid(); $this->checkValid();

View file

@ -102,6 +102,7 @@ abstract class BaseModule implements ICanHandleRequests
* e.g. from protocol implementations. * e.g. from protocol implementations.
* *
* @param string[] $request The $_REQUEST content * @param string[] $request The $_REQUEST content
* @return void
*/ */
protected function rawContent(array $request = []) protected function rawContent(array $request = [])
{ {
@ -117,6 +118,7 @@ abstract class BaseModule implements ICanHandleRequests
* XML feed or a JSON output. * XML feed or a JSON output.
* *
* @param string[] $request The $_REQUEST content * @param string[] $request The $_REQUEST content
* @return string
*/ */
protected function content(array $request = []): string protected function content(array $request = []): string
{ {
@ -130,6 +132,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content * Doesn't display any content
* *
* @param string[] $request The $_REQUEST content * @param string[] $request The $_REQUEST content
* @return void
*/ */
protected function delete(array $request = []) protected function delete(array $request = [])
{ {
@ -142,6 +145,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content * Doesn't display any content
* *
* @param string[] $request The $_REQUEST content * @param string[] $request The $_REQUEST content
* @return void
*/ */
protected function patch(array $request = []) protected function patch(array $request = [])
{ {
@ -154,7 +158,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content * Doesn't display any content
* *
* @param string[] $request The $_REQUEST content * @param string[] $request The $_REQUEST content
* * @return void
*/ */
protected function post(array $request = []) protected function post(array $request = [])
{ {
@ -168,6 +172,7 @@ abstract class BaseModule implements ICanHandleRequests
* Doesn't display any content * Doesn't display any content
* *
* @param string[] $request The $_REQUEST content * @param string[] $request The $_REQUEST content
* @return void
*/ */
protected function put(array $request = []) protected function put(array $request = [])
{ {
@ -279,12 +284,12 @@ abstract class BaseModule implements ICanHandleRequests
/** /**
* Fetch a request value and apply default values and check against minimal and maximal values * Fetch a request value and apply default values and check against minimal and maximal values
* *
* @param array $input * @param array $input Input fields
* @param string $parameter * @param string $parameter Parameter
* @param mixed $default * @param mixed $default Default
* @param mixed $minimal_value * @param mixed $minimal_value Minimal value
* @param mixed $maximum_value * @param mixed $maximum_value Maximum value
* @return mixed * @return mixed null on error anything else on success (?)
*/ */
public function getRequestValue(array $input, string $parameter, $default = null, $minimal_value = null, $maximum_value = null) public function getRequestValue(array $input, string $parameter, $default = null, $minimal_value = null, $maximum_value = null)
{ {
@ -320,7 +325,7 @@ abstract class BaseModule implements ICanHandleRequests
return $value; return $value;
} }
/* /**
* Functions used to protect against Cross-Site Request Forgery * Functions used to protect against Cross-Site Request Forgery
* The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key. * The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key.
* In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes; * In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
@ -330,8 +335,11 @@ abstract class BaseModule implements ICanHandleRequests
* If the new page contains by any chance external elements, then the used security token is exposed by the referrer. * If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
* Actually, important actions should not be triggered by Links / GET-Requests at all, but sometimes they still are, * Actually, important actions should not be triggered by Links / GET-Requests at all, but sometimes they still are,
* so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types). * so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
*
* @param string $typename Type name
* @return string Security hash with timestamp
*/ */
public static function getFormSecurityToken($typename = '') public static function getFormSecurityToken(string $typename = ''): string
{ {
$user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']); $user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']);
$timestamp = time(); $timestamp = time();
@ -340,7 +348,14 @@ abstract class BaseModule implements ICanHandleRequests
return $timestamp . '.' . $sec_hash; return $timestamp . '.' . $sec_hash;
} }
public static function checkFormSecurityToken($typename = '', $formname = 'form_security_token') /**
* Checks if form's security (CSRF) token is valid.
*
* @param string $typename ???
* @param string $formname Name of form/field (???)
* @return bool Whether it is valid
*/
public static function checkFormSecurityToken(string $typename = '', string $formname = 'form_security_token'): bool
{ {
$hash = null; $hash = null;
@ -372,12 +387,12 @@ abstract class BaseModule implements ICanHandleRequests
return ($sec_hash == $x[1]); return ($sec_hash == $x[1]);
} }
public static function getFormSecurityStandardErrorMessage() public static function getFormSecurityStandardErrorMessage(): string
{ {
return DI::l10n()->t("The form security token was not correct. This probably happened because the form has been opened for too long \x28>3 hours\x29 before submitting it.") . EOL; return DI::l10n()->t("The form security token was not correct. This probably happened because the form has been opened for too long \x28>3 hours\x29 before submitting it.") . EOL;
} }
public static function checkFormSecurityTokenRedirectOnError($err_redirect, $typename = '', $formname = 'form_security_token') public static function checkFormSecurityTokenRedirectOnError(string $err_redirect, string $typename = '', string $formname = 'form_security_token')
{ {
if (!self::checkFormSecurityToken($typename, $formname)) { if (!self::checkFormSecurityToken($typename, $formname)) {
Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename); Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename);
@ -387,7 +402,7 @@ abstract class BaseModule implements ICanHandleRequests
} }
} }
public static function checkFormSecurityTokenForbiddenOnError($typename = '', $formname = 'form_security_token') public static function checkFormSecurityTokenForbiddenOnError(string $typename = '', string $formname = 'form_security_token')
{ {
if (!self::checkFormSecurityToken($typename, $formname)) { if (!self::checkFormSecurityToken($typename, $formname)) {
Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename); Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename);
@ -397,7 +412,7 @@ abstract class BaseModule implements ICanHandleRequests
} }
} }
protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab) protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab): array
{ {
$tabs = [ $tabs = [
[ [

View file

@ -82,7 +82,7 @@ HELP;
AddonCore::loadAddons(); AddonCore::loadAddons();
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);
@ -116,8 +116,7 @@ HELP;
/** /**
* Lists plugins * Lists plugins
* *
* @return int Return code of this command * @return int|bool Return code of this command, false on error (?)
*
* @throws \Exception * @throws \Exception
*/ */
private function list() private function list()
@ -165,10 +164,9 @@ HELP;
* Enables an addon * Enables an addon
* *
* @return int Return code of this command * @return int Return code of this command
*
* @throws \Exception * @throws \Exception
*/ */
private function enable() private function enable(): int
{ {
$addonname = $this->getArgument(1); $addonname = $this->getArgument(1);
@ -190,10 +188,9 @@ HELP;
* Disables an addon * Disables an addon
* *
* @return int Return code of this command * @return int Return code of this command
*
* @throws \Exception * @throws \Exception
*/ */
private function disable() private function disable(): int
{ {
$addonname = $this->getArgument(1); $addonname = $this->getArgument(1);

View file

@ -80,7 +80,7 @@ HELP;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -110,7 +110,7 @@ HELP;
$this->dba = $dba; $this->dba = $dba;
} }
protected function doExecute() protected function doExecute(): int
{ {
// Initialise the app // Initialise the app
$this->out("Initializing setup..."); $this->out("Initializing setup...");
@ -225,7 +225,7 @@ HELP;
$installer->resetChecks(); $installer->resetChecks();
if (!$installer->installDatabase($basePathConf)) { if (!$installer->installDatabase()) {
$errorMessage = $this->extractErrors($installer->getChecks()); $errorMessage = $this->extractErrors($installer->getChecks());
throw new RuntimeException($errorMessage); throw new RuntimeException($errorMessage);
} }

View file

@ -90,7 +90,7 @@ HELP;
$this->cache = $cache; $this->cache = $cache;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable); $this->out('Executable: ' . $this->executable);

View file

@ -102,7 +102,7 @@ HELP;
$this->config = $config; $this->config = $config;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable); $this->out('Executable: ' . $this->executable);

View file

@ -76,7 +76,7 @@ HELP;
$this->appMode = $appMode; $this->appMode = $appMode;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -45,7 +45,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -25,6 +25,12 @@ use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\Update; use Friendica\Core\Update;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Database\DBStructure; use Friendica\Database\DBStructure;
use Friendica\Database\Definition\DbaDefinition;
use Friendica\Database\Definition\ViewDefinition;
use Friendica\Util\BasePath;
use Friendica\Util\Writer\DbaDefinitionSqlWriter;
use Friendica\Util\Writer\DocWriter;
use Friendica\Util\Writer\ViewDefinitionSqlWriter;
use RuntimeException; use RuntimeException;
/** /**
@ -34,15 +40,21 @@ class DatabaseStructure extends \Asika\SimpleConsole\Console
{ {
protected $helpOptions = ['h', 'help', '?']; protected $helpOptions = ['h', 'help', '?'];
/** /** @var Database */
* @var Database
*/
private $dba; private $dba;
/**
* @var Cache /** @var Cache */
*/
private $configCache; private $configCache;
/** @var DbaDefinition */
private $dbaDefinition;
/** @var ViewDefinition */
private $viewDefinition;
/** @var string */
private $basePath;
protected function getHelp() protected function getHelp()
{ {
$help = <<<HELP $help = <<<HELP
@ -71,15 +83,18 @@ HELP;
return $help; return $help;
} }
public function __construct(Database $dba, Cache $configCache, $argv = null) public function __construct(Database $dba, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition, BasePath $basePath, Cache $configCache, $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);
$this->dba = $dba; $this->dba = $dba;
$this->dbaDefinition = $dbaDefinition;
$this->viewDefinition = $viewDefinition;
$this->configCache = $configCache; $this->configCache = $configCache;
$this->basePath = $basePath->getPath();
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);
@ -120,10 +135,9 @@ HELP;
$output = ob_get_clean(); $output = ob_get_clean();
break; break;
case "dumpsql": case "dumpsql":
DBStructure::writeStructure(); DocWriter::writeDbDefinition($this->dbaDefinition, $this->basePath);
ob_start(); $output = DbaDefinitionSqlWriter::create($this->dbaDefinition);
DBStructure::printStructure($basePath); $output .= ViewDefinitionSqlWriter::create($this->viewDefinition);
$output = ob_get_clean();
break; break;
case "toinnodb": case "toinnodb":
ob_start(); ob_start();

View file

@ -71,7 +71,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -49,7 +49,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -85,7 +85,7 @@ HELP;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -70,7 +70,7 @@ HELP;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -75,7 +75,7 @@ HELP;
$this->dba =$dba; $this->dba =$dba;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -84,7 +84,7 @@ HELP;
$this->lock = $lock; $this->lock = $lock;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable); $this->out('Executable: ' . $this->executable);

View file

@ -77,7 +77,7 @@ HELP;
$this->config = $config; $this->config = $config;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -67,7 +67,7 @@ HELP;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
protected function doExecute() protected function doExecute(): int
{ {
$duplicates = $this->dba->p("SELECT COUNT(*) AS `total`, `uri-id`, MAX(`url`) AS `url` FROM `contact` WHERE `uid` = 0 GROUP BY `uri-id` HAVING total > 1"); $duplicates = $this->dba->p("SELECT COUNT(*) AS `total`, `uri-id`, MAX(`url`) AS `url` FROM `contact` WHERE `uid` = 0 GROUP BY `uri-id` HAVING total > 1");
while ($duplicate = $this->dba->fetch($duplicates)) { while ($duplicate = $this->dba->fetch($duplicates)) {

View file

@ -85,7 +85,7 @@ HELP;
$this->config = $config; $this->config = $config;
} }
protected function doExecute() protected function doExecute(): int
{ {
if (!$this->config->get('system', 'avatar_cache')) { if (!$this->config->get('system', 'avatar_cache')) {
$this->err($this->l10n->t('The avatar cache needs to be enabled to use this command.')); $this->err($this->l10n->t('The avatar cache needs to be enabled to use this command.'));

View file

@ -63,7 +63,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -50,7 +50,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -69,7 +69,7 @@ HELP;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
protected function doExecute() protected function doExecute(): int
{ {
$a = \Friendica\DI::app(); $a = \Friendica\DI::app();

View file

@ -76,7 +76,7 @@ HELP;
$this->dba = $dba; $this->dba = $dba;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable); $this->out('Executable: ' . $this->executable);

View file

@ -74,7 +74,7 @@ HELP;
$this->config = $config; $this->config = $config;
} }
protected function doExecute() protected function doExecute(): int
{ {
if (count($this->args) == 0) { if (count($this->args) == 0) {
$this->out($this->getHelp()); $this->out($this->getHelp());

View file

@ -79,7 +79,7 @@ HELP;
$this->config = $config; $this->config = $config;
} }
protected function doExecute() protected function doExecute(): int
{ {
if (count($this->args) == 0) { if (count($this->args) == 0) {
$this->printBlockedServers($this->config); $this->printBlockedServers($this->config);

View file

@ -69,7 +69,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable); $this->out('Executable: ' . $this->executable);

View file

@ -42,7 +42,7 @@ HELP;
return $help; return $help;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -60,7 +60,7 @@ HELP;
$this->config = $config; $this->config = $config;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -97,7 +97,7 @@ HELP;
$this->pConfig = $pConfig; $this->pConfig = $pConfig;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__); $this->out('Class: ' . __CLASS__);

View file

@ -123,7 +123,7 @@ class Avatar
return $fields; return $fields;
} }
private static function getFilename(string $url) private static function getFilename(string $url): string
{ {
$guid = Item::guidFromUri($url, parse_url($url, PHP_URL_HOST)); $guid = Item::guidFromUri($url, parse_url($url, PHP_URL_HOST));

View file

@ -52,7 +52,7 @@ class LocalRelationship extends BaseFactory implements ICanCreateFromTableRow
$row['hub-verify'] ?? '', $row['hub-verify'] ?? '',
$row['protocol'] ?? Protocol::PHANTOM, $row['protocol'] ?? Protocol::PHANTOM,
$row['rating'] ?? null, $row['rating'] ?? null,
$row['priority'] ?? null $row['priority'] ?? 0
); );
} }
} }

View file

@ -49,7 +49,7 @@ class BoundariesPager extends Pager
* @param string $last_item_id The id† of the last item in the displayed item list * @param string $last_item_id The id† of the last item in the displayed item list
* @param integer $itemsPerPage An optional number of items per page to override the default value * @param integer $itemsPerPage An optional number of items per page to override the default value
*/ */
public function __construct(L10n $l10n, $queryString, $first_item_id = null, $last_item_id = null, $itemsPerPage = 50) public function __construct(L10n $l10n, string $queryString, string $first_item_id = null, string $last_item_id = null, int $itemsPerPage = 50)
{ {
parent::__construct($l10n, $queryString, $itemsPerPage); parent::__construct($l10n, $queryString, $itemsPerPage);
@ -73,12 +73,12 @@ class BoundariesPager extends Pager
} }
} }
public function getStart() public function getStart(): int
{ {
throw new \BadMethodCallException(); throw new \BadMethodCallException();
} }
public function getPage() public function getPage(): int
{ {
throw new \BadMethodCallException(); throw new \BadMethodCallException();
} }
@ -102,7 +102,7 @@ class BoundariesPager extends Pager
* @return string HTML string of the pager * @return string HTML string of the pager
* @throws \Exception * @throws \Exception
*/ */
public function renderMinimal(int $itemCount) public function renderMinimal(int $itemCount): string
{ {
$displayedItemCount = max(0, intval($itemCount)); $displayedItemCount = max(0, intval($itemCount));
@ -130,7 +130,10 @@ class BoundariesPager extends Pager
return Renderer::replaceMacros($tpl, ['pager' => $data]); return Renderer::replaceMacros($tpl, ['pager' => $data]);
} }
public function renderFull($itemCount) /**
* Unsupported method, must be type-compatible
*/
public function renderFull(int $itemCount): string
{ {
throw new \BadMethodCallException(); throw new \BadMethodCallException();
} }

View file

@ -41,7 +41,7 @@ class ContactSelector
* @param boolean $disabled optional, default false * @param boolean $disabled optional, default false
* @return string * @return string
*/ */
public static function pollInterval($current, $disabled = false) public static function pollInterval(string $current, bool $disabled = false): string
{ {
$dis = (($disabled) ? ' disabled="disabled" ' : ''); $dis = (($disabled) ? ' disabled="disabled" ' : '');
$o = ''; $o = '';
@ -84,7 +84,7 @@ class ContactSelector
* @return string Server URL * @return string Server URL
* @throws \Exception * @throws \Exception
*/ */
private static function getServerURLForProfile($profile) private static function getServerURLForProfile(string $profile): string
{ {
if (!empty(self::$server_url[$profile])) { if (!empty(self::$server_url[$profile])) {
return self::$server_url[$profile]; return self::$server_url[$profile];
@ -111,13 +111,16 @@ class ContactSelector
} }
/** /**
* Determines network name
*
* @param string $network network of the contact * @param string $network network of the contact
* @param string $profile optional, default empty * @param string $profile optional, default empty
* @param string $protocol (Optional) Protocol that is used for the transmission * @param string $protocol (Optional) Protocol that is used for the transmission
* @param int $gsid Server id
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function networkToName($network, $profile = '', $protocol = '', $gsid = 0) public static function networkToName(string $network, string $profile = '', string $protocol = '', int $gsid = null): string
{ {
$nets = [ $nets = [
Protocol::DFRN => DI::l10n()->t('DFRN'), Protocol::DFRN => DI::l10n()->t('DFRN'),
@ -179,12 +182,15 @@ class ContactSelector
} }
/** /**
* Determines network's icon name
*
* @param string $network network * @param string $network network
* @param string $profile optional, default empty * @param string $profile optional, default empty
* @return string * @param int $gsid Server id
* @return string Name for network icon
* @throws \Exception * @throws \Exception
*/ */
public static function networkToIcon($network, $profile = "", $gsid = 0) public static function networkToIcon(string $network, string $profile = "", int $gsid = null): string
{ {
$nets = [ $nets = [
Protocol::DFRN => 'friendica', Protocol::DFRN => 'friendica',

View file

@ -154,7 +154,7 @@ class Conversation
} }
// Skip when the causer of the parent is the same as the author of the announce // Skip when the causer of the parent is the same as the author of the announce
if (($verb == Activity::ANNOUNCE) && !empty($thread_parent['causer-id'] && ($thread_parent['causer-id'] == $activity['author-id']))) { if (($verb == Activity::ANNOUNCE) && !empty($thread_parent['causer-id']) && ($thread_parent['causer-id'] == $activity['author-id'])) {
continue; continue;
} }
@ -189,7 +189,7 @@ class Conversation
* @return string formatted text * @return string formatted text
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public function formatActivity(array $links, $verb, $id) public function formatActivity(array $links, string $verb, int $id): string
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
$o = ''; $o = '';
@ -275,7 +275,7 @@ class Conversation
return $o; return $o;
} }
public function statusEditor(array $x = [], $notes_cid = 0, $popup = false) public function statusEditor(array $x = [], int $notes_cid = 0, bool $popup = false): string
{ {
$user = User::getById($this->app->getLoggedInUserId(), ['uid', 'nickname', 'allow_location', 'default-location']); $user = User::getById($this->app->getLoggedInUserId(), ['uid', 'nickname', 'allow_location', 'default-location']);
if (empty($user['uid'])) { if (empty($user['uid'])) {
@ -414,8 +414,8 @@ class Conversation
* figures out how to determine page owner and other contextual items * figures out how to determine page owner and other contextual items
* that are based on unique features of the calling module. * that are based on unique features of the calling module.
* @param array $items * @param array $items
* @param $mode * @param string $mode
* @param $update * @param $update @TODO Which type?
* @param bool $preview * @param bool $preview
* @param string $order * @param string $order
* @param int $uid * @param int $uid
@ -423,7 +423,7 @@ class Conversation
* @throws ImagickException * @throws ImagickException
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public function create(array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0) public function create(array $items, string $mode, $update, bool $preview = false, string $order = 'commented', int $uid = 0): string
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
@ -582,7 +582,7 @@ class Conversation
$uriids[] = $item['uri-id']; $uriids[] = $item['uri-id'];
if (!$this->item->visibleActivity($item)) { if (!$this->item->isVisibleActivity($item)) {
continue; continue;
} }
@ -745,7 +745,7 @@ class Conversation
continue; continue;
} }
if (!$this->item->visibleActivity($item)) { if (!$this->item->isVisibleActivity($item)) {
continue; continue;
} }
@ -784,7 +784,7 @@ class Conversation
return $o; return $o;
} }
private function getBlocklist() private function getBlocklist(): array
{ {
if (!local_user()) { if (!local_user()) {
return []; return [];
@ -816,7 +816,7 @@ class Conversation
* *
* @return array items with parents and comments * @return array items with parents and comments
*/ */
private function addRowInformation(array $row, array $activity, array $thr_parent) private function addRowInformation(array $row, array $activity, array $thr_parent): array
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
@ -911,7 +911,7 @@ class Conversation
* @return array items with parents and comments * @return array items with parents and comments
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private function addChildren(array $parents, bool $block_authors, string $order, int $uid, string $mode) private function addChildren(array $parents, bool $block_authors, string $order, int $uid, string $mode): array
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
if (count($parents) > 1) { if (count($parents) > 1) {
@ -1005,7 +1005,7 @@ class Conversation
* @param bool $recursive * @param bool $recursive
* @return array * @return array
*/ */
private function getItemChildren(array &$item_list, array $parent, $recursive = true) private function getItemChildren(array &$item_list, array $parent, bool $recursive = true): array
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
$children = []; $children = [];
@ -1040,7 +1040,7 @@ class Conversation
* @param array $items * @param array $items
* @return array * @return array
*/ */
private function sortItemChildren(array $items) private function sortItemChildren(array $items): array
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
$result = $items; $result = $items;
@ -1086,7 +1086,7 @@ class Conversation
* @param array $parent A tree-like array of items * @param array $parent A tree-like array of items
* @return array * @return array
*/ */
private function smartFlattenConversation(array $parent) private function smartFlattenConversation(array $parent): array
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
if (!isset($parent['children']) || count($parent['children']) == 0) { if (!isset($parent['children']) || count($parent['children']) == 0) {
@ -1142,7 +1142,7 @@ class Conversation
* @return array * @return array
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private function convSort(array $item_list, $order) private function convSort(array $item_list, string $order): array
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
$parents = []; $parents = [];
@ -1222,7 +1222,7 @@ class Conversation
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrFeaturedReceived(array $a, array $b) private function sortThrFeaturedReceived(array $a, array $b): int
{ {
if ($b['featured'] && !$a['featured']) { if ($b['featured'] && !$a['featured']) {
return 1; return 1;
@ -1240,7 +1240,7 @@ class Conversation
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrFeaturedCommented(array $a, array $b) private function sortThrFeaturedCommented(array $a, array $b): int
{ {
if ($b['featured'] && !$a['featured']) { if ($b['featured'] && !$a['featured']) {
return 1; return 1;
@ -1258,7 +1258,7 @@ class Conversation
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrReceived(array $a, array $b) private function sortThrReceived(array $a, array $b): int
{ {
return strcmp($b['received'], $a['received']); return strcmp($b['received'], $a['received']);
} }
@ -1270,7 +1270,7 @@ class Conversation
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrReceivedRev(array $a, array $b) private function sortThrReceivedRev(array $a, array $b): int
{ {
return strcmp($a['received'], $b['received']); return strcmp($a['received'], $b['received']);
} }
@ -1282,7 +1282,7 @@ class Conversation
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrCommented(array $a, array $b) private function sortThrCommented(array $a, array $b): int
{ {
return strcmp($b['commented'], $a['commented']); return strcmp($b['commented'], $a['commented']);
} }
@ -1294,7 +1294,7 @@ class Conversation
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrCreated(array $a, array $b) private function sortThrCreated(array $a, array $b): int
{ {
return strcmp($b['created'], $a['created']); return strcmp($b['created'], $a['created']);
} }

View file

@ -85,7 +85,7 @@ class Item
* ] * ]
* ] * ]
*/ */
public function determineCategoriesTerms(array $item, int $uid = 0) public function determineCategoriesTerms(array $item, int $uid = 0): array
{ {
$categories = []; $categories = [];
$folders = []; $folders = [];
@ -141,16 +141,16 @@ class Item
* This function removes the tag $tag from the text $body and replaces it with * This function removes the tag $tag from the text $body and replaces it with
* the appropriate link. * the appropriate link.
* *
* @param string $body the text to replace the tag in * @param string $body the text to replace the tag in
* @param integer $profile_uid the user id to replace the tag for (0 = anyone) * @param int $profile_uid the user id to replace the tag for (0 = anyone)
* @param string $tag the tag to replace * @param string $tag the tag to replace
* @param string $network The network of the post * @param string $network The network of the post
* *
* @return array|bool ['replaced' => $replaced, 'contact' => $contact]; * @return array|bool ['replaced' => $replaced, 'contact' => $contact] or "false" on if already replaced
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function replaceTag(&$body, $profile_uid, $tag, $network = '') public static function replaceTag(string &$body, int $profile_uid, string $tag, string $network = '')
{ {
$replaced = false; $replaced = false;
@ -244,16 +244,17 @@ class Item
/** /**
* Render actions localized * Render actions localized
* *
* @param $item * @param array $item
* @return void
* @throws ImagickException * @throws ImagickException
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public function localize(&$item) public function localize(array &$item)
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
/// @todo The following functionality needs to be cleaned up. /// @todo The following functionality needs to be cleaned up.
if (!empty($item['verb'])) { if (!empty($item['verb'])) {
$xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; $xmlhead = '<?xml version="1.0" encoding="UTF-8" ?>';
if (stristr($item['verb'], Activity::POKE)) { if (stristr($item['verb'], Activity::POKE)) {
$verb = urldecode(substr($item['verb'], strpos($item['verb'],'#') + 1)); $verb = urldecode(substr($item['verb'], strpos($item['verb'],'#') + 1));
@ -261,7 +262,7 @@ class Item
$this->profiler->stopRecording(); $this->profiler->stopRecording();
return; return;
} }
if ($item['object-type'] == "" || $item['object-type'] !== Activity\ObjectType::PERSON) { if ($item['object-type'] == '' || $item['object-type'] !== Activity\ObjectType::PERSON) {
$this->profiler->stopRecording(); $this->profiler->stopRecording();
return; return;
} }
@ -270,18 +271,22 @@ class Item
$Bname = $obj->title; $Bname = $obj->title;
$Blink = $obj->id; $Blink = $obj->id;
$Bphoto = ""; $Bphoto = '';
foreach ($obj->link as $l) { foreach ($obj->link as $l) {
$atts = $l->attributes(); $atts = $l->attributes();
switch ($atts['rel']) { switch ($atts['rel']) {
case "alternate": $Blink = $atts['href']; case 'alternate': $Blink = $atts['href'];
case "photo": $Bphoto = $atts['href']; case 'photo': $Bphoto = $atts['href'];
} }
} }
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
];
$A = '[url=' . Contact::magicLinkByContact($author) . ']' . $item['author-name'] . '[/url]'; $A = '[url=' . Contact::magicLinkByContact($author) . ']' . $item['author-name'] . '[/url]';
if (!empty($Blink)) { if (!empty($Blink)) {
@ -290,7 +295,7 @@ class Item
$B = ''; $B = '';
} }
if ($Bphoto != "" && !empty($Blink)) { if ($Bphoto != '' && !empty($Blink)) {
$Bphoto = '[url=' . Contact::magicLink($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]'; $Bphoto = '[url=' . Contact::magicLink($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]';
} }
@ -305,9 +310,7 @@ class Item
$txt = str_replace($poked_t, $this->l10n->t($verb), $txt); $txt = str_replace($poked_t, $this->l10n->t($verb), $txt);
// then do the sprintf on the translation string // then do the sprintf on the translation string
$item['body'] = sprintf($txt, $A, $B) . "\n\n\n" . $Bphoto; $item['body'] = sprintf($txt, $A, $B) . "\n\n\n" . $Bphoto;
} }
if ($this->activity->match($item['verb'], Activity::TAG)) { if ($this->activity->match($item['verb'], Activity::TAG)) {
@ -319,12 +322,20 @@ class Item
return; return;
} }
$author_arr = ['uid' => 0, 'id' => $item['author-id'], $author_arr = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
];
$author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]'; $author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]';
$author_arr = ['uid' => 0, 'id' => $obj['author-id'], $author_arr = [
'network' => $obj['author-network'], 'url' => $obj['author-link']]; 'uid' => 0,
'id' => $obj['author-id'],
'network' => $obj['author-network'],
'url' => $obj['author-link'],
];
$objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]'; $objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]';
switch ($obj['verb']) { switch ($obj['verb']) {
@ -337,6 +348,7 @@ class Item
$post_type = $this->l10n->t('status'); $post_type = $this->l10n->t('status');
} }
break; break;
default: default:
if ($obj['resource-id']) { if ($obj['resource-id']) {
$post_type = $this->l10n->t('photo'); $post_type = $this->l10n->t('photo');
@ -360,25 +372,29 @@ class Item
$this->profiler->stopRecording(); $this->profiler->stopRecording();
} }
public function photoMenu($item, string $formSecurityToken) /**
* Renders photo menu based on item
*
* @param array $item
* @param string $formSecurityToken
* @return string
*/
public function photoMenu(array $item, string $formSecurityToken): string
{ {
$this->profiler->startRecording('rendering'); $this->profiler->startRecording('rendering');
$sub_link = ''; $sub_link = $poke_link = $contact_url = $pm_url = $status_link = '';
$poke_link = ''; $photos_link = $posts_link = $block_link = $ignore_link = '';
$contact_url = '';
$pm_url = '';
$status_link = '';
$photos_link = '';
$posts_link = '';
$block_link = '';
$ignore_link = '';
if (local_user() && local_user() == $item['uid'] && $item['gravity'] == GRAVITY_PARENT && !$item['self'] && !$item['mention']) { if (local_user() && local_user() == $item['uid'] && $item['gravity'] == GRAVITY_PARENT && !$item['self'] && !$item['mention']) {
$sub_link = 'javascript:doFollowThread(' . $item['id'] . '); return false;'; $sub_link = 'javascript:doFollowThread(' . $item['id'] . '); return false;';
} }
$author = ['uid' => 0, 'id' => $item['author-id'], $author = [
'network' => $item['author-network'], 'url' => $item['author-link']]; 'uid' => 0,
'id' => $item['author-id'],
'network' => $item['author-network'],
'url' => $item['author-link'],
];
$profile_link = Contact::magicLinkByContact($author, $item['author-link']); $profile_link = Contact::magicLinkByContact($author, $item['author-link']);
$sparkle = (strpos($profile_link, 'redir/') === 0); $sparkle = (strpos($profile_link, 'redir/') === 0);
@ -435,7 +451,7 @@ class Item
} }
if ($network == Protocol::DFRN) { if ($network == Protocol::DFRN) {
$menu[$this->l10n->t("Poke")] = $poke_link; $menu[$this->l10n->t('Poke')] = $poke_link;
} }
if ((($cid == 0) || ($rel == Contact::FOLLOWER)) && if ((($cid == 0) || ($rel == Contact::FOLLOWER)) &&
@ -465,24 +481,28 @@ class Item
return $o; return $o;
} }
public function visibleActivity($item) { /**
* Checks if the activity is visible to current user
*
* @param array $item Activity item
* @return bool Whether the item is visible to the user
*/
public function isVisibleActivity(array $item): bool
{
// Empty verb or hidden?
if (empty($item['verb']) || $this->activity->isHidden($item['verb'])) { if (empty($item['verb']) || $this->activity->isHidden($item['verb'])) {
return false; return false;
} }
// @TODO below if() block can be rewritten to a single line: $isVisible = allConditionsHere; // Check conditions
if ($this->activity->match($item['verb'], Activity::FOLLOW) && return (!($this->activity->match($item['verb'], Activity::FOLLOW) &&
$item['object-type'] === Activity\ObjectType::NOTE && $item['object-type'] === Activity\ObjectType::NOTE &&
empty($item['self']) && empty($item['self']) &&
$item['uid'] == local_user()) { $item['uid'] == local_user())
return false; );
}
return true;
} }
public function expandTags(array $item, bool $setPermissions = false) public function expandTags(array $item, bool $setPermissions = false): array
{ {
// Look for any tags and linkify them // Look for any tags and linkify them
$item['inform'] = ''; $item['inform'] = '';

View file

@ -62,7 +62,7 @@ class Nav
* *
* @param string $item * @param string $item
*/ */
public static function setSelected($item) public static function setSelected(string $item)
{ {
self::$selected[$item] = 'selected'; self::$selected[$item] = 'selected';
} }
@ -74,7 +74,7 @@ class Nav
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function build(App $a) public static function build(App $a): string
{ {
// Placeholder div for popup panel // Placeholder div for popup panel
$nav = '<div id="panel" style="display: none;"></div>'; $nav = '<div id="panel" style="display: none;"></div>';
@ -106,7 +106,7 @@ class Nav
* *
* @return array * @return array
*/ */
public static function getAppMenu() public static function getAppMenu(): array
{ {
if (is_null(self::$app_menu)) { if (is_null(self::$app_menu)) {
self::populateAppMenu(); self::populateAppMenu();
@ -117,6 +117,8 @@ class Nav
/** /**
* Fills the apps static variable with apps that require a menu * Fills the apps static variable with apps that require a menu
*
* @return void
*/ */
private static function populateAppMenu() private static function populateAppMenu()
{ {

View file

@ -49,7 +49,13 @@ use Friendica\Util\Strings;
*/ */
class OEmbed class OEmbed
{ {
public static function replaceCallback($matches) /**
* Callback for fetching URL, checking allowance and returning formatted HTML
*
* @param array $matches
* @return string Formatted HTML
*/
public static function replaceCallback(array $matches): string
{ {
$embedurl = $matches[1]; $embedurl = $matches[1];
$j = self::fetchURL($embedurl, !self::isAllowedURL($embedurl)); $j = self::fetchURL($embedurl, !self::isAllowedURL($embedurl));
@ -68,7 +74,7 @@ class OEmbed
* @return \Friendica\Object\OEmbed * @return \Friendica\Object\OEmbed
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function fetchURL($embedurl, bool $no_rich_type = false, bool $use_parseurl = true) public static function fetchURL(string $embedurl, bool $no_rich_type = false, bool $use_parseurl = true): \Friendica\Object\OEmbed
{ {
$embedurl = trim($embedurl, '\'"'); $embedurl = trim($embedurl, '\'"');
@ -209,12 +215,18 @@ class OEmbed
return $oembed; return $oembed;
} }
private static function formatObject(\Friendica\Object\OEmbed $oembed) /**
* Returns a formatted string from OEmbed object
*
* @param \Friendica\Object\OEmbed $oembed
* @return string
*/
private static function formatObject(\Friendica\Object\OEmbed $oembed): string
{ {
$ret = '<div class="oembed ' . $oembed->type . '">'; $ret = '<div class="oembed ' . $oembed->type . '">';
switch ($oembed->type) { switch ($oembed->type) {
case "video": case 'video':
if ($oembed->thumbnail_url) { if ($oembed->thumbnail_url) {
$tw = (isset($oembed->thumbnail_width) && intval($oembed->thumbnail_width)) ? $oembed->thumbnail_width : 200; $tw = (isset($oembed->thumbnail_width) && intval($oembed->thumbnail_width)) ? $oembed->thumbnail_width : 200;
$th = (isset($oembed->thumbnail_height) && intval($oembed->thumbnail_height)) ? $oembed->thumbnail_height : 180; $th = (isset($oembed->thumbnail_height) && intval($oembed->thumbnail_height)) ? $oembed->thumbnail_height : 180;
@ -236,14 +248,14 @@ class OEmbed
} }
break; break;
case "photo": case 'photo':
$ret .= '<img width="' . $oembed->width . '" src="' . Proxy::proxifyUrl($oembed->url) . '">'; $ret .= '<img width="' . $oembed->width . '" src="' . Proxy::proxifyUrl($oembed->url) . '">';
break; break;
case "link": case 'link':
break; break;
case "rich": case 'rich':
$ret .= Proxy::proxifyHtml($oembed->html); $ret .= Proxy::proxifyHtml($oembed->html);
break; break;
} }
@ -292,9 +304,15 @@ class OEmbed
return str_replace("\n", "", $ret); return str_replace("\n", "", $ret);
} }
public static function BBCode2HTML($text) /**
* Converts BBCode to HTML code
*
* @param string $text
* @return string
*/
public static function BBCode2HTML(string $text): string
{ {
$stopoembed = DI::config()->get("system", "no_oembed"); $stopoembed = DI::config()->get('system', 'no_oembed');
if ($stopoembed == true) { if ($stopoembed == true) {
return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>" . DI::l10n()->t('Embedding disabled') . " : $1</i><!-- /oembed $1 -->", $text); return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>" . DI::l10n()->t('Embedding disabled') . " : $1</i><!-- /oembed $1 -->", $text);
} }
@ -305,14 +323,13 @@ class OEmbed
* Find <span class='oembed'>..<a href='url' rel='oembed'>..</a></span> * Find <span class='oembed'>..<a href='url' rel='oembed'>..</a></span>
* and replace it with [embed]url[/embed] * and replace it with [embed]url[/embed]
* *
* @param $text * @param string $text
* @return string * @return string
*/ */
public static function HTML2BBCode($text) public static function HTML2BBCode(string $text): string
{ {
// start parser only if 'oembed' is in text // start parser only if 'oembed' is in text
if (strpos($text, "oembed")) { if (strpos($text, 'oembed')) {
// convert non ascii chars to html entities // convert non ascii chars to html entities
$html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text)); $html_text = mb_convert_encoding($text, 'HTML-ENTITIES', mb_detect_encoding($text));
@ -323,17 +340,17 @@ class OEmbed
} }
$xpath = new DOMXPath($dom); $xpath = new DOMXPath($dom);
$xattr = self::buildXPath("class", "oembed"); $xattr = self::buildXPath('class', 'oembed');
$entries = $xpath->query("//div[$xattr]"); $entries = $xpath->query("//div[$xattr]");
$xattr = "@rel='oembed'"; //oe_build_xpath("rel","oembed"); $xattr = "@rel='oembed'"; //oe_build_xpath("rel","oembed");
foreach ($entries as $e) { foreach ($entries as $e) {
$href = $xpath->evaluate("a[$xattr]/@href", $e)->item(0)->nodeValue; $href = $xpath->evaluate("a[$xattr]/@href", $e)->item(0)->nodeValue;
if (!is_null($href)) { if (!is_null($href)) {
$e->parentNode->replaceChild(new DOMText("[embed]" . $href . "[/embed]"), $e); $e->parentNode->replaceChild(new DOMText('[embed]' . $href . '[/embed]'), $e);
} }
} }
return self::getInnerHTML($dom->getElementsByTagName("body")->item(0)); return self::getInnerHTML($dom->getElementsByTagName('body')->item(0));
} else { } else {
return $text; return $text;
} }
@ -346,7 +363,7 @@ class OEmbed
* @return boolean * @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function isAllowedURL($url) public static function isAllowedURL(string $url): bool
{ {
if (!DI::config()->get('system', 'no_oembed_rich_content')) { if (!DI::config()->get('system', 'no_oembed_rich_content')) {
return true; return true;
@ -367,7 +384,14 @@ class OEmbed
return Network::isDomainAllowed($domain, $allowed); return Network::isDomainAllowed($domain, $allowed);
} }
public static function getHTML($url, $title = null) /**
* Returns a formmated HTML code from given URL and sets optional title
*
* @param string $url URL to fetch
* @param string $title Optional title (default: what comes from OEmbed object)
* @return string Formatted HTML
*/
public static function getHTML(string $url, string $title = '')
{ {
$o = self::fetchURL($url, !self::isAllowedURL($url)); $o = self::fetchURL($url, !self::isAllowedURL($url));
@ -401,12 +425,12 @@ class OEmbed
* @param string $src Original remote URL to embed * @param string $src Original remote URL to embed
* @param string $width * @param string $width
* @param string $height * @param string $height
* @return string formatted HTML * @return string Formatted HTML
* *
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @see oembed_format_object() * @see oembed_format_object()
*/ */
private static function iframe($src, $width, $height) private static function iframe(string $src, string $width, string $height): string
{ {
if (!$height || strstr($height, '%')) { if (!$height || strstr($height, '%')) {
$height = '200'; $height = '200';
@ -427,7 +451,7 @@ class OEmbed
* @param string $value Value to search in a space-separated list * @param string $value Value to search in a space-separated list
* @return string * @return string
*/ */
private static function buildXPath($attr, $value) private static function buildXPath(string $attr, $value): string
{ {
// https://www.westhoffswelt.de/blog/2009/6/9/select-html-elements-with-more-than-one-css-class-using-xpath // https://www.westhoffswelt.de/blog/2009/6/9/select-html-elements-with-more-than-one-css-class-using-xpath
return "contains(normalize-space(@$attr), ' $value ') or substring(normalize-space(@$attr), 1, string-length('$value') + 1) = '$value ' or substring(normalize-space(@$attr), string-length(@$attr) - string-length('$value')) = ' $value' or @$attr = '$value'"; return "contains(normalize-space(@$attr), ' $value ') or substring(normalize-space(@$attr), 1, string-length('$value') + 1) = '$value ' or substring(normalize-space(@$attr), string-length(@$attr) - string-length('$value')) = ' $value' or @$attr = '$value'";
@ -439,7 +463,7 @@ class OEmbed
* @param DOMNode $node * @param DOMNode $node
* @return string * @return string
*/ */
private static function getInnerHTML(DOMNode $node) private static function getInnerHTML(DOMNode $node): string
{ {
$innerHTML = ''; $innerHTML = '';
$children = $node->childNodes; $children = $node->childNodes;

View file

@ -64,7 +64,7 @@ class PageInfo
* @return string * @return string
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public static function appendDataToBody(string $body, array $data, bool $no_photos = false) public static function appendDataToBody(string $body, array $data, bool $no_photos = false): string
{ {
// Only one [attachment] tag per body is allowed // Only one [attachment] tag per body is allowed
$existingAttachmentPos = strpos($body, '[attachment'); $existingAttachmentPos = strpos($body, '[attachment');
@ -90,7 +90,7 @@ class PageInfo
* @return string * @return string
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public static function getFooterFromUrl(string $url, bool $no_photos = false, string $photo = '', bool $keywords = false, string $keyword_denylist = '') public static function getFooterFromUrl(string $url, bool $no_photos = false, string $photo = '', bool $keywords = false, string $keyword_denylist = ''): string
{ {
$data = self::queryUrl($url, $photo, $keywords, $keyword_denylist); $data = self::queryUrl($url, $photo, $keywords, $keyword_denylist);
@ -103,7 +103,7 @@ class PageInfo
* @return string * @return string
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public static function getFooterFromData(array $data, bool $no_photos = false) public static function getFooterFromData(array $data, bool $no_photos = false): string
{ {
Hook::callAll('page_info_data', $data); Hook::callAll('page_info_data', $data);
@ -220,7 +220,7 @@ class PageInfo
* @return array * @return array
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public static function getTagsFromUrl(string $url, string $photo = '', string $keyword_denylist = '') public static function getTagsFromUrl(string $url, string $photo = '', string $keyword_denylist = ''): array
{ {
$data = self::queryUrl($url, $photo, true, $keyword_denylist); $data = self::queryUrl($url, $photo, true, $keyword_denylist);
@ -282,7 +282,7 @@ class PageInfo
* @param string $url * @param string $url
* @return string * @return string
*/ */
protected static function stripTrailingUrlFromBody(string $body, string $url) protected static function stripTrailingUrlFromBody(string $body, string $url): string
{ {
$quotedUrl = preg_quote($url, '#'); $quotedUrl = preg_quote($url, '#');
$body = preg_replace_callback("#(?: $body = preg_replace_callback("#(?:

View file

@ -48,11 +48,11 @@ class Pager
* *
* Guesses the page number from the GET parameter 'page'. * Guesses the page number from the GET parameter 'page'.
* *
* @param L10n $l10n * @param L10n $l10n
* @param string $queryString The query string of the current page * @param string $queryString The query string of the current page
* @param integer $itemsPerPage An optional number of items per page to override the default value * @param int $itemsPerPage An optional number of items per page to override the default value
*/ */
public function __construct(L10n $l10n, $queryString, $itemsPerPage = 50) public function __construct(L10n $l10n, string $queryString, int $itemsPerPage = 50)
{ {
$this->l10n = $l10n; $this->l10n = $l10n;
@ -64,9 +64,9 @@ class Pager
/** /**
* Returns the start offset for a LIMIT clause. Starts at 0. * Returns the start offset for a LIMIT clause. Starts at 0.
* *
* @return integer * @return int
*/ */
public function getStart() public function getStart(): int
{ {
return max(0, ($this->page * $this->itemsPerPage) - $this->itemsPerPage); return max(0, ($this->page * $this->itemsPerPage) - $this->itemsPerPage);
} }
@ -74,9 +74,9 @@ class Pager
/** /**
* Returns the number of items per page * Returns the number of items per page
* *
* @return integer * @return int
*/ */
public function getItemsPerPage() public function getItemsPerPage(): int
{ {
return $this->itemsPerPage; return $this->itemsPerPage;
} }
@ -86,7 +86,7 @@ class Pager
* *
* @return int * @return int
*/ */
public function getPage() public function getPage(): int
{ {
return $this->page; return $this->page;
} }
@ -108,9 +108,9 @@ class Pager
/** /**
* Sets the number of items per page, 1 minimum. * Sets the number of items per page, 1 minimum.
* *
* @param integer $itemsPerPage * @param int $itemsPerPage
*/ */
public function setItemsPerPage($itemsPerPage) public function setItemsPerPage(int $itemsPerPage)
{ {
$this->itemsPerPage = max(1, intval($itemsPerPage)); $this->itemsPerPage = max(1, intval($itemsPerPage));
} }
@ -118,11 +118,11 @@ class Pager
/** /**
* Sets the current page number. Starts at 1. * Sets the current page number. Starts at 1.
* *
* @param integer $page * @param int $page
*/ */
public function setPage($page) public function setPage(int $page)
{ {
$this->page = max(1, intval($page)); $this->page = max(1, $page);
} }
/** /**
@ -132,7 +132,7 @@ class Pager
* *
* @param string $queryString * @param string $queryString
*/ */
public function setQueryString($queryString) public function setQueryString(string $queryString)
{ {
$stripped = preg_replace('/([&?]page=[0-9]*)/', '', $queryString); $stripped = preg_replace('/([&?]page=[0-9]*)/', '', $queryString);
@ -160,7 +160,7 @@ class Pager
* @return string HTML string of the pager * @return string HTML string of the pager
* @throws \Exception * @throws \Exception
*/ */
public function renderMinimal(int $itemCount) public function renderMinimal(int $itemCount): string
{ {
$displayedItemCount = max(0, intval($itemCount)); $displayedItemCount = max(0, intval($itemCount));
@ -199,13 +199,13 @@ class Pager
* *
* $html = $pager->renderFull(); * $html = $pager->renderFull();
* *
* @param integer $itemCount The total number of items including those note displayed on the page * @param int $itemCount The total number of items including those note displayed on the page
* @return string HTML string of the pager * @return string HTML string of the pager
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public function renderFull($itemCount) public function renderFull(int $itemCount): string
{ {
$totalItemCount = max(0, intval($itemCount)); $totalItemCount = max(0, $itemCount);
$data = []; $data = [];

View file

@ -39,10 +39,9 @@ class Smilies
* @param array $b Array of emoticons * @param array $b Array of emoticons
* @param string $smiley The text smilie * @param string $smiley The text smilie
* @param string $representation The replacement * @param string $representation The replacement
*
* @return void * @return void
*/ */
public static function add(&$b, $smiley, $representation) public static function add(array &$b, string $smiley, string $representation)
{ {
$found = array_search($smiley, $b['texts']); $found = array_search($smiley, $b['texts']);
@ -66,7 +65,7 @@ class Smilies
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @hook smilie ('texts' => smilies texts array, 'icons' => smilies html array) * @hook smilie ('texts' => smilies texts array, 'icons' => smilies html array)
*/ */
public static function getList() public static function getList(): array
{ {
$texts = [ $texts = [
'&lt;3', '&lt;3',
@ -169,7 +168,7 @@ class Smilies
* *
* @return string $subject with all substrings in the $search array replaced by the values in the $replace array * @return string $subject with all substrings in the $search array replaced by the values in the $replace array
*/ */
private static function strOrigReplace($search, $replace, $subject) private static function strOrigReplace(array $search, array $replace, string $subject): string
{ {
return strtr($subject, array_combine($search, $replace)); return strtr($subject, array_combine($search, $replace));
} }
@ -191,7 +190,7 @@ class Smilies
* @return string HTML Output of the Smilie * @return string HTML Output of the Smilie
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function replace($s, $no_images = false) public static function replace(string $s, bool $no_images = false): string
{ {
$smilies = self::getList(); $smilies = self::getList();
@ -211,7 +210,7 @@ class Smilies
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function replaceFromArray($text, array $smilies, $no_images = false) public static function replaceFromArray(string $text, array $smilies, bool $no_images = false): string
{ {
if (intval(DI::config()->get('system', 'no_smilies')) if (intval(DI::config()->get('system', 'no_smilies'))
|| (local_user() && intval(DI::pConfig()->get(local_user(), 'system', 'no_smilies'))) || (local_user() && intval(DI::pConfig()->get(local_user(), 'system', 'no_smilies')))
@ -234,7 +233,7 @@ class Smilies
$smilies = $cleaned; $smilies = $cleaned;
} }
$text = preg_replace_callback('/&lt;(3+)/', 'self::pregHeart', $text); $text = preg_replace_callback('/&lt;(3+)/', 'self::heartReplaceCallback', $text);
$text = self::strOrigReplace($smilies['texts'], $smilies['icons'], $text); $text = self::strOrigReplace($smilies['texts'], $smilies['icons'], $text);
$text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', 'self::decode', $text); $text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', 'self::decode', $text);
@ -244,22 +243,24 @@ class Smilies
} }
/** /**
* @param string $m string * Encodes smiley match array to BASE64 string
* *
* @param array $m Match array
* @return string base64 encoded string * @return string base64 encoded string
*/ */
private static function encode($m) private static function encode(array $m): string
{ {
return '<' . $m[1] . '>' . Strings::base64UrlEncode($m[2]) . '</' . $m[1] . '>'; return '<' . $m[1] . '>' . Strings::base64UrlEncode($m[2]) . '</' . $m[1] . '>';
} }
/** /**
* @param string $m string * Decodes a previously BASE64-encoded match array to a string
* *
* @param array $m Matches array
* @return string base64 decoded string * @return string base64 decoded string
* @throws \Exception * @throws \Exception
*/ */
private static function decode($m) private static function decode(array $m): string
{ {
return '<' . $m[1] . '>' . Strings::base64UrlDecode($m[2]) . '</' . $m[1] . '>'; return '<' . $m[1] . '>' . Strings::base64UrlDecode($m[2]) . '</' . $m[1] . '>';
} }
@ -268,24 +269,20 @@ class Smilies
/** /**
* expand <3333 to the correct number of hearts * expand <3333 to the correct number of hearts
* *
* @param string $x string * @param array $matches
*
* @return string HTML Output * @return string HTML Output
*
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function pregHeart($x) private static function heartReplaceCallback(array $matches): string
{ {
if (strlen($x[1]) == 1) { if (strlen($matches[1]) == 1) {
return $x[0]; return $matches[0];
} }
$t = ''; $t = '';
for ($cnt = 0; $cnt < strlen($x[1]); $cnt ++) { for ($cnt = 0; $cnt < strlen($matches[1]); $cnt ++) {
$t .= '❤'; $t .= '❤';
} }
$r = str_replace($x[0], $t, $x[0]); return str_replace($matches[0], $t, $matches[0]);
return $r;
} }
} }

View file

@ -81,7 +81,7 @@ class BBCode
* 'description' -> Description of the attachment * 'description' -> Description of the attachment
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function getOldAttachmentData($body) private static function getOldAttachmentData(string $body): array
{ {
$post = []; $post = [];
@ -152,7 +152,7 @@ class BBCode
* 'description' -> Description of the attachment * 'description' -> Description of the attachment
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function getAttachmentData($body) public static function getAttachmentData(string $body): array
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$data = [ $data = [
@ -187,26 +187,31 @@ class BBCode
case 'publisher_name': case 'publisher_name':
$data['provider_name'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $data['provider_name'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
break; break;
case 'publisher_url': case 'publisher_url':
$data['provider_url'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $data['provider_url'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
break; break;
case 'author_name': case 'author_name':
$data['author_name'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $data['author_name'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
if ($data['provider_name'] == $data['author_name']) { if ($data['provider_name'] == $data['author_name']) {
$data['author_name'] = ''; $data['author_name'] = '';
} }
break; break;
case 'author_url': case 'author_url':
$data['author_url'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $data['author_url'] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
if ($data['provider_url'] == $data['author_url']) { if ($data['provider_url'] == $data['author_url']) {
$data['author_url'] = ''; $data['author_url'] = '';
} }
break; break;
case 'title': case 'title':
$value = self::convert(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), false, true); $value = self::convert(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), false, true);
$value = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $value = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
$value = str_replace(['[', ']'], ['&#91;', '&#93;'], $value); $value = str_replace(['[', ']'], ['&#91;', '&#93;'], $value);
$data['title'] = $value; $data['title'] = $value;
default: default:
$data[$field] = html_entity_decode($value, ENT_QUOTES, 'UTF-8'); $data[$field] = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
break; break;
@ -241,7 +246,7 @@ class BBCode
return $data; return $data;
} }
public static function getAttachedData($body, $item = []) public static function getAttachedData(string $body, array $item = []): array
{ {
/* /*
- text: - text:
@ -303,7 +308,7 @@ class BBCode
// Workaround: // Workaround:
// Sometimes photo posts to the own album are not detected at the start. // Sometimes photo posts to the own album are not detected at the start.
// So we seem to cannot use the cache for these cases. That's strange. // So we seem to cannot use the cache for these cases. That's strange.
if (($data['type'] != 'photo') && strstr($pictures[0][1], "/photos/")) { if (($data['type'] != 'photo') && strstr($pictures[0][1], '/photos/')) {
$data = ParseUrl::getSiteinfo($pictures[0][1]); $data = ParseUrl::getSiteinfo($pictures[0][1]);
} }
@ -320,7 +325,7 @@ class BBCode
$post['text'] = trim(str_replace($pictures[0][0], '', $body)); $post['text'] = trim(str_replace($pictures[0][0], '', $body));
} else { } else {
$imgdata = Images::getInfoFromURLCached($pictures[0][1]); $imgdata = Images::getInfoFromURLCached($pictures[0][1]);
if ($imgdata && substr($imgdata['mime'], 0, 6) == 'image/') { if (($imgdata) && substr($imgdata['mime'], 0, 6) == 'image/') {
$post['type'] = 'photo'; $post['type'] = 'photo';
$post['image'] = $pictures[0][1]; $post['image'] = $pictures[0][1];
$post['preview'] = $pictures[0][2]; $post['preview'] = $pictures[0][2];
@ -390,7 +395,7 @@ class BBCode
} }
if (!isset($post['type'])) { if (!isset($post['type'])) {
$post['type'] = "text"; $post['type'] = 'text';
$post['text'] = trim($body); $post['text'] = trim($body);
} }
@ -419,10 +424,9 @@ class BBCode
* *
* @param string $body * @param string $body
* @param boolean $no_link_desc No link description * @param boolean $no_link_desc No link description
*
* @return string with replaced body * @return string with replaced body
*/ */
public static function removeAttachment($body, $no_link_desc = false) public static function removeAttachment(string $body, bool $no_link_desc = false): string
{ {
return preg_replace_callback("/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism", return preg_replace_callback("/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism",
function ($match) use ($body, $no_link_desc) { function ($match) use ($body, $no_link_desc) {
@ -442,12 +446,11 @@ class BBCode
/** /**
* Converts a BBCode text into plaintext * Converts a BBCode text into plaintext
* *
* @param $text * @param string $text
* @param bool $keep_urls Whether to keep URLs in the resulting plaintext * @param bool $keep_urls Whether to keep URLs in the resulting plaintext
*
* @return string * @return string
*/ */
public static function toPlaintext($text, $keep_urls = true) public static function toPlaintext(string $text, bool $keep_urls = true): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
// Remove pictures in advance to avoid unneeded proxy calls // Remove pictures in advance to avoid unneeded proxy calls
@ -463,7 +466,7 @@ class BBCode
return $naked_text; return $naked_text;
} }
private static function proxyUrl($image, $simplehtml = self::INTERNAL, $uriid = 0, $size = '') private static function proxyUrl(string $image, int $simplehtml = self::INTERNAL, int $uriid = 0, string $size = ''): string
{ {
// Only send proxied pictures to API and for internal display // Only send proxied pictures to API and for internal display
if (!in_array($simplehtml, [self::INTERNAL, self::API])) { if (!in_array($simplehtml, [self::INTERNAL, self::API])) {
@ -483,7 +486,7 @@ class BBCode
* @param string $srctext The body with images * @param string $srctext The body with images
* @return string The body with possibly scaled images * @return string The body with possibly scaled images
*/ */
public static function scaleExternalImages(string $srctext) public static function scaleExternalImages(string $srctext): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$s = $srctext; $s = $srctext;
@ -551,7 +554,7 @@ class BBCode
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function limitBodySize($body) public static function limitBodySize(string $body): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$maxlen = DI::config()->get('config', 'max_import_size', 0); $maxlen = DI::config()->get('config', 'max_import_size', 0);
@ -646,7 +649,7 @@ class BBCode
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function convertAttachment($text, $simplehtml = self::INTERNAL, $tryoembed = true, array $data = [], $uriid = 0) public static function convertAttachment(string $text, int $simplehtml = self::INTERNAL, bool $tryoembed = true, array $data = [], int $uriid = 0): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$data = $data ?: self::getAttachmentData($text); $data = $data ?: self::getAttachmentData($text);
@ -659,10 +662,10 @@ class BBCode
$data['title'] = strip_tags($data['title']); $data['title'] = strip_tags($data['title']);
$data['title'] = str_replace(['http://', 'https://'], '', $data['title']); $data['title'] = str_replace(['http://', 'https://'], '', $data['title']);
} else { } else {
$data['title'] = null; $data['title'] = '';
} }
if (((strpos($data['text'], "[img=") !== false) || (strpos($data['text'], "[img]") !== false) || DI::config()->get('system', 'always_show_preview')) && !empty($data['image'])) { if (((strpos($data['text'], '[img=') !== false) || (strpos($data['text'], '[img]') !== false) || DI::config()->get('system', 'always_show_preview')) && !empty($data['image'])) {
$data['preview'] = $data['image']; $data['preview'] = $data['image'];
$data['image'] = ''; $data['image'] = '';
} }
@ -716,14 +719,14 @@ class BBCode
return trim(($data['text'] ?? '') . ' ' . $return . ' ' . ($data['after'] ?? '')); return trim(($data['text'] ?? '') . ' ' . $return . ' ' . ($data['after'] ?? ''));
} }
public static function removeShareInformation($Text, $plaintext = false, $nolink = false) public static function removeShareInformation(string $text, bool $plaintext = false, bool $nolink = false): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$data = self::getAttachmentData($Text); $data = self::getAttachmentData($text);
if (!$data) { if (!$data) {
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $Text; return $text;
} elseif ($nolink) { } elseif ($nolink) {
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $data['text'] . ($data['after'] ?? ''); return $data['text'] . ($data['after'] ?? '');
@ -767,7 +770,7 @@ class BBCode
* @param array $match Array with the matching values * @param array $match Array with the matching values
* @return string reformatted link including HTML codes * @return string reformatted link including HTML codes
*/ */
private static function convertUrlForActivityPubCallback($match) private static function convertUrlForActivityPubCallback(array $match): string
{ {
$url = $match[1]; $url = $match[1];
@ -789,10 +792,9 @@ class BBCode
* @param string $url URL that is about to be reformatted * @param string $url URL that is about to be reformatted
* @return string reformatted link including HTML codes * @return string reformatted link including HTML codes
*/ */
private static function convertUrlForActivityPub($url) private static function convertUrlForActivityPub(string $url): string
{ {
$html = '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>'; return sprintf('<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', $url, self::getStyledURL($url));
return sprintf($html, $url, self::getStyledURL($url));
} }
/** /**
@ -801,7 +803,7 @@ class BBCode
* @param string $url URL that is about to be reformatted * @param string $url URL that is about to be reformatted
* @return string reformatted link * @return string reformatted link
*/ */
private static function getStyledURL($url) private static function getStyledURL(string $url): string
{ {
$parts = parse_url($url); $parts = parse_url($url);
$scheme = $parts['scheme'] . '://'; $scheme = $parts['scheme'] . '://';
@ -818,8 +820,11 @@ class BBCode
* [noparse][i]italic[/i][/noparse] turns into * [noparse][i]italic[/i][/noparse] turns into
* [noparse][ i ]italic[ /i ][/noparse], * [noparse][ i ]italic[ /i ][/noparse],
* to hide them from parser. * to hide them from parser.
*
* @param array $match
* @return string
*/ */
private static function escapeNoparseCallback($match) private static function escapeNoparseCallback(array $match): string
{ {
$whole_match = $match[0]; $whole_match = $match[0];
$captured = $match[1]; $captured = $match[1];
@ -832,8 +837,11 @@ class BBCode
* The previously spacefied [noparse][ i ]italic[ /i ][/noparse], * The previously spacefied [noparse][ i ]italic[ /i ][/noparse],
* now turns back and the [noparse] tags are trimed * now turns back and the [noparse] tags are trimed
* returning [i]italic[/i] * returning [i]italic[/i]
*
* @param array $match
* @return string
*/ */
private static function unescapeNoparseCallback($match) private static function unescapeNoparseCallback(array $match): string
{ {
$captured = $match[1]; $captured = $match[1];
$unspacefied = preg_replace("/\[ (.*?)\ ]/", "[$1]", $captured); $unspacefied = preg_replace("/\[ (.*?)\ ]/", "[$1]", $captured);
@ -849,7 +857,7 @@ class BBCode
* @param int $occurrences Number of first occurrences to skip * @param int $occurrences Number of first occurrences to skip
* @return boolean|array * @return boolean|array
*/ */
public static function getTagPosition($text, $name, $occurrences = 0) public static function getTagPosition(string $text, string $name, int $occurrences = 0)
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
if ($occurrences < 0) { if ($occurrences < 0) {
@ -913,7 +921,7 @@ class BBCode
* @param string $text Text to search * @param string $text Text to search
* @return string * @return string
*/ */
public static function pregReplaceInTag($pattern, $replace, $name, $text) public static function pregReplaceInTag(string $pattern, string $replace, string $name, string $text): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$occurrences = 0; $occurrences = 0;
@ -936,7 +944,7 @@ class BBCode
return $text; return $text;
} }
private static function extractImagesFromItemBody($body) private static function extractImagesFromItemBody(string $body): array
{ {
$saved_image = []; $saved_image = [];
$orig_body = $body; $orig_body = $body;
@ -977,7 +985,7 @@ class BBCode
return ['body' => $new_body, 'images' => $saved_image]; return ['body' => $new_body, 'images' => $saved_image];
} }
private static function interpolateSavedImagesIntoItemBody($uriid, $body, array $images) private static function interpolateSavedImagesIntoItemBody(int $uriid, string $body, array $images): string
{ {
$newbody = $body; $newbody = $body;
@ -995,29 +1003,51 @@ class BBCode
} }
/** /**
* * @param string $text A BBCode string
* @param string $text A BBCode string * @return array Empty array if no share tag is present or the following array, missing attributes end up empty strings:
* @return array share attributes * - comment: Text before the opening share tag
* - shared : Text inside the share tags
* - author : (Optional) Display name of the shared author
* - profile: (Optional) Profile page URL of the shared author
* - avatar : (Optional) Profile picture URL of the shared author
* - link : (Optional) Canonical URL of the shared post
* - posted : (Optional) Date the shared post was initially posted ("Y-m-d H:i:s" in GMT)
* - guid : (Optional) Shared post GUID if any
*/ */
public static function fetchShareAttributes($text) public static function fetchShareAttributes(string $text): array
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
// See Issue https://github.com/friendica/friendica/issues/10454 // See Issue https://github.com/friendica/friendica/issues/10454
// Hashtags in usernames are expanded to links. This here is a quick fix. // Hashtags in usernames are expanded to links. This here is a quick fix.
$text = preg_replace('/([@!#])\[url\=.*?\](.*?)\[\/url\]/ism', '$1$2', $text); $text = preg_replace('~([@!#])\[url=.*?](.*?)\[/url]~ism', '$1$2', $text);
$attributes = []; if (!preg_match('~(.*?)\[share(.*?)](.*)\[/share]~ism', $text, $matches)) {
if (!preg_match("/(.*?)\[share(.*?)\](.*)\[\/share\]/ism", $text, $matches)) {
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();
return $attributes; return [];
} }
$attribute_string = $matches[2]; $attributes = self::extractShareAttributes($matches[2]);
$attributes['comment'] = trim($matches[1]);
$attributes['shared'] = trim($matches[3]);
DI::profiler()->stopRecording();
return $attributes;
}
/**
* @see BBCode::fetchShareAttributes()
* @param string $shareString Internal opening share tag string matched by the regular expression
* @return array A fixed attribute array where missing attribute are represented by empty strings
*/
private static function extractShareAttributes(string $shareString): array
{
$attributes = [];
foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid'] as $field) { foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid'] as $field) {
preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches); preg_match("/$field=(['\"])(.+?)\\1/ism", $shareString, $matches);
$attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8');
} }
DI::profiler()->stopRecording();
return $attributes; return $attributes;
} }
@ -1040,18 +1070,13 @@ class BBCode
* @param callable $callback * @param callable $callback
* @return string The BBCode string with all [share] blocks replaced * @return string The BBCode string with all [share] blocks replaced
*/ */
public static function convertShare($text, callable $callback, int $uriid = 0) public static function convertShare(string $text, callable $callback, int $uriid = 0): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$return = preg_replace_callback( $return = preg_replace_callback(
"/(.*?)\[share(.*?)\](.*)\[\/share\]/ism", '~(.*?)\[share(.*?)](.*)\[/share]~ism',
function ($match) use ($callback, $uriid) { function ($match) use ($callback, $uriid) {
$attribute_string = $match[2]; $attributes = self::extractShareAttributes($match[2]);
$attributes = [];
foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid'] as $field) {
preg_match("/$field=(['\"])(.+?)\\1/ism", $attribute_string, $matches);
$attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8');
}
$author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']); $author_contact = Contact::getByURL($attributes['profile'], false, ['id', 'url', 'addr', 'name', 'micro']);
$author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']); $author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']);
@ -1086,7 +1111,7 @@ class BBCode
* @param integer $uriid * @param integer $uriid
* @return string * @return string
*/ */
private static function convertImages(string $text, int $simplehtml, int $uriid = 0):string private static function convertImages(string $text, int $simplehtml, int $uriid = 0): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$return = preg_replace_callback( $return = preg_replace_callback(
@ -1129,7 +1154,7 @@ class BBCode
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function convertShareCallback(array $attributes, array $author_contact, $content, $is_quote_share, $simplehtml) private static function convertShareCallback(array $attributes, array $author_contact, string $content, bool $is_quote_share, int $simplehtml): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$mention = $attributes['author'] . ' (' . ($author_contact['addr'] ?? '') . ')'; $mention = $attributes['author'] . ' (' . ($author_contact['addr'] ?? '') . ')';
@ -1198,7 +1223,7 @@ class BBCode
return $text; return $text;
} }
private static function removePictureLinksCallback($match) private static function removePictureLinksCallback(array $match): string
{ {
$cache_key = 'remove:' . $match[1]; $cache_key = 'remove:' . $match[1];
$text = DI::cache()->get($cache_key); $text = DI::cache()->get($cache_key);
@ -1212,9 +1237,9 @@ class BBCode
} }
if (substr($mimetype, 0, 6) == 'image/') { if (substr($mimetype, 0, 6) == 'image/') {
$text = "[url=" . $match[1] . ']' . $match[1] . "[/url]"; $text = '[url=' . $match[1] . ']' . $match[1] . '[/url]';
} else { } else {
$text = "[url=" . $match[2] . ']' . $match[2] . "[/url]"; $text = '[url=' . $match[2] . ']' . $match[2] . '[/url]';
// if its not a picture then look if its a page that contains a picture link // if its not a picture then look if its a page that contains a picture link
$body = DI::httpClient()->fetch($match[1], HttpClientAccept::HTML, 0); $body = DI::httpClient()->fetch($match[1], HttpClientAccept::HTML, 0);
@ -1226,7 +1251,7 @@ class BBCode
$doc = new DOMDocument(); $doc = new DOMDocument();
@$doc->loadHTML($body); @$doc->loadHTML($body);
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query("//meta[@name]"); $list = $xpath->query('//meta[@name]');
foreach ($list as $node) { foreach ($list as $node) {
$attr = []; $attr = [];
@ -1247,16 +1272,28 @@ class BBCode
return $text; return $text;
} }
private static function expandLinksCallback($match) /**
* Callback: Expands links from given $match array
*
* @param arrat $match Array with link match
* @return string BBCode
*/
private static function expandLinksCallback(array $match): string
{ {
if (($match[3] == '') || ($match[2] == $match[3]) || stristr($match[2], $match[3])) { if (($match[3] == '') || ($match[2] == $match[3]) || stristr($match[2], $match[3])) {
return ($match[1] . "[url]" . $match[2] . "[/url]"); return ($match[1] . '[url]' . $match[2] . '[/url]');
} else { } else {
return ($match[1] . $match[3] . " [url]" . $match[2] . "[/url]"); return ($match[1] . $match[3] . ' [url]' . $match[2] . '[/url]');
} }
} }
private static function cleanPictureLinksCallback($match) /**
* Callback: Cleans picture links
*
* @param arrat $match Array with link match
* @return string BBCode
*/
private static function cleanPictureLinksCallback(array $match): string
{ {
// When the picture link is the own photo path then we can avoid fetching the link // When the picture link is the own photo path then we can avoid fetching the link
$own_photo_url = preg_quote(Strings::normaliseLink(DI::baseUrl()->get()) . '/photos/'); $own_photo_url = preg_quote(Strings::normaliseLink(DI::baseUrl()->get()) . '/photos/');
@ -1302,7 +1339,7 @@ class BBCode
$doc = new DOMDocument(); $doc = new DOMDocument();
@$doc->loadHTML($body); @$doc->loadHTML($body);
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query("//meta[@name]"); $list = $xpath->query('//meta[@name]');
foreach ($list as $node) { foreach ($list as $node) {
$attr = []; $attr = [];
if ($node->attributes->length) { if ($node->attributes->length) {
@ -1325,7 +1362,13 @@ class BBCode
return $text; return $text;
} }
public static function cleanPictureLinks($text) /**
* Cleans picture links
*
* @param string $text HTML/BBCode string
* @return string Cleaned HTML/BBCode
*/
public static function cleanPictureLinks(string $text): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $text); $return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $text);
@ -1334,7 +1377,13 @@ class BBCode
return $return; return $return;
} }
public static function removeLinks(string $bbcode) /**
* Removes links
*
* @param string $text HTML/BBCode string
* @return string Cleaned HTML/BBCode
*/
public static function removeLinks(string $bbcode): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $1 ', $bbcode); $bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $1 ', $bbcode);
@ -1350,10 +1399,10 @@ class BBCode
/** /**
* Replace names in mentions with nicknames * Replace names in mentions with nicknames
* *
* @param string $body * @param string $body HTML/BBCode
* @return string Body with replaced mentions * @return string Body with replaced mentions
*/ */
public static function setMentionsToNicknames(string $body):string public static function setMentionsToNicknames(string $body): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; $regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
@ -1366,10 +1415,10 @@ class BBCode
* Callback function to replace a Friendica style mention in a mention with the nickname * Callback function to replace a Friendica style mention in a mention with the nickname
* *
* @param array $match Matching values for the callback * @param array $match Matching values for the callback
* @return string Replaced mention * @return string Replaced mention or empty string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function mentionCallback($match) private static function mentionCallback(array $match): string
{ {
if (empty($match[2])) { if (empty($match[2])) {
return ''; return '';
@ -1407,7 +1456,7 @@ class BBCode
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function convertForUriId(int $uriid = null, string $text = null, int $simple_html = self::INTERNAL) public static function convertForUriId(int $uriid = null, string $text = null, int $simple_html = self::INTERNAL): string
{ {
$try_oembed = ($simple_html == self::INTERNAL); $try_oembed = ($simple_html == self::INTERNAL);
@ -1437,10 +1486,10 @@ class BBCode
* @param int $simple_html * @param int $simple_html
* @param bool $for_plaintext * @param bool $for_plaintext
* @param int $uriid * @param int $uriid
* @return string * @return string Converted code or empty string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function convert(string $text = null, $try_oembed = true, $simple_html = self::INTERNAL, $for_plaintext = false, $uriid = 0) public static function convert(string $text = null, bool $try_oembed = true, int $simple_html = self::INTERNAL, bool $for_plaintext = false, int $uriid = 0): string
{ {
// Accounting for null default column values // Accounting for null default column values
if (is_null($text) || $text === '') { if (is_null($text) || $text === '') {
@ -1462,10 +1511,10 @@ class BBCode
* $match[1] = $url * $match[1] = $url
* $match[2] = $title or absent * $match[2] = $title or absent
*/ */
$try_oembed_callback = function ($match) $try_oembed_callback = function (array $match)
{ {
$url = $match[1]; $url = $match[1];
$title = $match[2] ?? null; $title = $match[2] ?? '';
try { try {
$return = OEmbed::getHTML($url, $title); $return = OEmbed::getHTML($url, $title);
@ -1788,7 +1837,7 @@ class BBCode
$text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . DI::l10n()->t('Encrypted content') . '" /><br>', $text); $text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . DI::l10n()->t('Encrypted content') . '" /><br>', $text);
$text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br>', $text); $text = preg_replace("/\[crypt(.*?)\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br>', $text);
//$Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br>', $Text); //$text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism", '<br><img src="' .DI::baseUrl() . '/images/lock_icon.gif" alt="' . DI::l10n()->t('Encrypted content') . '" title="' . '$1' . ' ' . DI::l10n()->t('Encrypted content') . '" /><br>', $text);
// Simplify "video" element // Simplify "video" element
$text = preg_replace('(\[video[^\]]*?\ssrc\s?=\s?([^\s\]]+)[^\]]*?\].*?\[/video\])ism', '[video]$1[/video]', $text); $text = preg_replace('(\[video[^\]]*?\ssrc\s?=\s?([^\s\]]+)[^\]]*?\].*?\[/video\])ism', '[video]$1[/video]', $text);
@ -1916,8 +1965,8 @@ class BBCode
if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) { if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) {
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text); $text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text);
//$Text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $Text); //$text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $text);
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]',$text); $text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]', $text);
} }
// Perform URL Search // Perform URL Search
@ -2142,7 +2191,7 @@ class BBCode
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
private static function bbCodeMention2DiasporaCallback($match) private static function bbCodeMention2DiasporaCallback(array $match): string
{ {
$contact = Contact::getByURL($match[3], false, ['addr']); $contact = Contact::getByURL($match[3], false, ['addr']);
if (empty($contact['addr'])) { if (empty($contact['addr'])) {
@ -2164,7 +2213,7 @@ class BBCode
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function toMarkdown($text, $for_diaspora = true) public static function toMarkdown(string $text, bool $for_diaspora = true): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$original_text = $text; $original_text = $text;
@ -2201,7 +2250,7 @@ class BBCode
$tagline .= '#' . $tag . ' '; $tagline .= '#' . $tag . ' ';
} }
} }
$text = $text . " " . $tagline; $text = $text . ' ' . $tagline;
} }
} else { } else {
$text = self::convert($text, false, self::CONNECTORS); $text = self::convert($text, false, self::CONNECTORS);
@ -2246,10 +2295,9 @@ class BBCode
* Returns array of tags found, or empty array. * Returns array of tags found, or empty array.
* *
* @param string $string Post content * @param string $string Post content
*
* @return array List of tag and person names * @return array List of tag and person names
*/ */
public static function getTags($string) public static function getTags(string $string): array
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$ret = []; $ret = [];
@ -2309,13 +2357,13 @@ class BBCode
/** /**
* Expand tags to URLs, checks the tag is at the start of a line or preceded by a non-word character * Expand tags to URLs, checks the tag is at the start of a line or preceded by a non-word character
* *
* @param string $body * @param string $body HTML/BBCode
* @return string body with expanded tags * @return string body with expanded tags
*/ */
public static function expandTags(string $body) public static function expandTags(string $body): string
{ {
return preg_replace_callback("/(?<=\W|^)([!#@])([^\^ \x0D\x0A,;:?'\"]*[^\^ \x0D\x0A,;:?!'\".])/", return preg_replace_callback("/(?<=\W|^)([!#@])([^\^ \x0D\x0A,;:?'\"]*[^\^ \x0D\x0A,;:?!'\".])/",
function ($match) { function (array $match) {
switch ($match[1]) { switch ($match[1]) {
case '!': case '!':
case '@': case '@':
@ -2326,6 +2374,7 @@ class BBCode
return $match[1] . $match[2]; return $match[1] . $match[2];
} }
break; break;
case '#': case '#':
default: default:
return $match[1] . '[url=' . DI::baseUrl() . '/search?tag=' . $match[2] . ']' . $match[2] . '[/url]'; return $match[1] . '[url=' . DI::baseUrl() . '/search?tag=' . $match[2] . ']' . $match[2] . '[/url]';
@ -2336,7 +2385,7 @@ class BBCode
/** /**
* Perform a custom function on a text after having escaped blocks enclosed in the provided tag list. * Perform a custom function on a text after having escaped blocks enclosed in the provided tag list.
* *
* @param string $text * @param string $text HTML/BBCode
* @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre'] * @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
* @param callable $callback * @param callable $callback
* @return string * @return string
@ -2352,14 +2401,14 @@ class BBCode
/** /**
* Replaces mentions in the provided message body in BBCode links for the provided user and network if any * Replaces mentions in the provided message body in BBCode links for the provided user and network if any
* *
* @param $body * @param string $body HTML/BBCode
* @param $profile_uid * @param int $profile_uid Profile user id
* @param $network * @param string $network Network name
* @return string * @return string HTML/BBCode with inserted images
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function setMentions($body, $profile_uid = 0, $network = '') public static function setMentions(string $body, $profile_uid = 0, $network = '')
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$body = self::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) { $body = self::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) {
@ -2406,7 +2455,7 @@ class BBCode
* @return string * @return string
* @TODO Rewrite to handle over whole record array * @TODO Rewrite to handle over whole record array
*/ */
public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null) public static function getShareOpeningTag(string $author, string $profile, string $avatar, string $link, string $posted, string $guid = null): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$header = "[share author='" . str_replace(["'", "[", "]"], ["&#x27;", "&#x5B;", "&#x5D;"], $author) . $header = "[share author='" . str_replace(["'", "[", "]"], ["&#x27;", "&#x5B;", "&#x5D;"], $author) .
@ -2438,8 +2487,7 @@ class BBCode
* @param string|null $tags * @param string|null $tags
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*@see ParseUrl::getSiteinfoCached * @see ParseUrl::getSiteinfoCached
*
*/ */
public static function embedURL(string $url, bool $tryAttachment = true, string $title = null, string $description = null, string $tags = null): string public static function embedURL(string $url, bool $tryAttachment = true, string $title = null, string $description = null, string $tags = null): string
{ {

View file

@ -61,7 +61,7 @@ class HTML
* inner value from an attribute value and disregard the tag children. * inner value from an attribute value and disregard the tag children.
* @return bool Whether a replacement was done * @return bool Whether a replacement was done
*/ */
private static function tagToBBCodeSub(DOMDocument $doc, string $tag, array $attributes, string $startbb, string $endbb, bool $ignoreChildren = false) private static function tagToBBCodeSub(DOMDocument $doc, string $tag, array $attributes, string $startbb, string $endbb, bool $ignoreChildren = false): bool
{ {
$savestart = str_replace('$', '\x01', $startbb); $savestart = str_replace('$', '\x01', $startbb);
$replace = false; $replace = false;
@ -141,8 +141,16 @@ class HTML
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function toBBCode($message, $basepath = '') public static function toBBCode(string $message, string $basepath = ''): string
{ {
/*
* Check if message is empty to prevent a lot code below being executed
* for just an empty message.
*/
if (empty($message)) {
return '';
}
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$message = str_replace("\r", "", $message); $message = str_replace("\r", "", $message);
@ -409,7 +417,7 @@ class HTML
* *
* @return string The expanded URL * @return string The expanded URL
*/ */
private static function qualifyURLsSub($matches, $basepath) private static function qualifyURLsSub(array $matches, string $basepath): string
{ {
$base = parse_url($basepath); $base = parse_url($basepath);
unset($base['query']); unset($base['query']);
@ -436,7 +444,7 @@ class HTML
* *
* @return string Body with expanded URLs * @return string Body with expanded URLs
*/ */
private static function qualifyURLs($body, $basepath) private static function qualifyURLs(string $body, string $basepath): string
{ {
$URLSearchString = "^\[\]"; $URLSearchString = "^\[\]";
@ -462,7 +470,7 @@ class HTML
return $body; return $body;
} }
private static function breakLines($line, $level, $wraplength = 75) private static function breakLines(string $line, int $level, int $wraplength = 75): string
{ {
if ($wraplength == 0) { if ($wraplength == 0) {
$wraplength = 2000000; $wraplength = 2000000;
@ -503,7 +511,7 @@ class HTML
return implode("\n", $newlines); return implode("\n", $newlines);
} }
private static function quoteLevel($message, $wraplength = 75) private static function quoteLevel(string $message, int $wraplength = 75): string
{ {
$lines = explode("\n", $message); $lines = explode("\n", $message);
@ -539,7 +547,7 @@ class HTML
return implode("\n", $newlines); return implode("\n", $newlines);
} }
private static function collectURLs($message) private static function collectURLs(string $message): array
{ {
$pattern = '/<a.*?href="(.*?)".*?>(.*?)<\/a>/is'; $pattern = '/<a.*?href="(.*?)".*?>(.*?)<\/a>/is';
preg_match_all($pattern, $message, $result, PREG_SET_ORDER); preg_match_all($pattern, $message, $result, PREG_SET_ORDER);
@ -585,7 +593,7 @@ class HTML
* @param bool $compact True: Completely strips image tags; False: Keeps image URLs * @param bool $compact True: Completely strips image tags; False: Keeps image URLs
* @return string * @return string
*/ */
public static function toPlaintext(string $html, $wraplength = 75, $compact = false) public static function toPlaintext(string $html, int $wraplength = 75, bool $compact = false): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$message = str_replace("\r", "", $html); $message = str_replace("\r", "", $html);
@ -705,7 +713,7 @@ class HTML
* @param string $html * @param string $html
* @return string * @return string
*/ */
public static function toMarkdown($html) public static function toMarkdown(string $html): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$converter = new HtmlConverter(['hard_break' => true]); $converter = new HtmlConverter(['hard_break' => true]);
@ -721,7 +729,7 @@ class HTML
* @param string $s * @param string $s
* @return string * @return string
*/ */
public static function toBBCodeVideo($s) public static function toBBCodeVideo(string $s): string
{ {
$s = preg_replace( $s = preg_replace(
'#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism', '#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism',
@ -751,7 +759,7 @@ class HTML
* @param string $base base url * @param string $base base url
* @return string * @return string
*/ */
public static function relToAbs($text, $base) public static function relToAbs(string $text, string $base): string
{ {
if (empty($base)) { if (empty($base)) {
return $text; return $text;
@ -790,7 +798,7 @@ class HTML
* @return string html for loader * @return string html for loader
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function scrollLoader() public static function scrollLoader(): string
{ {
$tpl = Renderer::getMarkupTemplate("scroll_loader.tpl"); $tpl = Renderer::getMarkupTemplate("scroll_loader.tpl");
return Renderer::replaceMacros($tpl, [ return Renderer::replaceMacros($tpl, [
@ -819,7 +827,7 @@ class HTML
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function micropro($contact, $redirect = false, $class = '', $textmode = false) public static function micropro(array $contact, bool $redirect = false, string $class = '', bool $textmode = false): string
{ {
// Use the contact URL if no address is available // Use the contact URL if no address is available
if (empty($contact['addr'])) { if (empty($contact['addr'])) {
@ -859,13 +867,12 @@ class HTML
* *
* @param string $s Search query. * @param string $s Search query.
* @param string $id HTML id * @param string $id HTML id
* @param string $url Search url.
* @param bool $aside Display the search widgit aside. * @param bool $aside Display the search widgit aside.
* *
* @return string Formatted HTML. * @return string Formatted HTML.
* @throws \Exception * @throws \Exception
*/ */
public static function search($s, $id = 'search-box', $aside = true) public static function search(string $s, string $id = 'search-box', bool $aside = true): string
{ {
$mode = 'text'; $mode = 'text';
@ -906,7 +913,7 @@ class HTML
* @param string $s * @param string $s
* @return string * @return string
*/ */
public static function toLink($s) public static function toLink(string $s): string
{ {
$s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $s); $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', $s);
$s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism", '<$1$2=$3&$4>', $s); $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism", '<$1$2=$3&$4>', $s);
@ -923,7 +930,7 @@ class HTML
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function applyContentFilter($html, array $reasons) public static function applyContentFilter(string $html, array $reasons): string
{ {
if (count($reasons)) { if (count($reasons)) {
$tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl'); $tpl = Renderer::getMarkupTemplate('wall/content_filter.tpl');
@ -943,7 +950,7 @@ class HTML
* @param string $s * @param string $s
* @return string * @return string
*/ */
public static function unamp($s) public static function unamp(string $s): string
{ {
return str_replace('&amp;', '&', $s); return str_replace('&amp;', '&', $s);
} }

View file

@ -36,7 +36,7 @@ class Plaintext
* *
* @todo For Twitter URLs aren't shortened, but they have to be calculated as if. * @todo For Twitter URLs aren't shortened, but they have to be calculated as if.
*/ */
public static function shorten(string $msg, int $limit, int $uid = 0):string public static function shorten(string $msg, int $limit, int $uid = 0): string
{ {
$ellipsis = html_entity_decode("&#x2026;", ENT_QUOTES, 'UTF-8'); $ellipsis = html_entity_decode("&#x2026;", ENT_QUOTES, 'UTF-8');

View file

@ -45,7 +45,7 @@ class Widget
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function follow($value = "") public static function follow(string $value = ''): string
{ {
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/follow.tpl'), array( return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/follow.tpl'), array(
'$connect' => DI::l10n()->t('Add New Contact'), '$connect' => DI::l10n()->t('Add New Contact'),
@ -58,8 +58,10 @@ class Widget
/** /**
* Return Find People widget * Return Find People widget
*
* @return string HTML code respresenting "People Widget"
*/ */
public static function findPeople() public static function findPeople(): string
{ {
$global_dir = Search::getGlobalDirectory(); $global_dir = Search::getGlobalDirectory();
@ -97,7 +99,7 @@ class Widget
* *
* @return array Unsupported networks * @return array Unsupported networks
*/ */
public static function unavailableNetworks() public static function unavailableNetworks(): array
{ {
// Always hide content from these networks // Always hide content from these networks
$networks = [Protocol::PHANTOM, Protocol::FACEBOOK, Protocol::APPNET, Protocol::ZOT]; $networks = [Protocol::PHANTOM, Protocol::FACEBOOK, Protocol::APPNET, Protocol::ZOT];
@ -154,7 +156,7 @@ class Widget
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
private static function filter($type, $title, $desc, $all, $baseUrl, array $options, $selected = null) private static function filter(string $type, string $title, string $desc, string $all, string $baseUrl, array $options, string $selected = null): string
{ {
$queryString = parse_url($baseUrl, PHP_URL_QUERY); $queryString = parse_url($baseUrl, PHP_URL_QUERY);
$queryArray = []; $queryArray = [];
@ -191,7 +193,7 @@ class Widget
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
public static function groups($baseurl, $selected = '') public static function groups(string $baseurl, string $selected = ''): string
{ {
if (!local_user()) { if (!local_user()) {
return ''; return '';
@ -223,7 +225,7 @@ class Widget
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
public static function contactRels($baseurl, $selected = '') public static function contactRels(string $baseurl, string $selected = ''): string
{ {
if (!local_user()) { if (!local_user()) {
return ''; return '';
@ -254,7 +256,7 @@ class Widget
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function networks($baseurl, $selected = '') public static function networks(string $baseurl, string $selected = ''): string
{ {
if (!local_user()) { if (!local_user()) {
return ''; return '';
@ -292,10 +294,10 @@ class Widget
* *
* @param string $baseurl baseurl * @param string $baseurl baseurl
* @param string $selected optional, default empty * @param string $selected optional, default empty
* @return string|void * @return string
* @throws \Exception * @throws \Exception
*/ */
public static function fileAs($baseurl, $selected = '') public static function fileAs(string $baseurl, string $selected = ''): string
{ {
if (!local_user()) { if (!local_user()) {
return ''; return '';
@ -323,10 +325,10 @@ class Widget
* @param int $uid Id of the user owning the categories * @param int $uid Id of the user owning the categories
* @param string $baseurl Base page URL * @param string $baseurl Base page URL
* @param string $selected Selected category * @param string $selected Selected category
* @return string|void * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function categories(int $uid, string $baseurl, string $selected = '') public static function categories(int $uid, string $baseurl, string $selected = ''): string
{ {
if (!Feature::isEnabled($uid, 'categories')) { if (!Feature::isEnabled($uid, 'categories')) {
return ''; return '';
@ -353,11 +355,11 @@ class Widget
* *
* @param int $uid Viewed profile user ID * @param int $uid Viewed profile user ID
* @param string $nickname Viewed profile user nickname * @param string $nickname Viewed profile user nickname
* @return string|void * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function commonFriendsVisitor(int $uid, string $nickname) public static function commonFriendsVisitor(int $uid, string $nickname): string
{ {
if (local_user() == $uid) { if (local_user() == $uid) {
return ''; return '';
@ -414,7 +416,7 @@ class Widget
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function tagCloud(int $uid, int $limit = 50) public static function tagCloud(int $uid, int $limit = 50): string
{ {
if (empty($uid)) { if (empty($uid)) {
return ''; return '';
@ -439,7 +441,7 @@ class Widget
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function postedByYear(string $url, int $uid, bool $wall) public static function postedByYear(string $url, int $uid, bool $wall): string
{ {
$o = ''; $o = '';
@ -510,10 +512,10 @@ class Widget
* The account type value is added as a parameter to the url * The account type value is added as a parameter to the url
* *
* @param string $base Basepath * @param string $base Basepath
* @param int $accounttype Acount type * @param string $accounttype Account type
* @return string * @return string
*/ */
public static function accounttypes(string $base, $accounttype) public static function accountTypes(string $base, string $accounttype): string
{ {
$accounts = [ $accounts = [
['ref' => 'person', 'name' => DI::l10n()->t('Persons')], ['ref' => 'person', 'name' => DI::l10n()->t('Persons')],

View file

@ -124,7 +124,7 @@ class Addon
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function uninstall($addon) public static function uninstall(string $addon)
{ {
$addon = Strings::sanitizeFilePathItem($addon); $addon = Strings::sanitizeFilePathItem($addon);
@ -149,7 +149,7 @@ class Addon
* @return bool * @return bool
* @throws \Exception * @throws \Exception
*/ */
public static function install($addon) public static function install(string $addon): bool
{ {
$addon = Strings::sanitizeFilePathItem($addon); $addon = Strings::sanitizeFilePathItem($addon);
@ -185,6 +185,8 @@ class Addon
/** /**
* reload all updated addons * reload all updated addons
*
* @return void
*/ */
public static function reload() public static function reload()
{ {
@ -222,7 +224,7 @@ class Addon
* @return array with the addon information * @return array with the addon information
* @throws \Exception * @throws \Exception
*/ */
public static function getInfo($addon) public static function getInfo(string $addon): array
{ {
$addon = Strings::sanitizeFilePathItem($addon); $addon = Strings::sanitizeFilePathItem($addon);
@ -287,7 +289,7 @@ class Addon
* @param string $addon * @param string $addon
* @return boolean * @return boolean
*/ */
public static function isEnabled($addon) public static function isEnabled(string $addon): bool
{ {
return in_array($addon, self::$addons); return in_array($addon, self::$addons);
} }
@ -297,7 +299,7 @@ class Addon
* *
* @return array * @return array
*/ */
public static function getEnabledList() public static function getEnabledList(): array
{ {
return self::$addons; return self::$addons;
} }
@ -308,7 +310,7 @@ class Addon
* @return array * @return array
* @throws \Exception * @throws \Exception
*/ */
public static function getVisibleList() public static function getVisibleList(): array
{ {
$visible_addons = []; $visible_addons = [];
$stmt = DBA::select('addon', ['name'], ['hidden' => false, 'installed' => true]); $stmt = DBA::select('addon', ['name'], ['hidden' => false, 'installed' => true]);

View file

@ -68,6 +68,17 @@ class MemcacheCache extends AbstractCache implements ICanCacheInMemory
} }
} }
/**
* Memcache doesn't allow spaces in keys
*
* @param string $key
* @return string
*/
protected function getCacheKey(string $key): string
{
return str_replace(' ', '_', parent::getCacheKey($key));
}
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */

View file

@ -93,6 +93,17 @@ class MemcachedCache extends AbstractCache implements ICanCacheInMemory
} }
} }
/**
* Memcached doesn't allow spaces in keys
*
* @param string $key
* @return string
*/
protected function getCacheKey(string $key): string
{
return str_replace(' ', '_', parent::getCacheKey($key));
}
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */

View file

@ -121,7 +121,7 @@ HELP;
$this->dice = $dice; $this->dice = $dice;
} }
protected function doExecute() protected function doExecute(): int
{ {
if ($this->getOption('v')) { if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable); $this->out('Executable: ' . $this->executable);

View file

@ -49,6 +49,8 @@ class Hook
/** /**
* Load hooks * Load hooks
*
* @return void
*/ */
public static function loadHooks() public static function loadHooks()
{ {
@ -69,8 +71,9 @@ class Hook
* @param string $hook * @param string $hook
* @param string $file * @param string $file
* @param string $function * @param string $function
* @return void
*/ */
public static function add($hook, $file, $function) public static function add(string $hook, string $file, string $function)
{ {
if (!array_key_exists($hook, self::$hooks)) { if (!array_key_exists($hook, self::$hooks)) {
self::$hooks[$hook] = []; self::$hooks[$hook] = [];
@ -90,7 +93,7 @@ class Hook
* @return mixed|bool * @return mixed|bool
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function register($hook, $file, $function, $priority = 0) public static function register(string $hook, string $file, string $function, int $priority = 0)
{ {
$file = str_replace(DI::app()->getBasePath() . DIRECTORY_SEPARATOR, '', $file); $file = str_replace(DI::app()->getBasePath() . DIRECTORY_SEPARATOR, '', $file);
@ -111,7 +114,7 @@ class Hook
* @return boolean * @return boolean
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function unregister($hook, $file, $function) public static function unregister(string $hook, string $file, string $function): bool
{ {
$relative_file = str_replace(DI::app()->getBasePath() . DIRECTORY_SEPARATOR, '', $file); $relative_file = str_replace(DI::app()->getBasePath() . DIRECTORY_SEPARATOR, '', $file);
@ -120,8 +123,8 @@ class Hook
self::delete($condition); self::delete($condition);
$condition = ['hook' => $hook, 'file' => $relative_file, 'function' => $function]; $condition = ['hook' => $hook, 'file' => $relative_file, 'function' => $function];
$result = self::delete($condition);
return $result; return self::delete($condition);
} }
/** /**
@ -130,7 +133,7 @@ class Hook
* @param string $name Name of the hook * @param string $name Name of the hook
* @return array * @return array
*/ */
public static function getByName($name) public static function getByName(string $name): array
{ {
$return = []; $return = [];
@ -149,9 +152,10 @@ class Hook
* @param integer $priority of the hook * @param integer $priority of the hook
* @param string $name of the hook to call * @param string $name of the hook to call
* @param mixed $data to transmit to the callback handler * @param mixed $data to transmit to the callback handler
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function fork($priority, $name, $data = null) public static function fork(int $priority, string $name, $data = null)
{ {
if (array_key_exists($name, self::$hooks)) { if (array_key_exists($name, self::$hooks)) {
foreach (self::$hooks[$name] as $hook) { foreach (self::$hooks[$name] as $hook) {
@ -184,9 +188,10 @@ class Hook
* *
* @param string $name of the hook to call * @param string $name of the hook to call
* @param string|array &$data to transmit to the callback handler * @param string|array &$data to transmit to the callback handler
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function callAll($name, &$data = null) public static function callAll(string $name, &$data = null)
{ {
if (array_key_exists($name, self::$hooks)) { if (array_key_exists($name, self::$hooks)) {
foreach (self::$hooks[$name] as $hook) { foreach (self::$hooks[$name] as $hook) {
@ -202,9 +207,10 @@ class Hook
* @param string $name of the hook to call * @param string $name of the hook to call
* @param array $hook Hook data * @param array $hook Hook data
* @param string|array &$data to transmit to the callback handler * @param string|array &$data to transmit to the callback handler
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function callSingle(App $a, $name, $hook, &$data = null) public static function callSingle(App $a, string $name, array $hook, &$data = null)
{ {
// Don't run a theme's hook if the user isn't using the theme // Don't run a theme's hook if the user isn't using the theme
if (strpos($hook[0], 'view/theme/') !== false && strpos($hook[0], 'view/theme/' . $a->getCurrentTheme()) === false) { if (strpos($hook[0], 'view/theme/') !== false && strpos($hook[0], 'view/theme/' . $a->getCurrentTheme()) === false) {
@ -229,7 +235,7 @@ class Hook
* @param string $name Name of the addon * @param string $name Name of the addon
* @return boolean * @return boolean
*/ */
public static function isAddonApp($name) public static function isAddonApp(string $name): bool
{ {
$name = Strings::sanitizeFilePathItem($name); $name = Strings::sanitizeFilePathItem($name);
@ -253,7 +259,7 @@ class Hook
* @return bool * @return bool
* @throws \Exception * @throws \Exception
*/ */
public static function delete(array $condition) public static function delete(array $condition): bool
{ {
$result = DBA::delete('hook', $condition); $result = DBA::delete('hook', $condition);
@ -273,7 +279,7 @@ class Hook
* @return bool * @return bool
* @throws \Exception * @throws \Exception
*/ */
private static function insert(array $condition) private static function insert(array $condition): bool
{ {
$result = DBA::insert('hook', $condition); $result = DBA::insert('hook', $condition);

View file

@ -189,14 +189,12 @@ class Installer
/*** /***
* Installs the DB-Scheme for Friendica * Installs the DB-Scheme for Friendica
* *
* @param string $basePath The base path of this application
*
* @return bool true if the installation was successful, otherwise false * @return bool true if the installation was successful, otherwise false
* @throws Exception * @throws Exception
*/ */
public function installDatabase($basePath) public function installDatabase(): bool
{ {
$result = DBStructure::install($basePath); $result = DBStructure::install();
if ($result) { if ($result) {
$txt = DI::l10n()->t('You may need to import the file "database.sql" manually using phpmyadmin or mysql.') . EOL; $txt = DI::l10n()->t('You may need to import the file "database.sql" manually using phpmyadmin or mysql.') . EOL;
@ -656,7 +654,7 @@ class Installer
* @return bool true if the check was successful, otherwise false * @return bool true if the check was successful, otherwise false
* @throws Exception * @throws Exception
*/ */
public function checkDB(Database $dba) public function checkDB(Database $dba): bool
{ {
$dba->reconnect(); $dba->reconnect();

View file

@ -128,7 +128,7 @@ class L10n
private function setLangFromSession(IHandleSessions $session) private function setLangFromSession(IHandleSessions $session)
{ {
if ($session->get('language') !== $this->lang) { if ($session->get('language') !== $this->lang) {
$this->loadTranslationTable($session->get('language')); $this->loadTranslationTable($session->get('language') ?? $this->lang);
} }
} }
@ -140,10 +140,10 @@ class L10n
* Uses an App object shim since all the strings files refer to $a->strings * Uses an App object shim since all the strings files refer to $a->strings
* *
* @param string $lang language code to load * @param string $lang language code to load
* * @return void
* @throws \Exception * @throws \Exception
*/ */
private function loadTranslationTable($lang) private function loadTranslationTable(string $lang)
{ {
$lang = Strings::sanitizeFilePathItem($lang); $lang = Strings::sanitizeFilePathItem($lang);
@ -183,7 +183,7 @@ class L10n
* *
* @return string The two-letter language code * @return string The two-letter language code
*/ */
public static function detectLanguage(array $server, array $get, string $sysLang = self::DEFAULT) public static function detectLanguage(array $server, array $get, string $sysLang = self::DEFAULT): string
{ {
$lang_variable = $server['HTTP_ACCEPT_LANGUAGE'] ?? null; $lang_variable = $server['HTTP_ACCEPT_LANGUAGE'] ?? null;
@ -269,7 +269,7 @@ class L10n
* *
* @return string * @return string
*/ */
public function t($s, ...$vars) public function t(string $s, ...$vars): string
{ {
if (empty($s)) { if (empty($s)) {
return ''; return '';
@ -307,7 +307,7 @@ class L10n
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
public function tt(string $singular, string $plural, int $count) public function tt(string $singular, string $plural, int $count): string
{ {
$s = null; $s = null;
@ -352,7 +352,7 @@ class L10n
* *
* @return bool * @return bool
*/ */
private function stringPluralSelectDefault($n) private function stringPluralSelectDefault(int $n): bool
{ {
return $n != 1; return $n != 1;
} }
@ -369,7 +369,7 @@ class L10n
* *
* @return array * @return array
*/ */
public static function getAvailableLanguages() public static function getAvailableLanguages(): array
{ {
$langs = []; $langs = [];
$strings_file_paths = glob('view/lang/*/strings.php'); $strings_file_paths = glob('view/lang/*/strings.php');
@ -391,10 +391,9 @@ class L10n
* Translate days and months names. * Translate days and months names.
* *
* @param string $s String with day or month name. * @param string $s String with day or month name.
*
* @return string Translated string. * @return string Translated string.
*/ */
public function getDay($s) public function getDay(string $s): string
{ {
$ret = str_replace(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], $ret = str_replace(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
[$this->t('Monday'), $this->t('Tuesday'), $this->t('Wednesday'), $this->t('Thursday'), $this->t('Friday'), $this->t('Saturday'), $this->t('Sunday')], [$this->t('Monday'), $this->t('Tuesday'), $this->t('Wednesday'), $this->t('Thursday'), $this->t('Friday'), $this->t('Saturday'), $this->t('Sunday')],
@ -411,10 +410,9 @@ class L10n
* Translate short days and months names. * Translate short days and months names.
* *
* @param string $s String with short day or month name. * @param string $s String with short day or month name.
*
* @return string Translated string. * @return string Translated string.
*/ */
public function getDayShort($s) public function getDayShort(string $s): string
{ {
$ret = str_replace(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], $ret = str_replace(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
[$this->t('Mon'), $this->t('Tue'), $this->t('Wed'), $this->t('Thu'), $this->t('Fri'), $this->t('Sat'), $this->t('Sun')], [$this->t('Mon'), $this->t('Tue'), $this->t('Wed'), $this->t('Thu'), $this->t('Fri'), $this->t('Sat'), $this->t('Sun')],
@ -435,7 +433,7 @@ class L10n
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @hook poke_verbs pokes array * @hook poke_verbs pokes array
*/ */
public function getPokeVerbs() public function getPokeVerbs(): array
{ {
// index is present tense verb // index is present tense verb
// value is array containing past tense verb, translation of present, translation of past // value is array containing past tense verb, translation of present, translation of past
@ -461,7 +459,7 @@ class L10n
* @return static A new L10n instance * @return static A new L10n instance
* @throws \Exception * @throws \Exception
*/ */
public function withLang(string $lang) public function withLang(string $lang): L10n
{ {
// Don't create a new instance for same language // Don't create a new instance for same language
if ($lang === $this->lang) { if ($lang === $this->lang) {

View file

@ -47,7 +47,7 @@ class Logger
/** /**
* @return LoggerInterface * @return LoggerInterface
*/ */
private static function getWorker() private static function getInstance()
{ {
if (self::$type === self::TYPE_LOGGER) { if (self::$type === self::TYPE_LOGGER) {
return DI::logger(); return DI::logger();
@ -66,7 +66,7 @@ class Logger
public static function enableWorker(string $functionName) public static function enableWorker(string $functionName)
{ {
self::$type = self::TYPE_WORKER; self::$type = self::TYPE_WORKER;
self::getWorker()->setFunctionName($functionName); self::getInstance()->setFunctionName($functionName);
} }
/** /**
@ -82,15 +82,14 @@ class Logger
* *
* @see LoggerInterface::emergency() * @see LoggerInterface::emergency()
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function emergency($message, $context = []) public static function emergency(string $message, array $context = [])
{ {
self::getWorker()->emergency($message, $context); self::getInstance()->emergency($message, $context);
} }
/** /**
@ -100,15 +99,14 @@ class Logger
* Example: Entire website down, database unavailable, etc. This should * Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up. * trigger the SMS alerts and wake you up.
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function alert($message, $context = []) public static function alert(string $message, array $context = [])
{ {
self::getWorker()->alert($message, $context); self::getInstance()->alert($message, $context);
} }
/** /**
@ -117,15 +115,14 @@ class Logger
* *
* Example: Application component unavailable, unexpected exception. * Example: Application component unavailable, unexpected exception.
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function critical($message, $context = []) public static function critical(string $message, array $context = [])
{ {
self::getWorker()->critical($message, $context); self::getInstance()->critical($message, $context);
} }
/** /**
@ -133,15 +130,14 @@ class Logger
* be logged and monitored. * be logged and monitored.
* @see LoggerInterface::error() * @see LoggerInterface::error()
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function error($message, $context = []) public static function error(string $message, array $context = [])
{ {
self::getWorker()->error($message, $context); self::getInstance()->error($message, $context);
} }
/** /**
@ -151,30 +147,28 @@ class Logger
* Example: Use of deprecated APIs, poor use of an API, undesirable things * Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong. * that are not necessarily wrong.
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function warning($message, $context = []) public static function warning(string $message, array $context = [])
{ {
self::getWorker()->warning($message, $context); self::getInstance()->warning($message, $context);
} }
/** /**
* Normal but significant events. * Normal but significant events.
* @see LoggerInterface::notice() * @see LoggerInterface::notice()
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function notice($message, $context = []) public static function notice(string $message, array $context = [])
{ {
self::getWorker()->notice($message, $context); self::getInstance()->notice($message, $context);
} }
/** /**
@ -189,24 +183,23 @@ class Logger
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function info($message, $context = []) public static function info(string $message, array $context = [])
{ {
self::getWorker()->info($message, $context); self::getInstance()->info($message, $context);
} }
/** /**
* Detailed debug information. * Detailed debug information.
* @see LoggerInterface::debug() * @see LoggerInterface::debug()
* *
* @param string $message * @param string $message Message to log
* @param array $context * @param array $context Optional variables
*
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
public static function debug($message, $context = []) public static function debug(string $message, array $context = [])
{ {
self::getWorker()->debug($message, $context); self::getInstance()->debug($message, $context);
} }
/** /**
@ -216,12 +209,13 @@ class Logger
* to isolate particular elements they are targetting * to isolate particular elements they are targetting
* personally without background noise * personally without background noise
* *
* @param string $msg * @param string $message Message to log
* @param string $level * @param string $level Logging level
* @return void
* @throws \Exception * @throws \Exception
*/ */
public static function devLog($msg, $level = LogLevel::DEBUG) public static function devLog(string $message, string $level = LogLevel::DEBUG)
{ {
DI::devLogger()->log($level, $msg); DI::devLogger()->log($level, $message);
} }
} }

View file

@ -21,6 +21,7 @@
namespace Friendica\Core\Logger\Type\Monolog; namespace Friendica\Core\Logger\Type\Monolog;
use Friendica\App\Request;
use Monolog\Handler; use Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
@ -38,15 +39,22 @@ class DevelopHandler extends Handler\AbstractHandler
private $developerIp; private $developerIp;
/** /**
* @param string $developerIp The IP of the developer who wants to debug * @var string The IP of the current request
* @param int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($developerIp, $level = Logger::DEBUG, bool $bubble = true) private $remoteAddress;
/**
* @param Request $request The current http request
* @param string $developerIp The IP of the developer who wants to debug
* @param int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct(Request $request, $developerIp, int $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->developerIp = $developerIp; $this->developerIp = $developerIp;
$this->remoteAddress = $request->getRemoteAddress();
} }
/** /**
@ -59,7 +67,7 @@ class DevelopHandler extends Handler\AbstractHandler
} }
/// Just in case the remote IP is the same as the developer IP log the output /// Just in case the remote IP is the same as the developer IP log the output
if (!is_null($this->developerIp) && $_SERVER['REMOTE_ADDR'] != $this->developerIp) { if (!is_null($this->developerIp) && $this->remoteAddress != $this->developerIp) {
return false; return false;
} }

View file

@ -71,7 +71,7 @@ class Renderer
* @return string * @return string
* @throws ServiceUnavailableException * @throws ServiceUnavailableException
*/ */
public static function replaceMacros(string $template, array $vars = []) public static function replaceMacros(string $template, array $vars = []): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');

View file

@ -441,6 +441,12 @@ class System
* *
* @param string $url The new Location to redirect * @param string $url The new Location to redirect
* @param int $code The redirection code, which is used (Default is 302) * @param int $code The redirection code, which is used (Default is 302)
*
* @throws FoundException
* @throws MovedPermanentlyException
* @throws TemporaryRedirectException
*
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function externalRedirect($url, $code = 302) public static function externalRedirect($url, $code = 302)
{ {

View file

@ -32,7 +32,7 @@ require_once 'boot.php';
*/ */
class Theme class Theme
{ {
public static function getAllowedList() public static function getAllowedList(): array
{ {
$allowed_themes_str = DI::config()->get('system', 'allowed_themes'); $allowed_themes_str = DI::config()->get('system', 'allowed_themes');
$allowed_themes_raw = explode(',', str_replace(' ', '', $allowed_themes_str)); $allowed_themes_raw = explode(',', str_replace(' ', '', $allowed_themes_str));
@ -69,7 +69,7 @@ class Theme
* @param string $theme the name of the theme * @param string $theme the name of the theme
* @return array * @return array
*/ */
public static function getInfo($theme) public static function getInfo(string $theme): array
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
@ -133,7 +133,7 @@ class Theme
* @return string * @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function getScreenshot($theme) public static function getScreenshot(string $theme): string
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
@ -146,7 +146,13 @@ class Theme
return DI::baseUrl() . '/images/blank.png'; return DI::baseUrl() . '/images/blank.png';
} }
public static function uninstall($theme) /**
* Uninstalls given theme name
*
* @param string $theme Name of theme
* @return bool true on success
*/
public static function uninstall(string $theme)
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
@ -167,10 +173,18 @@ class Theme
if ($key !== false) { if ($key !== false) {
unset($allowed_themes[$key]); unset($allowed_themes[$key]);
Theme::setAllowedList($allowed_themes); Theme::setAllowedList($allowed_themes);
return true;
} }
return false;
} }
public static function install($theme) /**
* Installs given theme name
*
* @param string $theme Name of theme
* @return bool true on success
*/
public static function install(string $theme): bool
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
@ -208,7 +222,7 @@ class Theme
* @return string Path to the file or empty string if the file isn't found * @return string Path to the file or empty string if the file isn't found
* @throws \Exception * @throws \Exception
*/ */
public static function getPathForFile($file) public static function getPathForFile(string $file): string
{ {
$a = DI::app(); $a = DI::app();
@ -237,10 +251,9 @@ class Theme
* Provide a sane default if nothing is chosen or the specified theme does not exist. * Provide a sane default if nothing is chosen or the specified theme does not exist.
* *
* @param string $theme Theme name * @param string $theme Theme name
*
* @return string * @return string
*/ */
public static function getStylesheetPath($theme) public static function getStylesheetPath(string $theme): string
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
@ -263,10 +276,10 @@ class Theme
/** /**
* Returns the path of the provided theme * Returns the path of the provided theme
* *
* @param $theme * @param string $theme Theme name
* @return string|null * @return string|null
*/ */
public static function getConfigFile($theme) public static function getConfigFile(string $theme)
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);
@ -285,11 +298,11 @@ class Theme
/** /**
* Returns the background color of the provided theme if available. * Returns the background color of the provided theme if available.
* *
* @param string $theme * @param string $theme Theme name
* @param int|null $uid Current logged-in user id * @param int|null $uid Current logged-in user id
* @return string|null * @return string|null
*/ */
public static function getBackgroundColor(string $theme, $uid = null) public static function getBackgroundColor(string $theme, int $uid = null)
{ {
$theme = Strings::sanitizeFilePathItem($theme); $theme = Strings::sanitizeFilePathItem($theme);

View file

@ -41,7 +41,7 @@ class Update
* @param string $basePath The base path of this application * @param string $basePath The base path of this application
* @param boolean $via_worker Is the check run via the worker? * @param boolean $via_worker Is the check run via the worker?
* @param App\Mode $mode The current app mode * @param App\Mode $mode The current app mode
* * @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function check(string $basePath, bool $via_worker, App\Mode $mode) public static function check(string $basePath, bool $via_worker, App\Mode $mode)
@ -73,7 +73,7 @@ class Update
} }
// The postupdate has to completed version 1288 for the new post views to take over // The postupdate has to completed version 1288 for the new post views to take over
$postupdate = DI::config()->get("system", "post_update_version", NEW_TABLE_STRUCTURE_VERSION); $postupdate = DI::config()->get('system', 'post_update_version', NEW_TABLE_STRUCTURE_VERSION);
if ($postupdate < NEW_TABLE_STRUCTURE_VERSION) { if ($postupdate < NEW_TABLE_STRUCTURE_VERSION) {
$error = DI::l10n()->t('Updates from postupdate version %s are not supported. Please update at least to version 2021.01 and wait until the postupdate finished version 1383.', $postupdate); $error = DI::l10n()->t('Updates from postupdate version %s are not supported. Please update at least to version 2021.01 and wait until the postupdate finished version 1383.', $postupdate);
if (DI::mode()->getExecutor() == Mode::INDEX) { if (DI::mode()->getExecutor() == Mode::INDEX) {
@ -85,9 +85,11 @@ class Update
if ($build < DB_UPDATE_VERSION) { if ($build < DB_UPDATE_VERSION) {
if ($via_worker) { if ($via_worker) {
// Calling the database update directly via the worker enables us to perform database changes to the workerqueue table itself. /*
// This is a fallback, since normally the database update will be performed by a worker job. * Calling the database update directly via the worker enables us to perform database changes to the workerqueue table itself.
// This worker job doesn't work for changes to the "workerqueue" table itself. * This is a fallback, since normally the database update will be performed by a worker job.
* This worker job doesn't work for changes to the "workerqueue" table itself.
*/
self::run($basePath); self::run($basePath);
} else { } else {
Worker::add(PRIORITY_CRITICAL, 'DBUpdate'); Worker::add(PRIORITY_CRITICAL, 'DBUpdate');
@ -103,11 +105,10 @@ class Update
* @param bool $override Overrides any running/stuck updates * @param bool $override Overrides any running/stuck updates
* @param bool $verbose Run the Update-Check verbose * @param bool $verbose Run the Update-Check verbose
* @param bool $sendMail Sends a Mail to the administrator in case of success/failure * @param bool $sendMail Sends a Mail to the administrator in case of success/failure
*
* @return string Empty string if the update is successful, error messages otherwise * @return string Empty string if the update is successful, error messages otherwise
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function run(string $basePath, bool $force = false, bool $override = false, bool $verbose = false, bool $sendMail = true) public static function run(string $basePath, bool $force = false, bool $override = false, bool $verbose = false, bool $sendMail = true): string
{ {
// In force mode, we release the dbupdate lock first // In force mode, we release the dbupdate lock first
// Necessary in case of an stuck update // Necessary in case of an stuck update
@ -228,11 +229,10 @@ class Update
* @param int $version the DB version number of the function * @param int $version the DB version number of the function
* @param string $prefix the prefix of the function (update, pre_update) * @param string $prefix the prefix of the function (update, pre_update)
* @param bool $sendMail whether to send emails on success/failure * @param bool $sendMail whether to send emails on success/failure
* @return bool true, if the update function worked * @return bool true, if the update function worked
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function runUpdateFunction(int $version, string $prefix, bool $sendMail = true) public static function runUpdateFunction(int $version, string $prefix, bool $sendMail = true): bool
{ {
$funcname = $prefix . '_' . $version; $funcname = $prefix . '_' . $version;
@ -284,6 +284,7 @@ class Update
* *
* @param int $update_id number of failed update * @param int $update_id number of failed update
* @param string $error_message error message * @param string $error_message error message
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function updateFailed(int $update_id, string $error_message) { private static function updateFailed(int $update_id, string $error_message) {

View file

@ -86,7 +86,7 @@ class UserImport
* @return array|bool * @return array|bool
* @throws \Exception * @throws \Exception
*/ */
private static function dbImportAssoc($table, $arr) private static function dbImportAssoc(string $table, array $arr)
{ {
if (isset($arr['id'])) { if (isset($arr['id'])) {
unset($arr['id']); unset($arr['id']);
@ -105,10 +105,11 @@ class UserImport
* Import account file exported from mod/uexport * Import account file exported from mod/uexport
* *
* @param array $file array from $_FILES * @param array $file array from $_FILES
* @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function importAccount($file) public static function importAccount(array $file)
{ {
Logger::notice("Start user import from " . $file['tmp_name']); Logger::notice("Start user import from " . $file['tmp_name']);
/* /*

View file

@ -60,7 +60,7 @@ class Worker
* @return void * @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function processQueue($run_cron, Process $process) public static function processQueue(bool $run_cron, Process $process)
{ {
self::$up_start = microtime(true); self::$up_start = microtime(true);
@ -169,7 +169,7 @@ class Worker
* *
* @return boolean * @return boolean
*/ */
public static function isReady() public static function isReady(): bool
{ {
// Count active workers and compare them with a maximum value that depends on the load // Count active workers and compare them with a maximum value that depends on the load
if (self::tooMuchWorkers()) { if (self::tooMuchWorkers()) {
@ -204,7 +204,7 @@ class Worker
* @return boolean Returns "true" if tasks are existing * @return boolean Returns "true" if tasks are existing
* @throws \Exception * @throws \Exception
*/ */
public static function entriesExists() public static function entriesExists(): bool
{ {
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
$exists = DBA::exists('workerqueue', ["NOT `done` AND `pid` = 0 AND `next_try` < ?", DateTimeFormat::utcNow()]); $exists = DBA::exists('workerqueue', ["NOT `done` AND `pid` = 0 AND `next_try` < ?", DateTimeFormat::utcNow()]);
@ -218,7 +218,7 @@ class Worker
* @return integer Number of deferred entries in the worker queue * @return integer Number of deferred entries in the worker queue
* @throws \Exception * @throws \Exception
*/ */
private static function deferredEntries() private static function deferredEntries(): int
{ {
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
$count = DBA::count('workerqueue', ["NOT `done` AND `pid` = 0 AND `retrial` > ?", 0]); $count = DBA::count('workerqueue', ["NOT `done` AND `pid` = 0 AND `retrial` > ?", 0]);
@ -233,7 +233,7 @@ class Worker
* @return integer Number of non executed entries in the worker queue * @return integer Number of non executed entries in the worker queue
* @throws \Exception * @throws \Exception
*/ */
private static function totalEntries() private static function totalEntries(): int
{ {
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
$count = DBA::count('workerqueue', ['done' => false, 'pid' => 0]); $count = DBA::count('workerqueue', ['done' => false, 'pid' => 0]);
@ -248,7 +248,7 @@ class Worker
* @return integer Number of active worker processes * @return integer Number of active worker processes
* @throws \Exception * @throws \Exception
*/ */
private static function highestPriority() private static function highestPriority(): int
{ {
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
$condition = ["`pid` = 0 AND NOT `done` AND `next_try` < ?", DateTimeFormat::utcNow()]; $condition = ["`pid` = 0 AND NOT `done` AND `next_try` < ?", DateTimeFormat::utcNow()];
@ -269,7 +269,7 @@ class Worker
* @return integer Is there a process running with that priority? * @return integer Is there a process running with that priority?
* @throws \Exception * @throws \Exception
*/ */
private static function processWithPriorityActive($priority) private static function processWithPriorityActive(int $priority): int
{ {
$condition = ["`priority` <= ? AND `pid` != 0 AND NOT `done`", $priority]; $condition = ["`priority` <= ? AND `pid` != 0 AND NOT `done`", $priority];
return DBA::exists('workerqueue', $condition); return DBA::exists('workerqueue', $condition);
@ -281,7 +281,7 @@ class Worker
* @param mixed $file * @param mixed $file
* @return bool * @return bool
*/ */
private static function validateInclude(&$file) private static function validateInclude(&$file): bool
{ {
$orig_file = $file; $orig_file = $file;
@ -321,7 +321,7 @@ class Worker
* @return boolean "true" if further processing should be stopped * @return boolean "true" if further processing should be stopped
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function execute($queue) public static function execute(array $queue): bool
{ {
$mypid = getmypid(); $mypid = getmypid();
@ -454,7 +454,7 @@ class Worker
* @return void * @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function execFunction($queue, $funcname, $argv, $method_call) private static function execFunction(array $queue, string $funcname, array $argv, bool $method_call)
{ {
$a = DI::app(); $a = DI::app();
@ -543,7 +543,7 @@ class Worker
* @return bool Are more than 3/4 of the maximum connections used? * @return bool Are more than 3/4 of the maximum connections used?
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function maxConnectionsReached() private static function maxConnectionsReached(): bool
{ {
// Fetch the max value from the config. This is needed when the system cannot detect the correct value by itself. // Fetch the max value from the config. This is needed when the system cannot detect the correct value by itself.
$max = DI::config()->get("system", "max_connections"); $max = DI::config()->get("system", "max_connections");
@ -627,7 +627,7 @@ class Worker
* @return bool Are there too much workers running? * @return bool Are there too much workers running?
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function tooMuchWorkers() private static function tooMuchWorkers(): bool
{ {
$queues = DI::config()->get("system", "worker_queues", 10); $queues = DI::config()->get("system", "worker_queues", 10);
@ -751,7 +751,7 @@ class Worker
* @return integer Number of active worker processes * @return integer Number of active worker processes
* @throws \Exception * @throws \Exception
*/ */
private static function activeWorkers() private static function activeWorkers(): int
{ {
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
$count = DI::process()->countCommand('Worker.php'); $count = DI::process()->countCommand('Worker.php');
@ -766,7 +766,7 @@ class Worker
* @return array List of worker process ids * @return array List of worker process ids
* @throws \Exception * @throws \Exception
*/ */
private static function getWorkerPIDList() private static function getWorkerPIDList(): array
{ {
$ids = []; $ids = [];
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
@ -787,7 +787,7 @@ class Worker
/** /**
* Returns waiting jobs for the current process id * Returns waiting jobs for the current process id
* *
* @return array waiting workerqueue jobs * @return array|bool waiting workerqueue jobs or FALSE on failture
* @throws \Exception * @throws \Exception
*/ */
private static function getWaitingJobForPID() private static function getWaitingJobForPID()
@ -809,7 +809,7 @@ class Worker
* @return array array with next jobs * @return array array with next jobs
* @throws \Exception * @throws \Exception
*/ */
private static function nextProcess(int $limit) private static function nextProcess(int $limit): array
{ {
$priority = self::nextPriority(); $priority = self::nextPriority();
if (empty($priority)) { if (empty($priority)) {
@ -844,7 +844,7 @@ class Worker
/** /**
* Returns the priority of the next workerqueue job * Returns the priority of the next workerqueue job
* *
* @return string priority * @return string|bool priority or FALSE on failure
* @throws \Exception * @throws \Exception
*/ */
private static function nextPriority() private static function nextPriority()
@ -915,7 +915,7 @@ class Worker
/** /**
* Find and claim the next worker process for us * Find and claim the next worker process for us
* *
* @return boolean Have we found something? * @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
private static function findWorkerProcesses() private static function findWorkerProcesses()
@ -993,7 +993,7 @@ class Worker
* @return array worker processes * @return array worker processes
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function workerProcess() public static function workerProcess(): array
{ {
// There can already be jobs for us in the queue. // There can already be jobs for us in the queue.
$waiting = self::getWaitingJobForPID(); $waiting = self::getWaitingJobForPID();
@ -1003,7 +1003,7 @@ class Worker
$stamp = (float)microtime(true); $stamp = (float)microtime(true);
if (!DI::lock()->acquire(self::LOCK_PROCESS)) { if (!DI::lock()->acquire(self::LOCK_PROCESS)) {
return false; return [];
} }
self::$lock_duration += (microtime(true) - $stamp); self::$lock_duration += (microtime(true) - $stamp);
@ -1011,7 +1011,9 @@ class Worker
DI::lock()->release(self::LOCK_PROCESS); DI::lock()->release(self::LOCK_PROCESS);
return self::getWaitingJobForPID(); // Prevents "Return value of Friendica\Core\Worker::workerProcess() must be of the type array, bool returned"
$process = self::getWaitingJobForPID();
return (is_array($process) ? $process : []);
} }
/** /**
@ -1097,7 +1099,7 @@ class Worker
* @return void * @return void
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function spawnWorker($do_cron = false) public static function spawnWorker(bool $do_cron = false)
{ {
if (Worker\Daemon::isMode() && DI::config()->get('system', 'worker_fork')) { if (Worker\Daemon::isMode() && DI::config()->get('system', 'worker_fork')) {
self::forkProcess($do_cron); self::forkProcess($do_cron);
@ -1231,7 +1233,7 @@ class Worker
return $added; return $added;
} }
public static function countWorkersByCommand(string $command) public static function countWorkersByCommand(string $command): int
{ {
return DBA::count('workerqueue', ['done' => false, 'pid' => 0, 'command' => $command]); return DBA::count('workerqueue', ['done' => false, 'pid' => 0, 'command' => $command]);
} }
@ -1244,7 +1246,7 @@ class Worker
* @param integer $max_level maximum retrial level * @param integer $max_level maximum retrial level
* @return integer the next retrial level value * @return integer the next retrial level value
*/ */
private static function getNextRetrial($queue, $max_level) private static function getNextRetrial(array $queue, int $max_level): int
{ {
$created = strtotime($queue['created']); $created = strtotime($queue['created']);
$retrial_time = time() - $created; $retrial_time = time() - $created;
@ -1314,9 +1316,10 @@ class Worker
/** /**
* Check if the system is inside the defined maintenance window * Check if the system is inside the defined maintenance window
* *
* @param bool $check_last_execution Whether check last execution
* @return boolean * @return boolean
*/ */
public static function isInMaintenanceWindow(bool $check_last_execution = false) public static function isInMaintenanceWindow(bool $check_last_execution = false): bool
{ {
// Calculate the seconds of the start end end of the maintenance window // Calculate the seconds of the start end end of the maintenance window
$start = strtotime(DI::config()->get('system', 'maintenance_start')) % 86400; $start = strtotime(DI::config()->get('system', 'maintenance_start')) % 86400;

View file

@ -65,11 +65,27 @@ abstract class DI
/** /**
* @return Database\Database * @return Database\Database
*/ */
public static function dba() public static function dba(): Database\Database
{ {
return self::$dice->create(Database\Database::class); return self::$dice->create(Database\Database::class);
} }
/**
* @return \Friendica\Database\Definition\DbaDefinition
*/
public static function dbaDefinition(): Database\Definition\DbaDefinition
{
return self::$dice->create(Database\Definition\DbaDefinition::class);
}
/**
* @return \Friendica\Database\Definition\ViewDefinition
*/
public static function viewDefinition(): Database\Definition\ViewDefinition
{
return self::$dice->create(Database\Definition\ViewDefinition::class);
}
// //
// "App" namespace instances // "App" namespace instances
// //

View file

@ -42,7 +42,7 @@ class DBA
*/ */
const NULL_DATETIME = '0001-01-01 00:00:00'; const NULL_DATETIME = '0001-01-01 00:00:00';
public static function connect() public static function connect(): bool
{ {
return DI::dba()->connect(); return DI::dba()->connect();
} }
@ -58,7 +58,7 @@ class DBA
/** /**
* Perform a reconnect of an existing database connection * Perform a reconnect of an existing database connection
*/ */
public static function reconnect() public static function reconnect(): bool
{ {
return DI::dba()->reconnect(); return DI::dba()->reconnect();
} }
@ -77,7 +77,7 @@ class DBA
* *
* @return string with either "pdo" or "mysqli" * @return string with either "pdo" or "mysqli"
*/ */
public static function getDriver() public static function getDriver(): string
{ {
return DI::dba()->getDriver(); return DI::dba()->getDriver();
} }
@ -90,7 +90,7 @@ class DBA
* *
* @return string * @return string
*/ */
public static function serverInfo() public static function serverInfo(): string
{ {
return DI::dba()->serverInfo(); return DI::dba()->serverInfo();
} }
@ -101,7 +101,7 @@ class DBA
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
public static function databaseName() public static function databaseName(): string
{ {
return DI::dba()->databaseName(); return DI::dba()->databaseName();
} }
@ -112,7 +112,7 @@ class DBA
* @param string $str * @param string $str
* @return string escaped string * @return string escaped string
*/ */
public static function escape($str) public static function escape(string $str): string
{ {
return DI::dba()->escape($str); return DI::dba()->escape($str);
} }
@ -122,7 +122,7 @@ class DBA
* *
* @return boolean is the database connected? * @return boolean is the database connected?
*/ */
public static function connected() public static function connected(): bool
{ {
return DI::dba()->connected(); return DI::dba()->connected();
} }
@ -138,7 +138,7 @@ class DBA
* @param string $sql An SQL string without the values * @param string $sql An SQL string without the values
* @return string The input SQL string modified if necessary. * @return string The input SQL string modified if necessary.
*/ */
public static function anyValueFallback($sql) public static function anyValueFallback(string $sql): string
{ {
return DI::dba()->anyValueFallback($sql); return DI::dba()->anyValueFallback($sql);
} }
@ -152,7 +152,7 @@ class DBA
* @param string $sql An SQL string without the values * @param string $sql An SQL string without the values
* @return string The input SQL string modified if necessary. * @return string The input SQL string modified if necessary.
*/ */
public static function cleanQuery($sql) public static function cleanQuery(string $sql): string
{ {
$search = ["\t", "\n", "\r", " "]; $search = ["\t", "\n", "\r", " "];
$replace = [' ', ' ', ' ', ' ']; $replace = [' ', ' ', ' ', ' '];
@ -169,7 +169,7 @@ class DBA
* @param array $args Parameter array * @param array $args Parameter array
* @return array universalized parameter array * @return array universalized parameter array
*/ */
public static function getParam($args) public static function getParam(array $args): array
{ {
unset($args[0]); unset($args[0]);
@ -192,7 +192,7 @@ class DBA
* @return bool|object statement object or result object * @return bool|object statement object or result object
* @throws \Exception * @throws \Exception
*/ */
public static function p($sql) public static function p(string $sql)
{ {
$params = self::getParam(func_get_args()); $params = self::getParam(func_get_args());
@ -208,8 +208,8 @@ class DBA
* @return boolean Was the query successfull? False is returned only if an error occurred * @return boolean Was the query successfull? False is returned only if an error occurred
* @throws \Exception * @throws \Exception
*/ */
public static function e($sql) { public static function e(string $sql): bool
{
$params = self::getParam(func_get_args()); $params = self::getParam(func_get_args());
return DI::dba()->e($sql, $params); return DI::dba()->e($sql, $params);
@ -218,13 +218,12 @@ class DBA
/** /**
* Check if data exists * Check if data exists
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $condition array of fields for condition * @param array $condition Array of fields for condition
*
* @return boolean Are there rows for that condition? * @return boolean Are there rows for that condition?
* @throws \Exception * @throws \Exception
*/ */
public static function exists($table, $condition) public static function exists(string $table, array $condition): bool
{ {
return DI::dba()->exists($table, $condition); return DI::dba()->exists($table, $condition);
} }
@ -238,7 +237,7 @@ class DBA
* @return array first row of query * @return array first row of query
* @throws \Exception * @throws \Exception
*/ */
public static function fetchFirst($sql) public static function fetchFirst(string $sql)
{ {
$params = self::getParam(func_get_args()); $params = self::getParam(func_get_args());
@ -250,7 +249,7 @@ class DBA
* *
* @return int Number of rows * @return int Number of rows
*/ */
public static function affectedRows() public static function affectedRows(): int
{ {
return DI::dba()->affectedRows(); return DI::dba()->affectedRows();
} }
@ -261,7 +260,7 @@ class DBA
* @param object Statement object * @param object Statement object
* @return int Number of columns * @return int Number of columns
*/ */
public static function columnCount($stmt) public static function columnCount($stmt): int
{ {
return DI::dba()->columnCount($stmt); return DI::dba()->columnCount($stmt);
} }
@ -271,7 +270,7 @@ class DBA
* @param PDOStatement|mysqli_result|mysqli_stmt Statement object * @param PDOStatement|mysqli_result|mysqli_stmt Statement object
* @return int Number of rows * @return int Number of rows
*/ */
public static function numRows($stmt) public static function numRows($stmt): int
{ {
return DI::dba()->numRows($stmt); return DI::dba()->numRows($stmt);
} }
@ -290,14 +289,13 @@ class DBA
/** /**
* Insert a row into a table * Insert a row into a table
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $param parameter array * @param array $param parameter array
* @param int $duplicate_mode What to do on a duplicated entry * @param int $duplicate_mode What to do on a duplicated entry
*
* @return boolean was the insert successful? * @return boolean was the insert successful?
* @throws \Exception * @throws \Exception
*/ */
public static function insert($table, array $param, int $duplicate_mode = Database::INSERT_DEFAULT) public static function insert(string $table, array $param, int $duplicate_mode = Database::INSERT_DEFAULT): bool
{ {
return DI::dba()->insert($table, $param, $duplicate_mode); return DI::dba()->insert($table, $param, $duplicate_mode);
} }
@ -306,13 +304,12 @@ class DBA
* Inserts a row with the provided data in the provided table. * Inserts a row with the provided data in the provided table.
* If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead. * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $param parameter array * @param array $param parameter array
*
* @return boolean was the insert successful? * @return boolean was the insert successful?
* @throws \Exception * @throws \Exception
*/ */
public static function replace($table, $param) public static function replace(string $table, array $param): bool
{ {
return DI::dba()->replace($table, $param); return DI::dba()->replace($table, $param);
} }
@ -322,7 +319,7 @@ class DBA
* *
* @return integer Last inserted id * @return integer Last inserted id
*/ */
public static function lastInsertId() public static function lastInsertId(): int
{ {
return DI::dba()->lastInsertId(); return DI::dba()->lastInsertId();
} }
@ -332,12 +329,11 @@ class DBA
* *
* This function can be extended in the future to accept a table array as well. * This function can be extended in the future to accept a table array as well.
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
*
* @return boolean was the lock successful? * @return boolean was the lock successful?
* @throws \Exception * @throws \Exception
*/ */
public static function lock($table) public static function lock(string $table): bool
{ {
return DI::dba()->lock($table); return DI::dba()->lock($table);
} }
@ -348,7 +344,7 @@ class DBA
* @return boolean was the unlock successful? * @return boolean was the unlock successful?
* @throws \Exception * @throws \Exception
*/ */
public static function unlock() public static function unlock(): bool
{ {
return DI::dba()->unlock(); return DI::dba()->unlock();
} }
@ -358,7 +354,7 @@ class DBA
* *
* @return boolean Was the command executed successfully? * @return boolean Was the command executed successfully?
*/ */
public static function transaction() public static function transaction(): bool
{ {
return DI::dba()->transaction(); return DI::dba()->transaction();
} }
@ -368,7 +364,7 @@ class DBA
* *
* @return boolean Was the command executed successfully? * @return boolean Was the command executed successfully?
*/ */
public static function commit() public static function commit(): bool
{ {
return DI::dba()->commit(); return DI::dba()->commit();
} }
@ -378,7 +374,7 @@ class DBA
* *
* @return boolean Was the command executed successfully? * @return boolean Was the command executed successfully?
*/ */
public static function rollback() public static function rollback(): bool
{ {
return DI::dba()->rollback(); return DI::dba()->rollback();
} }
@ -386,13 +382,13 @@ class DBA
/** /**
* Delete a row from a table * Delete a row from a table
* *
* @param string|array $table Table name * @param string $table Table name
* @param array $conditions Field condition(s) * @param array $conditions Field condition(s)
* *
* @return boolean was the delete successful? * @return boolean was the delete successful?
* @throws \Exception * @throws \Exception
*/ */
public static function delete($table, array $conditions, array $options = []) public static function delete(string $table, array $conditions, array $options = []): bool
{ {
return DI::dba()->delete($table, $conditions, $options); return DI::dba()->delete($table, $conditions, $options);
} }
@ -418,7 +414,7 @@ class DBA
* Only set $old_fields to a boolean value when you are sure that you will update a single row. * Only set $old_fields to a boolean value when you are sure that you will update a single row.
* When you set $old_fields to "true" then $fields must contain all relevant fields! * When you set $old_fields to "true" then $fields must contain all relevant fields!
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields contains the fields that are updated * @param array $fields contains the fields that are updated
* @param array $condition condition array with the key values * @param array $condition condition array with the key values
* @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields) * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
@ -427,7 +423,7 @@ class DBA
* @return boolean was the update successfull? * @return boolean was the update successfull?
* @throws \Exception * @throws \Exception
*/ */
public static function update($table, $fields, $condition, $old_fields = [], $params = []) public static function update(string $table, array $fields, array $condition, $old_fields = [], array $params = []): bool
{ {
return DI::dba()->update($table, $fields, $condition, $old_fields, $params); return DI::dba()->update($table, $fields, $condition, $old_fields, $params);
} }
@ -435,7 +431,7 @@ class DBA
/** /**
* Retrieve a single record from a table and returns it in an associative array * Retrieve a single record from a table and returns it in an associative array
* *
* @param string|array $table Table name or array [schema => table] * @param string|array $table Table name in format schema.table (while scheme is optiona)
* @param array $fields * @param array $fields
* @param array $condition * @param array $condition
* @param array $params * @param array $params
@ -443,7 +439,7 @@ class DBA
* @throws \Exception * @throws \Exception
* @see self::select * @see self::select
*/ */
public static function selectFirst($table, array $fields = [], array $condition = [], $params = []) public static function selectFirst($table, array $fields = [], array $condition = [], array $params = [])
{ {
return DI::dba()->selectFirst($table, $fields, $condition, $params); return DI::dba()->selectFirst($table, $fields, $condition, $params);
} }
@ -451,16 +447,16 @@ class DBA
/** /**
* Select rows from a table and fills an array with the data * Select rows from a table and fills an array with the data
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* *
* @return array Data array * @return array Data array
* @throws \Exception * @throws \Exception
* @see self::select * @see self::select
*/ */
public static function selectToArray($table, array $fields = [], array $condition = [], array $params = []) public static function selectToArray(string $table, array $fields = [], array $condition = [], array $params = [])
{ {
return DI::dba()->selectToArray($table, $fields, $condition, $params); return DI::dba()->selectToArray($table, $fields, $condition, $params);
} }
@ -468,10 +464,10 @@ class DBA
/** /**
* Select rows from a table * Select rows from a table
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* *
* @return boolean|object * @return boolean|object
* *
@ -488,7 +484,7 @@ class DBA
* $data = DBA::select($table, $fields, $condition, $params); * $data = DBA::select($table, $fields, $condition, $params);
* @throws \Exception * @throws \Exception
*/ */
public static function select($table, array $fields = [], array $condition = [], array $params = []) public static function select(string $table, array $fields = [], array $condition = [], array $params = [])
{ {
return DI::dba()->select($table, $fields, $condition, $params); return DI::dba()->select($table, $fields, $condition, $params);
} }
@ -496,9 +492,9 @@ class DBA
/** /**
* Counts the rows from a table satisfying the provided condition * Counts the rows from a table satisfying the provided condition
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $condition array of fields for condition * @param array $condition array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* *
* @return int * @return int
* *
@ -512,7 +508,7 @@ class DBA
* $count = DBA::count($table, $condition); * $count = DBA::count($table, $condition);
* @throws \Exception * @throws \Exception
*/ */
public static function count($table, array $condition = [], array $params = []) public static function count(string $table, array $condition = [], array $params = []): int
{ {
return DI::dba()->count($table, $condition, $params); return DI::dba()->count($table, $condition, $params);
} }
@ -525,37 +521,30 @@ class DBA
* - [table1, table2, ...] * - [table1, table2, ...]
* - [schema1 => table1, schema2 => table2, table3, ...] * - [schema1 => table1, schema2 => table2, table3, ...]
* *
* @param string|array $tables * @param array $tables Table names
* @return string * @return string
*/ */
public static function buildTableString($tables) public static function buildTableString(array $tables): string
{ {
if (is_string($tables)) { // Quote each entry
$tables = [$tables]; return implode(',', array_map(['self', 'quoteIdentifier'], $tables));
}
$quotedTables = [];
foreach ($tables as $schema => $table) {
if (is_numeric($schema)) {
$quotedTables[] = self::quoteIdentifier($table);
} else {
$quotedTables[] = self::quoteIdentifier($schema) . '.' . self::quoteIdentifier($table);
}
}
return implode(', ', $quotedTables);
} }
/** /**
* Escape an identifier (table or field name) * Escape an identifier (table or field name) optional with a schema like (schema.)table
* *
* @param $identifier * @param $identifier Table, field name
* @return string * @return string Quotes table or field name
*/ */
public static function quoteIdentifier($identifier) public static function quoteIdentifier(string $identifier): string
{ {
return '`' . str_replace('`', '``', $identifier) . '`'; return implode(
'.',
array_map(
function (string $identifier) { return '`' . str_replace('`', '``', $identifier) . '`'; },
explode('.', $identifier)
)
);
} }
/** /**
@ -576,7 +565,7 @@ class DBA
* @param array $condition * @param array $condition
* @return string * @return string
*/ */
public static function buildCondition(array &$condition = []) public static function buildCondition(array &$condition = []): string
{ {
$condition = self::collapseCondition($condition); $condition = self::collapseCondition($condition);
@ -600,7 +589,7 @@ class DBA
* @param array $condition * @param array $condition
* @return array * @return array
*/ */
public static function collapseCondition(array $condition) public static function collapseCondition(array $condition): array
{ {
// Ensures an always true condition is returned // Ensures an always true condition is returned
if (count($condition) < 1) { if (count($condition) < 1) {
@ -675,7 +664,7 @@ class DBA
* @return array A collapsed condition * @return array A collapsed condition
* @see DBA::collapseCondition() for the condition array formats * @see DBA::collapseCondition() for the condition array formats
*/ */
public static function mergeConditions(array ...$conditions) public static function mergeConditions(array ...$conditions): array
{ {
if (count($conditions) == 1) { if (count($conditions) == 1) {
return current($conditions); return current($conditions);
@ -724,7 +713,7 @@ class DBA
* @param array $params * @param array $params
* @return string * @return string
*/ */
public static function buildParameter(array $params = []) public static function buildParameter(array $params = []): string
{ {
$groupby_string = ''; $groupby_string = '';
if (!empty($params['group_by'])) { if (!empty($params['group_by'])) {
@ -771,7 +760,7 @@ class DBA
* *
* @return array Data array * @return array Data array
*/ */
public static function toArray($stmt, $do_close = true, int $count = 0) public static function toArray($stmt, bool $do_close = true, int $count = 0): array
{ {
return DI::dba()->toArray($stmt, $do_close, $count); return DI::dba()->toArray($stmt, $do_close, $count);
} }
@ -783,7 +772,7 @@ class DBA
* @param array $fields * @param array $fields
* @return array casted fields * @return array casted fields
*/ */
public static function castFields(string $table, array $fields) public static function castFields(string $table, array $fields): array
{ {
return DI::dba()->castFields($table, $fields); return DI::dba()->castFields($table, $fields);
} }
@ -793,7 +782,7 @@ class DBA
* *
* @return string Error number (0 if no error) * @return string Error number (0 if no error)
*/ */
public static function errorNo() public static function errorNo(): int
{ {
return DI::dba()->errorNo(); return DI::dba()->errorNo();
} }
@ -803,7 +792,7 @@ class DBA
* *
* @return string Error message ('' if no error) * @return string Error message ('' if no error)
*/ */
public static function errorMessage() public static function errorMessage(): string
{ {
return DI::dba()->errorMessage(); return DI::dba()->errorMessage();
} }
@ -814,7 +803,7 @@ class DBA
* @param object $stmt statement object * @param object $stmt statement object
* @return boolean was the close successful? * @return boolean was the close successful?
*/ */
public static function close($stmt) public static function close($stmt): bool
{ {
return DI::dba()->close($stmt); return DI::dba()->close($stmt);
} }
@ -827,7 +816,7 @@ class DBA
* 'amount' => Number of concurrent database processes * 'amount' => Number of concurrent database processes
* @throws \Exception * @throws \Exception
*/ */
public static function processlist() public static function processlist(): array
{ {
return DI::dba()->processlist(); return DI::dba()->processlist();
} }
@ -847,10 +836,9 @@ class DBA
* Checks if $array is a filled array with at least one entry. * Checks if $array is a filled array with at least one entry.
* *
* @param mixed $array A filled array with at least one entry * @param mixed $array A filled array with at least one entry
*
* @return boolean Whether $array is a filled array or an object with rows * @return boolean Whether $array is a filled array or an object with rows
*/ */
public static function isResult($array) public static function isResult($array): bool
{ {
return DI::dba()->isResult($array); return DI::dba()->isResult($array);
} }
@ -862,7 +850,7 @@ class DBA
* @param boolean $add_quotation add quotation marks for string values * @param boolean $add_quotation add quotation marks for string values
* @return void * @return void
*/ */
public static function escapeArray(&$arr, $add_quotation = false) public static function escapeArray(&$arr, bool $add_quotation = false)
{ {
DI::dba()->escapeArray($arr, $add_quotation); DI::dba()->escapeArray($arr, $add_quotation);
} }

View file

@ -22,13 +22,12 @@
namespace Friendica\Database; namespace Friendica\Database;
use Exception; use Exception;
use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Writer\DbaDefinitionSqlWriter;
/** /**
* This class contains functions that doesn't need to know if pdo, mysqli or whatever is used. * This class contains functions that doesn't need to know if pdo, mysqli or whatever is used.
@ -42,13 +41,6 @@ class DBStructure
const RENAME_COLUMN = 0; const RENAME_COLUMN = 0;
const RENAME_PRIMARY_KEY = 1; const RENAME_PRIMARY_KEY = 1;
/**
* Database structure definition loaded from config/dbstructure.config.php
*
* @var array
*/
private static $definition = [];
/** /**
* Set a database version to trigger update functions * Set a database version to trigger update functions
* *
@ -73,7 +65,7 @@ class DBStructure
*/ */
public static function dropTables(bool $execute) public static function dropTables(bool $execute)
{ {
$postupdate = DI::config()->get("system", "post_update_version", PostUpdate::VERSION); $postupdate = DI::config()->get('system', 'post_update_version', PostUpdate::VERSION);
if ($postupdate < PostUpdate::VERSION) { if ($postupdate < PostUpdate::VERSION) {
echo DI::l10n()->t('The post update is at version %d, it has to be at %d to safely drop the tables.', $postupdate, PostUpdate::VERSION); echo DI::l10n()->t('The post update is at version %d, it has to be at %d to safely drop the tables.', $postupdate, PostUpdate::VERSION);
return; return;
@ -84,7 +76,7 @@ class DBStructure
'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge', 'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge',
'auth_codes', 'tokens', 'clients', 'profile_check', 'host']; 'auth_codes', 'tokens', 'clients', 'profile_check', 'host'];
$tables = DBA::selectToArray(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_NAME'], $tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']);
if (empty($tables)) { if (empty($tables)) {
@ -119,13 +111,13 @@ class DBStructure
public static function convertToInnoDB() public static function convertToInnoDB()
{ {
$tables = DBA::selectToArray( $tables = DBA::selectToArray(
['information_schema' => 'tables'], 'information_schema.tables',
['table_name'], ['table_name'],
['engine' => 'MyISAM', 'table_schema' => DBA::databaseName()] ['engine' => 'MyISAM', 'table_schema' => DBA::databaseName()]
); );
$tables = array_merge($tables, DBA::selectToArray( $tables = array_merge($tables, DBA::selectToArray(
['information_schema' => 'tables'], 'information_schema.tables',
['table_name'], ['table_name'],
['engine' => 'InnoDB', 'ROW_FORMAT' => ['COMPACT', 'REDUNDANT'], 'table_schema' => DBA::databaseName()] ['engine' => 'InnoDB', 'ROW_FORMAT' => ['COMPACT', 'REDUNDANT'], 'table_schema' => DBA::databaseName()]
)); ));
@ -153,7 +145,7 @@ class DBStructure
* *
* @return string Error message * @return string Error message
*/ */
private static function printUpdateError($message) private static function printUpdateError(string $message): string
{ {
echo DI::l10n()->t("\nError %d occurred during database update:\n%s\n", echo DI::l10n()->t("\nError %d occurred during database update:\n%s\n",
DBA::errorNo(), DBA::errorMessage()); DBA::errorNo(), DBA::errorMessage());
@ -161,334 +153,15 @@ class DBStructure
return DI::l10n()->t('Errors encountered performing database changes: ') . $message . EOL; return DI::l10n()->t('Errors encountered performing database changes: ') . $message . EOL;
} }
public static function writeStructure()
{
$tables = [];
foreach (self::definition(null) 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 = DI::basePath() . '/doc/database/db_' . $name . '.md';
file_put_contents($filename, $content);
}
asort($tables);
$content = Renderer::replaceMacros(Renderer::getMarkupTemplate('tables.tpl'), [
'$tables' => $tables,
]);
$filename = DI::basePath() . '/doc/database.md';
file_put_contents($filename, $content);
}
public static function printStructure($basePath)
{
$database = self::definition($basePath, false);
echo "-- ------------------------------------------\n";
echo "-- " . FRIENDICA_PLATFORM . " " . FRIENDICA_VERSION . " (" . FRIENDICA_CODENAME, ")\n";
echo "-- DB_UPDATE_VERSION " . DB_UPDATE_VERSION . "\n";
echo "-- ------------------------------------------\n\n\n";
foreach ($database as $name => $structure) {
echo "--\n";
echo "-- TABLE $name\n";
echo "--\n";
self::createTable($name, $structure, true, false);
echo "\n";
}
View::printStructure($basePath);
}
/**
* Loads the database structure definition from the static/dbstructure.config.php file.
* On first pass, defines DB_UPDATE_VERSION constant.
*
* @see static/dbstructure.config.php
* @param boolean $with_addons_structure Whether to tack on addons additional tables
* @param string $basePath The base path of this application
* @return array
* @throws Exception
*/
public static function definition($basePath, $with_addons_structure = true)
{
if (!self::$definition) {
if (empty($basePath)) {
$basePath = DI::app()->getBasePath();
}
$filename = $basePath . '/static/dbstructure.config.php';
if (!is_readable($filename)) {
throw new Exception('Missing database structure config file static/dbstructure.config.php');
}
$definition = require $filename;
if (!$definition) {
throw new Exception('Corrupted database structure config file static/dbstructure.config.php');
}
self::$definition = $definition;
} else {
$definition = self::$definition;
}
if ($with_addons_structure) {
Hook::callAll('dbstructure_definition', $definition);
}
return $definition;
}
/**
* Get field data for the given table
*
* @param string $table
* @param array $data data fields
* @return array fields for the given
*/
public static function getFieldsForTable(string $table, array $data = [])
{
$definition = DBStructure::definition('', false);
if (empty($definition[$table])) {
return [];
}
$fieldnames = array_keys($definition[$table]['fields']);
$fields = [];
// Assign all field that are present in the table
foreach ($fieldnames as $field) {
if (isset($data[$field])) {
// Limit the length of varchar, varbinary, char and binrary fields
if (is_string($data[$field]) && preg_match("/char\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
$data[$field] = mb_substr($data[$field], 0, $result[1]);
} elseif (is_string($data[$field]) && preg_match("/binary\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
$data[$field] = substr($data[$field], 0, $result[1]);
}
$fields[$field] = $data[$field];
}
}
return $fields;
}
private static function createTable($name, $structure, $verbose, $action)
{
$r = true;
$engine = "";
$comment = "";
$sql_rows = [];
$primary_keys = [];
$foreign_keys = [];
foreach ($structure["fields"] as $fieldname => $field) {
$sql_rows[] = "`" . DBA::escape($fieldname) . "` " . self::FieldCommand($field);
if (!empty($field['primary'])) {
$primary_keys[] = $fieldname;
}
if (!empty($field['foreign'])) {
$foreign_keys[$fieldname] = $field;
}
}
if (!empty($structure["indexes"])) {
foreach ($structure["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($name, $fieldname, $parameters);
}
if (isset($structure["engine"])) {
$engine = " ENGINE=" . $structure["engine"];
}
if (isset($structure["comment"])) {
$comment = " COMMENT='" . DBA::escape($structure["comment"]) . "'";
}
$sql = implode(",\n\t", $sql_rows);
$sql = sprintf("CREATE TABLE IF NOT EXISTS `%s` (\n\t", DBA::escape($name)) . $sql .
"\n)" . $engine . " DEFAULT COLLATE utf8mb4_general_ci" . $comment;
if ($verbose) {
echo $sql . ";\n";
}
if ($action) {
$r = DBA::e($sql);
}
return $r;
}
private static function FieldCommand($parameters, $create = true)
{
$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 '" . DBA::escape($parameters["comment"]) . "'";
}
/*if (($parameters["primary"] != "") && $create)
$fieldstruct .= " PRIMARY KEY";*/
return ($fieldstruct);
}
private static function createIndex($indexname, $fieldnames, $method = "ADD")
{
$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 .= "`" . DBA::escape($matches[1]) . "`(" . intval($matches[2]) . ")";
} else {
$names .= "`" . DBA::escape($fieldname) . "`";
}
}
if ($indexname == "PRIMARY") {
return sprintf("%s PRIMARY KEY(%s)", $method, $names);
}
$sql = sprintf("%s INDEX `%s` (%s)", $method, DBA::escape($indexname), $names);
return ($sql);
}
/** /**
* Perform a database structure dryrun (means: just simulating) * Perform a database structure dryrun (means: just simulating)
* *
* @return string Empty string if the update is successful, error messages otherwise
* @throws Exception * @throws Exception
*/ */
public static function dryRun() public static function dryRun(): string
{ {
self::update(DI::app()->getBasePath(), true, false); return self::update(true, false);
} }
/** /**
@ -500,13 +173,13 @@ class DBStructure
* @return string Empty string if the update is successful, error messages otherwise * @return string Empty string if the update is successful, error messages otherwise
* @throws Exception * @throws Exception
*/ */
public static function performUpdate(bool $enable_maintenance_mode = true, bool $verbose = false) public static function performUpdate(bool $enable_maintenance_mode = true, bool $verbose = false): string
{ {
if ($enable_maintenance_mode) { if ($enable_maintenance_mode) {
DI::config()->set('system', 'maintenance', 1); DI::config()->set('system', 'maintenance', 1);
} }
$status = self::update(DI::app()->getBasePath(), $verbose, true); $status = self::update($verbose, true);
if ($enable_maintenance_mode) { if ($enable_maintenance_mode) {
DI::config()->set('system', 'maintenance', 0); DI::config()->set('system', 'maintenance', 0);
@ -519,20 +192,17 @@ class DBStructure
/** /**
* Updates DB structure from the installation and returns eventual errors messages * Updates DB structure from the installation and returns eventual errors messages
* *
* @param string $basePath The base path of this application
*
* @return string Empty string if the update is successful, error messages otherwise * @return string Empty string if the update is successful, error messages otherwise
* @throws Exception * @throws Exception
*/ */
public static function install(string $basePath) public static function install(): string
{ {
return self::update($basePath, false, true, true); return self::update(false, true, true);
} }
/** /**
* Updates DB structure and returns eventual errors messages * Updates DB structure and returns eventual errors messages
* *
* @param string $basePath The base path of this application
* @param bool $verbose * @param bool $verbose
* @param bool $action Whether to actually apply the update * @param bool $action Whether to actually apply the update
* @param bool $install Is this the initial update during the installation? * @param bool $install Is this the initial update during the installation?
@ -541,7 +211,7 @@ class DBStructure
* @return string Empty string if the update is successful, error messages otherwise * @return string Empty string if the update is successful, error messages otherwise
* @throws Exception * @throws Exception
*/ */
private static function update($basePath, $verbose, $action, $install = false, array $tables = null, array $definition = null) private static function update(bool $verbose, bool $action, bool $install = false, array $tables = null, array $definition = null): string
{ {
$in_maintenance_mode = DI::config()->get('system', 'maintenance'); $in_maintenance_mode = DI::config()->get('system', 'maintenance');
@ -579,7 +249,7 @@ class DBStructure
// Get the definition // Get the definition
if (is_null($definition)) { if (is_null($definition)) {
$definition = self::definition($basePath); $definition = DI::dbaDefinition()->getAll();
} }
// MySQL >= 5.7.4 doesn't support the IGNORE keyword in ALTER TABLE statements // MySQL >= 5.7.4 doesn't support the IGNORE keyword in ALTER TABLE statements
@ -593,11 +263,17 @@ class DBStructure
// Compare it // Compare it
foreach ($definition as $name => $structure) { foreach ($definition as $name => $structure) {
$is_new_table = false; $is_new_table = false;
$sql3 = ""; $sql3 = "";
if (!isset($database[$name])) { if (!isset($database[$name])) {
$r = self::createTable($name, $structure, $verbose, $action); $sql = DbaDefinitionSqlWriter::createTable($name, $structure, $verbose, $action);
if (!DBA::isResult($r)) { if ($verbose) {
$errors .= self::printUpdateError($name); echo $sql;
}
if ($action) {
$r = DBA::e($sql);
if (!DBA::isResult($r)) {
$errors .= self::printUpdateError($name);
}
} }
$is_new_table = true; $is_new_table = true;
} else { } else {
@ -606,15 +282,15 @@ class DBStructure
* or the definition differ from current status * or the definition differ from current status
* and index name doesn't start with "local_" * and index name doesn't start with "local_"
*/ */
foreach ($database[$name]["indexes"] as $indexname => $fieldnames) { foreach ($database[$name]["indexes"] as $indexName => $fieldNames) {
$current_index_definition = implode(",", $fieldnames); $current_index_definition = implode(",", $fieldNames);
if (isset($structure["indexes"][$indexname])) { if (isset($structure["indexes"][$indexName])) {
$new_index_definition = implode(",", $structure["indexes"][$indexname]); $new_index_definition = implode(",", $structure["indexes"][$indexName]);
} else { } else {
$new_index_definition = "__NOT_SET__"; $new_index_definition = "__NOT_SET__";
} }
if ($current_index_definition != $new_index_definition && substr($indexname, 0, 6) != 'local_') { if ($current_index_definition != $new_index_definition && substr($indexName, 0, 6) != 'local_') {
$sql2 = self::dropIndex($indexname); $sql2 = DbaDefinitionSqlWriter::dropIndex($indexName);
if ($sql3 == "") { if ($sql3 == "") {
$sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2; $sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2;
} else { } else {
@ -623,9 +299,9 @@ class DBStructure
} }
} }
// Compare the field structure field by field // Compare the field structure field by field
foreach ($structure["fields"] as $fieldname => $parameters) { foreach ($structure["fields"] as $fieldName => $parameters) {
if (!isset($database[$name]["fields"][$fieldname])) { if (!isset($database[$name]["fields"][$fieldName])) {
$sql2 = self::addTableField($fieldname, $parameters); $sql2 = DbaDefinitionSqlWriter::addTableField($fieldName, $parameters);
if ($sql3 == "") { if ($sql3 == "") {
$sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2; $sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2;
} else { } else {
@ -633,7 +309,7 @@ class DBStructure
} }
} else { } else {
// Compare the field definition // Compare the field definition
$field_definition = $database[$name]["fields"][$fieldname]; $field_definition = $database[$name]["fields"][$fieldName];
// Remove the relation data that is used for the referential integrity // Remove the relation data that is used for the referential integrity
unset($parameters['relation']); unset($parameters['relation']);
@ -651,9 +327,9 @@ class DBStructure
} }
$current_field_definition = DBA::cleanQuery(implode(",", $field_definition)); $current_field_definition = DBA::cleanQuery(implode(",", $field_definition));
$new_field_definition = DBA::cleanQuery(implode(",", $parameters)); $new_field_definition = DBA::cleanQuery(implode(",", $parameters));
if ($current_field_definition != $new_field_definition) { if ($current_field_definition != $new_field_definition) {
$sql2 = self::modifyTableField($fieldname, $parameters); $sql2 = DbaDefinitionSqlWriter::modifyTableField($fieldName, $parameters);
if ($sql3 == "") { if ($sql3 == "") {
$sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2; $sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2;
} else { } else {
@ -670,15 +346,15 @@ class DBStructure
* Don't create keys if table is new * Don't create keys if table is new
*/ */
if (!$is_new_table) { if (!$is_new_table) {
foreach ($structure["indexes"] as $indexname => $fieldnames) { foreach ($structure["indexes"] as $indexName => $fieldNames) {
if (isset($database[$name]["indexes"][$indexname])) { if (isset($database[$name]["indexes"][$indexName])) {
$current_index_definition = implode(",", $database[$name]["indexes"][$indexname]); $current_index_definition = implode(",", $database[$name]["indexes"][$indexName]);
} else { } else {
$current_index_definition = "__NOT_SET__"; $current_index_definition = "__NOT_SET__";
} }
$new_index_definition = implode(",", $fieldnames); $new_index_definition = implode(",", $fieldNames);
if ($current_index_definition != $new_index_definition) { if ($current_index_definition != $new_index_definition) {
$sql2 = self::createIndex($indexname, $fieldnames); $sql2 = DbaDefinitionSqlWriter::createIndex($indexName, $fieldNames);
if ($sql2 != "") { if ($sql2 != "") {
if ($sql3 == "") { if ($sql3 == "") {
@ -694,17 +370,17 @@ class DBStructure
// Foreign keys // Foreign keys
// Compare the field structure field by field // Compare the field structure field by field
foreach ($structure["fields"] as $fieldname => $parameters) { foreach ($structure["fields"] as $fieldName => $parameters) {
if (empty($parameters['foreign'])) { if (empty($parameters['foreign'])) {
continue; continue;
} }
$constraint = self::getConstraintName($name, $fieldname, $parameters); $constraint = self::getConstraintName($name, $fieldName, $parameters);
unset($existing_foreign_keys[$constraint]); unset($existing_foreign_keys[$constraint]);
if (empty($database[$name]['foreign_keys'][$constraint])) { if (empty($database[$name]['foreign_keys'][$constraint])) {
$sql2 = self::addForeignKey($name, $fieldname, $parameters); $sql2 = DbaDefinitionSqlWriter::addForeignKey($fieldName, $parameters);
if ($sql3 == "") { if ($sql3 == "") {
$sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2; $sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2;
@ -715,7 +391,7 @@ class DBStructure
} }
foreach ($existing_foreign_keys as $param) { foreach ($existing_foreign_keys as $param) {
$sql2 = self::dropForeignKey($param['CONSTRAINT_NAME']); $sql2 = DbaDefinitionSqlWriter::dropForeignKey($param['CONSTRAINT_NAME']);
if ($sql3 == "") { if ($sql3 == "") {
$sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2; $sql3 = "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2;
@ -767,9 +443,9 @@ class DBStructure
// Now have a look at the field collations // Now have a look at the field collations
// Compare the field structure field by field // Compare the field structure field by field
foreach ($structure["fields"] as $fieldname => $parameters) { foreach ($structure["fields"] as $fieldName => $parameters) {
// Compare the field definition // Compare the field definition
$field_definition = ($database[$name]["fields"][$fieldname] ?? '') ?: ['Collation' => '']; $field_definition = ($database[$name]["fields"][$fieldName] ?? '') ?: ['Collation' => ''];
// Define the default collation if not given // Define the default collation if not given
if (!isset($parameters['Collation']) && !empty($field_definition['Collation'])) { if (!isset($parameters['Collation']) && !empty($field_definition['Collation'])) {
@ -779,7 +455,7 @@ class DBStructure
} }
if ($field_definition['Collation'] != $parameters['Collation']) { if ($field_definition['Collation'] != $parameters['Collation']) {
$sql2 = self::modifyTableField($fieldname, $parameters); $sql2 = DbaDefinitionSqlWriter::modifyTableField($fieldName, $parameters);
if (($sql3 == "") || (substr($sql3, -2, 2) == "; ")) { if (($sql3 == "") || (substr($sql3, -2, 2) == "; ")) {
$sql3 .= "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2; $sql3 .= "ALTER" . $ignore . " TABLE `" . $name . "` " . $sql2;
} else { } else {
@ -826,23 +502,29 @@ class DBStructure
return $errors; return $errors;
} }
private static function tableStructure($table) /**
* Returns an array with table structure information
*
* @param string $table Name of table
* @return array Table structure information
*/
private static function tableStructure(string $table): array
{ {
// This query doesn't seem to be executable as a prepared statement // This query doesn't seem to be executable as a prepared statement
$indexes = DBA::toArray(DBA::p("SHOW INDEX FROM " . DBA::quoteIdentifier($table))); $indexes = DBA::toArray(DBA::p("SHOW INDEX FROM " . DBA::quoteIdentifier($table)));
$fields = DBA::selectToArray(['INFORMATION_SCHEMA' => 'COLUMNS'], $fields = DBA::selectToArray('INFORMATION_SCHEMA.COLUMNS',
['COLUMN_NAME', 'COLUMN_TYPE', 'IS_NULLABLE', 'COLUMN_DEFAULT', 'EXTRA', ['COLUMN_NAME', 'COLUMN_TYPE', 'IS_NULLABLE', 'COLUMN_DEFAULT', 'EXTRA',
'COLUMN_KEY', 'COLLATION_NAME', 'COLUMN_COMMENT'], 'COLUMN_KEY', 'COLLATION_NAME', 'COLUMN_COMMENT'],
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?",
DBA::databaseName(), $table]); DBA::databaseName(), $table]);
$foreign_keys = DBA::selectToArray(['INFORMATION_SCHEMA' => 'KEY_COLUMN_USAGE'], $foreign_keys = DBA::selectToArray('INFORMATION_SCHEMA.KEY_COLUMN_USAGE',
['COLUMN_NAME', 'CONSTRAINT_NAME', 'REFERENCED_TABLE_NAME', 'REFERENCED_COLUMN_NAME'], ['COLUMN_NAME', 'CONSTRAINT_NAME', 'REFERENCED_TABLE_NAME', 'REFERENCED_COLUMN_NAME'],
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL",
DBA::databaseName(), $table]); DBA::databaseName(), $table]);
$table_status = DBA::selectFirst(['INFORMATION_SCHEMA' => 'TABLES'], $table_status = DBA::selectFirst('INFORMATION_SCHEMA.TABLES',
['ENGINE', 'TABLE_COLLATION', 'TABLE_COMMENT'], ['ENGINE', 'TABLE_COLLATION', 'TABLE_COMMENT'],
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?",
DBA::databaseName(), $table]); DBA::databaseName(), $table]);
@ -909,65 +591,20 @@ class DBStructure
} }
} }
return ["fields" => $fielddata, "indexes" => $indexdata, return [
"foreign_keys" => $foreigndata, "table_status" => $table_status]; 'fields' => $fielddata,
'indexes' => $indexdata,
'foreign_keys' => $foreigndata,
'table_status' => $table_status
];
} }
private static function dropIndex($indexname) private static function getConstraintName(string $tableName, string $fieldName, array $parameters): string
{
$sql = sprintf("DROP INDEX `%s`", DBA::escape($indexname));
return ($sql);
}
private static function addTableField($fieldname, $parameters)
{
$sql = sprintf("ADD `%s` %s", DBA::escape($fieldname), self::FieldCommand($parameters));
return ($sql);
}
private static function modifyTableField($fieldname, $parameters)
{
$sql = sprintf("MODIFY `%s` %s", DBA::escape($fieldname), self::FieldCommand($parameters, false));
return ($sql);
}
private static function getConstraintName(string $tablename, string $fieldname, array $parameters)
{ {
$foreign_table = array_keys($parameters['foreign'])[0]; $foreign_table = array_keys($parameters['foreign'])[0];
$foreign_field = array_values($parameters['foreign'])[0]; $foreign_field = array_values($parameters['foreign'])[0];
return $tablename . "-" . $fieldname. "-" . $foreign_table. "-" . $foreign_field; return $tableName . '-' . $fieldName. '-' . $foreign_table. '-' . $foreign_field;
}
private static function foreignCommand(string $tablename, string $fieldname, array $parameters) {
$foreign_table = array_keys($parameters['foreign'])[0];
$foreign_field = array_values($parameters['foreign'])[0];
$sql = "FOREIGN KEY (`" . $fieldname . "`) 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;
}
private static function addForeignKey(string $tablename, string $fieldname, array $parameters)
{
return sprintf("ADD %s", self::foreignCommand($tablename, $fieldname, $parameters));
}
private static function dropForeignKey(string $constraint)
{
return sprintf("DROP FOREIGN KEY `%s`", $constraint);
} }
/** /**
@ -983,7 +620,7 @@ class DBStructure
* @return boolean Was the renaming successful? * @return boolean Was the renaming successful?
* @throws Exception * @throws Exception
*/ */
public static function rename($table, $columns, $type = self::RENAME_COLUMN) public static function rename(string $table, array $columns, int $type = self::RENAME_COLUMN): bool
{ {
if (empty($table) || empty($columns)) { if (empty($table) || empty($columns)) {
return false; return false;
@ -1019,7 +656,7 @@ class DBStructure
return false; return false;
} }
$sql .= ";"; $sql .= ';';
$stmt = DBA::p($sql); $stmt = DBA::p($sql);
@ -1043,7 +680,7 @@ class DBStructure
* @return boolean Does the table exist? * @return boolean Does the table exist?
* @throws Exception * @throws Exception
*/ */
public static function existsColumn($table, $columns = []) public static function existsColumn(string $table, array $columns = []): bool
{ {
if (empty($table)) { if (empty($table)) {
return false; return false;
@ -1079,39 +716,33 @@ class DBStructure
/** /**
* Check if a foreign key exists for the given table field * Check if a foreign key exists for the given table field
* *
* @param string $table * @param string $table Table name
* @param string $field * @param string $field Field name
* @return boolean * @return boolean Wether a foreign key exists
*/ */
public static function existsForeignKeyForField(string $table, string $field) public static function existsForeignKeyForField(string $table, string $field): bool
{ {
return DBA::exists(['INFORMATION_SCHEMA' => 'KEY_COLUMN_USAGE'], return DBA::exists('INFORMATION_SCHEMA.KEY_COLUMN_USAGE',
["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL", ["`TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ? AND `REFERENCED_TABLE_SCHEMA` IS NOT NULL",
DBA::databaseName(), $table, $field]); DBA::databaseName(), $table, $field]);
} }
/** /**
* Check if a table exists * Check if a table exists
*
* @param string|array $table Table name
* *
* @param string $table Single table name (please loop yourself)
* @return boolean Does the table exist? * @return boolean Does the table exist?
* @throws Exception * @throws Exception
*/ */
public static function existsTable($table) public static function existsTable(string $table): bool
{ {
if (empty($table)) { if (empty($table)) {
return false; return false;
} }
if (is_array($table)) { $condition = ['table_schema' => DBA::databaseName(), 'table_name' => $table];
$condition = ['table_schema' => key($table), 'table_name' => current($table)];
} else {
$condition = ['table_schema' => DBA::databaseName(), 'table_name' => $table];
}
$result = DBA::exists(['information_schema' => 'tables'], $condition); return DBA::exists('information_schema.tables', $condition);
return $result;
} }
/** /**
@ -1122,7 +753,7 @@ class DBStructure
* @return array An array of the table columns * @return array An array of the table columns
* @throws Exception * @throws Exception
*/ */
public static function getColumns($table) public static function getColumns(string $table): array
{ {
$stmtColumns = DBA::p("SHOW COLUMNS FROM `" . $table . "`"); $stmtColumns = DBA::p("SHOW COLUMNS FROM `" . $table . "`");
return DBA::toArray($stmtColumns); return DBA::toArray($stmtColumns);
@ -1130,6 +761,9 @@ class DBStructure
/** /**
* Check if initial database values do exist - or create them * Check if initial database values do exist - or create them
*
* @param bool $verbose Whether to output messages
* @return void
*/ */
public static function checkInitialValues(bool $verbose = false) public static function checkInitialValues(bool $verbose = false)
{ {
@ -1163,9 +797,9 @@ class DBStructure
if (self::existsTable('user') && !DBA::exists('user', ['uid' => 0])) { if (self::existsTable('user') && !DBA::exists('user', ['uid' => 0])) {
$user = [ $user = [
"verified" => true, 'verified' => true,
"page-flags" => User::PAGE_FLAGS_SOAPBOX, 'page-flags' => User::PAGE_FLAGS_SOAPBOX,
"account-type" => User::ACCOUNT_TYPE_RELAY, 'account-type' => User::ACCOUNT_TYPE_RELAY,
]; ];
DBA::insert('user', $user); DBA::insert('user', $user);
$lastid = DBA::lastInsertId(); $lastid = DBA::lastInsertId();
@ -1265,12 +899,14 @@ class DBStructure
* *
* @return boolean * @return boolean
*/ */
private static function isUpdating() private static function isUpdating(): bool
{ {
$isUpdate = false; $isUpdate = false;
$processes = DBA::select(['information_schema' => 'processlist'], ['info'], $processes = DBA::select('information_schema.processlist', ['info'], [
['db' => DBA::databaseName(), 'command' => ['Query', 'Execute']]); 'db' => DBA::databaseName(),
'command' => ['Query', 'Execute']
]);
while ($process = DBA::fetch($processes)) { while ($process = DBA::fetch($processes)) {
$parts = explode(' ', $process['info']); $parts = explode(' ', $process['info']);

View file

@ -23,9 +23,12 @@ namespace Friendica\Database;
use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\Definition\DbaDefinition;
use Friendica\Database\Definition\ViewDefinition;
use Friendica\Network\HTTPException\ServiceUnavailableException; use Friendica\Network\HTTPException\ServiceUnavailableException;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use InvalidArgumentException;
use mysqli; use mysqli;
use mysqli_result; use mysqli_result;
use mysqli_stmt; use mysqli_stmt;
@ -63,7 +66,7 @@ class Database
protected $server_info = ''; protected $server_info = '';
/** @var PDO|mysqli */ /** @var PDO|mysqli */
protected $connection; protected $connection;
protected $driver; protected $driver = '';
protected $pdo_emulate_prepares = false; protected $pdo_emulate_prepares = false;
private $error = false; private $error = false;
private $errorno = 0; private $errorno = 0;
@ -72,23 +75,29 @@ class Database
protected $in_retrial = false; protected $in_retrial = false;
protected $testmode = false; protected $testmode = false;
private $relation = []; private $relation = [];
/** @var DbaDefinition */
protected $dbaDefinition;
/** @var ViewDefinition */
protected $viewDefinition;
public function __construct(Cache $configCache, Profiler $profiler, LoggerInterface $logger) public function __construct(Cache $configCache, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition, LoggerInterface $logger)
{ {
// We are storing these values for being able to perform a reconnect // We are storing these values for being able to perform a reconnect
$this->configCache = $configCache; $this->configCache = $configCache;
$this->profiler = $profiler; $this->profiler = $profiler;
$this->logger = $logger; $this->logger = $logger;
$this->dbaDefinition = $dbaDefinition;
$this->viewDefinition = $viewDefinition;
$this->connect(); $this->connect();
if ($this->isConnected()) {
// Loads DB_UPDATE_VERSION constant
DBStructure::definition($configCache->get('system', 'basepath'), false);
}
} }
public function connect() /**
* Tries to connect to database
*
* @return bool Success
*/
public function connect(): bool
{ {
if (!is_null($this->connection) && $this->connected()) { if (!is_null($this->connection) && $this->connected()) {
return $this->connected; return $this->connected;
@ -175,7 +184,7 @@ class Database
// No suitable SQL driver was found. // No suitable SQL driver was found.
if (!$this->connected) { if (!$this->connected) {
$this->driver = null; $this->driver = '';
$this->connection = null; $this->connection = null;
} }
@ -227,7 +236,7 @@ class Database
} }
} }
$this->driver = null; $this->driver = '';
$this->connected = false; $this->connected = false;
} }
@ -255,7 +264,7 @@ class Database
* *
* @return string with either "pdo" or "mysqli" * @return string with either "pdo" or "mysqli"
*/ */
public function getDriver() public function getDriver(): string
{ {
return $this->driver; return $this->driver;
} }
@ -266,9 +275,9 @@ class Database
* This function discriminate between the deprecated mysql API and the current * This function discriminate between the deprecated mysql API and the current
* object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1 * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
* *
* @return string * @return string Database server information
*/ */
public function serverInfo() public function serverInfo(): string
{ {
if ($this->server_info == '') { if ($this->server_info == '') {
switch ($this->driver) { switch ($this->driver) {
@ -286,10 +295,10 @@ class Database
/** /**
* Returns the selected database name * Returns the selected database name
* *
* @return string * @return string Database name
* @throws \Exception * @throws \Exception
*/ */
public function databaseName() public function databaseName(): string
{ {
$ret = $this->p("SELECT DATABASE() AS `db`"); $ret = $this->p("SELECT DATABASE() AS `db`");
$data = $this->toArray($ret); $data = $this->toArray($ret);
@ -300,10 +309,10 @@ class Database
* Analyze a database query and log this if some conditions are met. * Analyze a database query and log this if some conditions are met.
* *
* @param string $query The database query that will be analyzed * @param string $query The database query that will be analyzed
* * @return void
* @throws \Exception * @throws \Exception
*/ */
private function logIndex($query) private function logIndex(string $query)
{ {
if (!$this->configCache->get('system', 'db_log_index')) { if (!$this->configCache->get('system', 'db_log_index')) {
@ -359,11 +368,10 @@ class Database
* Removes every not allowlisted character from the identifier string * Removes every not allowlisted character from the identifier string
* *
* @param string $identifier * @param string $identifier
*
* @return string sanitized identifier * @return string sanitized identifier
* @throws \Exception * @throws \Exception
*/ */
private function sanitizeIdentifier($identifier) private function sanitizeIdentifier(string $identifier): string
{ {
return preg_replace('/[^A-Za-z0-9_\-]+/', '', $identifier); return preg_replace('/[^A-Za-z0-9_\-]+/', '', $identifier);
} }
@ -383,11 +391,21 @@ class Database
} }
} }
public function isConnected() /**
* Returns connected flag
*
* @return bool Whether connection to database was success
*/
public function isConnected(): bool
{ {
return $this->connected; return $this->connected;
} }
/**
* Checks connection status
*
* @return bool Whether connection to database was success
*/
public function connected() public function connected()
{ {
$connected = false; $connected = false;
@ -424,7 +442,7 @@ class Database
* *
* @return string The input SQL string modified if necessary. * @return string The input SQL string modified if necessary.
*/ */
public function anyValueFallback($sql) public function anyValueFallback(string $sql): string
{ {
$server_info = $this->serverInfo(); $server_info = $this->serverInfo();
if (version_compare($server_info, '5.7.5', '<') || if (version_compare($server_info, '5.7.5', '<') ||
@ -442,7 +460,7 @@ class Database
* *
* @return string The replaced SQL query * @return string The replaced SQL query
*/ */
private function replaceParameters($sql, $args) private function replaceParameters(string $sql, array $args): string
{ {
$offset = 0; $offset = 0;
foreach ($args as $param => $value) { foreach ($args as $param => $value) {
@ -476,7 +494,7 @@ class Database
* @return bool|object statement object or result object * @return bool|object statement object or result object
* @throws \Exception * @throws \Exception
*/ */
public function p($sql) public function p(string $sql)
{ {
$this->profiler->startRecording('database'); $this->profiler->startRecording('database');
@ -541,7 +559,7 @@ class Database
if (!$retval = $this->connection->query($this->replaceParameters($sql, $args))) { if (!$retval = $this->connection->query($this->replaceParameters($sql, $args))) {
$errorInfo = $this->connection->errorInfo(); $errorInfo = $this->connection->errorInfo();
$this->error = $errorInfo[2]; $this->error = $errorInfo[2];
$this->errorno = $errorInfo[1]; $this->errorno = (int) $errorInfo[1];
$retval = false; $retval = false;
$is_error = true; $is_error = true;
break; break;
@ -554,7 +572,7 @@ class Database
if (!$stmt = $this->connection->prepare($sql)) { if (!$stmt = $this->connection->prepare($sql)) {
$errorInfo = $this->connection->errorInfo(); $errorInfo = $this->connection->errorInfo();
$this->error = $errorInfo[2]; $this->error = $errorInfo[2];
$this->errorno = $errorInfo[1]; $this->errorno = (int) $errorInfo[1];
$retval = false; $retval = false;
$is_error = true; $is_error = true;
break; break;
@ -574,7 +592,7 @@ class Database
if (!$stmt->execute()) { if (!$stmt->execute()) {
$errorInfo = $stmt->errorInfo(); $errorInfo = $stmt->errorInfo();
$this->error = $errorInfo[2]; $this->error = $errorInfo[2];
$this->errorno = $errorInfo[1]; $this->errorno = (int) $errorInfo[1];
$retval = false; $retval = false;
$is_error = true; $is_error = true;
} else { } else {
@ -709,7 +727,7 @@ class Database
} }
$this->error = $error; $this->error = $error;
$this->errorno = $errorno; $this->errorno = (int) $errorno;
} }
$this->profiler->stopRecording(); $this->profiler->stopRecording();
@ -741,8 +759,9 @@ class Database
* @return boolean Was the query successfull? False is returned only if an error occurred * @return boolean Was the query successfull? False is returned only if an error occurred
* @throws \Exception * @throws \Exception
*/ */
public function e($sql) public function e(string $sql): bool
{ {
$retval = false;
$this->profiler->startRecording('database_write'); $this->profiler->startRecording('database_write');
@ -804,13 +823,14 @@ class Database
/** /**
* Check if data exists * Check if data exists
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $condition array of fields for condition * @param array $condition Array of fields for condition
* *
* @return boolean Are there rows for that condition? * @return boolean Are there rows for that condition?
* @throws \Exception * @throws \Exception
* @todo Please unwrap the DBStructure::existsTable() call so this method has one behavior only: checking existence on records
*/ */
public function exists($table, $condition) public function exists(string $table, array $condition): bool
{ {
if (empty($table)) { if (empty($table)) {
return false; return false;
@ -850,10 +870,10 @@ class Database
* *
* @param string $sql SQL statement * @param string $sql SQL statement
* *
* @return array first row of query * @return array|bool first row of query or false on failure
* @throws \Exception * @throws \Exception
*/ */
public function fetchFirst($sql) public function fetchFirst(string $sql)
{ {
$params = DBA::getParam(func_get_args()); $params = DBA::getParam(func_get_args());
@ -875,7 +895,7 @@ class Database
* *
* @return int Number of rows * @return int Number of rows
*/ */
public function affectedRows() public function affectedRows(): int
{ {
return $this->affected_rows; return $this->affected_rows;
} }
@ -887,7 +907,7 @@ class Database
* *
* @return int Number of columns * @return int Number of columns
*/ */
public function columnCount($stmt) public function columnCount($stmt): int
{ {
if (!is_object($stmt)) { if (!is_object($stmt)) {
return 0; return 0;
@ -908,7 +928,7 @@ class Database
* *
* @return int Number of rows * @return int Number of rows
*/ */
public function numRows($stmt) public function numRows($stmt): int
{ {
if (!is_object($stmt)) { if (!is_object($stmt)) {
return 0; return 0;
@ -927,7 +947,7 @@ class Database
* *
* @param bool|PDOStatement|mysqli_stmt $stmt statement object * @param bool|PDOStatement|mysqli_stmt $stmt statement object
* *
* @return array|false current row * @return array|bool Current row or false on failure
*/ */
public function fetch($stmt) public function fetch($stmt)
{ {
@ -987,14 +1007,14 @@ class Database
/** /**
* Insert a row into a table. Field value objects will be cast as string. * Insert a row into a table. Field value objects will be cast as string.
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $param parameter array * @param array $param parameter array
* @param int $duplicate_mode What to do on a duplicated entry * @param int $duplicate_mode What to do on a duplicated entry
* *
* @return boolean was the insert successful? * @return boolean was the insert successful?
* @throws \Exception * @throws \Exception
*/ */
public function insert($table, array $param, int $duplicate_mode = self::INSERT_DEFAULT) public function insert(string $table, array $param, int $duplicate_mode = self::INSERT_DEFAULT): bool
{ {
if (empty($table) || empty($param)) { if (empty($table) || empty($param)) {
$this->logger->info('Table and fields have to be set'); $this->logger->info('Table and fields have to be set');
@ -1003,7 +1023,7 @@ class Database
$param = $this->castFields($table, $param); $param = $this->castFields($table, $param);
$table_string = DBA::buildTableString($table); $table_string = DBA::buildTableString([$table]);
$fields_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], array_keys($param))); $fields_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], array_keys($param)));
@ -1038,13 +1058,12 @@ class Database
* Inserts a row with the provided data in the provided table. * Inserts a row with the provided data in the provided table.
* If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead. * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $param parameter array * @param array $param parameter array
*
* @return boolean was the insert successful? * @return boolean was the insert successful?
* @throws \Exception * @throws \Exception
*/ */
public function replace($table, array $param) public function replace(string $table, array $param): bool
{ {
if (empty($table) || empty($param)) { if (empty($table) || empty($param)) {
$this->logger->info('Table and fields have to be set'); $this->logger->info('Table and fields have to be set');
@ -1053,7 +1072,7 @@ class Database
$param = $this->castFields($table, $param); $param = $this->castFields($table, $param);
$table_string = DBA::buildTableString($table); $table_string = DBA::buildTableString([$table]);
$fields_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], array_keys($param))); $fields_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], array_keys($param)));
@ -1069,7 +1088,7 @@ class Database
* *
* @return integer Last inserted id * @return integer Last inserted id
*/ */
public function lastInsertId() public function lastInsertId(): int
{ {
switch ($this->driver) { switch ($this->driver) {
case self::PDO: case self::PDO:
@ -1087,12 +1106,11 @@ class Database
* *
* This function can be extended in the future to accept a table array as well. * This function can be extended in the future to accept a table array as well.
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
*
* @return boolean was the lock successful? * @return boolean was the lock successful?
* @throws \Exception * @throws \Exception
*/ */
public function lock($table) public function lock(string $table): bool
{ {
// See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html // See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html
if ($this->driver == self::PDO) { if ($this->driver == self::PDO) {
@ -1102,7 +1120,7 @@ class Database
$this->connection->autocommit(false); $this->connection->autocommit(false);
} }
$success = $this->e("LOCK TABLES " . DBA::buildTableString($table) . " WRITE"); $success = $this->e("LOCK TABLES " . DBA::buildTableString([$table]) . " WRITE");
if ($this->driver == self::PDO) { if ($this->driver == self::PDO) {
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->pdo_emulate_prepares); $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->pdo_emulate_prepares);
@ -1126,7 +1144,7 @@ class Database
* @return boolean was the unlock successful? * @return boolean was the unlock successful?
* @throws \Exception * @throws \Exception
*/ */
public function unlock() public function unlock(): bool
{ {
// See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html // See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html
$this->performCommit(); $this->performCommit();
@ -1177,7 +1195,12 @@ class Database
return true; return true;
} }
protected function performCommit() /**
* Performs the commit
*
* @return boolean Was the command executed successfully?
*/
protected function performCommit(): bool
{ {
switch ($this->driver) { switch ($this->driver) {
case self::PDO: case self::PDO:
@ -1199,7 +1222,7 @@ class Database
* *
* @return boolean Was the command executed successfully? * @return boolean Was the command executed successfully?
*/ */
public function commit() public function commit(): bool
{ {
if (!$this->performCommit()) { if (!$this->performCommit()) {
return false; return false;
@ -1213,7 +1236,7 @@ class Database
* *
* @return boolean Was the command executed successfully? * @return boolean Was the command executed successfully?
*/ */
public function rollback() public function rollback(): bool
{ {
$ret = false; $ret = false;
@ -1230,6 +1253,7 @@ class Database
$ret = $this->connection->rollback(); $ret = $this->connection->rollback();
break; break;
} }
$this->in_transaction = false; $this->in_transaction = false;
return $ret; return $ret;
} }
@ -1243,14 +1267,14 @@ class Database
* @return boolean was the delete successful? * @return boolean was the delete successful?
* @throws \Exception * @throws \Exception
*/ */
public function delete($table, array $conditions) public function delete(string $table, array $conditions): bool
{ {
if (empty($table) || empty($conditions)) { if (empty($table) || empty($conditions)) {
$this->logger->info('Table and conditions have to be set'); $this->logger->info('Table and conditions have to be set');
return false; return false;
} }
$table_string = DBA::buildTableString($table); $table_string = DBA::buildTableString([$table]);
$condition_string = DBA::buildCondition($conditions); $condition_string = DBA::buildCondition($conditions);
@ -1280,7 +1304,7 @@ class Database
* Only set $old_fields to a boolean value when you are sure that you will update a single row. * Only set $old_fields to a boolean value when you are sure that you will update a single row.
* When you set $old_fields to "true" then $fields must contain all relevant fields! * When you set $old_fields to "true" then $fields must contain all relevant fields!
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields contains the fields that are updated * @param array $fields contains the fields that are updated
* @param array $condition condition array with the key values * @param array $condition condition array with the key values
* @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields) * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
@ -1288,8 +1312,9 @@ class Database
* *
* @return boolean was the update successfull? * @return boolean was the update successfull?
* @throws \Exception * @throws \Exception
* @todo Implement "bool $update_on_duplicate" to avoid mixed type for $old_fields
*/ */
public function update($table, $fields, $condition, $old_fields = [], $params = []) public function update(string $table, array $fields, array $condition, $old_fields = [], array $params = [])
{ {
if (empty($table) || empty($fields) || empty($condition)) { if (empty($table) || empty($fields) || empty($condition)) {
$this->logger->info('Table, fields and condition have to be set'); $this->logger->info('Table, fields and condition have to be set');
@ -1322,7 +1347,7 @@ class Database
$fields = $this->castFields($table, $fields); $fields = $this->castFields($table, $fields);
$table_string = DBA::buildTableString($table); $table_string = DBA::buildTableString([$table]);
$condition_string = DBA::buildCondition($condition); $condition_string = DBA::buildCondition($condition);
@ -1345,16 +1370,16 @@ class Database
/** /**
* Retrieve a single record from a table and returns it in an associative array * Retrieve a single record from a table and returns it in an associative array
* *
* @param string|array $table * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields * @param array $fields Array of selected fields, empty for all
* @param array $condition * @param array $condition Array of fields for condition
* @param array $params * @param array $params Array of several parameters
* *
* @return bool|array * @return bool|array
* @throws \Exception * @throws \Exception
* @see $this->select * @see $this->select
*/ */
public function selectFirst($table, array $fields = [], array $condition = [], $params = []) public function selectFirst(string $table, array $fields = [], array $condition = [], array $params = [])
{ {
$params['limit'] = 1; $params['limit'] = 1;
$result = $this->select($table, $fields, $condition, $params); $result = $this->select($table, $fields, $condition, $params);
@ -1371,16 +1396,15 @@ class Database
/** /**
* Select rows from a table and fills an array with the data * Select rows from a table and fills an array with the data
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
*
* @return array Data array * @return array Data array
* @throws \Exception * @throws \Exception
* @see self::select * @see self::select
*/ */
public function selectToArray($table, array $fields = [], array $condition = [], array $params = []) public function selectToArray(string $table, array $fields = [], array $condition = [], array $params = [])
{ {
return $this->toArray($this->select($table, $fields, $condition, $params)); return $this->toArray($this->select($table, $fields, $condition, $params));
} }
@ -1390,9 +1414,9 @@ class Database
* *
* @param array $fields * @param array $fields
* @param array $options * @param array $options
* @return array * @return array Escaped fields
*/ */
private function escapeFields(array $fields, array $options) private function escapeFields(array $fields, array $options): array
{ {
// In the case of a "GROUP BY" we have to add all the ORDER fields to the fieldlist. // In the case of a "GROUP BY" we have to add all the ORDER fields to the fieldlist.
// This needs to done to apply the "ANY_VALUE(...)" treatment from below to them. // This needs to done to apply the "ANY_VALUE(...)" treatment from below to them.
@ -1446,14 +1470,14 @@ class Database
* *
* $data = DBA::select($table, $fields, $condition, $params); * $data = DBA::select($table, $fields, $condition, $params);
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* @return boolean|object * @return boolean|object
* @throws \Exception * @throws \Exception
*/ */
public function select($table, array $fields = [], array $condition = [], array $params = []) public function select(string $table, array $fields = [], array $condition = [], array $params = [])
{ {
if (empty($table)) { if (empty($table)) {
return false; return false;
@ -1466,7 +1490,7 @@ class Database
$select_string = '*'; $select_string = '*';
} }
$table_string = DBA::buildTableString($table); $table_string = DBA::buildTableString([$table]);
$condition_string = DBA::buildCondition($condition); $condition_string = DBA::buildCondition($condition);
@ -1486,11 +1510,11 @@ class Database
/** /**
* Counts the rows from a table satisfying the provided condition * Counts the rows from a table satisfying the provided condition
* *
* @param string|array $table Table name or array [schema => table] * @param string $table Table name in format schema.table (while scheme is optiona)
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* *
* @return int * @return int Count of rows
* *
* Example: * Example:
* $table = "post"; * $table = "post";
@ -1502,13 +1526,13 @@ class Database
* $count = DBA::count($table, $condition); * $count = DBA::count($table, $condition);
* @throws \Exception * @throws \Exception
*/ */
public function count($table, array $condition = [], array $params = []) public function count(string $table, array $condition = [], array $params = []): int
{ {
if (empty($table)) { if (empty($table)) {
return false; throw new InvalidArgumentException('Parameter "table" cannot be empty.');
} }
$table_string = DBA::buildTableString($table); $table_string = DBA::buildTableString([$table]);
$condition_string = DBA::buildCondition($condition); $condition_string = DBA::buildCondition($condition);
@ -1541,7 +1565,7 @@ class Database
* *
* @return array Data array * @return array Data array
*/ */
public function toArray($stmt, $do_close = true, int $count = 0) public function toArray($stmt, bool $do_close = true, int $count = 0): array
{ {
if (is_bool($stmt)) { if (is_bool($stmt)) {
return []; return [];
@ -1569,7 +1593,8 @@ class Database
* @param array $fields * @param array $fields
* @return array casted fields * @return array casted fields
*/ */
public function castFields(string $table, array $fields) { public function castFields(string $table, array $fields): array
{
// When there is no data, we don't need to do something // When there is no data, we don't need to do something
if (empty($fields)) { if (empty($fields)) {
return $fields; return $fields;
@ -1587,15 +1612,15 @@ class Database
$types = []; $types = [];
$tables = DBStructure::definition('', false); $tables = $this->dbaDefinition->getAll();
if (empty($tables[$table])) { if (empty($tables[$table])) {
// When a matching table wasn't found we check if it is a view // When a matching table wasn't found we check if it is a view
$views = View::definition('', false); $views = $this->viewDefinition->getAll();
if (empty($views[$table])) { if (empty($views[$table])) {
return $fields; return $fields;
} }
foreach(array_keys($fields) as $field) { foreach (array_keys($fields) as $field) {
if (!empty($views[$table]['fields'][$field])) { if (!empty($views[$table]['fields'][$field])) {
$viewdef = $views[$table]['fields'][$field]; $viewdef = $views[$table]['fields'][$field];
if (!empty($tables[$viewdef[0]]['fields'][$viewdef[1]]['type'])) { if (!empty($tables[$viewdef[0]]['fields'][$viewdef[1]]['type'])) {
@ -1632,7 +1657,7 @@ class Database
* *
* @return string Error number (0 if no error) * @return string Error number (0 if no error)
*/ */
public function errorNo() public function errorNo(): int
{ {
return $this->errorno; return $this->errorno;
} }
@ -1642,7 +1667,7 @@ class Database
* *
* @return string Error message ('' if no error) * @return string Error message ('' if no error)
*/ */
public function errorMessage() public function errorMessage(): string
{ {
return $this->error; return $this->error;
} }
@ -1654,7 +1679,7 @@ class Database
* *
* @return boolean was the close successful? * @return boolean was the close successful?
*/ */
public function close($stmt) public function close($stmt): bool
{ {
$this->profiler->startRecording('database'); $this->profiler->startRecording('database');
@ -1696,38 +1721,39 @@ class Database
* 'amount' => Number of concurrent database processes * 'amount' => Number of concurrent database processes
* @throws \Exception * @throws \Exception
*/ */
public function processlist() public function processlist(): array
{ {
$ret = $this->p("SHOW PROCESSLIST"); $ret = $this->p('SHOW PROCESSLIST');
$data = $this->toArray($ret); $data = $this->toArray($ret);
$processes = 0; $processes = 0;
$states = []; $states = [];
foreach ($data as $process) { foreach ($data as $process) {
$state = trim($process["State"]); $state = trim($process['State']);
// Filter out all non blocking processes // Filter out all non blocking processes
if (!in_array($state, ["", "init", "statistics", "updating"])) { if (!in_array($state, ['', 'init', 'statistics', 'updating'])) {
++$states[$state]; ++$states[$state];
++$processes; ++$processes;
} }
} }
$statelist = ""; $statelist = '';
foreach ($states as $state => $usage) { foreach ($states as $state => $usage) {
if ($statelist != "") { if ($statelist != '') {
$statelist .= ", "; $statelist .= ', ';
} }
$statelist .= $state . ": " . $usage; $statelist .= $state . ': ' . $usage;
} }
return (["list" => $statelist, "amount" => $processes]); return (['list' => $statelist, 'amount' => $processes]);
} }
/** /**
* Fetch a database variable * Fetch a database variable
* *
* @param string $name * @param string $name
* @return string content * @return string|null content or null if inexistent
* @throws \Exception
*/ */
public function getVariable(string $name) public function getVariable(string $name)
{ {
@ -1739,10 +1765,9 @@ class Database
* Checks if $array is a filled array with at least one entry. * Checks if $array is a filled array with at least one entry.
* *
* @param mixed $array A filled array with at least one entry * @param mixed $array A filled array with at least one entry
*
* @return boolean Whether $array is a filled array or an object with rows * @return boolean Whether $array is a filled array or an object with rows
*/ */
public function isResult($array) public function isResult($array): bool
{ {
// It could be a return value from an update statement // It could be a return value from an update statement
if (is_bool($array)) { if (is_bool($array)) {
@ -1762,10 +1787,9 @@ class Database
* @param mixed $value Array value * @param mixed $value Array value
* @param string $key Array key * @param string $key Array key
* @param boolean $add_quotation add quotation marks for string values * @param boolean $add_quotation add quotation marks for string values
*
* @return void * @return void
*/ */
private function escapeArrayCallback(&$value, $key, $add_quotation) private function escapeArrayCallback(&$value, string $key, bool $add_quotation)
{ {
if (!$add_quotation) { if (!$add_quotation) {
if (is_bool($value)) { if (is_bool($value)) {
@ -1790,10 +1814,9 @@ class Database
* *
* @param mixed $arr Array with values to be escaped * @param mixed $arr Array with values to be escaped
* @param boolean $add_quotation add quotation marks for string values * @param boolean $add_quotation add quotation marks for string values
*
* @return void * @return void
*/ */
public function escapeArray(&$arr, $add_quotation = false) public function escapeArray(&$arr, bool $add_quotation = false)
{ {
array_walk($arr, [$this, 'escapeArrayCallback'], $add_quotation); array_walk($arr, [$this, 'escapeArrayCallback'], $add_quotation);
} }
@ -1801,13 +1824,14 @@ class Database
/** /**
* Replaces a string in the provided fields of the provided table * Replaces a string in the provided fields of the provided table
* *
* @param string $table_name * @param string $table Table name
* @param array $fields List of field names in the provided table * @param array $fields List of field names in the provided table
* @param string $search * @param string $search String to search for
* @param string $replace * @param string $replace String to replace with
* @return void
* @throws \Exception * @throws \Exception
*/ */
public function replaceInTableFields(string $table_name, array $fields, string $search, string $replace) public function replaceInTableFields(string $table, array $fields, string $search, string $replace)
{ {
$search = $this->escape($search); $search = $this->escape($search);
$replace = $this->escape($replace); $replace = $this->escape($replace);
@ -1820,9 +1844,10 @@ class Database
$upds = implode(', ', $upd); $upds = implode(', ', $upd);
$r = $this->e(sprintf("UPDATE %s SET %s;", $table_name, $upds)); $r = $this->e(sprintf("UPDATE %s SET %s;", DBA::quoteIdentifier($table), $upds));
if (!$this->isResult($r)) { if (!$this->isResult($r)) {
throw new \RuntimeException("Failed updating `$table_name`: " . $this->errorMessage()); throw new \RuntimeException("Failed updating `$table`: " . $this->errorMessage());
} }
} }
} }

View file

@ -0,0 +1,126 @@
<?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\Database\Definition;
use Exception;
use Friendica\Core\Hook;
/**
* Stores the whole database definition
*/
class DbaDefinition
{
/** @var string The relative path of the db structure config file */
const DBSTRUCTURE_RELATIVE_PATH = '/static/dbstructure.config.php';
/** @var array The complete DB definition as an array */
protected $definition;
/** @var string */
protected $configFile;
/**
* @param string $basePath The basepath of the dbstructure file (loads relative path in case of null)
*
* @throws Exception in case the config file isn't available/readable
*/
public function __construct(string $basePath)
{
$this->configFile = $basePath . static::DBSTRUCTURE_RELATIVE_PATH;
if (!is_readable($this->configFile)) {
throw new Exception('Missing database structure config file static/dbstructure.config.php at basePath=' . $basePath);
}
}
/**
* @return array Returns the whole Definition as an array
*/
public function getAll(): array
{
return $this->definition;
}
/**
* Truncate field data for the given table
*
* @param string $table Name of the table to load field definitions for
* @param array $data data fields
*
* @return array fields for the given
*/
public function truncateFieldsForTable(string $table, array $data): array
{
$definition = $this->definition;
if (empty($definition[$table])) {
return [];
}
$fieldNames = array_keys($definition[$table]['fields']);
$fields = [];
// Assign all field that are present in the table
foreach ($fieldNames as $field) {
if (isset($data[$field])) {
// Limit the length of varchar, varbinary, char and binrary fields
if (is_string($data[$field]) && preg_match("/char\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
$data[$field] = mb_substr($data[$field], 0, $result[1]);
} elseif (is_string($data[$field]) && preg_match("/binary\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
$data[$field] = substr($data[$field], 0, $result[1]);
}
$fields[$field] = $data[$field];
}
}
return $fields;
}
/**
* Loads the database structure definition from the static/dbstructure.config.php file.
* On first pass, defines DB_UPDATE_VERSION constant.
*
* @param bool $withAddonStructure Whether to tack on addons additional tables
*
* @throws Exception in case the definition cannot be found
*
* @see static/dbstructure.config.php
*
* @return self The current instance
*/
public function load(bool $withAddonStructure = false): self
{
$definition = require $this->configFile;
if (!$definition) {
throw new Exception('Corrupted database structure config file static/dbstructure.config.php');
}
if ($withAddonStructure) {
Hook::callAll('dbstructure_definition', $definition);
}
$this->definition = $definition;
return $this;
}
}

View file

@ -0,0 +1,91 @@
<?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\Database\Definition;
use Exception;
use Friendica\Core\Hook;
/**
* Stores the whole View definitions
*/
class ViewDefinition
{
/** @var string the relative path to the database view config file */
const DBSTRUCTURE_RELATIVE_PATH = '/static/dbview.config.php';
/** @var array The complete view definition as an array */
protected $definition;
/** @var string */
protected $configFile;
/**
* @param string $basePath The basepath of the dbview file (loads relative path in case of null)
*
* @throws Exception in case the config file isn't available/readable
*/
public function __construct(string $basePath)
{
$this->configFile = $basePath . static::DBSTRUCTURE_RELATIVE_PATH;
if (!is_readable($this->configFile)) {
throw new Exception('Missing database structure config file static/dbview.config.php at basePath=' . $basePath);
}
}
/**
* @return array Returns the whole Definition as an array
*/
public function getAll(): array
{
return $this->definition;
}
/**
* Loads the database structure definition from the static/dbview.config.php file.
* On first pass, defines DB_UPDATE_VERSION constant.
*
* @param bool $withAddonStructure Whether to tack on addons additional tables
*
* @throws Exception in case the definition cannot be found
*
* @see static/dbview.config.php
*
* @return self The current instance
*/
public function load(bool $withAddonStructure = false): self
{
$definition = require $this->configFile;
if (!$definition) {
throw new Exception('Corrupted database structure config file static/dbstructure.config.php');
}
if ($withAddonStructure) {
Hook::callAll('dbview_definition', $definition);
}
$this->definition = $definition;
return $this;
}
}

View file

@ -133,7 +133,7 @@ class PostUpdate
} }
$max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]); $max_item_delivery_data = DBA::selectFirst('item-delivery-data', ['iid'], ['queue_count > 0 OR queue_done > 0'], ['order' => ['iid']]);
$max_iid = $max_item_delivery_data['iid']; $max_iid = $max_item_delivery_data['iid'] ?? 0;
Logger::info('Start update1297 with max iid: ' . $max_iid); Logger::info('Start update1297 with max iid: ' . $max_iid);
@ -538,7 +538,7 @@ class PostUpdate
private static function update1347() private static function update1347()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1347) { if (DI::config()->get('system', 'post_update_version') >= 1347) {
return true; return true;
} }
@ -547,7 +547,7 @@ class PostUpdate
return true; return true;
} }
$id = DI::config()->get("system", "post_update_version_1347_id", 0); $id = DI::config()->get('system', 'post_update_version_1347_id', 0);
Logger::info('Start', ['item' => $id]); Logger::info('Start', ['item' => $id]);
@ -582,12 +582,12 @@ class PostUpdate
} }
DBA::close($items); DBA::close($items);
DI::config()->set("system", "post_update_version_1347_id", $id); DI::config()->set('system', 'post_update_version_1347_id', $id);
Logger::info('Processed', ['rows' => $rows, 'last' => $id]); Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
if ($start_id == $id) { if ($start_id == $id) {
DI::config()->set("system", "post_update_version", 1347); DI::config()->set('system', 'post_update_version', 1347);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -605,11 +605,11 @@ class PostUpdate
private static function update1348() private static function update1348()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1348) { if (DI::config()->get('system', 'post_update_version') >= 1348) {
return true; return true;
} }
$id = DI::config()->get("system", "post_update_version_1348_id", 0); $id = DI::config()->get('system', 'post_update_version_1348_id', 0);
Logger::info('Start', ['contact' => $id]); Logger::info('Start', ['contact' => $id]);
@ -635,12 +635,12 @@ class PostUpdate
} }
DBA::close($contacts); DBA::close($contacts);
DI::config()->set("system", "post_update_version_1348_id", $id); DI::config()->set('system', 'post_update_version_1348_id', $id);
Logger::info('Processed', ['rows' => $rows, 'last' => $id]); Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
if ($start_id == $id) { if ($start_id == $id) {
DI::config()->set("system", "post_update_version", 1348); DI::config()->set('system', 'post_update_version', 1348);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -658,11 +658,11 @@ class PostUpdate
private static function update1349() private static function update1349()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1349) { if (DI::config()->get('system', 'post_update_version') >= 1349) {
return true; return true;
} }
$id = DI::config()->get("system", "post_update_version_1349_id", ''); $id = DI::config()->get('system', 'post_update_version_1349_id', '');
Logger::info('Start', ['apcontact' => $id]); Logger::info('Start', ['apcontact' => $id]);
@ -688,12 +688,12 @@ class PostUpdate
} }
DBA::close($apcontacts); DBA::close($apcontacts);
DI::config()->set("system", "post_update_version_1349_id", $id); DI::config()->set('system', 'post_update_version_1349_id', $id);
Logger::info('Processed', ['rows' => $rows, 'last' => $id]); Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
if ($start_id == $id) { if ($start_id == $id) {
DI::config()->set("system", "post_update_version", 1349); DI::config()->set('system', 'post_update_version', 1349);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -711,7 +711,7 @@ class PostUpdate
private static function update1383() private static function update1383()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1383) { if (DI::config()->get('system', 'post_update_version') >= 1383) {
return true; return true;
} }
@ -737,7 +737,7 @@ class PostUpdate
} }
DBA::close($photos); DBA::close($photos);
DI::config()->set("system", "post_update_version", 1383); DI::config()->set('system', 'post_update_version', 1383);
Logger::info('Done', ['deleted' => $deleted]); Logger::info('Done', ['deleted' => $deleted]);
return true; return true;
} }
@ -752,7 +752,7 @@ class PostUpdate
private static function update1384() private static function update1384()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1384) { if (DI::config()->get('system', 'post_update_version') >= 1384) {
return true; return true;
} }
@ -782,7 +782,7 @@ class PostUpdate
Logger::info('Processed', ['rows' => $rows]); Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) { if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1384); DI::config()->set('system', 'post_update_version', 1384);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -800,12 +800,12 @@ class PostUpdate
private static function update1400() private static function update1400()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1400) { if (DI::config()->get('system', 'post_update_version') >= 1400) {
return true; return true;
} }
if (!DBStructure::existsTable('item')) { if (!DBStructure::existsTable('item')) {
DI::config()->set("system", "post_update_version", 1400); DI::config()->set('system', 'post_update_version', 1400);
return true; return true;
} }
@ -829,7 +829,7 @@ class PostUpdate
Logger::info('Processed', ['rows' => $rows]); Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) { if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1400); DI::config()->set('system', 'post_update_version', 1400);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -847,7 +847,7 @@ class PostUpdate
private static function update1424() private static function update1424()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1424) { if (DI::config()->get('system', 'post_update_version') >= 1424) {
return true; return true;
} }
@ -871,7 +871,7 @@ class PostUpdate
Logger::info('Processed', ['rows' => $rows]); Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) { if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1424); DI::config()->set('system', 'post_update_version', 1424);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -889,7 +889,7 @@ class PostUpdate
private static function update1425() private static function update1425()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1425) { if (DI::config()->get('system', 'post_update_version') >= 1425) {
return true; return true;
} }
@ -918,7 +918,7 @@ class PostUpdate
Logger::info('Processed', ['rows' => $rows]); Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) { if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1425); DI::config()->set('system', 'post_update_version', 1425);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -936,7 +936,7 @@ class PostUpdate
private static function update1426() private static function update1426()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1426) { if (DI::config()->get('system', 'post_update_version') >= 1426) {
return true; return true;
} }
@ -965,7 +965,7 @@ class PostUpdate
Logger::info('Processed', ['rows' => $rows]); Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) { if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1426); DI::config()->set('system', 'post_update_version', 1426);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }
@ -983,7 +983,7 @@ class PostUpdate
private static function update1427() private static function update1427()
{ {
// Was the script completed? // Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1427) { if (DI::config()->get('system', 'post_update_version') >= 1427) {
return true; return true;
} }
@ -1012,7 +1012,7 @@ class PostUpdate
Logger::info('Processed', ['rows' => $rows]); Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) { if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1427); DI::config()->set('system', 'post_update_version', 1427);
Logger::info('Done'); Logger::info('Done');
return true; return true;
} }

View file

@ -21,136 +21,44 @@
namespace Friendica\Database; namespace Friendica\Database;
use Exception;
use Friendica\Core\Hook;
use Friendica\DI; use Friendica\DI;
use Friendica\Util\Writer\ViewDefinitionSqlWriter;
class View class View
{ {
/** /**
* view definition loaded from static/dbview.config.php * Creates a view
* *
* @var array * @param bool $verbose Whether to show SQL statements
* @param bool $action Whether to execute SQL statements
* @return void
*/ */
private static $definition = [];
/**
* Loads the database structure definition from the static/dbview.config.php file.
* On first pass, defines DB_UPDATE_VERSION constant.
*
* @see static/dbview.config.php
* @param boolean $with_addons_structure Whether to tack on addons additional tables
* @param string $basePath The base path of this application
* @return array
* @throws Exception
*/
public static function definition($basePath = '', $with_addons_structure = true)
{
if (!self::$definition) {
if (empty($basePath)) {
$basePath = DI::app()->getBasePath();
}
$filename = $basePath . '/static/dbview.config.php';
if (!is_readable($filename)) {
throw new Exception('Missing database view config file static/dbview.config.php');
}
$definition = require $filename;
if (!$definition) {
throw new Exception('Corrupted database view config file static/dbview.config.php');
}
self::$definition = $definition;
} else {
$definition = self::$definition;
}
if ($with_addons_structure) {
Hook::callAll('dbview_definition', $definition);
}
return $definition;
}
public static function create(bool $verbose, bool $action) public static function create(bool $verbose, bool $action)
{ {
// Delete previously used views that aren't used anymore // Delete previously used views that aren't used anymore
foreach(['post-view', 'post-thread-view'] as $view) { foreach (['post-view', 'post-thread-view'] as $view) {
if (self::isView($view)) { if (self::isView($view)) {
$sql = sprintf("DROP VIEW IF EXISTS `%s`", DBA::escape($view)); $sql = sprintf("DROP VIEW IF EXISTS `%s`", DBA::escape($view));
if (!empty($sql) && $verbose) { if (!empty($sql) && $verbose) {
echo $sql . ";\n"; echo $sql . ";\n";
} }
if (!empty($sql) && $action) { if (!empty($sql) && $action) {
DBA::e($sql); DBA::e($sql);
} }
} }
} }
$definition = self::definition(); $definition = DI::viewDefinition()->getAll();
foreach ($definition as $name => $structure) { foreach ($definition as $name => $structure) {
self::createview($name, $structure, $verbose, $action); if (self::isView($name)) {
} DBA::e(sprintf("DROP VIEW IF EXISTS `%s`", DBA::escape($name)));
} } elseif (self::isTable($name)) {
DBA::e(sprintf("DROP TABLE IF EXISTS `%s`", DBA::escape($name)));
public static function printStructure($basePath)
{
$database = self::definition($basePath, false);
foreach ($database as $name => $structure) {
echo "--\n";
echo "-- VIEW $name\n";
echo "--\n";
self::createView($name, $structure, true, false);
echo "\n";
}
}
private static function createview($name, $structure, $verbose, $action)
{
$r = true;
$sql_rows = [];
foreach ($structure["fields"] as $fieldname => $origin) {
if (is_string($origin)) {
$sql_rows[] = $origin . " AS `" . DBA::escape($fieldname) . "`";
} elseif (is_array($origin) && (sizeof($origin) == 2)) {
$sql_rows[] = "`" . DBA::escape($origin[0]) . "`.`" . DBA::escape($origin[1]) . "` AS `" . DBA::escape($fieldname) . "`";
} }
DBA::e(ViewDefinitionSqlWriter::createView($name, $structure));
} }
if (self::isView($name)) {
$sql = sprintf("DROP VIEW IF EXISTS `%s`", DBA::escape($name));
} elseif (self::isTable($name)) {
$sql = sprintf("DROP TABLE IF EXISTS `%s`", DBA::escape($name));
}
if (!empty($sql) && $verbose) {
echo $sql . ";\n";
}
if (!empty($sql) && $action) {
DBA::e($sql);
}
$sql = sprintf("CREATE VIEW `%s` AS SELECT \n\t", DBA::escape($name)) .
implode(",\n\t", $sql_rows) . "\n\t" . $structure['query'];
if ($verbose) {
echo $sql . ";\n";
}
if ($action) {
$r = DBA::e($sql);
}
return $r;
} }
/** /**
@ -159,9 +67,9 @@ class View
* @param string $view * @param string $view
* @return boolean "true" if it's a view * @return boolean "true" if it's a view
*/ */
private static function isView(string $view) private static function isView(string $view): bool
{ {
$status = DBA::selectFirst(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_TYPE'], $status = DBA::selectFirst('INFORMATION_SCHEMA.TABLES', ['TABLE_TYPE'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_NAME' => $view]); ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_NAME' => $view]);
if (empty($status['TABLE_TYPE'])) { if (empty($status['TABLE_TYPE'])) {
@ -177,9 +85,9 @@ class View
* @param string $table * @param string $table
* @return boolean "true" if it's a table * @return boolean "true" if it's a table
*/ */
private static function isTable(string $table) private static function isTable(string $table): bool
{ {
$status = DBA::selectFirst(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_TYPE'], $status = DBA::selectFirst('INFORMATION_SCHEMA.TABLES', ['TABLE_TYPE'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_NAME' => $table]); ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_NAME' => $table]);
if (empty($status['TABLE_TYPE'])) { if (empty($status['TABLE_TYPE'])) {

View file

@ -57,12 +57,17 @@ class Account extends BaseFactory
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws ImagickException|HTTPException\NotFoundException * @throws ImagickException|HTTPException\NotFoundException
*/ */
public function createFromContactId(int $contactId, $uid = 0): \Friendica\Object\Api\Mastodon\Account public function createFromContactId(int $contactId, int $uid = 0): \Friendica\Object\Api\Mastodon\Account
{ {
$contact = Contact::getById($contactId, ['uri-id']); $contact = Contact::getById($contactId, ['uri-id']);
if (empty($contact)) { if (empty($contact)) {
throw new HTTPException\NotFoundException('Contact ' . $contactId . ' not found'); throw new HTTPException\NotFoundException('Contact ' . $contactId . ' not found');
} }
if (empty($contact['uri-id'])) {
throw new HTTPException\NotFoundException('Contact ' . $contactId . ' has no uri-id set');
}
return self::createFromUriId($contact['uri-id'], $uid); return self::createFromUriId($contact['uri-id'], $uid);
} }
@ -74,7 +79,7 @@ class Account extends BaseFactory
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws ImagickException|HTTPException\NotFoundException * @throws ImagickException|HTTPException\NotFoundException
*/ */
public function createFromUriId(int $contactUriId, $uid = 0): \Friendica\Object\Api\Mastodon\Account public function createFromUriId(int $contactUriId, int $uid = 0): \Friendica\Object\Api\Mastodon\Account
{ {
$account = DBA::selectFirst('account-user-view', [], ['uri-id' => $contactUriId, 'uid' => [0, $uid]], ['order' => ['id' => true]]); $account = DBA::selectFirst('account-user-view', [], ['uri-id' => $contactUriId, 'uid' => [0, $uid]], ['order' => ['id' => true]]);
if (empty($account)) { if (empty($account)) {

View file

@ -190,7 +190,7 @@ class Status extends BaseFactory
*/ */
public function createFromMailId(int $id): \Friendica\Object\Api\Mastodon\Status public function createFromMailId(int $id): \Friendica\Object\Api\Mastodon\Status
{ {
$item = ActivityPub\Transmitter::ItemArrayFromMail($id, true); $item = ActivityPub\Transmitter::getItemArrayFromMail($id, true);
if (empty($item)) { if (empty($item)) {
$this->mstdnErrorFactory->RecordNotFound(); $this->mstdnErrorFactory->RecordNotFound();
} }

View file

@ -70,6 +70,7 @@ class Status extends BaseFactory
/** /**
* @param int $uriId Uri-ID of the item * @param int $uriId Uri-ID of the item
* @param int $uid Item user * @param int $uid Item user
* @param bool $include_entities Whether to include entities
* *
* @return \Friendica\Object\Api\Twitter\Status * @return \Friendica\Object\Api\Twitter\Status
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
@ -90,12 +91,13 @@ class Status extends BaseFactory
/** /**
* @param int $uriId Uri-ID of the item * @param int $uriId Uri-ID of the item
* @param int $uid Item user * @param int $uid Item user
* @param bool $include_entities Whether to include entities
* *
* @return \Friendica\Object\Api\Twitter\Status * @return \Friendica\Object\Api\Twitter\Status
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws ImagickException|HTTPException\NotFoundException * @throws ImagickException|HTTPException\NotFoundException
*/ */
public function createFromUriId(int $uriId, $uid = 0, $include_entities = false): \Friendica\Object\Api\Twitter\Status public function createFromUriId(int $uriId, int $uid = 0, bool $include_entities = false): \Friendica\Object\Api\Twitter\Status
{ {
$fields = ['parent-uri-id', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'causer-id', $fields = ['parent-uri-id', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'causer-id',
'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network','post-reason', 'language', 'gravity', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network','post-reason', 'language', 'gravity',
@ -110,6 +112,7 @@ class Status extends BaseFactory
/** /**
* @param array $item item array * @param array $item item array
* @param int $uid Item user * @param int $uid Item user
* @param bool $include_entities Whether to include entities
* *
* @return \Friendica\Object\Api\Twitter\Status * @return \Friendica\Object\Api\Twitter\Status
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException

View file

@ -51,7 +51,7 @@ class User extends BaseFactory
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public function createFromContactId(int $contactId, $uid = 0, $skip_status = true, $include_user_entities = true) public function createFromContactId(int $contactId, int $uid = 0, bool $skip_status = true, bool $include_user_entities = true)
{ {
$cdata = Contact::getPublicAndUserContactID($contactId, $uid); $cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if (!empty($cdata)) { if (!empty($cdata)) {
@ -78,7 +78,7 @@ class User extends BaseFactory
return new \Friendica\Object\Api\Twitter\User($publicContact, $apcontact, $userContact, $status, $include_user_entities); return new \Friendica\Object\Api\Twitter\User($publicContact, $apcontact, $userContact, $status, $include_user_entities);
} }
public function createFromUserId(int $uid, $skip_status = true, $include_user_entities = true) public function createFromUserId(int $uid, bool $skip_status = true, bool $include_user_entities = true)
{ {
return $this->createFromContactId(Contact::getPublicIdByUserId($uid), $uid, $skip_status, $include_user_entities); return $this->createFromContactId(Contact::getPublicIdByUserId($uid), $uid, $skip_status, $include_user_entities);
} }

View file

@ -57,7 +57,7 @@ class LegacyModule extends BaseModule
* @param string $file_path * @param string $file_path
* @throws \Exception * @throws \Exception
*/ */
private function setModuleFile($file_path) private function setModuleFile(string $file_path)
{ {
if (!is_readable($file_path)) { if (!is_readable($file_path)) {
throw new \Exception(DI::l10n()->t('Legacy module file not found: %s', $file_path)); throw new \Exception(DI::l10n()->t('Legacy module file not found: %s', $file_path));
@ -87,7 +87,7 @@ class LegacyModule extends BaseModule
* @return string * @return string
* @throws \Exception * @throws \Exception
*/ */
private function runModuleFunction(string $function_suffix) private function runModuleFunction(string $function_suffix): string
{ {
$function_name = $this->moduleName . '_' . $function_suffix; $function_name = $this->moduleName . '_' . $function_suffix;

Some files were not shown because too many files have changed in this diff Show more