Compare commits

..

62 Commits

Author SHA1 Message Date
Andrew Dolgov fd98d6d117
add branch tasks 2023-04-01 12:52:45 +03:00
Andrew Dolgov 92185933f9
more pipeline tweaks 2023-04-01 12:36:37 +03:00
Andrew Dolgov 4fd0d13b64
phpstan: depend on phpunit 2023-04-01 12:03:59 +03:00
Andrew Dolgov e2a02f1f4b
add some manual job triggers 2023-04-01 12:03:20 +03:00
Andrew Dolgov 00d2cb0f93
remove docs stage 2023-04-01 09:51:44 +03:00
Andrew Dolgov 2b01786124
CI: set some job filters 2023-04-01 09:50:32 +03:00
Andrew Dolgov 088dd049b5
fix phpdoc stage 2023-04-01 09:35:54 +03:00
Andrew Dolgov 28a911a2a8
add phpdoc job 2023-04-01 09:34:59 +03:00
Andrew Dolgov 066b9a29d7
WIP: gitlab-ci 2023-04-01 08:58:54 +03:00
fox 269c0f53b8 Merge pull request 'isLoggedIn adds a message to the system log when it returns false, fix for php8+' (#106) from rodneys_mission/tt-rss:master into master
Reviewed-on: https://gitea.tt-rss.org/tt-rss/tt-rss/pulls/106
2023-04-01 06:36:57 +03:00
Rodney Stromlund 80bd26b3b1 isLoggedIn adds a message to the system log when it returns false, fix for php8+, removed empty test for bool conversion. 2023-03-31 20:26:36 -05:00
Andrew Dolgov 3ca3e54251
lint job: remove useless cache hash 2023-03-29 17:19:57 +03:00
Rodney Stromlund 7795c415ab isLoggedIn adds a message to the system log when it returns false, fix for php8+ 2023-03-29 08:20:52 -05:00
Andrew Dolgov cf656125b9
good idea to checkout code before trying to document it 2023-03-28 18:02:26 +03:00
Andrew Dolgov 740d249aba
phpdoc job: verbosity 2023-03-28 18:00:00 +03:00
Andrew Dolgov dfffa20e78
simplify pipeline setup 2023-03-28 17:57:17 +03:00
Andrew Dolgov d25440f051
fix ssh for phpdoc pipeline 2023-03-28 17:55:10 +03:00
Andrew Dolgov da620ef5d8
add phpdoc job 2023-03-28 17:52:59 +03:00
Andrew Dolgov ecedc51162
add docker build cache 2023-03-27 09:34:02 +03:00
Andrew Dolgov 33a3672eab
use action for image meta 2023-03-26 20:48:07 +03:00
Andrew Dolgov a0b52caced
we don't need to do npm install either 2023-03-26 19:19:11 +03:00
Andrew Dolgov 37014d78fd
remove node_modules after eslint pass 2023-03-26 19:18:03 +03:00
Andrew Dolgov 0c42d99a93
shorten pipeline 2023-03-26 17:25:11 +03:00
Andrew Dolgov c41c1bd353
update FROM links for harbor 2023-03-26 10:28:33 +03:00
Andrew Dolgov d40b6c655f
enable multiarch 2023-03-26 00:09:24 +03:00
Andrew Dolgov 6cf4ebbabd
bring back nginx_xaccel 2023-03-25 23:05:06 +03:00
Andrew Dolgov 723323d746
add gitea-CI docker builder 2023-03-25 22:41:47 +03:00
Andrew Dolgov d305532bce
commit package-lock.json, add eslint task 2023-03-25 18:10:26 +03:00
Andrew Dolgov 903b9dbb8b
CI: add on PR 2023-03-25 11:26:53 +03:00
Andrew Dolgov 8e490af01c
jenkins lint -> gitea workflow 2023-03-25 10:58:02 +03:00
Andrew Dolgov ca86a0e239
add lint workflow 2023-03-25 09:56:59 +03:00
Andrew Dolgov 563675de09
* auth_internal OTP form: fix double-urlencode
* post-login redirect: handle ?return in a less idiotic fashion
2023-03-23 20:05:03 +03:00
Andrew Dolgov 0f9488ace0
combined mode: prevent left part of footer pushing right part out of viewport 2023-03-20 18:33:29 +03:00
fox cddbf5bf5a Merge pull request 'Replace special feed and category numbers with constants.' (#104) from wn/tt-rss:feature/special-feed-and-cat-consts into master
Reviewed-on: https://dev.tt-rss.org/tt-rss/tt-rss/pulls/104
2023-03-07 20:13:10 +03:00
wn_ b14a8a76eb Change 'FEED_NOTHING' to 'FEED_DASHBOARD'. 2023-03-07 15:45:07 +00:00
fox c4026b408b Merge pull request 'Bump PHPStan to 1.10.3, address some new warnings' (#103) from wn/tt-rss:feature/bump-phpstan into master
Reviewed-on: https://dev.tt-rss.org/tt-rss/tt-rss/pulls/103
2023-03-07 16:50:13 +03:00
wn_ c923fda8c9 Also use friendly names for special feed+cat IDs in the frontend. 2023-03-05 20:06:48 +00:00
wn_ fe08299ec4 Replace special feed and category numbers with constants. 2023-03-05 19:16:48 +00:00
wn_ 029cb8f442 Revert 7ed4fa4c1d and use @var instead.
PHPStan had trouble recognizing that ['items'] might have elements added.
2023-03-05 16:29:51 +00:00
wn_ 42b287e964 Remove unused 'Prefs::_delete()'.
Related to dabb85c7dd.
2023-03-05 15:30:12 +00:00
wn_ dabb85c7dd Address PHPStan warning about unused private method 'Prefs::_delete()'. 2023-03-05 14:20:19 +00:00
wn_ 7ed4fa4c1d Tweak to appease PHPStan in 'Pref_Feeds::_makefeedtree()'.
PHPStan flagged the 'count()' below this with: Comparison operation '>' between 0 and 0 is always false.
2023-03-05 14:20:19 +00:00
wn_ c4b16ca608 Address PHPStan 'right side always true' in 'PluginHost::lookup_command()'.
Since 'PluginHost::add_command()' is currently the only way to add to this private array, and it always sets an array, this is reasonably safe.
2023-03-05 14:20:19 +00:00
wn_ c48dd6a3c4 Address PHPStan 'right side always true' in FeedItem_RSS. 2023-03-05 14:20:19 +00:00
wn_ d34d01fd72 Bump PHPStan from 1.8.2 to 1.10.3
* https://phpstan.org/blog/phpstan-1-9-0-with-phpdoc-asserts-list-type
* https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector
2023-03-05 14:20:19 +00:00
Andrew Dolgov d210ae50ad
API:
- sharedToPublished: add optional sanitize parameter (defaults to true)
   if disabled, allows inserting HTML into shared article content;
 - clean() already invokes strip_tags() so it's pointless to do both;
2023-03-05 08:07:55 +03:00
Andrew Dolgov b7a6c948d0
tags display: instead of limiting to 5 tags, limit by container width % 2023-03-01 21:41:52 +03:00
Andrew Dolgov 04c2fa9f15
Merge branch 'master' of git.tt-rss.org:tt-rss/tt-rss 2023-02-25 19:31:07 +03:00
Andrew Dolgov 4d825fa6a6
require PHP to have support for flock() 2023-02-25 19:30:41 +03:00
fox 33c20d42df Merge pull request 'add override links to utility views' (#102) from levito/tt-rss:add-override-links-to-util-views into master
Reviewed-on: https://dev.tt-rss.org/tt-rss/tt-rss/pulls/102
2023-02-24 07:10:28 +03:00
Veit Lehmann aa2b770e30 add override links to utility views
This enables `local-overrides.css` and `local-overrides.js` for all utility views, for example to add polyfills, enable responsive styling or to adjust styles globally.
2023-02-24 00:46:40 +01:00
Andrew Dolgov a2af3a6bb4
API: add getFeedIcon endpoint, bump version 2023-02-23 18:00:18 +03:00
fox fcfcb69a2e Merge pull request 'Handle fetch issues in 'RSSUtils::update_basic_info'.' (#101) from wn/tt-rss:bugfix/handle-failed-basic-info-fetch into master
Reviewed-on: https://dev.tt-rss.org/tt-rss/tt-rss/pulls/101
2023-02-19 12:03:49 +03:00
wn_ fd55e492c3 Handle fetch issues in 'RSSUtils::update_basic_info'. 2023-02-17 12:10:51 +00:00
fox 7bbe71b818 Merge pull request 'Fix calculating average color for *.ico' (#100) from yan12125/tt-rss:fix-ico-avg-color into master
Reviewed-on: https://dev.tt-rss.org/tt-rss/tt-rss/pulls/100
2023-02-13 20:43:04 +03:00
Chih-Hsuan Yen 40afee5d12 Fix calculating average color for *.ico
Currently colorPalette() always fails for *.ico due to a logic error.
It's a regression from 8f749fe61b.
2023-02-10 18:20:42 +08:00
fox 0cd4abe4eb Merge pull request 'Attempt calculating custom favicon avg color, only try calculating once' (#99) from wn/tt-rss:feature/custom-favicon-detect-color into master
Reviewed-on: https://dev.tt-rss.org/tt-rss/tt-rss/pulls/99
2023-02-04 10:56:21 +03:00
wn_ 1646aba944 Minor tweak to favicon avg color debug log message. 2023-02-03 01:30:35 +00:00
wn_ b28d339bf2 Don't set 'favicon_avg_color' on feed obj unless it's valid. 2023-02-03 01:28:24 +00:00
wn_ f484988967 Fix logging favicon-related bools in 'RSSUtils::update_rss_feed()'. 2023-02-03 01:17:53 +00:00
wn_ 380624a484 Persist failure to detect favicon average color.
Previously, an empty string returned by '\Colors\calculate_avg_color()' would be set as the 'favicon_avg_color' value, resulting in always reattempting average color calculation.
2023-02-03 01:02:42 +00:00
wn_ f0f7a5f958 Ensure custom favicon color detection happens. 2023-02-03 00:45:04 +00:00
65 changed files with 6488 additions and 1118 deletions

81
.docker/app/Dockerfile Normal file
View File

@ -0,0 +1,81 @@
FROM registry.fakecake.org/docker.io/alpine:3.17
EXPOSE 9000/tcp
ENV SCRIPT_ROOT=/opt/tt-rss
ENV SRC_DIR=/src/tt-rss/
RUN apk add --no-cache dcron php81 php81-fpm php81-phar \
php81-pdo php81-gd php81-pgsql php81-pdo_pgsql php81-xmlwriter \
php81-mbstring php81-intl php81-xml php81-curl php81-simplexml \
php81-session php81-tokenizer php81-dom php81-fileinfo php81-ctype \
php81-json php81-iconv php81-pcntl php81-posix php81-zip php81-exif \
php81-openssl git postgresql-client sudo php81-pecl-xdebug rsync tzdata && \
sed -i 's/\(memory_limit =\) 128M/\1 256M/' /etc/php81/php.ini && \
sed -i -e 's/^listen = 127.0.0.1:9000/listen = 9000/' \
-e 's/;\(clear_env\) = .*/\1 = no/i' \
-e 's/^\(user\|group\) = .*/\1 = app/i' \
-e 's/;\(php_admin_value\[error_log\]\) = .*/\1 = \/tmp\/error.log/' \
-e 's/;\(php_admin_flag\[log_errors\]\) = .*/\1 = on/' \
/etc/php81/php-fpm.d/www.conf && \
mkdir -p /var/www ${SCRIPT_ROOT}/config.d
ADD --chmod=0755 startup.sh ${SCRIPT_ROOT}
ADD --chmod=0755 updater.sh ${SCRIPT_ROOT}
ADD --chmod=0755 dcron.sh ${SCRIPT_ROOT}
ADD --chmod=0755 backup.sh /etc/periodic/weekly/backup
ADD index.php ${SCRIPT_ROOT}
ADD config.docker.php ${SCRIPT_ROOT}
COPY --from=app-src . ${SRC_DIR}
ARG ORIGIN_REPO_MAIN=https://git.tt-rss.org/fox/tt-rss.git
ARG ORIGIN_REPO_XACCEL=https://git.tt-rss.org/fox/ttrss-nginx-xaccel.git
ARG ORIGIN_COMMIT=
ENV ORIGIN_REPO_MAIN=${ORIGIN_REPO_MAIN}
ENV ORIGIN_REPO_XACCEL=${ORIGIN_REPO_XACCEL}
ENV ORIGIN_COMMIT=${ORIGIN_COMMIT}
RUN git clone --depth=1 ${ORIGIN_REPO_XACCEL} ${SRC_DIR}/plugins.local/nginx_xaccel
ENV OWNER_UID=1000
ENV OWNER_GID=1000
ENV PHP_WORKER_MAX_CHILDREN=5
ENV PHP_WORKER_MEMORY_LIMIT=256M
# these are applied on every startup, if set
ENV ADMIN_USER_PASS=""
# see classes/UserHelper.php ACCESS_LEVEL_*
# setting this to -2 would effectively disable built-in admin user
# unless single user mode is enabled
ENV ADMIN_USER_ACCESS_LEVEL=""
# these are applied unless user already exists
ENV AUTO_CREATE_USER=""
ENV AUTO_CREATE_USER_PASS=""
ENV AUTO_CREATE_USER_ACCESS_LEVEL="0"
# TODO: remove prefix from container variables not used by tt-rss itself:
#
# - TTRSS_NO_STARTUP_PLUGIN_UPDATES -> NO_STARTUP_PLUGIN_UPDATES
# - TTRSS_XDEBUG_... -> XDEBUG_...
# don't try to update local plugins on startup
ENV TTRSS_NO_STARTUP_PLUGIN_UPDATES=""
# TTRSS_XDEBUG_HOST defaults to host IP if unset
ENV TTRSS_XDEBUG_ENABLED=""
ENV TTRSS_XDEBUG_HOST=""
ENV TTRSS_XDEBUG_PORT="9000"
ENV TTRSS_DB_TYPE="pgsql"
ENV TTRSS_DB_HOST="db"
ENV TTRSS_DB_PORT="5432"
ENV TTRSS_MYSQL_CHARSET="UTF8"
ENV TTRSS_PHP_EXECUTABLE="/usr/bin/php81"
ENV TTRSS_PLUGINS="auth_internal, note, nginx_xaccel"
CMD ${SCRIPT_ROOT}/startup.sh

31
.docker/app/backup.sh Normal file
View File

@ -0,0 +1,31 @@
#!/bin/sh -e
DST_DIR=/backups
KEEP_DAYS=28
APP_ROOT=/var/www/html/tt-rss
if pg_isready -h $TTRSS_DB_HOST -U $TTRSS_DB_USER; then
DST_FILE=ttrss-backup-$(date +%Y%m%d).sql.gz
echo backing up tt-rss database to $DST_DIR/$DST_FILE...
export PGPASSWORD=$TTRSS_DB_PASS
pg_dump --clean -h $TTRSS_DB_HOST -U $TTRSS_DB_USER $TTRSS_DB_NAME | gzip > $DST_DIR/$DST_FILE
DST_FILE=ttrss-backup-$(date +%Y%m%d).tar.gz
echo backing up tt-rss local directories to $DST_DIR/$DST_FILE...
tar -cz -f $DST_DIR/$DST_FILE $APP_ROOT/*.local \
$APP_ROOT/feed-icons/ \
$APP_ROOT/config.php
echo cleaning up...
find $DST_DIR -type f -name '*.gz' -mtime +$KEEP_DAYS -delete
echo done.
else
echo backup failed: database is not ready.
fi

View File

@ -0,0 +1,8 @@
<?php
$snippets = glob(getenv("SCRIPT_ROOT")."/config.d/*.php");
foreach ($snippets as $snippet) {
require_once $snippet;
}

5
.docker/app/dcron.sh Normal file
View File

@ -0,0 +1,5 @@
#!/bin/sh
# https://github.com/dubiousjim/dcron/issues/13
set -e
/usr/sbin/crond "$@"

3
.docker/app/index.php Normal file
View File

@ -0,0 +1,3 @@
<?php
header("Location: /tt-rss/");
return;

153
.docker/app/startup.sh Normal file
View File

@ -0,0 +1,153 @@
#!/bin/sh -e
while ! pg_isready -h $TTRSS_DB_HOST -U $TTRSS_DB_USER; do
echo waiting until $TTRSS_DB_HOST is ready...
sleep 3
done
# We don't need those here (HTTP_HOST would cause false SELF_URL_PATH check failures)
unset HTTP_PORT
unset HTTP_HOST
if ! id app >/dev/null 2>&1; then
addgroup -g $OWNER_GID app
adduser -D -h /var/www/html -G app -u $OWNER_UID app
fi
update-ca-certificates || true
DST_DIR=/var/www/html/tt-rss
[ -e $DST_DIR ] && rm -f $DST_DIR/.app_is_ready
export PGPASSWORD=$TTRSS_DB_PASS
[ ! -e /var/www/html/index.php ] && cp ${SCRIPT_ROOT}/index.php /var/www/html
if [ ! -d $DST_DIR ]; then
mkdir -p $DST_DIR
chown $OWNER_UID:$OWNER_GID $DST_DIR
sudo -u app rsync -a \
$SRC_DIR/ $DST_DIR/
else
chown -R $OWNER_UID:$OWNER_GID $DST_DIR
sudo -u app rsync -a --delete \
--exclude /cache \
--exclude /lock \
--exclude /feed-icons \
--exclude /plugins/af_comics/filters.local \
--exclude /plugins.local \
--exclude /templates.local \
--exclude /themes.local \
$SRC_DIR/ $DST_DIR/
sudo -u app rsync -a --delete \
$SRC_DIR/plugins.local/nginx_xaccel \
$DST_DIR/plugins.local/nginx_xaccel
fi
for d in cache lock feed-icons plugins.local themes.local; do
sudo -u app mkdir -p $DST_DIR/$d
done
for d in cache lock feed-icons; do
chmod 777 $DST_DIR/$d
find $DST_DIR/$d -type f -exec chmod 666 {} \;
done
sudo -u app cp ${SCRIPT_ROOT}/config.docker.php $DST_DIR/config.php
chmod 644 $DST_DIR/config.php
chown -R $OWNER_UID:$OWNER_GID $DST_DIR \
/var/log/php81
if [ -z "$TTRSS_NO_STARTUP_PLUGIN_UPDATES" ]; then
echo updating all local plugins...
find $DST_DIR/plugins.local -mindepth 1 -maxdepth 1 -type d | while read PLUGIN; do
if [ -d $PLUGIN/.git ]; then
echo updating $PLUGIN...
cd $PLUGIN && \
sudo -u app git config core.filemode false && \
sudo -u app git config pull.rebase false && \
sudo -u app git pull origin master || echo warning: attempt to update plugin $PLUGIN failed.
fi
done
else
echo skipping local plugin updates, disabled.
fi
PSQL="psql -q -h $TTRSS_DB_HOST -U $TTRSS_DB_USER $TTRSS_DB_NAME"
$PSQL -c "create extension if not exists pg_trgm"
RESTORE_SCHEMA=${SCRIPT_ROOT}/restore-schema.sql.gz
if [ -r $RESTORE_SCHEMA ]; then
$PSQL -c "drop schema public cascade; create schema public;"
zcat $RESTORE_SCHEMA | $PSQL
fi
# this was previously generated
rm -f $DST_DIR/config.php.bak
if [ ! -z "${TTRSS_XDEBUG_ENABLED}" ]; then
if [ -z "${TTRSS_XDEBUG_HOST}" ]; then
export TTRSS_XDEBUG_HOST=$(ip ro sh 0/0 | cut -d " " -f 3)
fi
echo enabling xdebug with the following parameters:
env | grep TTRSS_XDEBUG
cat > /etc/php81/conf.d/50_xdebug.ini <<EOF
zend_extension=xdebug.so
xdebug.mode=develop,trace,debug
xdebug.start_with_request = yes
xdebug.client_port = ${TTRSS_XDEBUG_PORT}
xdebug.client_host = ${TTRSS_XDEBUG_HOST}
EOF
fi
sed -i.bak "s/^\(memory_limit\) = \(.*\)/\1 = ${PHP_WORKER_MEMORY_LIMIT}/" \
/etc/php81/php.ini
sed -i.bak "s/^\(pm.max_children\) = \(.*\)/\1 = ${PHP_WORKER_MAX_CHILDREN}/" \
/etc/php81/php-fpm.d/www.conf
sudo -Eu app php81 $DST_DIR/update.php --update-schema=force-yes
if [ ! -z "$ADMIN_USER_PASS" ]; then
sudo -Eu app php81 $DST_DIR/update.php --user-set-password "admin:$ADMIN_USER_PASS"
else
if sudo -Eu app php81 $DST_DIR/update.php --user-check-password "admin:password"; then
RANDOM_PASS=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 16 ; echo '')
echo "*****************************************************************************"
echo "* Setting initial built-in admin user password to '$RANDOM_PASS' *"
echo "* If you want to set it manually, use ADMIN_USER_PASS environment variable. *"
echo "*****************************************************************************"
sudo -Eu app php81 $DST_DIR/update.php --user-set-password "admin:$RANDOM_PASS"
fi
fi
if [ ! -z "$ADMIN_USER_ACCESS_LEVEL" ]; then
sudo -Eu app php81 $DST_DIR/update.php --user-set-access-level "admin:$ADMIN_USER_ACCESS_LEVEL"
fi
if [ ! -z "$AUTO_CREATE_USER" ]; then
sudo -Eu app /bin/sh -c "php81 $DST_DIR/update.php --user-exists $AUTO_CREATE_USER ||
php81 $DST_DIR/update.php --force-yes --user-add \"$AUTO_CREATE_USER:$AUTO_CREATE_USER_PASS:$AUTO_CREATE_USER_ACCESS_LEVEL\""
fi
rm -f /tmp/error.log && mkfifo /tmp/error.log && chown app:app /tmp/error.log
(tail -q -f /tmp/error.log >> /proc/1/fd/2) &
unset ADMIN_USER_PASS
unset AUTO_CREATE_USER_PASS
touch $DST_DIR/.app_is_ready
exec /usr/sbin/php-fpm81 --nodaemonize --force-stderr

33
.docker/app/updater.sh Normal file
View File

@ -0,0 +1,33 @@
#!/bin/sh -e
# We don't need those here (HTTP_HOST would cause false SELF_URL_PATH check failures)
unset HTTP_PORT
unset HTTP_HOST
unset ADMIN_USER_PASS
unset AUTO_CREATE_USER_PASS
# wait for the app container to delete .app_is_ready and perform rsync, etc.
sleep 30
if ! id app; then
addgroup -g $OWNER_GID app
adduser -D -h /var/www/html -G app -u $OWNER_UID app
fi
while ! pg_isready -h $TTRSS_DB_HOST -U $TTRSS_DB_USER; do
echo waiting until $TTRSS_DB_HOST is ready...
sleep 3
done
sed -i.bak "s/^\(memory_limit\) = \(.*\)/\1 = ${PHP_WORKER_MEMORY_LIMIT}/" \
/etc/php81/php.ini
DST_DIR=/var/www/html/tt-rss
while [ ! -s $DST_DIR/config.php -a -e $DST_DIR/.app_is_ready ]; do
echo waiting for app container...
sleep 3
done
sudo -E -u app /usr/bin/php81 /var/www/html/tt-rss/update_daemon2.php

View File

@ -0,0 +1,15 @@
FROM registry.fakecake.org/docker.io/nginx:alpine
HEALTHCHECK CMD curl --fail http://localhost/tt-rss/index.php || exit 1
COPY nginx.conf /etc/nginx/templates/nginx.conf.template
# By default, nginx will send the php requests to "app" server, but this server
# name can be overridden at runtime by passing an APP_UPSTREAM env var
ENV APP_UPSTREAM=${APP_UPSTREAM:-app}
# It's necessary to set the following NGINX_ENVSUBST_OUTPUT_DIR env var to tell
# nginx to replace the env vars of /etc/nginx/templates/nginx.conf.template
# and put the result in /etc/nginx/nginx.conf (instead of /etc/nginx/conf.d/nginx.conf)
# See https://github.com/docker-library/docs/tree/master/nginx#using-environment-variables-in-nginx-configuration-new-in-119
ENV NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx

View File

@ -0,0 +1,61 @@
worker_processes auto;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /dev/stdout;
error_log /dev/stderr warn;
sendfile on;
index index.php;
upstream app {
server ${APP_UPSTREAM}:9000;
}
server {
listen 80;
listen [::]:80;
root /var/www/html;
location /tt-rss/cache {
aio threads;
internal;
}
location /tt-rss/backups {
internal;
}
location ~ \.php$ {
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass app;
}
location / {
try_files $uri $uri/ =404;
}
}
}

123
.gitea/workflows/build.yml Normal file
View File

@ -0,0 +1,123 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: build
on:
push:
branches:
- "master"
workflow_dispatch: {}
defaults:
run:
shell: sh
jobs:
phpdoc:
runs-on: alpine-3.16
steps:
- uses: https://gitea.com/actions/checkout@v3
- name: phpdoc
run: php81 /phpDocumentor.phar -d classes -d include -t phpdoc --visibility=public
- name: prepare ssh
run: |
mkdir -p ~/.ssh
echo "${{ secrets.APK_DEPLOY_SSH_KEY }}" | tr -d \\r > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: upload results
run: rsync -av -e 'ssh -o StrictHostKeyChecking=no' phpdoc/ ${{ secrets.APK_DEPLOY_USER }}@${{ secrets.APK_DEPLOY_HOST }}:phpdoc/
build:
runs-on: alpine-3.16
steps:
- uses: https://gitea.com/actions/checkout@v3
- name: eslint
run: npx eslint js plugins
- run: rm -rf node_modules
- name: phpunit
run: php81 ./vendor/bin/phpunit
- name: calculate cache key hash
uses: actions/go-hashfiles@v0.0.1
id: cache-hash
with:
patterns: |
classes/*.php
include/*.php
plugins/**/*.php
- name: phpstan
run: php81 -d memory_limit=-1 ./vendor/bin/phpstan --memory-limit=2G
- name: setup qemu
uses: https://github.com/docker/setup-qemu-action@v2
- name: setup buildx
uses: https://github.com/docker/setup-buildx-action@v2
- name: login to registry
uses: https://github.com/docker/login-action@v2
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: login to docker hub
uses: https://github.com/docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: get docker meta for web-nginx
id: meta_web_nginx
uses: https://github.com/docker/metadata-action@v4
with:
images: |
${{ secrets.REGISTRY_HOST }}/cthulhoo/ttrss-web-nginx
cthulhoo/ttrss-web-nginx
tags: |
type=sha,prefix={{ date 'YY.MM-' tz='UTC'}}
type=raw,value=latest,enable={{ is_default_branch }}
- name: build web-nginx image
uses: https://github.com/docker/build-push-action@v4
with:
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .docker/web-nginx
tags: ${{ steps.meta_web_nginx.outputs.tags }}
labels: ${{ steps.meta_web_nginx.outputs.labels }}
provenance: false
cache-from: type=registry,ref=${{ secrets.REGISTRY_HOST }}/cthulhoo/ttrss-web-nginx:latest
cache-to: type=inline
- name: get docker meta for app
id: meta_app
uses: https://github.com/docker/metadata-action@v4
with:
images: |
${{ secrets.REGISTRY_HOST }}/cthulhoo/ttrss-fpm-pgsql-static
cthulhoo/ttrss-fpm-pgsql-static
tags: |
type=sha,prefix={{ date 'YY.MM-' tz='UTC'}}
type=raw,value=latest,enable={{ is_default_branch }}
- name: build app image
uses: https://github.com/docker/build-push-action@v4
with:
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .docker/app
build-contexts:
app-src=.
tags: ${{ steps.meta_app.outputs.tags }}
labels: ${{ steps.meta_app.outputs.labels }}
provenance: false
cache-from: type=registry,ref=${{ secrets.REGISTRY_HOST }}/cthulhoo/ttrss-fpm-pgsql-static:latest
cache-to: type=inline

View File

@ -0,0 +1,33 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: lint
on:
push:
branches-ignore:
- "master"
workflow_dispatch: {}
pull_request: {}
defaults:
run:
shell: sh
jobs:
lint:
runs-on: alpine-3.16
steps:
- name: checkout source
uses: actions/checkout@v3
- name: npm install
run: npm install
- name: eslint
run: npx eslint js plugins
- name: phpunit
run: php81 ./vendor/bin/phpunit
- name: phpstan
run: php81 -d memory_limit=-1 ./vendor/bin/phpstan --memory-limit=2G

1
.gitignore vendored
View File

@ -3,7 +3,6 @@ Thumbs.db
/messages.mo /messages.mo
/node_modules /node_modules
/locale/**/*.po~ /locale/**/*.po~
/package-lock.json
/plugins.local/* /plugins.local/*
/themes.local/* /themes.local/*
/config.php /config.php

150
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,150 @@
stages:
- lint
- build
.build-master:
image:
name: ${CI_DOCKER_IMAGE}
stage: build
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "web"
changes:
- '**/*.php'
- '**/*.js'
- '.docker/**/*'
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
before_script:
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_TOKEN}
- docker run --privileged --rm registry.fakecake.org/docker.io/tonistiigi/binfmt --install all
- docker buildx create --name mp-builder --driver docker-container --bootstrap --use
script:
- BUILD_TIMESTAMP=$(date -d $(echo ${CI_COMMIT_TIMESTAMP} | cut -b 1-10) +%y.%m)
- docker buildx build
--push
--platform linux/amd64,linux/arm64,linux/arm/v7
--tag ${CI_REGISTRY}/cthulhoo/${CI_JOB_NAME}:${BUILD_TIMESTAMP}-${CI_COMMIT_SHORT_SHA}
--tag ${CI_REGISTRY}/cthulhoo/${CI_JOB_NAME}:latest
--tag cthulhoo/${CI_JOB_NAME}:${BUILD_TIMESTAMP}-${CI_COMMIT_SHORT_SHA}
--tag cthulhoo/${CI_JOB_NAME}:latest
--build-context app-src=.
--cache-from type=registry,ref=${CI_REGISTRY}/cthulhoo/${CI_JOB_NAME}:latest
--cache-to type=inline
${BUILD_CONTEXT}
# note: CI_REGISTRY, etc. variables are privileged
#
# .build-branch:
# image:
# name: ${CI_DOCKER_IMAGE}
# stage: build
# rules:
# - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "web"
# changes:
# - '**/*.php'
# - '**/*.js'
# - '.docker/**/*'
# - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
# when: manual
# before_script:
# - docker buildx create --name mp-builder --driver docker-container --bootstrap --use
# - CONTAINER_NAME=$(echo ${CI_JOB_NAME} | sed 's/branch://')
# script:
# - docker buildx build
# --push
# --platform linux/amd64
# --tag ${CI_REGISTRY}/cthulhoo/$CONTAINER_NAME:${CI_COMMIT_BRANCH}-${CI_COMMIT_SHORT_SHA}
# --tag ${CI_REGISTRY}/cthulhoo/$CONTAINER_NAME:${CI_COMMIT_BRANCH}
# --build-context app-src=.
# --cache-from type=registry,ref=${CI_REGISTRY}/cthulhoo/$CONTAINER_NAME:${CI_COMMIT_BRANCH}
# --cache-to type=inline
# ${BUILD_CONTEXT}
phpunit:
image:
name: ${CI_DOCKER_IMAGE}
stage: lint
rules:
- if: $CI_COMMIT_BRANCH && $CI_PIPELINE_SOURCE != "web"
changes:
- '**/*.php'
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH
when: manual
allow_failure: true
script:
- php81 ./vendor/bin/phpunit
eslint:
image:
name: ${CI_DOCKER_IMAGE}
stage: lint
rules:
- if: $CI_COMMIT_BRANCH && $CI_PIPELINE_SOURCE != "web"
changes:
- '**/*.js'
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH
when: manual
allow_failure: true
script:
- npm install
- npx eslint js plugins
phpstan:
image:
name: ${CI_DOCKER_IMAGE}
needs:
- phpunit
stage: lint
rules:
- if: $CI_COMMIT_BRANCH && $CI_PIPELINE_SOURCE != "web"
changes:
- '**/*.php'
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_BRANCH
when: manual
allow_failure: true
script:
- php81 -d memory_limit=-1 ./vendor/bin/phpstan --memory-limit=2G
ttrss-web-nginx:
extends: .build-master
variables:
BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/web-nginx
ttrss-fpm-pgsql-static:
extends: .build-master
variables:
BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/app
# branch:ttrss-web-nginx:
# extends: .build-branch
# variables:
# BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/web-nginx
# branch:ttrss-fpm-pgsql-static:
# extends: .build-branch
# variables:
# BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/app
phpdoc:
image:
name: ${CI_DOCKER_IMAGE}
stage: build
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "web"
changes:
- '**/*.php'
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
script:
- php81 /phpDocumentor.phar -d classes -d include -t phpdoc --visibility=public
- mkdir -p ~/.ssh &&
cp ${PHPDOC_DEPLOY_SSH_KEY} ~/.ssh/id_ed25519 &&
chmod 0600 ~/.ssh/id_ed25519
- rsync -av -e 'ssh -o StrictHostKeyChecking=no' phpdoc/ ${PHPDOC_DEPLOY_HOST}:phpdoc/

33
Jenkinsfile vendored
View File

@ -1,33 +0,0 @@
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '5'))
}
stages {
stage('phpunit') {
steps {
sh """
docker run --rm \
--workdir /app \
-v ${env.WORKSPACE}:/app \
registry.fakecake.org/php:8.1-cli \
php ./vendor/bin/phpunit
"""
}
}
stage('phpstan') {
steps {
sh """
# php -d memory_limit=-1 ....
docker run --rm \
--workdir /app \
-v ${env.WORKSPACE}:/app \
registry.fakecake.org/php:8.1-cli \
php -d memory_limit=-1 ./vendor/bin/phpstan --memory-limit=2G
"""
}
}
}
}

View File

@ -1,7 +1,7 @@
<?php <?php
class API extends Handler { class API extends Handler {
const API_LEVEL = 18; const API_LEVEL = 20;
const STATUS_OK = 0; const STATUS_OK = 0;
const STATUS_ERR = 1; const STATUS_ERR = 1;
@ -12,6 +12,7 @@ class API extends Handler {
const E_INCORRECT_USAGE = "INCORRECT_USAGE"; const E_INCORRECT_USAGE = "INCORRECT_USAGE";
const E_UNKNOWN_METHOD = "UNKNOWN_METHOD"; const E_UNKNOWN_METHOD = "UNKNOWN_METHOD";
const E_OPERATION_FAILED = "E_OPERATION_FAILED"; const E_OPERATION_FAILED = "E_OPERATION_FAILED";
const E_NOT_FOUND = "E_NOT_FOUND";
/** @var int|null */ /** @var int|null */
private $seq; private $seq;
@ -98,7 +99,7 @@ class API extends Handler {
} }
function isLoggedIn(): bool { function isLoggedIn(): bool {
return $this->_wrap(self::STATUS_OK, array("status" => $_SESSION["uid"] != '')); return $this->_wrap(self::STATUS_OK, array("status" => (bool)($_SESSION["uid"] ?? '')));
} }
function getUnread(): bool { function getUnread(): bool {
@ -168,7 +169,7 @@ class API extends Handler {
} }
} }
foreach ([-2,-1,0] as $cat_id) { foreach ([Feeds::CATEGORY_LABELS, Feeds::CATEGORY_SPECIAL, Feeds::CATEGORY_UNCATEGORIZED] as $cat_id) {
if ($include_empty || !$this->_is_cat_empty($cat_id)) { if ($include_empty || !$this->_is_cat_empty($cat_id)) {
$unread = Feeds::_get_counters($cat_id, true, true); $unread = Feeds::_get_counters($cat_id, true, true);
@ -503,9 +504,14 @@ class API extends Handler {
} }
function shareToPublished(): bool { function shareToPublished(): bool {
$title = strip_tags(clean($_REQUEST["title"])); $title = clean($_REQUEST["title"]);
$url = strip_tags(clean($_REQUEST["url"])); $url = clean($_REQUEST["url"]);
$content = strip_tags(clean($_REQUEST["content"])); $sanitize_content = self::_param_to_bool($_REQUEST["sanitize"] ?? true);
if ($sanitize_content)
$content = clean($_REQUEST["content"]);
else
$content = $_REQUEST["content"];
if (Article::_create_published_article($title, $url, $content, "", $_SESSION["uid"])) { if (Article::_create_published_article($title, $url, $content, "", $_SESSION["uid"])) {
return $this->_wrap(self::STATUS_OK, array("status" => 'OK')); return $this->_wrap(self::STATUS_OK, array("status" => 'OK'));
@ -522,8 +528,8 @@ class API extends Handler {
/* Labels */ /* Labels */
/* API only: -4 All feeds, including virtual feeds */ /* API only: -4 (Feeds::CATEGORY_ALL) All feeds, including virtual feeds */
if ($cat_id == -4 || $cat_id == -2) { if ($cat_id == Feeds::CATEGORY_ALL || $cat_id == Feeds::CATEGORY_LABELS) {
$counters = Counters::get_labels(); $counters = Counters::get_labels();
foreach (array_values($counters) as $cv) { foreach (array_values($counters) as $cv) {
@ -534,7 +540,7 @@ class API extends Handler {
'id' => (int) $cv['id'], 'id' => (int) $cv['id'],
'title' => $cv['description'], 'title' => $cv['description'],
'unread' => $cv['counter'], 'unread' => $cv['counter'],
'cat_id' => -2, 'cat_id' => Feeds::CATEGORY_LABELS,
]; ];
array_push($feeds, $row); array_push($feeds, $row);
@ -544,7 +550,7 @@ class API extends Handler {
/* Virtual feeds */ /* Virtual feeds */
$vfeeds = PluginHost::getInstance()->get_feeds(-1); $vfeeds = PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL);
if (is_array($vfeeds)) { if (is_array($vfeeds)) {
foreach ($vfeeds as $feed) { foreach ($vfeeds as $feed) {
@ -559,7 +565,7 @@ class API extends Handler {
'id' => PluginHost::pfeed_to_feed_id($feed['id']), 'id' => PluginHost::pfeed_to_feed_id($feed['id']),
'title' => $feed['title'], 'title' => $feed['title'],
'unread' => $unread, 'unread' => $unread,
'cat_id' => -1, 'cat_id' => Feeds::CATEGORY_SPECIAL,
]; ];
array_push($feeds, $row); array_push($feeds, $row);
@ -567,8 +573,9 @@ class API extends Handler {
} }
} }
if ($cat_id == -4 || $cat_id == -1) { if ($cat_id == Feeds::CATEGORY_ALL || $cat_id == Feeds::CATEGORY_SPECIAL) {
foreach ([-1, -2, -3, -4, -6, 0] as $i) { foreach ([Feeds::FEED_STARRED, Feeds::FEED_PUBLISHED, Feeds::FEED_FRESH,
Feeds::FEED_ALL, Feeds::FEED_RECENTLY_READ, Feeds::FEED_ARCHIVED] as $i) {
$unread = Feeds::_get_counters($i, false, true); $unread = Feeds::_get_counters($i, false, true);
if ($unread || !$unread_only) { if ($unread || !$unread_only) {
@ -578,7 +585,7 @@ class API extends Handler {
'id' => $i, 'id' => $i,
'title' => $title, 'title' => $title,
'unread' => $unread, 'unread' => $unread,
'cat_id' => -1, 'cat_id' => Feeds::CATEGORY_SPECIAL,
]; ];
array_push($feeds, $row); array_push($feeds, $row);
@ -614,7 +621,7 @@ class API extends Handler {
/* Real feeds */ /* Real feeds */
/* API only: -3 All feeds, excluding virtual feeds (e.g. Labels and such) */ /* API only: -3 (Feeds::CATEGORY_ALL_EXCEPT_VIRTUAL) All feeds, excluding virtual feeds (e.g. Labels and such) */
$feeds_obj = ORM::for_table('ttrss_feeds') $feeds_obj = ORM::for_table('ttrss_feeds')
->select_many('id', 'feed_url', 'cat_id', 'title', 'order_id') ->select_many('id', 'feed_url', 'cat_id', 'title', 'order_id')
->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated') ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated')
@ -625,7 +632,7 @@ class API extends Handler {
if ($limit) $feeds_obj->limit($limit); if ($limit) $feeds_obj->limit($limit);
if ($offset) $feeds_obj->offset($offset); if ($offset) $feeds_obj->offset($offset);
if ($cat_id != -3 && $cat_id != -4) { if ($cat_id != Feeds::CATEGORY_ALL_EXCEPT_VIRTUAL && $cat_id != Feeds::CATEGORY_ALL) {
$feeds_obj->where_raw('(cat_id = ? OR (? = 0 AND cat_id IS NULL))', [$cat_id, $cat_id]); $feeds_obj->where_raw('(cat_id = ? OR (? = 0 AND cat_id IS NULL))', [$cat_id, $cat_id]);
} }
@ -912,15 +919,26 @@ class API extends Handler {
array("categories" => $pf->_makefeedtree())); array("categories" => $pf->_makefeedtree()));
} }
function getFeedIcon(): bool {
$id = (int)$_REQUEST['id'];
$cache = DiskCache::instance('feed-icons');
if ($cache->exists((string)$id)) {
return $cache->send((string)$id) > 0;
} else {
return $this->_wrap(self::STATUS_ERR, array("error" => self::E_NOT_FOUND));
}
}
// only works for labels or uncategorized for the time being // only works for labels or uncategorized for the time being
private function _is_cat_empty(int $id): bool { private function _is_cat_empty(int $id): bool {
if ($id == -2) { if ($id == Feeds::CATEGORY_LABELS) {
$label_count = ORM::for_table('ttrss_labels2') $label_count = ORM::for_table('ttrss_labels2')
->where('owner_uid', $_SESSION['uid']) ->where('owner_uid', $_SESSION['uid'])
->count(); ->count();
return $label_count == 0; return $label_count == 0;
} else if ($id == 0) { } else if ($id == Feeds::CATEGORY_UNCATEGORIZED) {
$uncategorized_count = ORM::for_table('ttrss_feeds') $uncategorized_count = ORM::for_table('ttrss_feeds')
->where_null('cat_id') ->where_null('cat_id')
->where('owner_uid', $_SESSION['uid']) ->where('owner_uid', $_SESSION['uid'])

View File

@ -541,6 +541,10 @@ class Config {
array_push($errors, "PHP support for JSON is required, but was not found."); array_push($errors, "PHP support for JSON is required, but was not found.");
} }
if (!function_exists("flock")) {
array_push($errors, "PHP support for flock() function is required.");
}
if (!class_exists("PDO")) { if (!class_exists("PDO")) {
array_push($errors, "PHP support for PDO is required but was not found."); array_push($errors, "PHP support for PDO is required but was not found.");
} }

View File

@ -60,8 +60,8 @@ class Counters {
/* Labels category */ /* Labels category */
$cv = array("id" => -2, "kind" => "cat", $cv = array("id" => Feeds::CATEGORY_LABELS, "kind" => "cat",
"counter" => Feeds::_get_cat_unread(-2)); "counter" => Feeds::_get_cat_unread(Feeds::CATEGORY_LABELS));
array_push($ret, $cv); array_push($ret, $cv);
@ -244,28 +244,29 @@ class Counters {
$ret = []; $ret = [];
for ($i = 0; $i >= -4; $i--) { foreach ([Feeds::FEED_ARCHIVED, Feeds::FEED_STARRED, Feeds::FEED_PUBLISHED,
Feeds::FEED_FRESH, Feeds::FEED_ALL] as $feed_id) {
$count = Feeds::_get_counters($i, false, true); $count = Feeds::_get_counters($feed_id, false, true);
if ($i == 0 || $i == -1 || $i == -2) if (in_array($feed_id, [Feeds::FEED_ARCHIVED, Feeds::FEED_STARRED, Feeds::FEED_PUBLISHED]))
$auxctr = Feeds::_get_counters($i, false); $auxctr = Feeds::_get_counters($feed_id, false);
else else
$auxctr = 0; $auxctr = 0;
$cv = [ $cv = [
"id" => $i, "id" => $feed_id,
"counter" => (int) $count, "counter" => (int) $count,
"auxcounter" => (int) $auxctr "auxcounter" => (int) $auxctr
]; ];
if ($i == -1) if ($feed_id == Feeds::FEED_STARRED)
$cv["markedcounter"] = $auxctr; $cv["markedcounter"] = $auxctr;
array_push($ret, $cv); array_push($ret, $cv);
} }
$feeds = PluginHost::getInstance()->get_feeds(-1); $feeds = PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL);
if (is_array($feeds)) { if (is_array($feeds)) {
foreach ($feeds as $feed) { foreach ($feeds as $feed) {

View File

@ -84,15 +84,6 @@ class FeedItem_RSS extends FeedItem_Common {
/** @var DOMElement|null */ /** @var DOMElement|null */
$contentB = $this->elem->getElementsByTagName("description")->item(0); $contentB = $this->elem->getElementsByTagName("description")->item(0);
if ($contentA && !$contentB) {
return $this->subtree_or_text($contentA);
}
if ($contentB && !$contentA) {
return $this->subtree_or_text($contentB);
}
if ($contentA && $contentB) { if ($contentA && $contentB) {
$resultA = $this->subtree_or_text($contentA); $resultA = $this->subtree_or_text($contentA);
$resultB = $this->subtree_or_text($contentB); $resultB = $this->subtree_or_text($contentB);
@ -100,6 +91,14 @@ class FeedItem_RSS extends FeedItem_Common {
return mb_strlen($resultA) > mb_strlen($resultB) ? $resultA : $resultB; return mb_strlen($resultA) > mb_strlen($resultB) ? $resultA : $resultB;
} }
if ($contentA) {
return $this->subtree_or_text($contentA);
}
if ($contentB) {
return $this->subtree_or_text($contentB);
}
return ''; return '';
} }

View File

@ -2,8 +2,52 @@
require_once "colors.php"; require_once "colors.php";
class Feeds extends Handler_Protected { class Feeds extends Handler_Protected {
const NEVER_GROUP_FEEDS = [ -6, 0 ]; /** special feed for archived articles */
const NEVER_GROUP_BY_DATE = [ -2, -1, -3 ]; const FEED_ARCHIVED = 0;
/** special feed for starred articles */
const FEED_STARRED = -1;
/** special feed for published articles */
const FEED_PUBLISHED = -2;
/** special feed for archived articles */
const FEED_FRESH = -3;
/** special feed for all articles */
const FEED_ALL = -4;
/**
* a special case feed used to display auxiliary information when there's nothing to load (e.g. no stuff in fresh feed)
*
* TODO: Remove this and 'Feeds::_generate_dashboard_feed()'? It only seems to be used if 'Feeds::view()' (also potentially removable)
* gets passed the ID.
*/
const FEED_DASHBOARD = -5;
/** special feed for recently read articles */
const FEED_RECENTLY_READ = -6;
/** special feed for error scenarios (e.g. feed not found) */
const FEED_ERROR = -7;
/** special "category" for uncategorized articles */
const CATEGORY_UNCATEGORIZED = 0;
/** special category for "special" articles (e.g. Starred, Published, Archived, plugin-provided, etc.) */
const CATEGORY_SPECIAL = -1;
/** special category for labels */
const CATEGORY_LABELS = -2;
/** special category for all feeds, excluding virtual feeds (e.g. labels and such) */
const CATEGORY_ALL_EXCEPT_VIRTUAL = -3;
/** special category for all feeds, including virtual feeds (e.g. labels and such) */
const CATEGORY_ALL = -4;
const NEVER_GROUP_FEEDS = [ Feeds::FEED_RECENTLY_READ, Feeds::FEED_ARCHIVED ];
const NEVER_GROUP_BY_DATE = [ Feeds::FEED_PUBLISHED, Feeds::FEED_STARRED, Feeds::FEED_FRESH ];
/** @var int|float int on 64-bit, float on 32-bit */ /** @var int|float int on 64-bit, float on 32-bit */
private $viewfeed_timestamp; private $viewfeed_timestamp;
@ -205,7 +249,7 @@ class Feeds extends Handler_Protected {
// normalize archived feed // normalize archived feed
if ($line['feed_id'] === null) { if ($line['feed_id'] === null) {
$line['feed_id'] = 0; $line['feed_id'] = Feeds::FEED_ARCHIVED;
$line["feed_title"] = __("Archived articles"); $line["feed_title"] = __("Archived articles");
} }
@ -478,10 +522,7 @@ class Feeds extends Handler_Protected {
if (is_numeric($feed)) $feed = (int) $feed; if (is_numeric($feed)) $feed = (int) $feed;
/* Feed -5 is a special case: it is used to display auxiliary information if ($feed == Feeds::FEED_DASHBOARD) {
* when there's nothing to load - e.g. no stuff in fresh feed */
if ($feed == -5) {
print json_encode($this->_generate_dashboard_feed()); print json_encode($this->_generate_dashboard_feed());
return; return;
} }
@ -566,7 +607,7 @@ class Feeds extends Handler_Protected {
private function _generate_dashboard_feed(): array { private function _generate_dashboard_feed(): array {
$reply = array(); $reply = array();
$reply['headlines']['id'] = -5; $reply['headlines']['id'] = Feeds::FEED_DASHBOARD;
$reply['headlines']['is_cat'] = false; $reply['headlines']['is_cat'] = false;
$reply['headlines']['toolbar'] = ''; $reply['headlines']['toolbar'] = '';
@ -610,7 +651,7 @@ class Feeds extends Handler_Protected {
private function _generate_error_feed(string $error): array { private function _generate_error_feed(string $error): array {
$reply = array(); $reply = array();
$reply['headlines']['id'] = -7; $reply['headlines']['id'] = Feeds::FEED_ERROR;
$reply['headlines']['is_cat'] = false; $reply['headlines']['is_cat'] = false;
$reply['headlines']['toolbar'] = ''; $reply['headlines']['toolbar'] = '';
@ -706,6 +747,7 @@ class Feeds extends Handler_Protected {
<?= javascript_tag("js/common.js") ?> <?= javascript_tag("js/common.js") ?>
<?= javascript_tag("lib/dojo/dojo.js") ?> <?= javascript_tag("lib/dojo/dojo.js") ?>
<?= javascript_tag("lib/dojo/tt-rss-layer.js") ?> <?= javascript_tag("lib/dojo/tt-rss-layer.js") ?>
<?= Config::get_override_links() ?>
</head> </head>
<body class="flat ttrss_utility feed_debugger css_loading"> <body class="flat ttrss_utility feed_debugger css_loading">
<script type="text/javascript"> <script type="text/javascript">
@ -826,7 +868,9 @@ class Feeds extends Handler_Protected {
if ($feed_id >= 0) { if ($feed_id >= 0) {
if ($feed_id > 0) { if ($feed_id == Feeds::CATEGORY_UNCATEGORIZED) {
$cat_qpart = "cat_id IS NULL";
} else {
$children = self::_get_child_cats($feed_id, $owner_uid); $children = self::_get_child_cats($feed_id, $owner_uid);
array_push($children, $feed_id); array_push($children, $feed_id);
$children = array_map("intval", $children); $children = array_map("intval", $children);
@ -834,8 +878,6 @@ class Feeds extends Handler_Protected {
$children = join(",", $children); $children = join(",", $children);
$cat_qpart = "cat_id IN ($children)"; $cat_qpart = "cat_id IN ($children)";
} else {
$cat_qpart = "cat_id IS NULL";
} }
$sth = $pdo->prepare("UPDATE ttrss_user_entries $sth = $pdo->prepare("UPDATE ttrss_user_entries
@ -846,7 +888,7 @@ class Feeds extends Handler_Protected {
(SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart AND $search_qpart) as tmp)"); (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart AND $search_qpart) as tmp)");
$sth->execute([$owner_uid]); $sth->execute([$owner_uid]);
} else if ($feed_id == -2) { } else if ($feed_id == Feeds::CATEGORY_LABELS) {
$sth = $pdo->prepare("UPDATE ttrss_user_entries $sth = $pdo->prepare("UPDATE ttrss_user_entries
SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*) SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
@ -866,7 +908,7 @@ class Feeds extends Handler_Protected {
} else if ($feed_id < 0 && $feed_id > LABEL_BASE_INDEX) { // special, like starred } else if ($feed_id < 0 && $feed_id > LABEL_BASE_INDEX) { // special, like starred
if ($feed_id == -1) { if ($feed_id == Feeds::FEED_STARRED) {
$sth = $pdo->prepare("UPDATE ttrss_user_entries $sth = $pdo->prepare("UPDATE ttrss_user_entries
SET unread = false, last_read = NOW() WHERE ref_id IN SET unread = false, last_read = NOW() WHERE ref_id IN
(SELECT id FROM (SELECT id FROM
@ -875,7 +917,7 @@ class Feeds extends Handler_Protected {
$sth->execute([$owner_uid]); $sth->execute([$owner_uid]);
} }
if ($feed_id == -2) { if ($feed_id == Feeds::FEED_PUBLISHED) {
$sth = $pdo->prepare("UPDATE ttrss_user_entries $sth = $pdo->prepare("UPDATE ttrss_user_entries
SET unread = false, last_read = NOW() WHERE ref_id IN SET unread = false, last_read = NOW() WHERE ref_id IN
(SELECT id FROM (SELECT id FROM
@ -884,7 +926,7 @@ class Feeds extends Handler_Protected {
$sth->execute([$owner_uid]); $sth->execute([$owner_uid]);
} }
if ($feed_id == -3) { if ($feed_id == Feeds::FEED_FRESH) {
$intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE); $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE);
@ -903,7 +945,7 @@ class Feeds extends Handler_Protected {
$sth->execute([$owner_uid]); $sth->execute([$owner_uid]);
} }
if ($feed_id == -4) { if ($feed_id == Feeds::FEED_ALL) {
$sth = $pdo->prepare("UPDATE ttrss_user_entries $sth = $pdo->prepare("UPDATE ttrss_user_entries
SET unread = false, last_read = NOW() WHERE ref_id IN SET unread = false, last_read = NOW() WHERE ref_id IN
(SELECT id FROM (SELECT id FROM
@ -972,7 +1014,7 @@ class Feeds extends Handler_Protected {
} else { } else {
return 0; return 0;
} }
} else if ($n_feed == -6) { } else if ($n_feed == Feeds::FEED_RECENTLY_READ) {
return 0; return 0;
// tags // tags
} else if ($feed != "0" && $n_feed == 0) { } else if ($feed != "0" && $n_feed == 0) {
@ -988,11 +1030,11 @@ class Feeds extends Handler_Protected {
// Handle 'SUM()' returning null if there are no results // Handle 'SUM()' returning null if there are no results
return $row["count"] ?? 0; return $row["count"] ?? 0;
} else if ($n_feed == -1) { } else if ($n_feed == Feeds::FEED_STARRED) {
$match_part = "marked = true"; $match_part = "marked = true";
} else if ($n_feed == -2) { } else if ($n_feed == Feeds::FEED_PUBLISHED) {
$match_part = "published = true"; $match_part = "published = true";
} else if ($n_feed == -3) { } else if ($n_feed == Feeds::FEED_FRESH) {
$match_part = "unread = true AND score >= 0"; $match_part = "unread = true AND score >= 0";
$intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE, $owner_uid); $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE, $owner_uid);
@ -1005,11 +1047,11 @@ class Feeds extends Handler_Protected {
$need_entries = true; $need_entries = true;
} else if ($n_feed == -4) { } else if ($n_feed == Feeds::FEED_ALL) {
$match_part = "true"; $match_part = "true";
} else if ($n_feed >= 0) { } else if ($n_feed >= 0) {
if ($n_feed != 0) { if ($n_feed != Feeds::FEED_ARCHIVED) {
$match_part = sprintf("feed_id = %d", $n_feed); $match_part = sprintf("feed_id = %d", $n_feed);
} else { } else {
$match_part = "feed_id IS NULL"; $match_part = "feed_id IS NULL";
@ -1190,17 +1232,17 @@ class Feeds extends Handler_Protected {
*/ */
static function _get_icon(int $id) { static function _get_icon(int $id) {
switch ($id) { switch ($id) {
case 0: case Feeds::FEED_ARCHIVED:
return "archive"; return "archive";
case -1: case Feeds::FEED_STARRED:
return "star"; return "star";
case -2: case Feeds::FEED_PUBLISHED:
return "rss_feed"; return "rss_feed";
case -3: case Feeds::FEED_FRESH:
return "whatshot"; return "whatshot";
case -4: case Feeds::FEED_ALL:
return "inbox"; return "inbox";
case -6: case Feeds::FEED_RECENTLY_READ:
return "restore"; return "restore";
default: default:
if ($id < LABEL_BASE_INDEX) { if ($id < LABEL_BASE_INDEX) {
@ -1263,17 +1305,17 @@ class Feeds extends Handler_Protected {
if ($cat) { if ($cat) {
return self::_get_cat_title($id); return self::_get_cat_title($id);
} else if ($id == -1) { } else if ($id == Feeds::FEED_STARRED) {
return __("Starred articles"); return __("Starred articles");
} else if ($id == -2) { } else if ($id == Feeds::FEED_PUBLISHED) {
return __("Published articles"); return __("Published articles");
} else if ($id == -3) { } else if ($id == Feeds::FEED_FRESH) {
return __("Fresh articles"); return __("Fresh articles");
} else if ($id == -4) { } else if ($id == Feeds::FEED_ALL) {
return __("All articles"); return __("All articles");
} else if ($id === 0) { } else if ($id === Feeds::FEED_ARCHIVED) {
return __("Archived articles"); return __("Archived articles");
} else if ($id == -6) { } else if ($id == Feeds::FEED_RECENTLY_READ) {
return __("Recently read"); return __("Recently read");
} else if ($id < LABEL_BASE_INDEX) { } else if ($id < LABEL_BASE_INDEX) {
@ -1349,9 +1391,9 @@ class Feeds extends Handler_Protected {
if ($row = $sth->fetch()) { if ($row = $sth->fetch()) {
return (int) $row["unread"]; return (int) $row["unread"];
} }
} else if ($cat == -1) { } else if ($cat == Feeds::CATEGORY_SPECIAL) {
return 0; return 0;
} else if ($cat == -2) { } else if ($cat == Feeds::CATEGORY_LABELS) {
$sth = $pdo->prepare("SELECT COUNT(DISTINCT article_id) AS unread $sth = $pdo->prepare("SELECT COUNT(DISTINCT article_id) AS unread
FROM ttrss_user_entries ue, ttrss_user_labels2 l FROM ttrss_user_entries ue, ttrss_user_labels2 l
@ -1406,11 +1448,11 @@ class Feeds extends Handler_Protected {
static function _get_cat_title(int $cat_id): string { static function _get_cat_title(int $cat_id): string {
switch ($cat_id) { switch ($cat_id) {
case 0: case Feeds::CATEGORY_UNCATEGORIZED:
return __("Uncategorized"); return __("Uncategorized");
case -1: case Feeds::CATEGORY_SPECIAL:
return __("Special"); return __("Special");
case -2: case Feeds::CATEGORY_LABELS:
return __("Labels"); return __("Labels");
default: default:
$cat = ORM::for_table('ttrss_feed_categories') $cat = ORM::for_table('ttrss_feed_categories')
@ -1525,6 +1567,7 @@ class Feeds extends Handler_Protected {
if ($search) { if ($search) {
$view_query_part = " "; $view_query_part = " ";
} else if ($feed != -1) { } else if ($feed != -1) {
// not Feeds::FEED_STARRED or Feeds::CATEGORY_SPECIAL
$unread = Feeds::_get_counters($feed, $cat_view, true); $unread = Feeds::_get_counters($feed, $cat_view, true);
@ -1549,7 +1592,7 @@ class Feeds extends Handler_Protected {
$view_query_part = " published = true AND "; $view_query_part = " published = true AND ";
} }
if ($view_mode == "unread" && $feed != -6) { if ($view_mode == "unread" && $feed != Feeds::FEED_RECENTLY_READ) {
$view_query_part = " unread = true AND "; $view_query_part = " unread = true AND ";
} }
@ -1593,13 +1636,13 @@ class Feeds extends Handler_Protected {
} else { } else {
$query_strategy_part = "feed_id = " . $pdo->quote((string)$feed); $query_strategy_part = "feed_id = " . $pdo->quote((string)$feed);
} }
} else if ($feed == 0 && !$cat_view) { // archive virtual feed } else if ($feed == Feeds::FEED_ARCHIVED && !$cat_view) { // archive virtual feed
$query_strategy_part = "feed_id IS NULL"; $query_strategy_part = "feed_id IS NULL";
$allow_archived = true; $allow_archived = true;
} else if ($feed == 0 && $cat_view) { // uncategorized } else if ($feed == Feeds::CATEGORY_UNCATEGORIZED && $cat_view) { // uncategorized
$query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL"; $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
} else if ($feed == -1) { // starred virtual feed } else if ($feed == -1) { // starred virtual feed, Feeds::FEED_STARRED or Feeds::CATEGORY_SPECIAL
$query_strategy_part = "marked = true"; $query_strategy_part = "marked = true";
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
$allow_archived = true; $allow_archived = true;
@ -1608,7 +1651,7 @@ class Feeds extends Handler_Protected {
$override_order = "last_marked DESC, date_entered DESC, updated DESC"; $override_order = "last_marked DESC, date_entered DESC, updated DESC";
} }
} else if ($feed == -2) { // published virtual feed OR labels category } else if ($feed == -2) { // published virtual feed (Feeds::FEED_PUBLISHED) OR labels category (Feeds::CATEGORY_LABELS)
if (!$cat_view) { if (!$cat_view) {
$query_strategy_part = "published = true"; $query_strategy_part = "published = true";
@ -1628,7 +1671,7 @@ class Feeds extends Handler_Protected {
ttrss_user_labels2.article_id = ref_id"; ttrss_user_labels2.article_id = ref_id";
} }
} else if ($feed == -6) { // recently read } else if ($feed == Feeds::FEED_RECENTLY_READ) { // recently read
$query_strategy_part = "unread = false AND last_read IS NOT NULL"; $query_strategy_part = "unread = false AND last_read IS NOT NULL";
if (Config::get(Config::DB_TYPE) == "pgsql") { if (Config::get(Config::DB_TYPE) == "pgsql") {
@ -1643,7 +1686,7 @@ class Feeds extends Handler_Protected {
if (!$override_order) $override_order = "last_read DESC"; if (!$override_order) $override_order = "last_read DESC";
} else if ($feed == -3) { // fresh virtual feed } else if ($feed == Feeds::FEED_FRESH) { // fresh virtual feed
$query_strategy_part = "unread = true AND score >= 0"; $query_strategy_part = "unread = true AND score >= 0";
$intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE, $owner_uid); $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE, $owner_uid);
@ -1655,7 +1698,7 @@ class Feeds extends Handler_Protected {
} }
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
} else if ($feed == -4) { // all articles virtual feed } else if ($feed == Feeds::FEED_ALL) { // all articles virtual feed
$allow_archived = true; $allow_archived = true;
$query_strategy_part = "true"; $query_strategy_part = "true";
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
@ -1770,7 +1813,7 @@ class Feeds extends Handler_Protected {
$first_id_query_strategy_part = $query_strategy_part; $first_id_query_strategy_part = $query_strategy_part;
if ($feed == -3) if ($feed == Feeds::FEED_FRESH)
$first_id_query_strategy_part = "true"; $first_id_query_strategy_part = "true";
if (Config::get(Config::DB_TYPE) == "pgsql") { if (Config::get(Config::DB_TYPE) == "pgsql") {
@ -1784,7 +1827,7 @@ class Feeds extends Handler_Protected {
} }
// except for Labels category // except for Labels category
if (get_pref(Prefs::HEADLINES_NO_DISTINCT, $owner_uid) && !($feed == -2 && $cat_view)) { if (get_pref(Prefs::HEADLINES_NO_DISTINCT, $owner_uid) && !($feed == Feeds::CATEGORY_LABELS && $cat_view)) {
$distinct_qpart = ""; $distinct_qpart = "";
} }

View File

@ -20,9 +20,9 @@ class Handler_Public extends Handler {
if (!$override_order) { if (!$override_order) {
$override_order = "date_entered DESC, updated DESC"; $override_order = "date_entered DESC, updated DESC";
if ($feed == -2 && !$is_cat) { if ($feed == Feeds::FEED_PUBLISHED && !$is_cat) {
$override_order = "last_published DESC"; $override_order = "last_published DESC";
} else if ($feed == -1 && !$is_cat) { } else if ($feed == Feeds::FEED_STARRED && !$is_cat) {
$override_order = "last_marked DESC"; $override_order = "last_marked DESC";
} }
} }
@ -269,7 +269,7 @@ class Handler_Public extends Handler {
if ($fresh) { if ($fresh) {
print ";"; print ";";
print Feeds::_get_counters(-3, false, true, $uid); print Feeds::_get_counters(Feeds::FEED_FRESH, false, true, $uid);
} }
} else { } else {
print "-1;User not found"; print "-1;User not found";
@ -416,10 +416,10 @@ class Handler_Public extends Handler {
$_SESSION["login_error_msg"] ??= __("Incorrect username or password"); $_SESSION["login_error_msg"] ??= __("Incorrect username or password");
} }
$return = clean($_REQUEST['return']); $return = clean($_REQUEST['return'] ?? '');
if ($_REQUEST['return'] && mb_strpos($return, Config::get_self_url()) === 0) { if ($return && mb_strpos($return, Config::get_self_url()) === 0) {
header("Location: " . clean($_REQUEST['return'])); header("Location: $return");
} else { } else {
header("Location: " . Config::get_self_url()); header("Location: " . Config::get_self_url());
} }
@ -451,6 +451,7 @@ class Handler_Public extends Handler {
echo javascript_tag("lib/dojo/dojo.js"); echo javascript_tag("lib/dojo/dojo.js");
echo javascript_tag("lib/dojo/tt-rss-layer.js"); echo javascript_tag("lib/dojo/tt-rss-layer.js");
?> ?>
<?= Config::get_override_links() ?>
</head> </head>
<body class='flat ttrss_utility'> <body class='flat ttrss_utility'>
<div class='container'> <div class='container'>

View File

@ -588,7 +588,7 @@ class PluginHost {
function lookup_command(string $command) { function lookup_command(string $command) {
$command = "-" . strtolower($command); $command = "-" . strtolower($command);
if (array_key_exists($command, $this->commands) && is_array($this->commands[$command])) { if (array_key_exists($command, $this->commands)) {
return $this->commands[$command]["class"]; return $this->commands[$command]["class"];
} else { } else {
return false; return false;
@ -781,7 +781,7 @@ class PluginHost {
// Plugin feed functions are *EXPERIMENTAL*! // Plugin feed functions are *EXPERIMENTAL*!
// cat_id: only -1 is supported (Special) // cat_id: only -1 (Feeds::CATEGORY_SPECIAL) is supported (Special)
function add_feed(int $cat_id, string $title, string $icon, Plugin $sender): int { function add_feed(int $cat_id, string $title, string $icon, Plugin $sender): int {
if (empty($this->feeds[$cat_id])) if (empty($this->feeds[$cat_id]))

View File

@ -135,18 +135,19 @@ class Pref_Feeds extends Handler_Protected {
if (clean($_REQUEST['mode'] ?? 0) == 2) { if (clean($_REQUEST['mode'] ?? 0) == 2) {
if ($enable_cats) { if ($enable_cats) {
$cat = $this->feedlist_init_cat(-1); $cat = $this->feedlist_init_cat(Feeds::CATEGORY_SPECIAL);
} else { } else {
$cat['items'] = array(); $cat['items'] = array();
} }
foreach (array(-4, -3, -1, -2, 0, -6) as $i) { foreach ([Feeds::FEED_ALL, Feeds::FEED_FRESH, Feeds::FEED_STARRED, Feeds::FEED_PUBLISHED,
array_push($cat['items'], $this->feedlist_init_feed($i)); Feeds::FEED_ARCHIVED, Feeds::FEED_RECENTLY_READ] as $feed_id) {
array_push($cat['items'], $this->feedlist_init_feed($feed_id));
} }
/* Plugin feeds for -1 */ /* Plugin feeds for -1 (Feeds::CATEGORY_SPECIAL) */
$feeds = PluginHost::getInstance()->get_feeds(-1); $feeds = PluginHost::getInstance()->get_feeds(Feeds::CATEGORY_SPECIAL);
if ($feeds) { if ($feeds) {
foreach ($feeds as $feed) { foreach ($feeds as $feed) {
@ -180,7 +181,7 @@ class Pref_Feeds extends Handler_Protected {
$sth->execute([$_SESSION['uid']]); $sth->execute([$_SESSION['uid']]);
if (get_pref(Prefs::ENABLE_FEED_CATS)) { if (get_pref(Prefs::ENABLE_FEED_CATS)) {
$cat = $this->feedlist_init_cat(-2); $cat = $this->feedlist_init_cat(Feeds::CATEGORY_LABELS);
} else { } else {
$cat['items'] = []; $cat['items'] = [];
} }
@ -241,7 +242,12 @@ class Pref_Feeds extends Handler_Protected {
//$root['param'] += count($cat['items']); //$root['param'] += count($cat['items']);
} }
/* Uncategorized is a special case */ /**
* Uncategorized is a special case.
*
* Define a minimal array shape to help PHPStan with the type of $cat['items']
* @var array{items: array<int, array<string, mixed>>} $cat
*/
$cat = [ $cat = [
'id' => 'CAT:0', 'id' => 'CAT:0',
'bare_id' => 0, 'bare_id' => 0,
@ -1027,7 +1033,7 @@ class Pref_Feeds extends Handler_Protected {
<?= format_notice('Published articles can be subscribed by anyone who knows the following URL:') ?></h3> <?= format_notice('Published articles can be subscribed by anyone who knows the following URL:') ?></h3>
<button dojoType='dijit.form.Button' class='alt-primary' <button dojoType='dijit.form.Button' class='alt-primary'
onclick="CommonDialogs.generatedFeed(-2, false)"> onclick="CommonDialogs.generatedFeed(<?= Feeds::FEED_PUBLISHED ?>, false)">
<?= \Controls\icon('share') ?> <?= \Controls\icon('share') ?>
<?= __('Display URL') ?> <?= __('Display URL') ?>
</button> </button>

View File

@ -325,14 +325,6 @@ class Prefs {
return self::get_instance()->_set($pref_name, $value, $owner_uid, $profile_id); return self::get_instance()->_set($pref_name, $value, $owner_uid, $profile_id);
} }
private function _delete(string $pref_name, int $owner_uid, ?int $profile_id): bool {
$sth = $this->pdo->prepare("DELETE FROM ttrss_user_prefs2
WHERE pref_name = :name AND owner_uid = :uid AND
(profile = :profile OR (:profile IS NULL AND profile IS NULL))");
return $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name ]);
}
/** /**
* @param bool|int|string $value * @param bool|int|string $value
*/ */
@ -350,10 +342,6 @@ class Prefs {
$value = Config::cast_to($value, $type_hint); $value = Config::cast_to($value, $type_hint);
// is this a good idea or not? probably not (user-set value remains user-set even if its at default)
//if ($value == $def_val)
// return $this->_delete($pref_name, $owner_uid, $profile_id);
if ($value == $this->_get($pref_name, $owner_uid, $profile_id)) if ($value == $this->_get($pref_name, $owner_uid, $profile_id))
return false; return false;

View File

@ -316,6 +316,7 @@ class RSSUtils {
$feed_data = trim($feed_data); $feed_data = trim($feed_data);
if ($feed_data) {
$rss = new FeedParser($feed_data); $rss = new FeedParser($feed_data);
$rss->init(); $rss->init();
@ -324,6 +325,11 @@ class RSSUtils {
'title' => mb_substr(clean($rss->get_title()), 0, 199), 'title' => mb_substr(clean($rss->get_title()), 0, 199),
'site_url' => mb_substr(UrlHelper::rewrite_relative($feed->feed_url, clean($rss->get_link())), 0, 245), 'site_url' => mb_substr(UrlHelper::rewrite_relative($feed->feed_url, clean($rss->get_link())), 0, 245),
]; ];
} else {
Debug::log(sprintf("unable to parse feed for basic info: %s", $rss->error()), Debug::LOG_VERBOSE);
}
} else {
Debug::log(sprintf("unable to fetch feed for basic info: %s [%s]", UrlHelper::$fetch_last_error, UrlHelper::$fetch_last_error_code), Debug::LOG_VERBOSE);
} }
} }
@ -597,10 +603,13 @@ class RSSUtils {
Debug::log("site_url: {$feed_obj->site_url}", Debug::LOG_VERBOSE); Debug::log("site_url: {$feed_obj->site_url}", Debug::LOG_VERBOSE);
Debug::log("feed_title: {$rss->get_title()}", Debug::LOG_VERBOSE); Debug::log("feed_title: {$rss->get_title()}", Debug::LOG_VERBOSE);
Debug::log("favicon: needs check: {$feed_obj->favicon_needs_check} is custom: {$feed_obj->favicon_is_custom} avg color: {$feed_obj->favicon_avg_color}", Debug::log('favicon: needs check: ' . ($feed_obj->favicon_needs_check ? 'true' : 'false')
. ', is custom: ' . ($feed_obj->favicon_is_custom ? 'true' : 'false')
. ", avg color: {$feed_obj->favicon_avg_color}",
Debug::LOG_VERBOSE); Debug::LOG_VERBOSE);
if ($feed_obj->favicon_needs_check || $force_refetch) { if ($feed_obj->favicon_needs_check || $force_refetch
|| ($feed_obj->favicon_is_custom && !$feed_obj->favicon_avg_color)) {
// restrict update attempts to once per 12h // restrict update attempts to once per 12h
$feed_obj->favicon_last_checked = Db::NOW(); $feed_obj->favicon_last_checked = Db::NOW();
@ -631,13 +640,16 @@ class RSSUtils {
$feed_obj->favicon_avg_color = 'fail'; $feed_obj->favicon_avg_color = 'fail';
$feed_obj->save(); $feed_obj->save();
$feed_obj->favicon_avg_color = \Colors\calculate_avg_color($favicon_cache->get_full_path($feed)); $calculated_avg_color = \Colors\calculate_avg_color($favicon_cache->get_full_path($feed));
if ($calculated_avg_color) {
$feed_obj->favicon_avg_color = $calculated_avg_color;
$feed_obj->save(); $feed_obj->save();
}
Debug::log("favicon: avg color: {$feed_obj->favicon_avg_color}", Debug::LOG_VERBOSE); Debug::log("favicon: calculated avg color: {$calculated_avg_color}, setting avg color: {$feed_obj->favicon_avg_color}", Debug::LOG_VERBOSE);
} else if ($feed_obj->favicon_avg_color == 'fail') { } else if ($feed_obj->favicon_avg_color == 'fail') {
Debug::log("floicon failed on $feed, not trying to recalculate avg color", Debug::LOG_VERBOSE); Debug::log("floicon failed on $feed or a suitable avg color couldn't be determined, not trying to recalculate avg color", Debug::LOG_VERBOSE);
} }
} }

View File

@ -16,7 +16,7 @@
"j4mie/idiorm": "dev-master" "j4mie/idiorm": "dev-master"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "1.8.2", "phpstan/phpstan": "1.10.3",
"phpunit/phpunit": "9.5.16" "phpunit/phpunit": "9.5.16"
} }
} }

24
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "6beda4561e770d2f0c7c532c5e3693d3", "content-hash": "d2be3e2749aff1bebf6257ecbfd6dcb3",
"packages": [ "packages": [
{ {
"name": "beberlei/assert", "name": "beberlei/assert",
@ -1130,16 +1130,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.8.2", "version": "1.10.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c" "reference": "5419375b5891add97dc74be71e6c1c34baaddf64"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c53312ecc575caf07b0e90dee43883fdf90ca67c", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64",
"reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c", "reference": "5419375b5891add97dc74be71e6c1c34baaddf64",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1163,9 +1163,13 @@
"MIT" "MIT"
], ],
"description": "PHPStan - PHP Static Analysis Tool", "description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.8.2" "source": "https://github.com/phpstan/phpstan/tree/1.10.3"
}, },
"funding": [ "funding": [
{ {
@ -1176,16 +1180,12 @@
"url": "https://github.com/phpstan", "url": "https://github.com/phpstan",
"type": "github" "type": "github"
}, },
{
"url": "https://www.patreon.com/phpstan",
"type": "patreon"
},
{ {
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-07-20T09:57:31+00:00" "time": "2023-02-25T14:47:13+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -2690,5 +2690,5 @@
"prefer-lowest": false, "prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "2.0.0" "plugin-api-version": "2.3.0"
} }

View File

@ -355,8 +355,9 @@ function colorPalette(string $imageFile, int $numColors, int $granularity = 5):
} else { } else {
$img = @$ico->images[count($ico->images)-1]->getImageResource(); $img = @$ico->images[count($ico->images)-1]->getImageResource();
} }
} } else {
return null; return null;
}
} else if ($size[0] > 0 && $size[1] > 0) { } else if ($size[0] > 0 && $size[1] > 0) {
$img = @imagecreatefromstring(file_get_contents($imageFile)); $img = @imagecreatefromstring(file_get_contents($imageFile));
} }

View File

@ -366,7 +366,6 @@
function file_is_locked(string $filename): bool { function file_is_locked(string $filename): bool {
if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/$filename")) { if (file_exists(Config::get(Config::LOCK_DIRECTORY) . "/$filename")) {
if (function_exists('flock')) {
$fp = @fopen(Config::get(Config::LOCK_DIRECTORY) . "/$filename", "r"); $fp = @fopen(Config::get(Config::LOCK_DIRECTORY) . "/$filename", "r");
if ($fp) { if ($fp) {
if (flock($fp, LOCK_EX | LOCK_NB)) { if (flock($fp, LOCK_EX | LOCK_NB)) {
@ -379,8 +378,6 @@
} else { } else {
return false; return false;
} }
}
return true; // consider the file always locked and skip the test
} else { } else {
return false; return false;
} }

View File

@ -1198,19 +1198,19 @@ const App = {
} }
}; };
this.hotkey_actions["goto_read"] = () => { this.hotkey_actions["goto_read"] = () => {
Feeds.open({feed: -6}); Feeds.open({feed: Feeds.FEED_RECENTLY_READ});
}; };
this.hotkey_actions["goto_all"] = () => { this.hotkey_actions["goto_all"] = () => {
Feeds.open({feed: -4}); Feeds.open({feed: Feeds.FEED_ALL});
}; };
this.hotkey_actions["goto_fresh"] = () => { this.hotkey_actions["goto_fresh"] = () => {
Feeds.open({feed: -3}); Feeds.open({feed: Feeds.FEED_FRESH});
}; };
this.hotkey_actions["goto_marked"] = () => { this.hotkey_actions["goto_marked"] = () => {
Feeds.open({feed: -1}); Feeds.open({feed: Feeds.FEED_STARRED});
}; };
this.hotkey_actions["goto_published"] = () => { this.hotkey_actions["goto_published"] = () => {
Feeds.open({feed: -2}); Feeds.open({feed: Feeds.FEED_PUBLISHED});
}; };
this.hotkey_actions["goto_prefs"] = () => { this.hotkey_actions["goto_prefs"] = () => {
App.openPreferences(); App.openPreferences();

View File

@ -146,10 +146,8 @@ const Article = {
</div>`; </div>`;
}, },
renderTags: function (id, tags) { renderTags: function (id, tags) {
const tags_short = tags.length > 5 ? tags.slice(0, 5) : tags;
return `<span class="tags" title="${tags.join(", ")}" data-tags-for="${id}"> return `<span class="tags" title="${tags.join(", ")}" data-tags-for="${id}">
${tags_short.length > 0 ? tags_short.map((tag) => ` ${tags.length > 0 ? tags.map((tag) => `
<a href="#" onclick="Feeds.open({feed: '${tag.trim()}'})" class="tag">${tag}</a>` <a href="#" onclick="Feeds.open({feed: '${tag.trim()}'})" class="tag">${tag}</a>`
).join(", ") : `${__("no tags")}`}</span>`; ).join(", ") : `${__("no tags")}`}</span>`;
}, },

View File

@ -225,8 +225,8 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co
if (item.auxcounter > 0) rc += " Has_Aux"; if (item.auxcounter > 0) rc += " Has_Aux";
if (item.markedcounter > 0) rc += " Has_Marked"; if (item.markedcounter > 0) rc += " Has_Marked";
if (item.updates_disabled > 0) rc += " UpdatesDisabled"; if (item.updates_disabled > 0) rc += " UpdatesDisabled";
if (item.bare_id >= App.LABEL_BASE_INDEX && item.bare_id < 0 && !is_cat || item.bare_id == 0 && !is_cat) rc += " Special"; if (item.bare_id >= App.LABEL_BASE_INDEX && item.bare_id < 0 && !is_cat || item.bare_id == Feeds.FEED_ARCHIVED && !is_cat) rc += " Special";
if (item.bare_id == -1 && is_cat) rc += " AlwaysVisible"; if (item.bare_id == Feeds.CATEGORY_SPECIAL && is_cat) rc += " AlwaysVisible";
if (item.bare_id < App.LABEL_BASE_INDEX) rc += " Label"; if (item.bare_id < App.LABEL_BASE_INDEX) rc += " Label";
return rc; return rc;

View File

@ -3,6 +3,19 @@
/* global __, App, Headlines, xhr, dojo, dijit, fox, PluginHost, Notify, fox */ /* global __, App, Headlines, xhr, dojo, dijit, fox, PluginHost, Notify, fox */
const Feeds = { const Feeds = {
FEED_ARCHIVED: 0,
FEED_STARRED: -1,
FEED_PUBLISHED: -2,
FEED_FRESH: -3,
FEED_ALL: -4,
FEED_DASHBOARD: -5,
FEED_RECENTLY_READ: -6,
FEED_ERROR: -7,
CATEGORY_UNCATEGORIZED: 0,
CATEGORY_SPECIAL: -1,
CATEGORY_LABELS: -2,
CATEGORY_ALL_EXCEPT_VIRTUAL: -3,
CATEGORY_ALL: -4,
_default_feed_id: -3, _default_feed_id: -3,
counters_last_request: 0, counters_last_request: 0,
_active_feed_id: undefined, _active_feed_id: undefined,

View File

@ -307,7 +307,7 @@ const Headlines = {
offset = unread_in_buffer; offset = unread_in_buffer;
break; break;
case "adaptive": case "adaptive":
if (!(Feeds.getActive() == -1 && !Feeds.activeIsCat())) if (!(Feeds.getActive() == Feeds.FEED_STARRED && !Feeds.activeIsCat()))
offset = num_unread > 0 ? unread_in_buffer : num_all; offset = num_unread > 0 ? unread_in_buffer : num_all;
break; break;
} }
@ -746,7 +746,7 @@ const Headlines = {
feed_id = reply['headlines']['id']; feed_id = reply['headlines']['id'];
Feeds.last_search_query = reply['headlines']['search_query']; Feeds.last_search_query = reply['headlines']['search_query'];
if (feed_id != -7 && (feed_id != Feeds.getActive() || is_cat != Feeds.activeIsCat())) if (feed_id != Feeds.FEED_ERROR && (feed_id != Feeds.getActive() || is_cat != Feeds.activeIsCat()))
return; return;
const headlines_count = reply['headlines-info']['count']; const headlines_count = reply['headlines-info']['count'];

4380
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,7 @@ class Auth_Internal extends Auth_Base {
return false; return false;
} else { } else {
$return = urlencode($_REQUEST["return"]); $return = urlencode(with_trailing_slash($_REQUEST["return"]));
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -76,11 +76,12 @@ class Auth_Internal extends Auth_Base {
}, },
}; };
</script> </script>
<?= Config::get_override_links() ?>
</head> </head>
<body class="flat ttrss_utility otp css_loading"> <body class="flat ttrss_utility otp css_loading">
<h1><?= __("Authentication") ?></h1> <h1><?= __("Authentication") ?></h1>
<div class="content"> <div class="content">
<form dojoType="dijit.form.Form" action="public.php?return=<?= urlencode(with_trailing_slash($return)) ?>" method="post" class="otpform"> <form dojoType="dijit.form.Form" action="public.php?return=<?= $return ?>" method="post" class="otpform">
<?php foreach (["login", "password", "bw_limit", "safe_mode", "remember_me", "profile"] as $key) { <?php foreach (["login", "password", "bw_limit", "safe_mode", "remember_me", "profile"] as $key) {
print \Controls\hidden_tag($key, $_POST[$key] ?? ""); print \Controls\hidden_tag($key, $_POST[$key] ?? "");

View File

@ -56,6 +56,7 @@ class Bookmarklets extends Plugin {
display : none; display : none;
} }
</style> </style>
<?= Config::get_override_links() ?>
</head> </head>
<body class='flat ttrss_utility css_loading'> <body class='flat ttrss_utility css_loading'>
<script type="text/javascript"> <script type="text/javascript">
@ -202,6 +203,7 @@ class Bookmarklets extends Plugin {
display : none; display : none;
} }
</style> </style>
<?= Config::get_override_links() ?>
</head> </head>
<body class='flat ttrss_utility share_popup css_loading'> <body class='flat ttrss_utility share_popup css_loading'>
<script type="text/javascript"> <script type="text/javascript">

View File

@ -195,6 +195,7 @@ class Share extends Plugin {
) )
), 500, "...")) ?>"> ), 500, "...")) ?>">
<meta property='og:image' content="<?= htmlspecialchars($og_image) ?>"> <meta property='og:image' content="<?= htmlspecialchars($og_image) ?>">
<?= Config::get_override_links() ?>
</head> </head>
<body class='flat ttrss_utility ttrss_zoom css_loading'> <body class='flat ttrss_utility ttrss_zoom css_loading'>

View File

@ -73,6 +73,12 @@ body.ttrss_main .post .header .title {
font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
word-break: break-all; word-break: break-all;
} }
body.ttrss_main .post .header .tags {
max-width: 25%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
body.ttrss_main .post div.content { body.ttrss_main .post div.content {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
@ -1311,6 +1317,7 @@ body.ttrss_utility hr {
} }
.cdm .footer .left { .cdm .footer .left {
flex-grow: 2; flex-grow: 2;
min-width: 0;
} }
.cdm .footer .left > * { .cdm .footer .left > * {
margin-right: 4px; margin-right: 4px;
@ -1318,6 +1325,12 @@ body.ttrss_utility hr {
.cdm .footer .right > * { .cdm .footer .right > * {
margin-left: 4px; margin-left: 4px;
} }
.cdm .footer .tags {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cdm .content-inner { .cdm .content-inner {
margin: 10px; margin: 10px;
line-height: 1.5; line-height: 1.5;

View File

@ -73,6 +73,12 @@ body.ttrss_main .post .header .title {
font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
word-break: break-all; word-break: break-all;
} }
body.ttrss_main .post .header .tags {
max-width: 25%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
body.ttrss_main .post div.content { body.ttrss_main .post div.content {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
@ -1311,6 +1317,7 @@ body.ttrss_utility hr {
} }
.cdm .footer .left { .cdm .footer .left {
flex-grow: 2; flex-grow: 2;
min-width: 0;
} }
.cdm .footer .left > * { .cdm .footer .left > * {
margin-right: 4px; margin-right: 4px;
@ -1318,6 +1325,12 @@ body.ttrss_utility hr {
.cdm .footer .right > * { .cdm .footer .right > * {
margin-left: 4px; margin-left: 4px;
} }
.cdm .footer .tags {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cdm .content-inner { .cdm .content-inner {
margin: 10px; margin: 10px;
line-height: 1.5; line-height: 1.5;

View File

@ -73,6 +73,12 @@ body.ttrss_main .post .header .title {
font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
word-break: break-all; word-break: break-all;
} }
body.ttrss_main .post .header .tags {
max-width: 25%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
body.ttrss_main .post div.content { body.ttrss_main .post div.content {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
@ -1311,6 +1317,7 @@ body.ttrss_utility hr {
} }
.cdm .footer .left { .cdm .footer .left {
flex-grow: 2; flex-grow: 2;
min-width: 0;
} }
.cdm .footer .left > * { .cdm .footer .left > * {
margin-right: 4px; margin-right: 4px;
@ -1318,6 +1325,12 @@ body.ttrss_utility hr {
.cdm .footer .right > * { .cdm .footer .right > * {
margin-left: 4px; margin-left: 4px;
} }
.cdm .footer .tags {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cdm .content-inner { .cdm .content-inner {
margin: 10px; margin: 10px;
line-height: 1.5; line-height: 1.5;

View File

@ -73,6 +73,12 @@ body.ttrss_main .post .header .title {
font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
word-break: break-all; word-break: break-all;
} }
body.ttrss_main .post .header .tags {
max-width: 25%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
body.ttrss_main .post div.content { body.ttrss_main .post div.content {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
@ -1311,6 +1317,7 @@ body.ttrss_utility hr {
} }
.cdm .footer .left { .cdm .footer .left {
flex-grow: 2; flex-grow: 2;
min-width: 0;
} }
.cdm .footer .left > * { .cdm .footer .left > * {
margin-right: 4px; margin-right: 4px;
@ -1318,6 +1325,12 @@ body.ttrss_utility hr {
.cdm .footer .right > * { .cdm .footer .right > * {
margin-left: 4px; margin-left: 4px;
} }
.cdm .footer .tags {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cdm .content-inner { .cdm .content-inner {
margin: 10px; margin: 10px;
line-height: 1.5; line-height: 1.5;

View File

@ -100,6 +100,7 @@
.left { .left {
flex-grow : 2; flex-grow : 2;
min-width : 0;
> * { > * {
margin-right : 4px; margin-right : 4px;
@ -111,6 +112,13 @@
margin-left : 4px; margin-left : 4px;
} }
} }
.tags {
max-width : 50%;
overflow : hidden;
white-space : nowrap;
text-overflow : ellipsis;
}
} }
.content-inner { .content-inner {

View File

@ -56,6 +56,13 @@ body.ttrss_main {
font-family : @fonts-ui; font-family : @fonts-ui;
word-break : break-all; word-break : break-all;
} }
.tags {
max-width : 25%;
overflow : hidden;
white-space : nowrap;
text-overflow : ellipsis;
}
} }
div.content { div.content {

View File

@ -74,6 +74,12 @@ body.ttrss_main .post .header .title {
font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
word-break: break-all; word-break: break-all;
} }
body.ttrss_main .post .header .tags {
max-width: 25%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
body.ttrss_main .post div.content { body.ttrss_main .post div.content {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
@ -1312,6 +1318,7 @@ body.ttrss_utility hr {
} }
.cdm .footer .left { .cdm .footer .left {
flex-grow: 2; flex-grow: 2;
min-width: 0;
} }
.cdm .footer .left > * { .cdm .footer .left > * {
margin-right: 4px; margin-right: 4px;
@ -1319,6 +1326,12 @@ body.ttrss_utility hr {
.cdm .footer .right > * { .cdm .footer .right > * {
margin-left: 4px; margin-left: 4px;
} }
.cdm .footer .tags {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cdm .content-inner { .cdm .content-inner {
margin: 10px; margin: 10px;
line-height: 1.5; line-height: 1.5;

View File

@ -74,6 +74,12 @@ body.ttrss_main .post .header .title {
font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
word-break: break-all; word-break: break-all;
} }
body.ttrss_main .post .header .tags {
max-width: 25%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
body.ttrss_main .post div.content { body.ttrss_main .post div.content {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
@ -1312,6 +1318,7 @@ body.ttrss_utility hr {
} }
.cdm .footer .left { .cdm .footer .left {
flex-grow: 2; flex-grow: 2;
min-width: 0;
} }
.cdm .footer .left > * { .cdm .footer .left > * {
margin-right: 4px; margin-right: 4px;
@ -1319,6 +1326,12 @@ body.ttrss_utility hr {
.cdm .footer .right > * { .cdm .footer .right > * {
margin-left: 4px; margin-left: 4px;
} }
.cdm .footer .tags {
max-width: 50%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cdm .content-inner { .cdm .content-inner {
margin: 10px; margin: 10px;
line-height: 1.5; line-height: 1.5;

18
vendor/autoload.php vendored
View File

@ -2,6 +2,24 @@
// autoload.php @generated by Composer // autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php'; require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056::getLoader(); return ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056::getLoader();

View File

@ -1 +0,0 @@
../nikic/php-parser/bin/php-parse

120
vendor/bin/php-parse vendored Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../nikic/php-parser/bin/php-parse)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse');
exit(0);
}
}
include __DIR__ . '/..'.'/nikic/php-parser/bin/php-parse';

1
vendor/bin/phpstan vendored
View File

@ -1 +0,0 @@
../phpstan/phpstan/phpstan

120
vendor/bin/phpstan vendored Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../phpstan/phpstan/phpstan)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan');
exit(0);
}
}
include __DIR__ . '/..'.'/phpstan/phpstan/phpstan';

View File

@ -1 +0,0 @@
../phpstan/phpstan/phpstan.phar

120
vendor/bin/phpstan.phar vendored Normal file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../phpstan/phpstan/phpstan.phar)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar');
exit(0);
}
}
include __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar';

View File

@ -42,30 +42,79 @@ namespace Composer\Autoload;
*/ */
class ClassLoader class ClassLoader
{ {
/** @var \Closure(string):void */
private static $includeFile;
/** @var ?string */
private $vendorDir; private $vendorDir;
// PSR-4 // PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array(); private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array(); private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array(); private $fallbackDirsPsr4 = array();
// PSR-0 // PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array(); private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array(); private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false; private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array(); private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false; private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array(); private $missingClasses = array();
/** @var ?string */
private $apcuPrefix; private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array(); private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null) public function __construct($vendorDir = null)
{ {
$this->vendorDir = $vendorDir; $this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
} }
/**
* @return string[]
*/
public function getPrefixes() public function getPrefixes()
{ {
if (!empty($this->prefixesPsr0)) { if (!empty($this->prefixesPsr0)) {
@ -75,28 +124,47 @@ class ClassLoader
return array(); return array();
} }
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4() public function getPrefixesPsr4()
{ {
return $this->prefixDirsPsr4; return $this->prefixDirsPsr4;
} }
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs() public function getFallbackDirs()
{ {
return $this->fallbackDirsPsr0; return $this->fallbackDirsPsr0;
} }
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4() public function getFallbackDirsPsr4()
{ {
return $this->fallbackDirsPsr4; return $this->fallbackDirsPsr4;
} }
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap() public function getClassMap()
{ {
return $this->classMap; return $this->classMap;
} }
/** /**
* @param array $classMap Class to filename map * @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/ */
public function addClassMap(array $classMap) public function addClassMap(array $classMap)
{ {
@ -112,8 +180,10 @@ class ClassLoader
* appending or prepending to the ones previously set for this prefix. * appending or prepending to the ones previously set for this prefix.
* *
* @param string $prefix The prefix * @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories * @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories * @param bool $prepend Whether to prepend the directories
*
* @return void
*/ */
public function add($prefix, $paths, $prepend = false) public function add($prefix, $paths, $prepend = false)
{ {
@ -157,10 +227,12 @@ class ClassLoader
* appending or prepending to the ones previously set for this namespace. * appending or prepending to the ones previously set for this namespace.
* *
* @param string $prefix The prefix/namespace, with trailing '\\' * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories * @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories * @param bool $prepend Whether to prepend the directories
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*
* @return void
*/ */
public function addPsr4($prefix, $paths, $prepend = false) public function addPsr4($prefix, $paths, $prepend = false)
{ {
@ -205,7 +277,9 @@ class ClassLoader
* replacing any others previously set for this prefix. * replacing any others previously set for this prefix.
* *
* @param string $prefix The prefix * @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories * @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/ */
public function set($prefix, $paths) public function set($prefix, $paths)
{ {
@ -221,9 +295,11 @@ class ClassLoader
* replacing any others previously set for this namespace. * replacing any others previously set for this namespace.
* *
* @param string $prefix The prefix/namespace, with trailing '\\' * @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories * @param string[]|string $paths The PSR-4 base directories
* *
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*
* @return void
*/ */
public function setPsr4($prefix, $paths) public function setPsr4($prefix, $paths)
{ {
@ -243,6 +319,8 @@ class ClassLoader
* Turns on searching the include path for class files. * Turns on searching the include path for class files.
* *
* @param bool $useIncludePath * @param bool $useIncludePath
*
* @return void
*/ */
public function setUseIncludePath($useIncludePath) public function setUseIncludePath($useIncludePath)
{ {
@ -265,6 +343,8 @@ class ClassLoader
* that have not been registered with the class map. * that have not been registered with the class map.
* *
* @param bool $classMapAuthoritative * @param bool $classMapAuthoritative
*
* @return void
*/ */
public function setClassMapAuthoritative($classMapAuthoritative) public function setClassMapAuthoritative($classMapAuthoritative)
{ {
@ -285,6 +365,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled. * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
* *
* @param string|null $apcuPrefix * @param string|null $apcuPrefix
*
* @return void
*/ */
public function setApcuPrefix($apcuPrefix) public function setApcuPrefix($apcuPrefix)
{ {
@ -305,14 +387,18 @@ class ClassLoader
* Registers this instance as an autoloader. * Registers this instance as an autoloader.
* *
* @param bool $prepend Whether to prepend the autoloader or not * @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/ */
public function register($prepend = false) public function register($prepend = false)
{ {
spl_autoload_register(array($this, 'loadClass'), true, $prepend); spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) { if (null === $this->vendorDir) {
//no-op return;
} elseif ($prepend) { }
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else { } else {
unset(self::$registeredLoaders[$this->vendorDir]); unset(self::$registeredLoaders[$this->vendorDir]);
@ -322,6 +408,8 @@ class ClassLoader
/** /**
* Unregisters this instance as an autoloader. * Unregisters this instance as an autoloader.
*
* @return void
*/ */
public function unregister() public function unregister()
{ {
@ -336,15 +424,18 @@ class ClassLoader
* Loads the given class or interface. * Loads the given class or interface.
* *
* @param string $class The name of the class * @param string $class The name of the class
* @return bool|null True if loaded, null otherwise * @return true|null True if loaded, null otherwise
*/ */
public function loadClass($class) public function loadClass($class)
{ {
if ($file = $this->findFile($class)) { if ($file = $this->findFile($class)) {
includeFile($file); $includeFile = self::$includeFile;
$includeFile($file);
return true; return true;
} }
return null;
} }
/** /**
@ -399,6 +490,11 @@ class ClassLoader
return self::$registeredLoaders; return self::$registeredLoaders;
} }
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext) private function findFileWithExtension($class, $ext)
{ {
// PSR-4 lookup // PSR-4 lookup
@ -464,14 +560,26 @@ class ClassLoader
return false; return false;
} }
}
/** /**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include. * Scope isolated include.
* *
* Prevents access to $this/self from included files. * Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/ */
function includeFile($file) self::$includeFile = \Closure::bind(static function($file) {
{
include $file; include $file;
}, null, null);
}
} }

View File

@ -18,414 +18,29 @@ use Composer\Semver\VersionParser;
/** /**
* This class is copied in every Composer installed project and available to all * This class is copied in every Composer installed project and available to all
* *
* To require it's presence, you can require `composer-runtime-api ^2.0` * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/ */
class InstalledVersions class InstalledVersions
{ {
private static $installed = array ( /**
'root' => * @var mixed[]|null
array ( * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
'pretty_version' => 'dev-master', */
'version' => 'dev-master', private static $installed;
'aliases' =>
array ( /**
), * @var bool|null
'reference' => '26c67dba776e1e6f8ac40eed70fe79995325863d', */
'name' => '__root__',
),
'versions' =>
array (
'__root__' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => '26c67dba776e1e6f8ac40eed70fe79995325863d',
),
'beberlei/assert' =>
array (
'pretty_version' => 'v3.3.2',
'version' => '3.3.2.0',
'aliases' =>
array (
),
'reference' => 'cb70015c04be1baee6f5f5c953703347c0ac1655',
),
'chillerlan/php-qrcode' =>
array (
'pretty_version' => '4.3.4',
'version' => '4.3.4.0',
'aliases' =>
array (
),
'reference' => '2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d',
),
'chillerlan/php-settings-container' =>
array (
'pretty_version' => '2.1.4',
'version' => '2.1.4.0',
'aliases' =>
array (
),
'reference' => '1beb7df3c14346d4344b0b2e12f6f9a74feabd4a',
),
'doctrine/instantiator' =>
array (
'pretty_version' => '1.4.1',
'version' => '1.4.1.0',
'aliases' =>
array (
),
'reference' => '10dcfce151b967d20fde1b34ae6640712c3891bc',
),
'j4mie/idiorm' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
0 => '9999999-dev',
),
'reference' => 'efc8ea06698f53e2c479c7696f2b154c47c3a3cb',
),
'mervick/material-design-icons' =>
array (
'pretty_version' => '2.2.0',
'version' => '2.2.0.0',
'aliases' =>
array (
),
'reference' => '635435c8d3df3a6da3241648caf8a65d1c07cc1a',
),
'myclabs/deep-copy' =>
array (
'pretty_version' => '1.11.0',
'version' => '1.11.0.0',
'aliases' =>
array (
),
'reference' => '14daed4296fae74d9e3201d2c4925d1acb7aa614',
),
'nikic/php-parser' =>
array (
'pretty_version' => 'v4.14.0',
'version' => '4.14.0.0',
'aliases' =>
array (
),
'reference' => '34bea19b6e03d8153165d8f30bba4c3be86184c1',
),
'paragonie/constant_time_encoding' =>
array (
'pretty_version' => 'v2.6.3',
'version' => '2.6.3.0',
'aliases' =>
array (
),
'reference' => '58c3f47f650c94ec05a151692652a868995d2938',
),
'phar-io/manifest' =>
array (
'pretty_version' => '2.0.3',
'version' => '2.0.3.0',
'aliases' =>
array (
),
'reference' => '97803eca37d319dfa7826cc2437fc020857acb53',
),
'phar-io/version' =>
array (
'pretty_version' => '3.2.1',
'version' => '3.2.1.0',
'aliases' =>
array (
),
'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74',
),
'phpdocumentor/reflection-common' =>
array (
'pretty_version' => '2.2.0',
'version' => '2.2.0.0',
'aliases' =>
array (
),
'reference' => '1d01c49d4ed62f25aa84a747ad35d5a16924662b',
),
'phpdocumentor/reflection-docblock' =>
array (
'pretty_version' => '5.3.0',
'version' => '5.3.0.0',
'aliases' =>
array (
),
'reference' => '622548b623e81ca6d78b721c5e029f4ce664f170',
),
'phpdocumentor/type-resolver' =>
array (
'pretty_version' => '1.6.1',
'version' => '1.6.1.0',
'aliases' =>
array (
),
'reference' => '77a32518733312af16a44300404e945338981de3',
),
'phpspec/prophecy' =>
array (
'pretty_version' => 'v1.15.0',
'version' => '1.15.0.0',
'aliases' =>
array (
),
'reference' => 'bbcd7380b0ebf3961ee21409db7b38bc31d69a13',
),
'phpstan/phpstan' =>
array (
'pretty_version' => '1.8.2',
'version' => '1.8.2.0',
'aliases' =>
array (
),
'reference' => 'c53312ecc575caf07b0e90dee43883fdf90ca67c',
),
'phpunit/php-code-coverage' =>
array (
'pretty_version' => '9.2.15',
'version' => '9.2.15.0',
'aliases' =>
array (
),
'reference' => '2e9da11878c4202f97915c1cb4bb1ca318a63f5f',
),
'phpunit/php-file-iterator' =>
array (
'pretty_version' => '3.0.6',
'version' => '3.0.6.0',
'aliases' =>
array (
),
'reference' => 'cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf',
),
'phpunit/php-invoker' =>
array (
'pretty_version' => '3.1.1',
'version' => '3.1.1.0',
'aliases' =>
array (
),
'reference' => '5a10147d0aaf65b58940a0b72f71c9ac0423cc67',
),
'phpunit/php-text-template' =>
array (
'pretty_version' => '2.0.4',
'version' => '2.0.4.0',
'aliases' =>
array (
),
'reference' => '5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28',
),
'phpunit/php-timer' =>
array (
'pretty_version' => '5.0.3',
'version' => '5.0.3.0',
'aliases' =>
array (
),
'reference' => '5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2',
),
'phpunit/phpunit' =>
array (
'pretty_version' => '9.5.16',
'version' => '9.5.16.0',
'aliases' =>
array (
),
'reference' => '5ff8c545a50226c569310a35f4fa89d79f1ddfdc',
),
'sebastian/cli-parser' =>
array (
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'aliases' =>
array (
),
'reference' => '442e7c7e687e42adc03470c7b668bc4b2402c0b2',
),
'sebastian/code-unit' =>
array (
'pretty_version' => '1.0.8',
'version' => '1.0.8.0',
'aliases' =>
array (
),
'reference' => '1fc9f64c0927627ef78ba436c9b17d967e68e120',
),
'sebastian/code-unit-reverse-lookup' =>
array (
'pretty_version' => '2.0.3',
'version' => '2.0.3.0',
'aliases' =>
array (
),
'reference' => 'ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5',
),
'sebastian/comparator' =>
array (
'pretty_version' => '4.0.6',
'version' => '4.0.6.0',
'aliases' =>
array (
),
'reference' => '55f4261989e546dc112258c7a75935a81a7ce382',
),
'sebastian/complexity' =>
array (
'pretty_version' => '2.0.2',
'version' => '2.0.2.0',
'aliases' =>
array (
),
'reference' => '739b35e53379900cc9ac327b2147867b8b6efd88',
),
'sebastian/diff' =>
array (
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => '3461e3fccc7cfdfc2720be910d3bd73c69be590d',
),
'sebastian/environment' =>
array (
'pretty_version' => '5.1.4',
'version' => '5.1.4.0',
'aliases' =>
array (
),
'reference' => '1b5dff7bb151a4db11d49d90e5408e4e938270f7',
),
'sebastian/exporter' =>
array (
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => '65e8b7db476c5dd267e65eea9cab77584d3cfff9',
),
'sebastian/global-state' =>
array (
'pretty_version' => '5.0.5',
'version' => '5.0.5.0',
'aliases' =>
array (
),
'reference' => '0ca8db5a5fc9c8646244e629625ac486fa286bf2',
),
'sebastian/lines-of-code' =>
array (
'pretty_version' => '1.0.3',
'version' => '1.0.3.0',
'aliases' =>
array (
),
'reference' => 'c1c2e997aa3146983ed888ad08b15470a2e22ecc',
),
'sebastian/object-enumerator' =>
array (
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => '5c9eeac41b290a3712d88851518825ad78f45c71',
),
'sebastian/object-reflector' =>
array (
'pretty_version' => '2.0.4',
'version' => '2.0.4.0',
'aliases' =>
array (
),
'reference' => 'b4f479ebdbf63ac605d183ece17d8d7fe49c15c7',
),
'sebastian/recursion-context' =>
array (
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => 'cd9d8cf3c5804de4341c283ed787f099f5506172',
),
'sebastian/resource-operations' =>
array (
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'aliases' =>
array (
),
'reference' => '0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8',
),
'sebastian/type' =>
array (
'pretty_version' => '2.3.4',
'version' => '2.3.4.0',
'aliases' =>
array (
),
'reference' => 'b8cd8a1c753c90bc1a0f5372170e3e489136f914',
),
'sebastian/version' =>
array (
'pretty_version' => '3.0.2',
'version' => '3.0.2.0',
'aliases' =>
array (
),
'reference' => 'c6c1022351a901512170118436c764e473f6de8c',
),
'spomky-labs/otphp' =>
array (
'pretty_version' => 'v10.0.3',
'version' => '10.0.3.0',
'aliases' =>
array (
),
'reference' => '9784d9f7c790eed26e102d6c78f12c754036c366',
),
'thecodingmachine/safe' =>
array (
'pretty_version' => 'v2.2.2',
'version' => '2.2.2.0',
'aliases' =>
array (
),
'reference' => '440284f9592c9df402832452a6871a8b3c48d97e',
),
'theseer/tokenizer' =>
array (
'pretty_version' => '1.2.1',
'version' => '1.2.1.0',
'aliases' =>
array (
),
'reference' => '34a41e998c2183e22995f158c581e7b5e755ab9e',
),
'webmozart/assert' =>
array (
'pretty_version' => '1.11.0',
'version' => '1.11.0.0',
'aliases' =>
array (
),
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
),
),
);
private static $canGetVendors; private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array(); private static $installedByVendor = array();
/** /**
@ -441,7 +56,6 @@ class InstalledVersions
$packages[] = array_keys($installed['versions']); $packages[] = array_keys($installed['versions']);
} }
if (1 === \count($packages)) { if (1 === \count($packages)) {
return $packages[0]; return $packages[0];
} }
@ -449,19 +63,42 @@ class InstalledVersions
return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
} }
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/** /**
* Checks whether the given package is installed * Checks whether the given package is installed
* *
* This also returns true if the package name is provided or replaced by another package * This also returns true if the package name is provided or replaced by another package
* *
* @param string $packageName * @param string $packageName
* @param bool $includeDevRequirements
* @return bool * @return bool
*/ */
public static function isInstalled($packageName) public static function isInstalled($packageName, $includeDevRequirements = true)
{ {
foreach (self::getInstalled() as $installed) { foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) { if (isset($installed['versions'][$packageName])) {
return true; return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
} }
} }
@ -478,7 +115,6 @@ class InstalledVersions
* @param VersionParser $parser Install composer/semver to have access to this class and functionality * @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName * @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
*
* @return bool * @return bool
*/ */
public static function satisfies(VersionParser $parser, $packageName, $constraint) public static function satisfies(VersionParser $parser, $packageName, $constraint)
@ -588,9 +224,26 @@ class InstalledVersions
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
} }
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/** /**
* @return array * @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]} * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/ */
public static function getRootPackage() public static function getRootPackage()
{ {
@ -602,14 +255,38 @@ class InstalledVersions
/** /**
* Returns the raw installed.php data for custom implementations * Returns the raw installed.php data for custom implementations
* *
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[] * @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>} * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/ */
public static function getRawData() public static function getRawData()
{ {
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed; return self::$installed;
} }
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/** /**
* Lets you reload the static array from another file * Lets you reload the static array from another file
* *
@ -626,7 +303,7 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set * @param array[] $data A vendor/composer/installed.php data set
* @return void * @return void
* *
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list<string, array{pretty_version: ?string, version: ?string, aliases: ?string[], reference: ?string, replaced: ?string[], provided: ?string[]}>} $data * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/ */
public static function reload($data) public static function reload($data)
{ {
@ -636,6 +313,7 @@ class InstalledVersions
/** /**
* @return array[] * @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/ */
private static function getInstalled() private static function getInstalled()
{ {
@ -646,16 +324,27 @@ class InstalledVersions
$installed = array(); $installed = array();
if (self::$canGetVendors) { if (self::$canGetVendors) {
// @phpstan-ignore-next-line
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) { if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir]; $installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) { } elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
} }
} }
} }
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed; $installed[] = self::$installed;
return $installed; return $installed;

View File

@ -1,3 +1,4 @@
Copyright (c) Nils Adermann, Jordi Boggiano Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
@ -17,3 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.

View File

@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer // autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__)); $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(

View File

@ -2,12 +2,10 @@
// autoload_files.php @generated by Composer // autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__)); $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'a4ecaeafb8cfb009ad0e052c90355e98' => $vendorDir . '/beberlei/assert/lib/Assert/functions.php', 'a4ecaeafb8cfb009ad0e052c90355e98' => $vendorDir . '/beberlei/assert/lib/Assert/functions.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'51fcf4e06c07cc00c920b44bcd900e7a' => $vendorDir . '/thecodingmachine/safe/deprecated/apc.php', '51fcf4e06c07cc00c920b44bcd900e7a' => $vendorDir . '/thecodingmachine/safe/deprecated/apc.php',
@ -97,4 +95,6 @@ return array(
'4af1dca6db8c527c6eed27bff85ff0e5' => $vendorDir . '/thecodingmachine/safe/generated/yaz.php', '4af1dca6db8c527c6eed27bff85ff0e5' => $vendorDir . '/thecodingmachine/safe/generated/yaz.php',
'fe43ca06499ac37bc2dedd823af71eb5' => $vendorDir . '/thecodingmachine/safe/generated/zip.php', 'fe43ca06499ac37bc2dedd823af71eb5' => $vendorDir . '/thecodingmachine/safe/generated/zip.php',
'356736db98a6834f0a886b8d509b0ecd' => $vendorDir . '/thecodingmachine/safe/generated/zlib.php', '356736db98a6834f0a886b8d509b0ecd' => $vendorDir . '/thecodingmachine/safe/generated/zlib.php',
'9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
); );

View File

@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer // autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__)); $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(

View File

@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer // autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__)); $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(

View File

@ -23,51 +23,26 @@ class ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056
} }
spl_autoload_register(array('ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php'; require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::getInitializer($loader)); call_user_func(\Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true); $loader->register(true);
if ($useStaticLoader) { $filesToLoad = \Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::$files;
$includeFiles = Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
} else { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$includeFiles = require __DIR__ . '/autoload_files.php'; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
} }
foreach ($includeFiles as $fileIdentifier => $file) { }, null, null);
composerRequire19fc2ff1c0f9a92279c7979386bb2056($fileIdentifier, $file); foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
} }
return $loader; return $loader;
} }
} }
function composerRequire19fc2ff1c0f9a92279c7979386bb2056($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@ -7,8 +7,6 @@ namespace Composer\Autoload;
class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056 class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
{ {
public static $files = array ( public static $files = array (
'9b38cf48e83f5d8f60375221cd213eee' => __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'a4ecaeafb8cfb009ad0e052c90355e98' => __DIR__ . '/..' . '/beberlei/assert/lib/Assert/functions.php', 'a4ecaeafb8cfb009ad0e052c90355e98' => __DIR__ . '/..' . '/beberlei/assert/lib/Assert/functions.php',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'51fcf4e06c07cc00c920b44bcd900e7a' => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated/apc.php', '51fcf4e06c07cc00c920b44bcd900e7a' => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated/apc.php',
@ -98,6 +96,8 @@ class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
'4af1dca6db8c527c6eed27bff85ff0e5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/yaz.php', '4af1dca6db8c527c6eed27bff85ff0e5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/yaz.php',
'fe43ca06499ac37bc2dedd823af71eb5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zip.php', 'fe43ca06499ac37bc2dedd823af71eb5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zip.php',
'356736db98a6834f0a886b8d509b0ecd' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zlib.php', '356736db98a6834f0a886b8d509b0ecd' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zlib.php',
'9b38cf48e83f5d8f60375221cd213eee' => __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
); );
public static $prefixLengthsPsr4 = array ( public static $prefixLengthsPsr4 = array (

View File

@ -954,17 +954,17 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.8.2", "version": "1.10.3",
"version_normalized": "1.8.2.0", "version_normalized": "1.10.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c" "reference": "5419375b5891add97dc74be71e6c1c34baaddf64"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/c53312ecc575caf07b0e90dee43883fdf90ca67c", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5419375b5891add97dc74be71e6c1c34baaddf64",
"reference": "c53312ecc575caf07b0e90dee43883fdf90ca67c", "reference": "5419375b5891add97dc74be71e6c1c34baaddf64",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -973,7 +973,7 @@
"conflict": { "conflict": {
"phpstan/phpstan-shim": "*" "phpstan/phpstan-shim": "*"
}, },
"time": "2022-07-20T09:57:31+00:00", "time": "2023-02-25T14:47:13+00:00",
"bin": [ "bin": [
"phpstan", "phpstan",
"phpstan.phar" "phpstan.phar"
@ -990,9 +990,13 @@
"MIT" "MIT"
], ],
"description": "PHPStan - PHP Static Analysis Tool", "description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan/issues", "issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.8.2" "source": "https://github.com/phpstan/phpstan/tree/1.10.3"
}, },
"funding": [ "funding": [
{ {
@ -1003,10 +1007,6 @@
"url": "https://github.com/phpstan", "url": "https://github.com/phpstan",
"type": "github" "type": "github"
}, },
{
"url": "https://www.patreon.com/phpstan",
"type": "patreon"
},
{ {
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift" "type": "tidelift"

View File

@ -1,403 +1,403 @@
<?php return array ( <?php return array(
'root' => 'root' => array(
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => '26c67dba776e1e6f8ac40eed70fe79995325863d',
'name' => '__root__', 'name' => '__root__',
),
'versions' =>
array (
'__root__' =>
array (
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'aliases' => 'reference' => 'b7a6c948d078a59739f14de8454e0e7237d0722e',
array ( 'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
), ),
'reference' => '26c67dba776e1e6f8ac40eed70fe79995325863d', 'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'b7a6c948d078a59739f14de8454e0e7237d0722e',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
), ),
'beberlei/assert' => 'beberlei/assert' => array(
array (
'pretty_version' => 'v3.3.2', 'pretty_version' => 'v3.3.2',
'version' => '3.3.2.0', 'version' => '3.3.2.0',
'aliases' =>
array (
),
'reference' => 'cb70015c04be1baee6f5f5c953703347c0ac1655', 'reference' => 'cb70015c04be1baee6f5f5c953703347c0ac1655',
'type' => 'library',
'install_path' => __DIR__ . '/../beberlei/assert',
'aliases' => array(),
'dev_requirement' => false,
), ),
'chillerlan/php-qrcode' => 'chillerlan/php-qrcode' => array(
array (
'pretty_version' => '4.3.4', 'pretty_version' => '4.3.4',
'version' => '4.3.4.0', 'version' => '4.3.4.0',
'aliases' =>
array (
),
'reference' => '2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d', 'reference' => '2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d',
'type' => 'library',
'install_path' => __DIR__ . '/../chillerlan/php-qrcode',
'aliases' => array(),
'dev_requirement' => false,
), ),
'chillerlan/php-settings-container' => 'chillerlan/php-settings-container' => array(
array (
'pretty_version' => '2.1.4', 'pretty_version' => '2.1.4',
'version' => '2.1.4.0', 'version' => '2.1.4.0',
'aliases' =>
array (
),
'reference' => '1beb7df3c14346d4344b0b2e12f6f9a74feabd4a', 'reference' => '1beb7df3c14346d4344b0b2e12f6f9a74feabd4a',
'type' => 'library',
'install_path' => __DIR__ . '/../chillerlan/php-settings-container',
'aliases' => array(),
'dev_requirement' => false,
), ),
'doctrine/instantiator' => 'doctrine/instantiator' => array(
array (
'pretty_version' => '1.4.1', 'pretty_version' => '1.4.1',
'version' => '1.4.1.0', 'version' => '1.4.1.0',
'aliases' =>
array (
),
'reference' => '10dcfce151b967d20fde1b34ae6640712c3891bc', 'reference' => '10dcfce151b967d20fde1b34ae6640712c3891bc',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/instantiator',
'aliases' => array(),
'dev_requirement' => true,
), ),
'j4mie/idiorm' => 'j4mie/idiorm' => array(
array (
'pretty_version' => 'dev-master', 'pretty_version' => 'dev-master',
'version' => 'dev-master', 'version' => 'dev-master',
'aliases' => 'reference' => 'efc8ea06698f53e2c479c7696f2b154c47c3a3cb',
array ( 'type' => 'library',
'install_path' => __DIR__ . '/../j4mie/idiorm',
'aliases' => array(
0 => '9999999-dev', 0 => '9999999-dev',
), ),
'reference' => 'efc8ea06698f53e2c479c7696f2b154c47c3a3cb', 'dev_requirement' => false,
), ),
'mervick/material-design-icons' => 'mervick/material-design-icons' => array(
array (
'pretty_version' => '2.2.0', 'pretty_version' => '2.2.0',
'version' => '2.2.0.0', 'version' => '2.2.0.0',
'aliases' =>
array (
),
'reference' => '635435c8d3df3a6da3241648caf8a65d1c07cc1a', 'reference' => '635435c8d3df3a6da3241648caf8a65d1c07cc1a',
'type' => 'library',
'install_path' => __DIR__ . '/../mervick/material-design-icons',
'aliases' => array(),
'dev_requirement' => false,
), ),
'myclabs/deep-copy' => 'myclabs/deep-copy' => array(
array (
'pretty_version' => '1.11.0', 'pretty_version' => '1.11.0',
'version' => '1.11.0.0', 'version' => '1.11.0.0',
'aliases' =>
array (
),
'reference' => '14daed4296fae74d9e3201d2c4925d1acb7aa614', 'reference' => '14daed4296fae74d9e3201d2c4925d1acb7aa614',
'type' => 'library',
'install_path' => __DIR__ . '/../myclabs/deep-copy',
'aliases' => array(),
'dev_requirement' => true,
), ),
'nikic/php-parser' => 'nikic/php-parser' => array(
array (
'pretty_version' => 'v4.14.0', 'pretty_version' => 'v4.14.0',
'version' => '4.14.0.0', 'version' => '4.14.0.0',
'aliases' =>
array (
),
'reference' => '34bea19b6e03d8153165d8f30bba4c3be86184c1', 'reference' => '34bea19b6e03d8153165d8f30bba4c3be86184c1',
'type' => 'library',
'install_path' => __DIR__ . '/../nikic/php-parser',
'aliases' => array(),
'dev_requirement' => true,
), ),
'paragonie/constant_time_encoding' => 'paragonie/constant_time_encoding' => array(
array (
'pretty_version' => 'v2.6.3', 'pretty_version' => 'v2.6.3',
'version' => '2.6.3.0', 'version' => '2.6.3.0',
'aliases' =>
array (
),
'reference' => '58c3f47f650c94ec05a151692652a868995d2938', 'reference' => '58c3f47f650c94ec05a151692652a868995d2938',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/constant_time_encoding',
'aliases' => array(),
'dev_requirement' => false,
), ),
'phar-io/manifest' => 'phar-io/manifest' => array(
array (
'pretty_version' => '2.0.3', 'pretty_version' => '2.0.3',
'version' => '2.0.3.0', 'version' => '2.0.3.0',
'aliases' =>
array (
),
'reference' => '97803eca37d319dfa7826cc2437fc020857acb53', 'reference' => '97803eca37d319dfa7826cc2437fc020857acb53',
'type' => 'library',
'install_path' => __DIR__ . '/../phar-io/manifest',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phar-io/version' => 'phar-io/version' => array(
array (
'pretty_version' => '3.2.1', 'pretty_version' => '3.2.1',
'version' => '3.2.1.0', 'version' => '3.2.1.0',
'aliases' =>
array (
),
'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74', 'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74',
'type' => 'library',
'install_path' => __DIR__ . '/../phar-io/version',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpdocumentor/reflection-common' => 'phpdocumentor/reflection-common' => array(
array (
'pretty_version' => '2.2.0', 'pretty_version' => '2.2.0',
'version' => '2.2.0.0', 'version' => '2.2.0.0',
'aliases' =>
array (
),
'reference' => '1d01c49d4ed62f25aa84a747ad35d5a16924662b', 'reference' => '1d01c49d4ed62f25aa84a747ad35d5a16924662b',
'type' => 'library',
'install_path' => __DIR__ . '/../phpdocumentor/reflection-common',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpdocumentor/reflection-docblock' => 'phpdocumentor/reflection-docblock' => array(
array (
'pretty_version' => '5.3.0', 'pretty_version' => '5.3.0',
'version' => '5.3.0.0', 'version' => '5.3.0.0',
'aliases' =>
array (
),
'reference' => '622548b623e81ca6d78b721c5e029f4ce664f170', 'reference' => '622548b623e81ca6d78b721c5e029f4ce664f170',
'type' => 'library',
'install_path' => __DIR__ . '/../phpdocumentor/reflection-docblock',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpdocumentor/type-resolver' => 'phpdocumentor/type-resolver' => array(
array (
'pretty_version' => '1.6.1', 'pretty_version' => '1.6.1',
'version' => '1.6.1.0', 'version' => '1.6.1.0',
'aliases' =>
array (
),
'reference' => '77a32518733312af16a44300404e945338981de3', 'reference' => '77a32518733312af16a44300404e945338981de3',
'type' => 'library',
'install_path' => __DIR__ . '/../phpdocumentor/type-resolver',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpspec/prophecy' => 'phpspec/prophecy' => array(
array (
'pretty_version' => 'v1.15.0', 'pretty_version' => 'v1.15.0',
'version' => '1.15.0.0', 'version' => '1.15.0.0',
'aliases' =>
array (
),
'reference' => 'bbcd7380b0ebf3961ee21409db7b38bc31d69a13', 'reference' => 'bbcd7380b0ebf3961ee21409db7b38bc31d69a13',
'type' => 'library',
'install_path' => __DIR__ . '/../phpspec/prophecy',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpstan/phpstan' => 'phpstan/phpstan' => array(
array ( 'pretty_version' => '1.10.3',
'pretty_version' => '1.8.2', 'version' => '1.10.3.0',
'version' => '1.8.2.0', 'reference' => '5419375b5891add97dc74be71e6c1c34baaddf64',
'aliases' => 'type' => 'library',
array ( 'install_path' => __DIR__ . '/../phpstan/phpstan',
'aliases' => array(),
'dev_requirement' => true,
), ),
'reference' => 'c53312ecc575caf07b0e90dee43883fdf90ca67c', 'phpunit/php-code-coverage' => array(
),
'phpunit/php-code-coverage' =>
array (
'pretty_version' => '9.2.15', 'pretty_version' => '9.2.15',
'version' => '9.2.15.0', 'version' => '9.2.15.0',
'aliases' =>
array (
),
'reference' => '2e9da11878c4202f97915c1cb4bb1ca318a63f5f', 'reference' => '2e9da11878c4202f97915c1cb4bb1ca318a63f5f',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpunit/php-file-iterator' => 'phpunit/php-file-iterator' => array(
array (
'pretty_version' => '3.0.6', 'pretty_version' => '3.0.6',
'version' => '3.0.6.0', 'version' => '3.0.6.0',
'aliases' =>
array (
),
'reference' => 'cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf', 'reference' => 'cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-file-iterator',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpunit/php-invoker' => 'phpunit/php-invoker' => array(
array (
'pretty_version' => '3.1.1', 'pretty_version' => '3.1.1',
'version' => '3.1.1.0', 'version' => '3.1.1.0',
'aliases' =>
array (
),
'reference' => '5a10147d0aaf65b58940a0b72f71c9ac0423cc67', 'reference' => '5a10147d0aaf65b58940a0b72f71c9ac0423cc67',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-invoker',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpunit/php-text-template' => 'phpunit/php-text-template' => array(
array (
'pretty_version' => '2.0.4', 'pretty_version' => '2.0.4',
'version' => '2.0.4.0', 'version' => '2.0.4.0',
'aliases' =>
array (
),
'reference' => '5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28', 'reference' => '5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-text-template',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpunit/php-timer' => 'phpunit/php-timer' => array(
array (
'pretty_version' => '5.0.3', 'pretty_version' => '5.0.3',
'version' => '5.0.3.0', 'version' => '5.0.3.0',
'aliases' =>
array (
),
'reference' => '5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2', 'reference' => '5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/php-timer',
'aliases' => array(),
'dev_requirement' => true,
), ),
'phpunit/phpunit' => 'phpunit/phpunit' => array(
array (
'pretty_version' => '9.5.16', 'pretty_version' => '9.5.16',
'version' => '9.5.16.0', 'version' => '9.5.16.0',
'aliases' =>
array (
),
'reference' => '5ff8c545a50226c569310a35f4fa89d79f1ddfdc', 'reference' => '5ff8c545a50226c569310a35f4fa89d79f1ddfdc',
'type' => 'library',
'install_path' => __DIR__ . '/../phpunit/phpunit',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/cli-parser' => 'sebastian/cli-parser' => array(
array (
'pretty_version' => '1.0.1', 'pretty_version' => '1.0.1',
'version' => '1.0.1.0', 'version' => '1.0.1.0',
'aliases' =>
array (
),
'reference' => '442e7c7e687e42adc03470c7b668bc4b2402c0b2', 'reference' => '442e7c7e687e42adc03470c7b668bc4b2402c0b2',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/cli-parser',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/code-unit' => 'sebastian/code-unit' => array(
array (
'pretty_version' => '1.0.8', 'pretty_version' => '1.0.8',
'version' => '1.0.8.0', 'version' => '1.0.8.0',
'aliases' =>
array (
),
'reference' => '1fc9f64c0927627ef78ba436c9b17d967e68e120', 'reference' => '1fc9f64c0927627ef78ba436c9b17d967e68e120',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/code-unit',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/code-unit-reverse-lookup' => 'sebastian/code-unit-reverse-lookup' => array(
array (
'pretty_version' => '2.0.3', 'pretty_version' => '2.0.3',
'version' => '2.0.3.0', 'version' => '2.0.3.0',
'aliases' =>
array (
),
'reference' => 'ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5', 'reference' => 'ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/comparator' => 'sebastian/comparator' => array(
array (
'pretty_version' => '4.0.6', 'pretty_version' => '4.0.6',
'version' => '4.0.6.0', 'version' => '4.0.6.0',
'aliases' =>
array (
),
'reference' => '55f4261989e546dc112258c7a75935a81a7ce382', 'reference' => '55f4261989e546dc112258c7a75935a81a7ce382',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/comparator',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/complexity' => 'sebastian/complexity' => array(
array (
'pretty_version' => '2.0.2', 'pretty_version' => '2.0.2',
'version' => '2.0.2.0', 'version' => '2.0.2.0',
'aliases' =>
array (
),
'reference' => '739b35e53379900cc9ac327b2147867b8b6efd88', 'reference' => '739b35e53379900cc9ac327b2147867b8b6efd88',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/complexity',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/diff' => 'sebastian/diff' => array(
array (
'pretty_version' => '4.0.4', 'pretty_version' => '4.0.4',
'version' => '4.0.4.0', 'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => '3461e3fccc7cfdfc2720be910d3bd73c69be590d', 'reference' => '3461e3fccc7cfdfc2720be910d3bd73c69be590d',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/diff',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/environment' => 'sebastian/environment' => array(
array (
'pretty_version' => '5.1.4', 'pretty_version' => '5.1.4',
'version' => '5.1.4.0', 'version' => '5.1.4.0',
'aliases' =>
array (
),
'reference' => '1b5dff7bb151a4db11d49d90e5408e4e938270f7', 'reference' => '1b5dff7bb151a4db11d49d90e5408e4e938270f7',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/environment',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/exporter' => 'sebastian/exporter' => array(
array (
'pretty_version' => '4.0.4', 'pretty_version' => '4.0.4',
'version' => '4.0.4.0', 'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => '65e8b7db476c5dd267e65eea9cab77584d3cfff9', 'reference' => '65e8b7db476c5dd267e65eea9cab77584d3cfff9',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/exporter',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/global-state' => 'sebastian/global-state' => array(
array (
'pretty_version' => '5.0.5', 'pretty_version' => '5.0.5',
'version' => '5.0.5.0', 'version' => '5.0.5.0',
'aliases' =>
array (
),
'reference' => '0ca8db5a5fc9c8646244e629625ac486fa286bf2', 'reference' => '0ca8db5a5fc9c8646244e629625ac486fa286bf2',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/global-state',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/lines-of-code' => 'sebastian/lines-of-code' => array(
array (
'pretty_version' => '1.0.3', 'pretty_version' => '1.0.3',
'version' => '1.0.3.0', 'version' => '1.0.3.0',
'aliases' =>
array (
),
'reference' => 'c1c2e997aa3146983ed888ad08b15470a2e22ecc', 'reference' => 'c1c2e997aa3146983ed888ad08b15470a2e22ecc',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/lines-of-code',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/object-enumerator' => 'sebastian/object-enumerator' => array(
array (
'pretty_version' => '4.0.4', 'pretty_version' => '4.0.4',
'version' => '4.0.4.0', 'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => '5c9eeac41b290a3712d88851518825ad78f45c71', 'reference' => '5c9eeac41b290a3712d88851518825ad78f45c71',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/object-enumerator',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/object-reflector' => 'sebastian/object-reflector' => array(
array (
'pretty_version' => '2.0.4', 'pretty_version' => '2.0.4',
'version' => '2.0.4.0', 'version' => '2.0.4.0',
'aliases' =>
array (
),
'reference' => 'b4f479ebdbf63ac605d183ece17d8d7fe49c15c7', 'reference' => 'b4f479ebdbf63ac605d183ece17d8d7fe49c15c7',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/object-reflector',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/recursion-context' => 'sebastian/recursion-context' => array(
array (
'pretty_version' => '4.0.4', 'pretty_version' => '4.0.4',
'version' => '4.0.4.0', 'version' => '4.0.4.0',
'aliases' =>
array (
),
'reference' => 'cd9d8cf3c5804de4341c283ed787f099f5506172', 'reference' => 'cd9d8cf3c5804de4341c283ed787f099f5506172',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/recursion-context',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/resource-operations' => 'sebastian/resource-operations' => array(
array (
'pretty_version' => '3.0.3', 'pretty_version' => '3.0.3',
'version' => '3.0.3.0', 'version' => '3.0.3.0',
'aliases' =>
array (
),
'reference' => '0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8', 'reference' => '0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/resource-operations',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/type' => 'sebastian/type' => array(
array (
'pretty_version' => '2.3.4', 'pretty_version' => '2.3.4',
'version' => '2.3.4.0', 'version' => '2.3.4.0',
'aliases' =>
array (
),
'reference' => 'b8cd8a1c753c90bc1a0f5372170e3e489136f914', 'reference' => 'b8cd8a1c753c90bc1a0f5372170e3e489136f914',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/type',
'aliases' => array(),
'dev_requirement' => true,
), ),
'sebastian/version' => 'sebastian/version' => array(
array (
'pretty_version' => '3.0.2', 'pretty_version' => '3.0.2',
'version' => '3.0.2.0', 'version' => '3.0.2.0',
'aliases' =>
array (
),
'reference' => 'c6c1022351a901512170118436c764e473f6de8c', 'reference' => 'c6c1022351a901512170118436c764e473f6de8c',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/version',
'aliases' => array(),
'dev_requirement' => true,
), ),
'spomky-labs/otphp' => 'spomky-labs/otphp' => array(
array (
'pretty_version' => 'v10.0.3', 'pretty_version' => 'v10.0.3',
'version' => '10.0.3.0', 'version' => '10.0.3.0',
'aliases' =>
array (
),
'reference' => '9784d9f7c790eed26e102d6c78f12c754036c366', 'reference' => '9784d9f7c790eed26e102d6c78f12c754036c366',
'type' => 'library',
'install_path' => __DIR__ . '/../spomky-labs/otphp',
'aliases' => array(),
'dev_requirement' => false,
), ),
'thecodingmachine/safe' => 'thecodingmachine/safe' => array(
array (
'pretty_version' => 'v2.2.2', 'pretty_version' => 'v2.2.2',
'version' => '2.2.2.0', 'version' => '2.2.2.0',
'aliases' =>
array (
),
'reference' => '440284f9592c9df402832452a6871a8b3c48d97e', 'reference' => '440284f9592c9df402832452a6871a8b3c48d97e',
'type' => 'library',
'install_path' => __DIR__ . '/../thecodingmachine/safe',
'aliases' => array(),
'dev_requirement' => false,
), ),
'theseer/tokenizer' => 'theseer/tokenizer' => array(
array (
'pretty_version' => '1.2.1', 'pretty_version' => '1.2.1',
'version' => '1.2.1.0', 'version' => '1.2.1.0',
'aliases' =>
array (
),
'reference' => '34a41e998c2183e22995f158c581e7b5e755ab9e', 'reference' => '34a41e998c2183e22995f158c581e7b5e755ab9e',
'type' => 'library',
'install_path' => __DIR__ . '/../theseer/tokenizer',
'aliases' => array(),
'dev_requirement' => true,
), ),
'webmozart/assert' => 'webmozart/assert' => array(
array (
'pretty_version' => '1.11.0', 'pretty_version' => '1.11.0',
'version' => '1.11.0.0', 'version' => '1.11.0.0',
'aliases' =>
array (
),
'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991', 'reference' => '11cb2199493b2f8a3b53e7f19068fc6aac760991',
'type' => 'library',
'install_path' => __DIR__ . '/../webmozart/assert',
'aliases' => array(),
'dev_requirement' => true,
), ),
), ),
); );

View File

@ -18,9 +18,9 @@ PHPStan focuses on finding errors in your code without actually running it. It c
even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code
can be checked before you run the actual line. can be checked before you run the actual line.
**[Read more about PHPStan in an introductory article »](https://phpstan.org/blog/find-bugs-in-your-code-without-writing-tests)** **[Read more about PHPStan »](https://phpstan.org/)**
**[Try out PHPStan on the on-line playground! »](https://phpstan.org/)** **[Try out PHPStan on the on-line playground! »](https://phpstan.org/try)**
## Sponsors ## Sponsors
@ -46,7 +46,7 @@ can be checked before you run the actual line.
<br> <br>
<a href="https://zol.fr?utm_source=phpstan"><img src="https://i.imgur.com/dzDgd4s.png" alt="ZOL" width="283" height="64"></a> <a href="https://zol.fr?utm_source=phpstan"><img src="https://i.imgur.com/dzDgd4s.png" alt="ZOL" width="283" height="64"></a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a href="https://kocourek.uk/"><img src="https://i.imgur.com/EX29z98.png" alt="Stepan Kocourek" width="294" height="64"></a> <a href="https://www.psyonix.com/"><img src="https://i.imgur.com/p8svxQZ.png" alt="Psyonix" width="254" height="65"></a>
<br> <br>
<a href="https://www.shopware.com/en/"><img src="https://i.imgur.com/L4X5w9s.png" alt="Shopware" width="284" height="64"></a> <a href="https://www.shopware.com/en/"><img src="https://i.imgur.com/L4X5w9s.png" alt="Shopware" width="284" height="64"></a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
@ -60,7 +60,8 @@ can be checked before you run the actual line.
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<a href="https://inviqa.com/"><img src="https://i.imgur.com/G99rj45.png" alt="Inviqa" width="254" height="65"></a> <a href="https://inviqa.com/"><img src="https://i.imgur.com/G99rj45.png" alt="Inviqa" width="254" height="65"></a>
<br> <br>
<a href="https://www.psyonix.com/"><img src="https://i.imgur.com/p8svxQZ.png" alt="Psyonix" width="254" height="65"></a> <a href="https://www.cdn77.com/"><img src="https://i.imgur.com/Oo3wA3m.png" alt="CDN77" width="283" height="64"></a>
[**You can now sponsor my open-source work on PHPStan through GitHub Sponsors.**](https://github.com/sponsors/ondrejmirtes) [**You can now sponsor my open-source work on PHPStan through GitHub Sponsors.**](https://github.com/sponsors/ondrejmirtes)
@ -85,7 +86,6 @@ PHPStan Pro is a paid add-on on top of open-source PHPStan Static Analysis Tool
* Web UI for browsing found errors, you can click and open your editor of choice on the offending line. * Web UI for browsing found errors, you can click and open your editor of choice on the offending line.
* Continuous analysis (watch mode): scans changed files in the background, refreshes the UI automatically. * Continuous analysis (watch mode): scans changed files in the background, refreshes the UI automatically.
* Interactive fixer: lets you choose the right fix for found errors :blush:
Try it on PHPStan 0.12.45 or later by running it with the `--pro` option. You can create an account either by following the on-screen instructions, or by visiting [account.phpstan.com](https://account.phpstan.com/). Try it on PHPStan 0.12.45 or later by running it with the `--pro` option. You can create an account either by following the on-screen instructions, or by visiting [account.phpstan.com](https://account.phpstan.com/).

View File

@ -21,8 +21,8 @@ final class PharAutoloader
if (self::$composerAutoloader === null) { if (self::$composerAutoloader === null) {
self::$composerAutoloader = require 'phar://' . __DIR__ . '/phpstan.phar/vendor/autoload.php'; self::$composerAutoloader = require 'phar://' . __DIR__ . '/phpstan.phar/vendor/autoload.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/clue/block-react/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/async/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-stream/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-stream/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-timer/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-timer/src/functions_include.php';
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise/src/functions_include.php';

View File

@ -2,6 +2,7 @@
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"description": "PHPStan - PHP Static Analysis Tool", "description": "PHPStan - PHP Static Analysis Tool",
"license": ["MIT"], "license": ["MIT"],
"keywords": ["dev", "static analysis"],
"require": { "require": {
"php": "^7.2|^8.0" "php": "^7.2|^8.0"
}, },

Binary file not shown.

View File

@ -1,16 +1,16 @@
-----BEGIN PGP SIGNATURE----- -----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE0yaA1ZV9xxFr4pwUzxoQjQ565yAFAmLX0XwACgkQzxoQjQ56 iQIzBAABCgAdFiEE0yaA1ZV9xxFr4pwUzxoQjQ565yAFAmP6H1oACgkQzxoQjQ56
5yAmDw/+KPoaY+vsYXrYjbNcYmXyH+9ca8Yl1WkgqMOAAiojQBAdN+PIeXqdM4P1 5yCVhxAAlXqhfvKCITSAJjkaIhncEBHpT5Ogby65BX1O5+SkKyc/OO7JnjlN8cLB
ODJq7SGPqhR5j9dK3k4wxvTkmAVlFHWCbOQS0eCueoV2s0w0Sg9xkut7zTMwwBTp DKbJpdc8P2Dbfo/uBHVR5YiNX5CWYzgdMzCpLQI20Ffa7qkeRbA5ZS2AfdIfLqhD
I+0TbU9W7DdnDk6k6xoNCNhT8OYpMElmwhS9nChoY2+cwdWJNZ0Lr5JeXwvb5R7W PswlQIYxg0F3lj0L+joTxfBiHgmR5wnOUx1sLXnEbqGtFzzGstPGDg9gjKKh5EnO
YnHUqO/zTIAjwJNdKwb27J9szwL3GQ4nB9gSoKXCFQTIONszfMYNwboy3VlD8m/F g207jrm1we05+k3kKzr233/ENvQD6bBWLr2yngyMoirOuDpurAuPjkBL7vN6PDbx
irh9ZZeZsMlCpEO9Rsqx4QyOvbLy0D2jEOKNYzXPQea9dx9gQvDiWJrVETsQBwkc DlyGDazsgU/R7aE2FtqmFC8KIU/BgnpbCSWTHBEJAqtncHbwTgneyEm+88lT6cUt
iWJ/HJVhs3ng7iyUfMr8VAPn7rf+3fSqYeQUs5Z5/ubqABF2ZI9/4DIPGsXtqH7q YM72Rh1+QTvS3WgJ/q7AQRik2aE83Kz2rc3XOl1ADd45J3Oan1rqQXdnKtH6qZeT
b5OTOvSucvRpsxudnJElKWhkCjZUyjYzBqGdZ6erCh+GTiM4uFNB/en/QObfHmP2 wMk6PE8SQPJ8NMHy3LT048PfMOakAtgBL5xonbVqX0IFAvKAMRijwBCNKat2yRsB
z4D41Dk9peLxstqlrwoLL0sJCHR4fQZFvPKrQ6sKlTKliu1zBc0HcEqq3BFalKF+ 0VGnoeH7klf1fSSo0xOD20Pd0uIILAWZlBjb2e/1NRytFQkokAr1bsLD/VJHiNYh
XO2PT1QVNXx/9IqOruKm+M5siY63VKDwE/DXJZZTUPz7GN/Cr9j5Jz3pjq8jL0CR lPQ4OztCT/uumJOAgTKm10T56CK1u0VxhIVtQ+/NkTvAwrmpX0KKqbYPP/1hZqVO
e4D+sEu7xXzlJa7MFqg2odZhLTRYuM/DHZCj3cOyQGlYUQ+tZMGsAiix9JyO7XhB vFaWJR4iLFNt+il7dlv56jmTAneUrGrWkQdVFbz5FVpqz7abIo+/cevbSSndFZCI
1jzEMugtAglPAsmHr0HR/+oz+YDgwQEmQA4VO4l0swCNmjzLITA= K1mn42aFdYXSqVTFpkzeOQeJPPysEYDysFk7ClhE+F48HN7WAtw=
=LM6h =A7BG
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----