Compare commits

...

58 Commits

Author SHA1 Message Date
Andrew Dolgov dc25a9cf68
disable app passwords in the UI if auth_internal is not loaded 2023-06-14 20:19:18 +03:00
Andrew Dolgov a9d8fd8bdc
move af_redditimgur to a separate repo 2023-06-10 08:40:23 +03:00
Andrew Dolgov d43d6f7dff
keep two sets of content-insert size cookies for wide & normal mode 2023-06-01 08:31:10 +03:00
Andrew Dolgov a28d9582e8
public/getUnread: fix PHP8 warning if fresh optional parameter is not given 2023-05-28 21:27:33 +03:00
Andrew Dolgov 718af52a1b Merge branch 'fix-version' into 'master'
Fix version string for unsupported installations

See merge request tt-rss/tt-rss!6
2023-05-27 06:24:35 +00:00
Chih-Hsuan Yen d26309b1e5 Fix version string for unsupported installations
For unsupported installations, $version['commit'] may not be defined,
leading to a warning:

E_WARNING (2)

Undefined array key "commit"
1. classes/config.php(316): ttrss_error_handler(Undefined array key "commit", classes/config.php)
2. prefs.php(173): get_version_html()
2023-05-27 10:58:11 +08:00
Andrew Dolgov 3468317bd3 Merge branch 'master' into 'master'
Update API

See merge request tt-rss/tt-rss!5
2023-05-21 05:21:49 +00:00
defkev af3e9eb4a0 Forgot delimiter 2023-05-21 06:16:36 +02:00
defkev 5bfd18d3e6 Update API
Add site_url property for 9e169dc3aa7c7e30c11d7d3d1bbc4bc66fa39760
2023-05-21 04:55:25 +02:00
Andrew Dolgov a4543de3ac Merge branch 'feature/php8.2' into 'master'
Bump to Alpine 3.18, PHP 8.2

See merge request tt-rss/tt-rss!4
2023-05-15 14:56:45 +00:00
wn_ b1187f0db6 Bump to Alpine 3.18, PHP 8.2 2023-05-15 14:08:11 +00:00
Andrew Dolgov 11946f0148 Update CONTRIBUTING.md 2023-05-09 08:09:20 +00:00
Andrew Dolgov 3de09b44f2
_order_to_override_query: fix custom sort plugins overriding each other 2023-05-06 14:07:10 +03:00
Andrew Dolgov 0578bf8025
add Headlines.default_move_on_expand tunable 2023-04-13 20:15:23 +03:00
Andrew Dolgov 1e90feef0e
fix 881f8805bd behaving improperly 2023-04-13 06:20:04 +03:00
Andrew Dolgov c314bd8742
add APCu & opcache 2023-04-10 20:28:02 +03:00
Andrew Dolgov 103fdd5e60
long year -> short year 2023-04-10 20:11:26 +03:00
Andrew Dolgov 7a54154d45
we don't need BUILD_TIMESTAMP either 2023-04-10 20:06:52 +03:00
Andrew Dolgov 27bd226f2b
move branch to version tooltip 2023-04-10 20:04:38 +03:00
Andrew Dolgov 15c9dbe270
use short sha CI envvar for version.json compatibility 2023-04-10 19:55:20 +03:00
Andrew Dolgov 2420feb91f
no more pointless txt files for version 2023-04-10 19:53:49 +03:00
Andrew Dolgov 8ccea1712e
a bit more diskcache tracing 2023-04-10 18:23:26 +03:00
Andrew Dolgov f3f2e7d043
don't try to pass array to span tags 2023-04-10 18:06:14 +03:00
Andrew Dolgov 6920c44587
better static version 2023-04-10 18:05:13 +03:00
Andrew Dolgov 566d164053
add CI_COMMIT_TIMESTAMP to version-static file 2023-04-10 17:49:42 +03:00
Andrew Dolgov 06aabdf60b
create version_static.txt on build 2023-04-10 17:43:39 +03:00
Andrew Dolgov 4e17fac8b7
fix typo 2023-04-10 07:24:01 +03:00
Andrew Dolgov 7dc83961bd
make jaeger service name configurable 2023-04-10 07:18:36 +03:00
Andrew Dolgov b0fc248c05
add tracing to UrlHelper 2023-04-09 23:26:51 +03:00
Andrew Dolgov 6ec01203a1
add php81-sockets 2023-04-09 23:19:21 +03:00
Andrew Dolgov 44137342a6
a bit more tracing 2023-04-09 22:36:37 +03:00
Andrew Dolgov fd5e0f98c4
even more tracing 2023-04-09 22:31:42 +03:00
Andrew Dolgov e18295a364
more tracing 2023-04-09 22:15:16 +03:00
Andrew Dolgov d68c736e47
Tracer: rework options to tags 2023-04-09 21:37:17 +03:00
Andrew Dolgov 6418157ccf
add icatchall 2023-04-09 21:30:04 +03:00
Andrew Dolgov d7c070b22b
make phpstan happy 2023-04-09 21:29:16 +03:00
Andrew Dolgov c1b3c99667
some tracer class fixes / unhardcode jaeger IP 2023-04-09 21:20:35 +03:00
Andrew Dolgov 8f3646a9c9
exp: jaeger tracing 2023-04-09 20:50:33 +03:00
Andrew Dolgov a37eab2610
Revert "docker: point xaccel plugin url to gitlab"
This reverts commit c4edc93182.
2023-04-09 15:27:03 +03:00
Andrew Dolgov 431b8778ed
bump dojo to 1.16.5 2023-04-09 11:14:46 +03:00
Andrew Dolgov c4edc93182
docker: point xaccel plugin url to gitlab 2023-04-09 10:37:41 +03:00
Andrew Dolgov 028bb846d9
Merge branch 'master' of gitlab.tt-rss.org:tt-rss/tt-rss 2023-04-09 10:31:20 +03:00
Andrew Dolgov 31ef788e02
add window.requestIdleCallback polyfill for safari 2023-04-09 10:29:42 +03:00
fox ae27d51197 Merge branch 'master' into 'master'
Update config.php: $ttrss_version in private function _get_version() replaced by $this->version

See merge request tt-rss/tt-rss!2
2023-04-06 19:58:03 +00:00
Jan Pieter Kunst a60c833ee4 Update config.php: $ttrss_version in private function _get_version() replaced by $this->version 2023-04-06 18:36:36 +00:00
Andrew Dolgov 0fcc2d1d66
CI: phpstan: check for PHPDOC_DEPLOY_SSH_KEY 2023-04-06 20:27:04 +03:00
Andrew Dolgov 152545b3c9
phpstan task: analyze only modified files 2023-04-06 20:25:48 +03:00
Andrew Dolgov 881f8805bd
filters: allow matching on tags if there are no tags 2023-04-06 20:25:24 +03:00
Andrew Dolgov 53bd56894d
make phpstan happy 2023-04-06 15:55:00 +03:00
Andrew Dolgov af5c64045b
add simple autocompleter for tags 2023-04-06 15:51:09 +03:00
Andrew Dolgov 0fcc715069
add branch targets 2023-04-05 08:51:35 +03:00
Andrew Dolgov fd684dae29
remove .gitea workflows 2023-04-03 17:48:42 +03:00
Andrew Dolgov 58450486a0
dockerfile cleanup 2023-04-03 17:47:03 +03:00
Andrew Dolgov 4a60652be9
fix eslint task extending .phpunit 2023-04-02 12:32:28 +03:00
Andrew Dolgov 521ac622e4
Merge branch 'master' of gitlab.tt-rss.org:tt-rss/tt-rss 2023-04-02 12:31:00 +03:00
Andrew Dolgov f0e0f7d5f5
use common CI template 2023-04-02 12:30:33 +03:00
fox c2d8ba5973 Merge branch 'bugfix/editfeed-focus' into 'master'
Make the edit feed dialog's 'remove icon' button a regular button.

See merge request tt-rss/tt-rss!1
2023-04-01 19:13:13 +00:00
wn_ 807f914338 Make edit feed dialog's 'remove icon' button a regular button.
Previously it was of type 'submit', and hitting Enter anywhere in the modal triggered its action (rather than the other submit button for saving).
2023-04-01 14:22:33 +00:00
311 changed files with 25391 additions and 2105 deletions

View File

@ -1,24 +1,36 @@
FROM registry.fakecake.org/docker.io/alpine:3.17
FROM registry.fakecake.org/docker.io/alpine:3.18
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 && \
RUN apk add --no-cache dcron php82 php82-fpm php82-phar php82-sockets php82-pecl-apcu \
php82-pdo php82-gd php82-pgsql php82-pdo_pgsql php82-xmlwriter php82-opcache \
php82-mbstring php82-intl php82-xml php82-curl php82-simplexml \
php82-session php82-tokenizer php82-dom php82-fileinfo php82-ctype \
php82-json php82-iconv php82-pcntl php82-posix php82-zip php82-exif \
php82-openssl git postgresql-client sudo php82-pecl-xdebug rsync tzdata && \
sed -i 's/\(memory_limit =\) 128M/\1 256M/' /etc/php82/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 && \
/etc/php82/php-fpm.d/www.conf && \
mkdir -p /var/www ${SCRIPT_ROOT}/config.d
ARG CI_COMMIT_BRANCH
ENV CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH}
ARG CI_COMMIT_SHORT_SHA
ENV CI_COMMIT_SHORT_SHA=${CI_COMMIT_SHORT_SHA}
ARG CI_COMMIT_TIMESTAMP
ENV CI_COMMIT_TIMESTAMP=${CI_COMMIT_TIMESTAMP}
ARG CI_COMMIT_SHA
ENV CI_COMMIT_SHA=${CI_COMMIT_SHA}
ADD --chmod=0755 startup.sh ${SCRIPT_ROOT}
ADD --chmod=0755 updater.sh ${SCRIPT_ROOT}
ADD --chmod=0755 dcron.sh ${SCRIPT_ROOT}
@ -29,13 +41,7 @@ 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
@ -75,7 +81,7 @@ ENV TTRSS_DB_HOST="db"
ENV TTRSS_DB_PORT="5432"
ENV TTRSS_MYSQL_CHARSET="UTF8"
ENV TTRSS_PHP_EXECUTABLE="/usr/bin/php81"
ENV TTRSS_PHP_EXECUTABLE="/usr/bin/php82"
ENV TTRSS_PLUGINS="auth_internal, note, nginx_xaccel"
CMD ${SCRIPT_ROOT}/startup.sh

View File

@ -61,7 +61,7 @@ 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
/var/log/php82
if [ -z "$TTRSS_NO_STARTUP_PLUGIN_UPDATES" ]; then
echo updating all local plugins...
@ -100,7 +100,7 @@ if [ ! -z "${TTRSS_XDEBUG_ENABLED}" ]; then
fi
echo enabling xdebug with the following parameters:
env | grep TTRSS_XDEBUG
cat > /etc/php81/conf.d/50_xdebug.ini <<EOF
cat > /etc/php82/conf.d/50_xdebug.ini <<EOF
zend_extension=xdebug.so
xdebug.mode=develop,trace,debug
xdebug.start_with_request = yes
@ -110,17 +110,17 @@ EOF
fi
sed -i.bak "s/^\(memory_limit\) = \(.*\)/\1 = ${PHP_WORKER_MEMORY_LIMIT}/" \
/etc/php81/php.ini
/etc/php82/php.ini
sed -i.bak "s/^\(pm.max_children\) = \(.*\)/\1 = ${PHP_WORKER_MAX_CHILDREN}/" \
/etc/php81/php-fpm.d/www.conf
/etc/php82/php-fpm.d/www.conf
sudo -Eu app php81 $DST_DIR/update.php --update-schema=force-yes
sudo -Eu app php82 $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"
sudo -Eu app php82 $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
if sudo -Eu app php82 $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 "*****************************************************************************"
@ -128,17 +128,17 @@ else
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"
sudo -Eu app php82 $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"
sudo -Eu app php82 $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\""
sudo -Eu app /bin/sh -c "php82 $DST_DIR/update.php --user-exists $AUTO_CREATE_USER ||
php82 $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
@ -150,4 +150,4 @@ unset AUTO_CREATE_USER_PASS
touch $DST_DIR/.app_is_ready
exec /usr/sbin/php-fpm81 --nodaemonize --force-stderr
exec /usr/sbin/php-fpm82 --nodaemonize --force-stderr

View File

@ -21,7 +21,7 @@ while ! pg_isready -h $TTRSS_DB_HOST -U $TTRSS_DB_USER; do
done
sed -i.bak "s/^\(memory_limit\) = \(.*\)/\1 = ${PHP_WORKER_MEMORY_LIMIT}/" \
/etc/php81/php.ini
/etc/php82/php.ini
DST_DIR=/var/www/html/tt-rss
@ -30,4 +30,4 @@ while [ ! -s $DST_DIR/config.php -a -e $DST_DIR/.app_is_ready ]; do
sleep 3
done
sudo -E -u app /usr/bin/php81 /var/www/html/tt-rss/update_daemon2.php
sudo -E -u app /usr/bin/php82 /var/www/html/tt-rss/update_daemon2.php

View File

@ -1,123 +0,0 @@
# 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

@ -1,33 +0,0 @@
# 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

View File

@ -2,145 +2,55 @@ 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}
variables:
ESLINT_PATHS: js plugins
- 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}
include:
- project: 'ci/ci-templates'
ref: master
file: .ci-build-docker.yml
- project: 'ci/ci-templates'
ref: master
file: .ci-lint-common.yml
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
extends: .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
extends: .eslint
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
extends: .phpstan
ttrss-web-nginx:
extends: .build-master
variables:
BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/web-nginx
ttrss-web-nginx:branch:
extends: .build-branch
variables:
BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/web-nginx
ttrss-fpm-pgsql-static:
extends: .build-master
variables:
BUILD_CONTEXT: ${CI_PROJECT_DIR}/.docker/app
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
ttrss-fpm-pgsql-static:branch:
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"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "web" && $PHPDOC_DEPLOY_SSH_KEY != null
changes:
- '**/*.php'
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $PHPDOC_DEPLOY_SSH_KEY != null
when: manual
script:
- php81 /phpDocumentor.phar -d classes -d include -t phpdoc --visibility=public

View File

@ -1,12 +1,15 @@
## Contributing code the right way
TLDR: it works like Github.
TLDR: it works *almost* like Github.
Due to spam, new Gitlab users are set to [external](https://docs.gitlab.com/ee/user/admin_area/external_users.html). In order to do anything, you'll need to ask for your account to be promoted. Sorry for the inconvenience.
1. Register on the [development website](https://dev.tt-rss.org);
2. Fork the repository you're interested in;
3. Do the needful;
4. Verify that your changes pass through PHPStan (see below);
4. File a PR against master branch;
2. Post on the forums asking for your account to be promoted;
3. Fork the repository you're interested in;
4. Do the needful;
5. Verify that your changes pass through PHPStan (see below);
6. File a PR against master branch;
If you have any other questions, see this [forum thread](https://discourse.tt-rss.org/t/how-to-contribute-code-via-pull-requests-on-git-tt-rss-org/1850).

View File

@ -30,11 +30,16 @@
$op = (string)clean($op);
$method = (string)clean($method);
$scope = Tracer::start(__FILE__, ['tags' => json_encode($_REQUEST)]);
startup_gettext();
$script_started = microtime(true);
if (!init_plugins()) return;
if (!init_plugins()) {
$scope->close();
return;
}
header("Content-Type: text/json; charset=utf-8");
@ -46,6 +51,9 @@
if (!\Sessions\validate_session()) {
header("Content-Type: text/json");
print Errors::to_json(Errors::E_UNAUTHORIZED);
$scope->getSpan()->setTag('error', Errors::E_UNAUTHORIZED);
$scope->close();
return;
}
UserHelper::load_user_plugins($_SESSION["uid"]);
@ -53,6 +61,9 @@
if (Config::is_migration_needed()) {
print Errors::to_json(Errors::E_SCHEMA_MISMATCH);
$scope->getSpan()->setTag('error', Errors::E_SCHEMA_MISMATCH);
$scope->close();
return;
}
@ -114,10 +125,14 @@
user_error("Refusing to invoke method $method of handler $op which starts with underscore.", E_USER_WARNING);
header("Content-Type: text/json");
print Errors::to_json(Errors::E_UNAUTHORIZED);
$scope->getSpan()->setTag('error', Errors::E_UNAUTHORIZED);
$scope->close();
return;
}
if ($override) {
/** @var Plugin|IHandler|ICatchall $handler */
$handler = $override;
} else {
$reflection = new ReflectionClass($op);
@ -125,10 +140,18 @@
}
if (implements_interface($handler, 'IHandler')) {
$h_scope = Tracer::start("construct/$op");
$handler->__construct($_REQUEST);
$h_scope->close();
if (validate_csrf($csrf_token) || $handler->csrf_ignore($method)) {
if ($handler->before($method)) {
$b_scope = Tracer::start("before/$method");
$before = $handler->before($method);
$b_scope->close();
if ($before) {
$m_scope = Tracer::start("method/$method");
if ($method && method_exists($handler, $method)) {
$reflection = new ReflectionMethod($handler, $method);
@ -137,6 +160,8 @@
} else {
user_error("Refusing to invoke method $method of handler $op which has required parameters.", E_USER_WARNING);
header("Content-Type: text/json");
$m_scope->getSpan()->setTag('error', Errors::E_UNAUTHORIZED);
print Errors::to_json(Errors::E_UNAUTHORIZED);
}
} else {
@ -144,20 +169,34 @@
$handler->catchall($method);
} else {
header("Content-Type: text/json");
$m_scope->getSpan()->setTag('error', Errors::E_UNKNOWN_METHOD);
print Errors::to_json(Errors::E_UNKNOWN_METHOD, ["info" => get_class($handler) . "->$method"]);
}
}
$m_scope->close();
$a_scope = Tracer::start("after/$method");
$handler->after();
$a_scope->close();
$scope->close();
return;
} else {
header("Content-Type: text/json");
print Errors::to_json(Errors::E_UNAUTHORIZED);
$scope->getSpan()->setTag('error', Errors::E_UNAUTHORIZED);
$scope->close();
return;
}
} else {
user_error("Refusing to invoke method $method of handler $op with invalid CSRF token.", E_USER_WARNING);
header("Content-Type: text/json");
print Errors::to_json(Errors::E_UNAUTHORIZED);
$scope->getSpan()->setTag('error', Errors::E_UNAUTHORIZED);
$scope->close();
return;
}
}
@ -166,4 +205,5 @@
header("Content-Type: text/json");
print Errors::to_json(Errors::E_UNKNOWN_METHOD, [ "info" => (isset($handler) ? get_class($handler) : "UNKNOWN:".$op) . "->$method"]);
?>
$scope->getSpan()->setTag('error', Errors::E_UNKNOWN_METHOD);
$scope->close();

View File

@ -1,7 +1,7 @@
<?php
class API extends Handler {
const API_LEVEL = 20;
const API_LEVEL = 21;
const STATUS_OK = 0;
const STATUS_ERR = 1;
@ -844,6 +844,8 @@ class API extends Handler {
$headline_row["note"] = $line["note"];
$headline_row["lang"] = $line["lang"];
$headline_row["site_url"] = $line["site_url"];
if ($show_content) {
$hook_object = ["headline" => &$headline_row];

View File

@ -239,8 +239,7 @@ class Article extends Handler_Protected {
print json_encode(["id" => (int)$id, "tags" => $this->_get_tags($id)]);
}
/*function completeTags() {
function completeTags(): void {
$search = clean($_REQUEST["search"]);
$sth = $this->pdo->prepare("SELECT DISTINCT tag_name FROM ttrss_tags
@ -250,12 +249,14 @@ class Article extends Handler_Protected {
$sth->execute([$_SESSION['uid'], "$search%"]);
print "<ul>";
$results = [];
while ($line = $sth->fetch()) {
print "<li>" . $line["tag_name"] . "</li>";
array_push($results, $line["tag_name"]);
}
print "</ul>";
}*/
print json_encode($results);
}
function assigntolabel(): void {
$this->_label_ops(true);
@ -297,6 +298,8 @@ class Article extends Handler_Protected {
* @return array{'formatted': string, 'entries': array<int, array<string, mixed>>}
*/
static function _format_enclosures(int $id, bool $always_display_enclosures, string $article_content, bool $hide_images = false): array {
$scope = Tracer::start(__METHOD__);
$enclosures = self::_get_enclosures($id);
$enclosures_formatted = "";
@ -323,6 +326,7 @@ class Article extends Handler_Protected {
$enclosures_formatted, $enclosures, $id, $always_display_enclosures, $article_content, $hide_images);
if (!empty($enclosures_formatted)) {
$scope->close();
return [
'formatted' => $enclosures_formatted,
'entries' => []
@ -366,6 +370,7 @@ class Article extends Handler_Protected {
}
}
$scope->close();
return $rv;
}
@ -373,6 +378,7 @@ class Article extends Handler_Protected {
* @return array<int, string>
*/
static function _get_tags(int $id, int $owner_uid = 0, ?string $tag_cache = null): array {
$scope = Tracer::start(__METHOD__);
$a_id = $id;
@ -421,6 +427,7 @@ class Article extends Handler_Protected {
$sth->execute([$tags_str, $id, $owner_uid]);
}
$scope->close();
return $tags;
}
@ -515,6 +522,8 @@ class Article extends Handler_Protected {
* @return array<int, array<int, int|string>>
*/
static function _get_labels(int $id, ?int $owner_uid = null): array {
$scope = Tracer::start(__METHOD__, []);
$rv = array();
if (!$owner_uid) $owner_uid = $_SESSION["uid"];
@ -560,6 +569,8 @@ class Article extends Handler_Protected {
else
Labels::update_cache($owner_uid, $id, array("no-labels" => 1));
$scope->close();
return $rv;
}
@ -570,6 +581,7 @@ class Article extends Handler_Protected {
* @return array<int, Article::ARTICLE_KIND_*|string>
*/
static function _get_image(array $enclosures, string $content, string $site_url, array $headline) {
$scope = Tracer::start(__METHOD__);
$article_image = "";
$article_stream = "";
@ -648,6 +660,8 @@ class Article extends Handler_Protected {
if ($article_stream && $cache->exists(sha1($article_stream)))
$article_stream = $cache->get_url(sha1($article_stream));
$scope->close();
return [$article_image, $article_stream, $article_kind];
}
@ -661,6 +675,8 @@ class Article extends Handler_Protected {
if (count($article_ids) == 0)
return [];
$scope = Tracer::start(__METHOD__);
$entries = ORM::for_table('ttrss_entries')
->table_alias('e')
->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue')
@ -680,6 +696,8 @@ class Article extends Handler_Protected {
}
}
$scope->close();
return array_unique($rv);
}
@ -691,6 +709,8 @@ class Article extends Handler_Protected {
if (count($article_ids) == 0)
return [];
$scope = Tracer::start(__METHOD__);
$entries = ORM::for_table('ttrss_entries')
->table_alias('e')
->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue')
@ -703,6 +723,8 @@ class Article extends Handler_Protected {
array_push($rv, $entry->feed_id);
}
$scope->close();
return array_unique($rv);
}
}

View File

@ -192,6 +192,12 @@ class Config {
/** delay updates for this feed if received HTTP 429 (Too Many Requests) for this amount of seconds (base value, actual delay is base...base*2) */
const HTTP_429_THROTTLE_INTERVAL = "HTTP_429_THROTTLE_INTERVAL";
/** host running Jaeger collector to receive traces (disabled if empty) */
const JAEGER_REPORTING_HOST = "JAEGER_REPORTING_HOST";
/** Jaeger service name */
const JAEGER_SERVICE_NAME = "JAEGER_SERVICE_NAME";
/** default values for all global configuration options */
private const _DEFAULTS = [
Config::DB_TYPE => [ "pgsql", Config::T_STRING ],
@ -249,6 +255,8 @@ class Config {
Config::HTTP_USER_AGENT => [ 'Tiny Tiny RSS/%s (https://tt-rss.org/)',
Config::T_STRING ],
Config::HTTP_429_THROTTLE_INTERVAL => [ 3600, Config::T_INT ],
Config::JAEGER_REPORTING_HOST => [ "", Config::T_STRING ],
Config::JAEGER_SERVICE_NAME => [ "tt-rss", Config::T_STRING ],
];
/** @var Config|null */
@ -303,7 +311,11 @@ class Config {
static function get_version_html() : string {
$version = self::get_version(false);
return sprintf("<span title=\"%s\">%s</span>", date("Y-m-d H:i:s", ($version['timestamp'] ?? 0)), $version['version']);
return sprintf("<span title=\"%s\n%s\n%s\">%s</span>",
date("Y-m-d H:i:s", ($version['timestamp'] ?? 0)),
$version['commit'] ?? '',
$version['branch'] ?? '',
$version['version']);
}
/**
@ -315,8 +327,16 @@ class Config {
if (empty($this->version)) {
$this->version["status"] = -1;
if (PHP_OS === "Darwin") {
$ttrss_version["version"] = "UNKNOWN (Unsupported, Darwin)";
if (getenv("CI_COMMIT_SHORT_SHA") && getenv("CI_COMMIT_TIMESTAMP")) {
$this->version["branch"] = getenv("CI_COMMIT_BRANCH");
$this->version["timestamp"] = strtotime(getenv("CI_COMMIT_TIMESTAMP"));
$this->version["version"] = sprintf("%s-%s", date("y.m", $this->version["timestamp"]), getenv("CI_COMMIT_SHORT_SHA"));
$this->version["commit"] = getenv("CI_COMMIT_SHORT_SHA");
$this->version["status"] = 0;
} else if (PHP_OS === "Darwin") {
$this->version["version"] = "UNKNOWN (Unsupported, Darwin)";
} else if (file_exists("$root_dir/version_static.txt")) {
$this->version["version"] = trim(file_get_contents("$root_dir/version_static.txt")) . " (Unsupported)";
} else if (ini_get("open_basedir")) {
@ -352,6 +372,7 @@ class Config {
$rv = [
"status" => -1,
"version" => "",
"branch" => "",
"commit" => "",
"timestamp" => 0,
];

View File

@ -145,6 +145,7 @@ class Counters {
* @return array<int, array<string, int|string>>
*/
private static function get_feeds(array $feed_ids = null): array {
$scope = Tracer::start(__METHOD__);
$ret = [];
@ -211,6 +212,8 @@ class Counters {
}
$scope->close();
return $ret;
}
@ -218,6 +221,8 @@ class Counters {
* @return array<int, array<string, int|string>>
*/
private static function get_global(): array {
$scope = Tracer::start(__METHOD__);
$ret = [
[
"id" => "global-unread",
@ -234,6 +239,8 @@ class Counters {
"counter" => $subcribed_feeds
]);
$scope->close();
return $ret;
}
@ -241,6 +248,7 @@ class Counters {
* @return array<int, array<string, int|string>>
*/
private static function get_virt(): array {
$scope = Tracer::start(__METHOD__);
$ret = [];
@ -287,6 +295,7 @@ class Counters {
}
}
$scope->close();
return $ret;
}
@ -295,6 +304,7 @@ class Counters {
* @return array<int, array<string, int|string>>
*/
static function get_labels(array $label_ids = null): array {
$scope = Tracer::start(__METHOD__);
$ret = [];
@ -346,6 +356,7 @@ class Counters {
array_push($ret, $cv);
}
$scope->close();
return $ret;
}
}

View File

@ -2,6 +2,7 @@
class Digest
{
static function send_headlines_digests(): void {
$scope = Tracer::start(__METHOD__);
$user_limit = 15; // amount of users to process (e.g. emails to send out)
$limit = 1000; // maximum amount of headlines to include
@ -75,6 +76,8 @@ class Digest
}
}
}
$scope->close();
Debug::log("All done.");
}

View File

@ -221,7 +221,11 @@ class DiskCache implements Cache_Adapter {
}
public function remove(string $filename): bool {
return $this->adapter->remove($filename);
$scope = Tracer::start(__METHOD__, ['filename' => $filename]);
$rc = $this->adapter->remove($filename);
$scope->close();
return $rc;
}
public function set_dir(string $dir) : void {
@ -244,14 +248,22 @@ class DiskCache implements Cache_Adapter {
}
public function exists(string $filename): bool {
return $this->adapter->exists(basename($filename));
$scope = Tracer::start(__METHOD__, ['filename' => $filename]);
$rc = $this->adapter->exists(basename($filename));
$scope->close();
return $rc;
}
/**
* @return int|false -1 if the file doesn't exist, false if an error occurred, size in bytes otherwise
*/
public function get_size(string $filename) {
return $this->adapter->get_size(basename($filename));
$scope = Tracer::start(__METHOD__, ['filename' => $filename]);
$rc = $this->adapter->get_size(basename($filename));
$scope->close();
return $rc;
}
/**
@ -260,7 +272,11 @@ class DiskCache implements Cache_Adapter {
* @return int|false Bytes written or false if an error occurred.
*/
public function put(string $filename, $data) {
return $this->adapter->put(basename($filename), $data);
$scope = Tracer::start(__METHOD__);
$rc = $this->adapter->put(basename($filename), $data);
$scope->close();
return $rc;
}
/** @deprecated we can't assume cached files are local, and other storages
@ -304,11 +320,16 @@ class DiskCache implements Cache_Adapter {
}
public function send(string $filename) {
$scope = Tracer::start(__METHOD__, ['filename' => $filename]);
$filename = basename($filename);
if (!$this->exists($filename)) {
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
echo "File not found.";
$scope->getSpan()->setTag('error', '404 not found');
$scope->close();
return false;
}
@ -317,6 +338,9 @@ class DiskCache implements Cache_Adapter {
if (($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '') == $gmt_modified || ($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') == $file_mtime) {
header('HTTP/1.1 304 Not Modified');
$scope->getSpan()->setTag('error', '304 not modified');
$scope->close();
return false;
}
@ -334,6 +358,9 @@ class DiskCache implements Cache_Adapter {
header("Content-type: text/plain");
print "Stored file has disallowed content type ($mimetype)";
$scope->getSpan()->setTag('error', '400 disallowed content type');
$scope->close();
return false;
}
@ -355,7 +382,13 @@ class DiskCache implements Cache_Adapter {
header_remove("Pragma");
return $this->adapter->send($filename);
$scope->getSpan()->setTag('mimetype', $mimetype);
$rc = $this->adapter->send($filename);
$scope->close();
return $rc;
}
public function get_full_path(string $filename): string {
@ -384,8 +417,14 @@ class DiskCache implements Cache_Adapter {
// plugins work on original source URLs used before caching
// NOTE: URLs should be already absolutized because this is called after sanitize()
static public function rewrite_urls(string $str): string {
$scope = Tracer::start(__METHOD__);
$res = trim($str);
if (!$res) return '';
if (!$res) {
$scope->close();
return '';
}
$doc = new DOMDocument();
if (@$doc->loadHTML('<?xml encoding="UTF-8">' . $res)) {
@ -397,6 +436,8 @@ class DiskCache implements Cache_Adapter {
$need_saving = false;
foreach ($entries as $entry) {
$e_scope = Tracer::start('entry', ['tagName' => $entry->tagName]);
foreach (array('src', 'poster') as $attr) {
if ($entry->hasAttribute($attr)) {
$url = $entry->getAttribute($attr);
@ -428,6 +469,8 @@ class DiskCache implements Cache_Adapter {
$entry->setAttribute("srcset", RSSUtils::encode_srcset($matches));
}
$e_scope->close();
}
if ($need_saving) {
@ -437,6 +480,9 @@ class DiskCache implements Cache_Adapter {
$res = $doc->saveHTML();
}
}
$scope->close();
return $res;
}
}

View File

@ -49,12 +49,6 @@ class Feeds extends Handler_Protected {
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 */
private $viewfeed_timestamp;
/** @var int|float int on 64-bit, float on 32-bit */
private $viewfeed_timestamp_last;
function csrf_ignore(string $method): bool {
$csrf_ignored = array("index");
@ -71,7 +65,7 @@ class Feeds extends Handler_Protected {
$disable_cache = false;
$this->_mark_timestamp("init");
$scope = Tracer::start(__METHOD__, [], func_get_args());
$reply = [];
$rgba_cache = [];
@ -157,8 +151,6 @@ class Feeds extends Handler_Protected {
$qfh_ret = $this->_get_headlines($params);
}
$this->_mark_timestamp("db query");
$vfeed_group_enabled = get_pref(Prefs::VFEED_GROUP_BY_FEED) &&
!(in_array($feed, self::NEVER_GROUP_FEEDS) && !$cat_view);
@ -176,6 +168,8 @@ class Feeds extends Handler_Protected {
$reply['search_query'] = [$search, $search_language];
$reply['vfeed_group_enabled'] = $vfeed_group_enabled;
$p_scope = Tracer::start('plugin_menu_items');
$plugin_menu_items = "";
PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2,
function ($result) use (&$plugin_menu_items) {
@ -208,13 +202,15 @@ class Feeds extends Handler_Protected {
},
$feed, $cat_view, $qfh_ret);
$this->_mark_timestamp("object header");
$p_scope->close();
$a_scope = Tracer::start('articles');
$headlines_count = 0;
if ($result instanceof PDOStatement) {
while ($line = $result->fetch(PDO::FETCH_ASSOC)) {
$this->_mark_timestamp("article start: " . $line["id"] . " " . $line["title"]);
$aa_scope = Tracer::start('article', ['id' => $line['id']]);
++$headlines_count;
@ -232,8 +228,6 @@ class Feeds extends Handler_Protected {
$line, $max_excerpt_length);
}
$this->_mark_timestamp(" hook_query_headlines");
$id = $line["id"];
// frontend doesn't expect pdo returning booleans as strings on mysql
@ -281,8 +275,6 @@ class Feeds extends Handler_Protected {
array_push($topmost_article_ids, $id);
}
$this->_mark_timestamp(" labels");
$line["feed_title"] = $line["feed_title"] ?? "";
$button_doc = new DOMDocument();
@ -312,6 +304,7 @@ class Feeds extends Handler_Protected {
$line);
$line["buttons"] = "";
PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_ARTICLE_BUTTON,
function ($result, $plugin) use (&$line, &$button_doc) {
if ($result && $button_doc->loadXML($result)) {
@ -335,13 +328,9 @@ class Feeds extends Handler_Protected {
},
$line);
$this->_mark_timestamp(" pre-sanitize");
$line["content"] = Sanitizer::sanitize($line["content"],
$line['hide_images'], null, $line["site_url"], $highlight_words, $line["id"]);
$this->_mark_timestamp(" sanitize");
if (!get_pref(Prefs::CDM_EXPANDED)) {
$line["cdm_excerpt"] = "<span class='collapse'>
<i class='material-icons' onclick='return Article.cdmUnsetActive(event)'
@ -352,8 +341,6 @@ class Feeds extends Handler_Protected {
}
}
$this->_mark_timestamp(" pre-enclosures");
if ($line["num_enclosures"] > 0) {
$line["enclosures"] = Article::_format_enclosures($id,
sql_bool_to_bool($line["always_display_enclosures"]),
@ -363,16 +350,12 @@ class Feeds extends Handler_Protected {
$line["enclosures"] = [ 'formatted' => '', 'entries' => [] ];
}
$this->_mark_timestamp(" enclosures");
$line["updated_long"] = TimeHelper::make_local_datetime($line["updated"],true);
$line["updated"] = TimeHelper::make_local_datetime($line["updated"], false, null, false, true);
$line['imported'] = T_sprintf("Imported at %s",
TimeHelper::make_local_datetime($line["date_entered"], false));
$this->_mark_timestamp(" local-datetime");
if ($line["tag_cache"])
$tags = explode(",", $line["tag_cache"]);
else
@ -382,14 +365,12 @@ class Feeds extends Handler_Protected {
//$line["tags"] = Article::_get_tags($line["id"], false, $line["tag_cache"]);
$this->_mark_timestamp(" tags");
$line['has_icon'] = self::_has_icon($feed_id);
//setting feed headline background color, needs to change text color based on dark/light
$fav_color = $line['favicon_avg_color'] ?? false;
$this->_mark_timestamp(" pre-color");
$c_scope = Tracer::start('colors');
require_once "colors.php";
@ -405,22 +386,16 @@ class Feeds extends Handler_Protected {
$line['feed_bg_color'] = 'rgba(' . implode(",", $rgba_cache[$feed_id]) . ',0.3)';
}
$this->_mark_timestamp(" color");
$this->_mark_timestamp(" pre-hook_render_cdm");
$c_scope->close();
PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE_CDM,
function ($result, $plugin) use (&$line) {
$line = $result;
$this->_mark_timestamp(" hook: " . get_class($plugin));
},
$line);
$this->_mark_timestamp(" hook_render_cdm");
$line['content'] = DiskCache::rewrite_urls($line['content']);
$this->_mark_timestamp(" disk_cache_rewrite");
/* we don't need those */
foreach (["date_entered", "guid", "last_published", "last_marked", "tag_cache", "favicon_avg_color",
@ -429,11 +404,11 @@ class Feeds extends Handler_Protected {
array_push($reply['content'], $line);
$this->_mark_timestamp("article end");
$aa_scope->close();
}
}
$this->_mark_timestamp("end of articles");
$a_scope->close();
if (!$headlines_count) {
@ -494,7 +469,7 @@ class Feeds extends Handler_Protected {
}
}
$this->_mark_timestamp("end");
$scope->close();
return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply);
}
@ -987,6 +962,7 @@ class Feeds extends Handler_Protected {
* @throws PDOException
*/
static function _get_counters($feed, bool $is_cat = false, bool $unread_only = false, ?int $owner_uid = null): int {
$scope = Tracer::start(__METHOD__, [], func_get_args());
$n_feed = (int) $feed;
$need_entries = false;
@ -1010,11 +986,14 @@ class Feeds extends Handler_Protected {
$handler = PluginHost::getInstance()->get_feed_handler($feed_id);
if (implements_interface($handler, 'IVirtualFeed')) {
/** @var IVirtualFeed $handler */
$scope->close();
return $handler->get_unread($feed_id);
} else {
$scope->close();
return 0;
}
} else if ($n_feed == Feeds::FEED_RECENTLY_READ) {
$scope->close();
return 0;
// tags
} else if ($feed != "0" && $n_feed == 0) {
@ -1028,6 +1007,7 @@ class Feeds extends Handler_Protected {
$row = $sth->fetch();
// Handle 'SUM()' returning null if there are no results
$scope->close();
return $row["count"] ?? 0;
} else if ($n_feed == Feeds::FEED_STARRED) {
@ -1061,6 +1041,7 @@ class Feeds extends Handler_Protected {
$label_id = Labels::feed_to_label_id($feed);
$scope->close();
return self::_get_label_unread($label_id, $owner_uid);
}
@ -1080,6 +1061,7 @@ class Feeds extends Handler_Protected {
$sth->execute([$owner_uid]);
$row = $sth->fetch();
$scope->close();
return $row["unread"];
} else {
@ -1092,6 +1074,7 @@ class Feeds extends Handler_Protected {
$sth->execute([$feed, $owner_uid]);
$row = $sth->fetch();
$scope->close();
return $row["unread"];
}
}
@ -1489,6 +1472,8 @@ class Feeds extends Handler_Protected {
*/
static function _get_headlines($params): array {
$scope = Tracer::start(__METHOD__, [], func_get_args());
$pdo = Db::pdo();
// WARNING: due to highly dynamic nature of this query its going to quote parameters
@ -1981,8 +1966,9 @@ class Feeds extends Handler_Protected {
$res = $pdo->query($query);
}
return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override);
$scope->close();
return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override);
}
/**
@ -2472,6 +2458,9 @@ class Feeds extends Handler_Protected {
PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINES_CUSTOM_SORT_OVERRIDE,
function ($result) use (&$query, &$skip_first_id) {
list ($query, $skip_first_id) = $result;
// run until first hard match
return !empty($query);
},
$order);
@ -2495,23 +2484,5 @@ class Feeds extends Handler_Protected {
return [$query, $skip_first_id];
}
private function _mark_timestamp(string $label): void {
if (empty($_REQUEST['timestamps']))
return;
if (!$this->viewfeed_timestamp) $this->viewfeed_timestamp = hrtime(true);
if (!$this->viewfeed_timestamp_last) $this->viewfeed_timestamp_last = hrtime(true);
$timestamp = hrtime(true);
printf("[%4d ms, %4d abs] %s\n",
($timestamp - $this->viewfeed_timestamp_last) / 1e6,
($timestamp - $this->viewfeed_timestamp) / 1e6,
$label);
$this->viewfeed_timestamp_last = $timestamp;
}
}

View File

@ -260,7 +260,7 @@ class Handler_Public extends Handler {
function getUnread(): void {
$login = clean($_REQUEST["login"]);
$fresh = clean($_REQUEST["fresh"]) == "1";
$fresh = clean($_REQUEST["fresh"] ?? "0") == "1";
$uid = UserHelper::find_user_by_login($login);

4
classes/icatchall.php Normal file
View File

@ -0,0 +1,4 @@
<?php
interface ICatchall {
function catchall(string $method): void;
}

View File

@ -339,10 +339,13 @@ class PluginHost {
*/
function chain_hooks_callback(string $hook, Closure $callback, &...$args): void {
$method = strtolower((string)$hook);
$scope = Tracer::start(__METHOD__, ['hook' => $hook]);
foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
$p_scope = Tracer::start("$hook - " . get_class($plugin));
try {
if ($callback($plugin->$method(...$args), $plugin))
break;
@ -351,7 +354,11 @@ class PluginHost {
} catch (Error $err) {
user_error($err, E_USER_WARNING);
}
$p_scope->close();
}
$scope->close();
}
/**
@ -431,6 +438,8 @@ class PluginHost {
* @param PluginHost::KIND_* $kind
*/
function load(string $classlist, int $kind, int $owner_uid = null, bool $skip_init = false): void {
$scope = Tracer::start(__METHOD__);
$plugins = explode(",", $classlist);
$this->owner_uid = (int) $owner_uid;
@ -439,18 +448,21 @@ class PluginHost {
$class = trim($class);
$class_file = strtolower(basename(clean($class)));
$p_scope = Tracer::start("loading $class_file");
// try system plugin directory first
$file = dirname(__DIR__) . "/plugins/$class_file/init.php";
if (!file_exists($file)) {
$file = dirname(__DIR__) . "/plugins.local/$class_file/init.php";
if (!file_exists($file))
if (!file_exists($file)) {
$p_scope->close();
continue;
}
}
if (!isset($this->plugins[$class])) {
// WIP hack
// we can't catch incompatible method signatures via Throwable
// this also enables global tt-rss safe mode in case there are more plugins like this
@ -464,6 +476,8 @@ class PluginHost {
$_SESSION["safe_mode"] = 1;
$p_scope->getSpan()->setTag('error', 'plugin is blacklisted');
$p_scope->close();
continue;
}
@ -474,16 +488,21 @@ class PluginHost {
} catch (Error $err) {
user_error($err, E_USER_WARNING);
$p_scope->getSpan()->setTag('error', $err);
$p_scope->close();
continue;
}
if (class_exists($class) && is_subclass_of($class, "Plugin")) {
$plugin = new $class($this);
$plugin_api = $plugin->api_version();
if ($plugin_api < self::API_VERSION) {
user_error("Plugin $class is not compatible with current API version (need: " . self::API_VERSION . ", got: $plugin_api)", E_USER_WARNING);
$p_scope->getSpan()->setTag('error', 'plugin is not compatible with API version');
$p_scope->close();
continue;
}
@ -492,6 +511,8 @@ class PluginHost {
_bind_textdomain_codeset($class, "UTF-8");
}
$i_scope = Tracer::start('init and register plugin');
try {
switch ($kind) {
case $this::KIND_SYSTEM:
@ -516,11 +537,17 @@ class PluginHost {
} catch (Error $err) {
user_error($err, E_USER_WARNING);
}
$i_scope->close();
}
}
$p_scope->close();
}
$this->load_data();
$scope->close();
}
function is_system(Plugin $plugin): bool {
@ -613,6 +640,8 @@ class PluginHost {
}
private function load_data(): void {
$scope = Tracer::start(__METHOD__);
if ($this->owner_uid && !$this->data_loaded && get_schema_version() > 100) {
$sth = $this->pdo->prepare("SELECT name, content FROM ttrss_plugin_storage
WHERE owner_uid = ?");
@ -624,10 +653,13 @@ class PluginHost {
$this->data_loaded = true;
}
$scope->close();
}
private function save_data(string $plugin): void {
if ($this->owner_uid) {
$scope = Tracer::start(__METHOD__);
if (!$this->pdo_data)
$this->pdo_data = Db::instance()->pdo_connect();
@ -655,6 +687,7 @@ class PluginHost {
}
$this->pdo_data->commit();
$scope->close();
}
}

View File

@ -1104,12 +1104,16 @@ class Pref_Feeds extends Handler_Protected {
* @return array<string, mixed>
*/
private function feedlist_init_feed(int $feed_id, ?string $title = null, bool $unread = false, string $error = '', string $updated = ''): array {
$scope = Tracer::start(__METHOD__, []);
if (!$title)
$title = Feeds::_get_title($feed_id, false);
if ($unread === false)
$unread = Feeds::_get_counters($feed_id, false, true);
$scope->close();
return [
'id' => 'FEED:' . $feed_id,
'name' => $title,

View File

@ -533,7 +533,11 @@ class Pref_Prefs extends Handler_Protected {
<?php $this->index_auth_password() ?>
</div>
<div dojoType='dijit.layout.ContentPane' title="<?= __('App passwords') ?>">
<?php $this->index_auth_app_passwords() ?>
<?php if (PluginHost::getInstance()->get_plugin('auth_internal')) { ?>
<?php $this->index_auth_app_passwords() ?>
<?php } else { ?>
<?= format_warning("App passwords are only available if <b>auth_internal<b> plugin is enabled."); ?>
<?php } ?>
</div>
<div dojoType='dijit.layout.ContentPane' title="<?= __('Authenticator (OTP)') ?>">
<?php $this->index_auth_2fa() ?>

View File

@ -106,6 +106,8 @@ class RPC extends Handler_Protected {
}
function getAllCounters(): void {
$scope = Tracer::start(__METHOD__);
@$seq = (int) $_REQUEST['seq'];
$feed_id_count = (int) ($_REQUEST["feed_id_count"] ?? -1);
@ -132,6 +134,7 @@ class RPC extends Handler_Protected {
'seq' => $seq
];
$scope->close();
print json_encode($reply);
}

View File

@ -68,6 +68,8 @@ class RSSUtils {
* @param array<string, false|string> $options
*/
static function update_daemon_common(int $limit = 0, array $options = []): int {
$scope = Tracer::start(__METHOD__);
if (!$limit) $limit = Config::get(Config::DAEMON_FEED_LIMIT);
if (Config::get_schema_version() != Config::SCHEMA_VERSION) {
@ -283,6 +285,8 @@ class RSSUtils {
// Send feed digests by email if needed.
Digest::send_headlines_digests();
$scope->close();
return $nf;
}
@ -349,6 +353,7 @@ class RSSUtils {
static function update_rss_feed(int $feed, bool $no_cache = false) : bool {
$scope = Tracer::start(__METHOD__, [], func_get_args());
Debug::log("start", Debug::LOG_VERBOSE);
$pdo = Db::pdo();
@ -383,16 +388,19 @@ class RSSUtils {
if ($user) {
if ($user->access_level == UserHelper::ACCESS_LEVEL_READONLY) {
Debug::log("error: denied update for $feed: permission denied by owner access level");
$scope->close();
return false;
}
} else {
// this would indicate database corruption of some kind
Debug::log("error: owner not found for feed: $feed");
$scope->close();
return false;
}
} else {
Debug::log("error: feeds table record not found for feed: $feed");
$scope->close();
return false;
}
@ -550,6 +558,7 @@ class RSSUtils {
$feed_obj->save();
}
$scope->close();
return $error_message == "";
}
@ -675,7 +684,7 @@ class RSSUtils {
]);
$feed_obj->save();
$scope->close();
return true; // no articles
}
@ -684,6 +693,8 @@ class RSSUtils {
$tstart = time();
foreach ($items as $item) {
$a_scope = Tracer::start('article');
$pdo->beginTransaction();
Debug::log("=================================================================================================================================",
@ -1276,6 +1287,7 @@ class RSSUtils {
Debug::log("article processed.", Debug::LOG_VERBOSE);
$pdo->commit();
$a_scope->close();
}
Debug::log("=================================================================================================================================",
@ -1317,10 +1329,12 @@ class RSSUtils {
unset($rss);
Debug::log("update failed.", Debug::LOG_VERBOSE);
$scope->close();
return false;
}
Debug::log("update done.", Debug::LOG_VERBOSE);
$scope->close();
return true;
}
@ -1485,6 +1499,8 @@ class RSSUtils {
* @return array<int, array<string, string>> An array of filter action arrays with keys "type" and "param"
*/
static function get_article_filters(array $filters, string $title, string $content, string $link, string $author, array $tags, array &$matched_rules = null, array &$matched_filters = null): array {
$scope = Tracer::start(__METHOD__);
$matches = array();
foreach ($filters as $filter) {
@ -1526,6 +1542,9 @@ class RSSUtils {
$match = @preg_match("/$reg_exp/iu", $author);
break;
case "tag":
if (count($tags) == 0)
array_push($tags, ''); // allow matching if there are no tags
foreach ($tags as $tag) {
if (@preg_match("/$reg_exp/iu", $tag)) {
$match = true;
@ -1565,6 +1584,8 @@ class RSSUtils {
}
}
$scope->close();
return $matches;
}

View File

@ -63,6 +63,7 @@ class Sanitizer {
* @return false|string The HTML, or false if an error occurred.
*/
public static function sanitize(string $str, ?bool $force_remove_images = false, int $owner = null, string $site_url = null, array $highlight_words = null, int $article_id = null) {
$scope = Tracer::start(__METHOD__);
if (!$owner && isset($_SESSION["uid"]))
$owner = $_SESSION["uid"];
@ -223,6 +224,8 @@ class Sanitizer {
$res = $doc->saveHTML();
$scope->close();
/* strip everything outside of <body>...</body> */
$res_frag = array();

69
classes/tracer.php Normal file
View File

@ -0,0 +1,69 @@
<?php
use OpenTracing\GlobalTracer;
use OpenTracing\Scope;
class Tracer {
/** @var Tracer $instance */
private static $instance;
public function __construct() {
$jaeger_host = Config::get(Config::JAEGER_REPORTING_HOST);
if ($jaeger_host) {
$config = new \Jaeger\Config(
[
'sampler' => [
'type' => \Jaeger\SAMPLER_TYPE_CONST,
'param' => true,
],
'logging' => true,
"local_agent" => [
"reporting_host" => $jaeger_host,
"reporting_port" => 6832
],
'dispatch_mode' => \Jaeger\Config::JAEGER_OVER_BINARY_UDP,
],
Config::get(Config::JAEGER_SERVICE_NAME)
);
$config->initializeTracer();
register_shutdown_function(function() {
$tracer = GlobalTracer::get();
$tracer->flush();
});
}
}
/**
* @param string $name
* @param array<string>|array<string, array<string, mixed>> $tags
* @param array<string> $args
* @return Scope
*/
private function _start(string $name, array $tags = [], array $args = []): Scope {
$tracer = GlobalTracer::get();
$tags['args'] = json_encode($args);
return $tracer->startActiveSpan($name, ['tags' => $tags]);
}
/**
* @param string $name
* @param array<string>|array<string, array<string, mixed>> $tags
* @param array<string> $args
* @return Scope
*/
public static function start(string $name, array $tags = [], array $args = []) : Scope {
return self::get_instance()->_start($name, $tags, $args);
}
public static function get_instance() : Tracer {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
}

View File

@ -185,10 +185,14 @@ class UrlHelper {
* @return false|string
*/
static function resolve_redirects(string $url, int $timeout, int $nest = 0) {
$scope = Tracer::start(__METHOD__, ['url' => $url]);
// too many redirects
if ($nest > 10)
if ($nest > 10) {
$scope->getSpan()->setTag('error', 'too many redirects');
$scope->close();
return false;
}
$context_options = array(
'http' => array(
@ -222,9 +226,12 @@ class UrlHelper {
}
}
$scope->close();
return $url;
}
$scope->getSpan()->setTag('error', 'request failed');
$scope->close();
// request failed?
return false;
}
@ -270,8 +277,10 @@ class UrlHelper {
"useragent" => @func_get_arg(7)
); */
}
$url = $options["url"];
$scope = Tracer::start(__METHOD__, ['url' => $url]);
$type = isset($options["type"]) ? $options["type"] : false;
$login = isset($options["login"]) ? $options["login"] : false;
$pass = isset($options["pass"]) ? $options["pass"] : false;
@ -293,6 +302,9 @@ class UrlHelper {
if (!$url) {
self::$fetch_last_error = "Requested URL failed extended validation.";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -301,6 +313,9 @@ class UrlHelper {
if (!$ip_addr || strpos($ip_addr, "127.") === 0) {
self::$fetch_last_error = "URL hostname failed to resolve or resolved to a loopback address ($ip_addr)";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -310,7 +325,12 @@ class UrlHelper {
$ch = curl_init($url);
if (!$ch) return false;
if (!$ch) {
self::$fetch_last_error = "curl_init() failed";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
$curl_http_headers = [];
@ -412,6 +432,8 @@ class UrlHelper {
if (!self::validate(self::$fetch_effective_url, true)) {
self::$fetch_last_error = "URL received after redirection failed extended validation.";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -420,6 +442,8 @@ class UrlHelper {
if (!self::$fetch_effective_ip_addr || strpos(self::$fetch_effective_ip_addr, "127.") === 0) {
self::$fetch_last_error = "URL hostname received after redirection failed to resolve or resolved to a loopback address (".self::$fetch_effective_ip_addr.")";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -435,6 +459,9 @@ class UrlHelper {
self::$fetch_last_error_content = $contents;
curl_close($ch);
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -445,6 +472,9 @@ class UrlHelper {
self::$fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
}
curl_close($ch);
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -458,6 +488,8 @@ class UrlHelper {
if ($tmp) $contents = $tmp;
}
$scope->close();
return $contents;
} else {
@ -511,6 +543,8 @@ class UrlHelper {
if (!self::validate(self::$fetch_effective_url, true)) {
self::$fetch_last_error = "URL received after redirection failed extended validation.";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -519,6 +553,8 @@ class UrlHelper {
if (!self::$fetch_effective_ip_addr || strpos(self::$fetch_effective_ip_addr, "127.") === 0) {
self::$fetch_last_error = "URL hostname received after redirection failed to resolve or resolved to a loopback address (".self::$fetch_effective_ip_addr.")";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -526,6 +562,9 @@ class UrlHelper {
if ($data === false) {
self::$fetch_last_error = "'file_get_contents' failed.";
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -561,6 +600,8 @@ class UrlHelper {
self::$fetch_last_error_content = $data;
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
@ -573,9 +614,13 @@ class UrlHelper {
if ($tmp) $data = $tmp;
}
$scope->close();
return $data;
} else {
self::$fetch_last_error = 'Successful response, but no content was received.';
$scope->getSpan()->setTag('error', self::$fetch_last_error);
$scope->close();
return false;
}
}

View File

@ -13,7 +13,8 @@
"spomky-labs/otphp": "^10.0",
"chillerlan/php-qrcode": "^4.3.3",
"mervick/material-design-icons": "^2.2",
"j4mie/idiorm": "dev-master"
"j4mie/idiorm": "dev-master",
"jonahgeorge/jaeger-client-php": "^1.4"
},
"require-dev": {
"phpstan/phpstan": "1.10.3",

259
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d2be3e2749aff1bebf6257ecbfd6dcb3",
"content-hash": "de4af0d5d452fc73fe4f5d584163af9a",
"packages": [
{
"name": "beberlei/assert",
@ -280,6 +280,74 @@
},
"time": "2022-03-26T15:19:01+00:00"
},
{
"name": "jonahgeorge/jaeger-client-php",
"version": "v1.4.4",
"source": {
"type": "git",
"url": "https://github.com/jonahgeorge/jaeger-client-php.git",
"reference": "3173d9c68ad8cea16058f25337982b00cc3d1c2b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jonahgeorge/jaeger-client-php/zipball/3173d9c68ad8cea16058f25337982b00cc3d1c2b",
"reference": "3173d9c68ad8cea16058f25337982b00cc3d1c2b",
"shasum": ""
},
"require": {
"ext-sockets": "*",
"opentracing/opentracing": "^1.0",
"packaged/thrift": "^0.13",
"php": "^7.1 || ^8.0 || ^8.1",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/log": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"cache/array-adapter": "^1.0",
"phpunit/phpunit": "^7 || ^8 || ^9",
"squizlabs/php_codesniffer": "3.*",
"symfony/polyfill-php73": "^1.10"
},
"type": "library",
"autoload": {
"files": [
"./src/Jaeger/Constants.php"
],
"psr-4": {
"Jaeger\\": "src/Jaeger/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonah George",
"homepage": "http://twitter.com/jonahgeorge"
},
{
"name": "José Carlos Chávez",
"email": "jcchavezs@gmail.com"
},
{
"name": "Contributors",
"homepage": "https://github.com/jonahgeorge/jaeger-client-php/graphs/contributors"
}
],
"description": "Jaeger Bindings for PHP OpenTracing API",
"keywords": [
"jaeger",
"opentracing",
"trace",
"tracing"
],
"support": {
"issues": "https://github.com/jonahgeorge/jaeger-client-php/issues",
"source": "https://github.com/jonahgeorge/jaeger-client-php/tree/v1.4.4"
},
"time": "2023-01-31T13:40:20+00:00"
},
{
"name": "mervick/material-design-icons",
"version": "2.2.0",
@ -323,6 +391,94 @@
},
"time": "2016-02-22T01:05:40+00:00"
},
{
"name": "opentracing/opentracing",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/opentracing/opentracing-php.git",
"reference": "cd60bd1fb2a25280600bc74c7f9e0c13881a9116"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentracing/opentracing-php/zipball/cd60bd1fb2a25280600bc74c7f9e0c13881a9116",
"reference": "cd60bd1fb2a25280600bc74c7f9e0c13881a9116",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "~0.12",
"phpunit/phpunit": "^7.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"type": "library",
"autoload": {
"files": [
"src/OpenTracing/Tags.php",
"src/OpenTracing/Formats.php"
],
"psr-4": {
"OpenTracing\\": "src/OpenTracing/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "José Carlos Chávez",
"email": "jcchavezs@gmail.com"
}
],
"description": "OpenTracing API for PHP",
"support": {
"issues": "https://github.com/opentracing/opentracing-php/issues",
"source": "https://github.com/opentracing/opentracing-php/tree/1.0.2"
},
"time": "2022-01-27T19:59:21+00:00"
},
{
"name": "packaged/thrift",
"version": "0.13.01",
"source": {
"type": "git",
"url": "https://github.com/packaged/thrift.git",
"reference": "e3dbcfb79e319971d64264ffe9c340590cc8a228"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/packaged/thrift/zipball/e3dbcfb79e319971d64264ffe9c340590cc8a228",
"reference": "e3dbcfb79e319971d64264ffe9c340590cc8a228",
"shasum": ""
},
"require": {
"php": "^5.5 || ^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Thrift\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "Apache Thrift",
"homepage": "http://thrift.apache.org/",
"keywords": [
"apache",
"thrift"
],
"support": {
"issues": "https://github.com/packaged/thrift/issues",
"source": "https://github.com/packaged/thrift/tree/0.13.01"
},
"time": "2021-01-25T13:32:28+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.6.3",
@ -390,6 +546,105 @@
},
"time": "2022-06-14T06:56:20+00:00"
},
{
"name": "psr/cache",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"support": {
"source": "https://github.com/php-fig/cache/tree/3.0.0"
},
"time": "2021-02-03T23:26:27+00:00"
},
{
"name": "psr/log",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
},
"time": "2021-07-14T16:46:02+00:00"
},
{
"name": "spomky-labs/otphp",
"version": "v10.0.3",
@ -2690,5 +2945,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.0.0"
}

View File

@ -810,8 +810,10 @@ const App = {
dojo.connect(dijit.byId('content-insert'), 'resize',
(args) => {
if (args && args.w >= 0 && args.h >= 0) {
Cookie.set("ttrss_ci_width", args.w, this.getInitParam("cookie_lifetime"));
Cookie.set("ttrss_ci_height", args.h, this.getInitParam("cookie_lifetime"));
const cookie_suffix = this._widescreen_mode ? "wide" : "normal";
Cookie.set("ttrss_ci_width:" + cookie_suffix, args.w, this.getInitParam("cookie_lifetime"));
Cookie.set("ttrss_ci_height:" + cookie_suffix, args.h, this.getInitParam("cookie_lifetime"));
}
});
@ -918,10 +920,6 @@ const App = {
return;
}
// reset stored sizes because geometry changed
Cookie.set("ttrss_ci_width", 0);
Cookie.set("ttrss_ci_height", 0);
this._widescreen_mode = wide;
const article_id = Article.getActive();
@ -938,9 +936,9 @@ const App = {
height: 'auto',
borderTopWidth: '0px' });
if (parseInt(Cookie.get("ttrss_ci_width")) > 0) {
if (parseInt(Cookie.get("ttrss_ci_width:wide")) > 0) {
content_insert.domNode.setStyle(
{width: Cookie.get("ttrss_ci_width") + "px" });
{width: Cookie.get("ttrss_ci_width:wide") + "px" });
}
headlines_frame.setStyle({ borderBottomWidth: '0px' });
@ -953,9 +951,9 @@ const App = {
height: '50%',
borderTopWidth: '0px'});
if (parseInt(Cookie.get("ttrss_ci_height")) > 0) {
if (parseInt(Cookie.get("ttrss_ci_height:normal")) > 0) {
content_insert.domNode.setStyle(
{height: Cookie.get("ttrss_ci_height") + "px" });
{height: Cookie.get("ttrss_ci_height:normal") + "px" });
}
headlines_frame.setStyle({ borderBottomWidth: '1px' });

View File

@ -333,6 +333,20 @@ const Article = {
return false;
},
autocompleteInject: function(elem, targetId) {
const target = App.byId(targetId);
if (!target)
return;
target.value = target.value.split(',')
.slice(0, -1)
.map((w) => w.trim())
.concat([elem.innerText])
.join(', ') + ', ';
target.focus();
},
editTags: function (id) {
const dialog = new fox.SingleUseDialog({
title: __("Article tags"),
@ -348,7 +362,7 @@ const Article = {
<section>
<textarea dojoType='dijit.form.SimpleTextarea' rows='4' disabled='true'
id='tags_str' name='tags_str'>${__("Loading, please wait...")}</textarea>
<div class='autocomplete' id='tags_choices' style='display:none'></div>
<span id='tags_choices'></span>
</section>
<footer>
@ -387,9 +401,15 @@ const Article = {
.attr('value', reply.tags.join(", "))
.attr('disabled', false);
/* new Ajax.Autocompleter("tags_str", "tags_choices",
"backend.php?op=article&method=completeTags",
{tokens: ',', paramName: "search"}); */
App.byId('tags_str').onkeyup = (e) => {
const last_tag = e.target.value.split(',').pop().trim();
xhr.json("backend.php", {op: 'article', method: 'completeTags', search: last_tag}, (data) => {
App.byId("tags_choices").innerHTML = `${data.map((tag) =>
`<a href="#" onclick="Article.autocompleteInject(this, 'tags_str')">${tag}</a>` )
.join(', ')}`
});
};
});
});

View File

@ -599,7 +599,7 @@ const CommonDialogs = {
<input style="display: none" type="file" onchange="App.dialogOf(this).uploadIcon(this)">
</label>
${App.FormFields.submit_tag(App.FormFields.icon("delete") + " " + __("Remove"), {class: "alt-danger", onclick: "App.dialogOf(this).removeIcon("+feed_id+")"})}
${App.FormFields.button_tag(App.FormFields.icon("delete") + " " + __("Remove"), "", {class: "alt-danger", onclick: "App.dialogOf(this).removeIcon("+feed_id+")"})}
</div>
<div dojoType="dijit.layout.ContentPane" title="${__('Plugins')}">
${reply.plugin_data}

View File

@ -13,6 +13,7 @@ const Headlines = {
_scroll_reset_timeout: false,
default_force_previous: false,
default_force_to_top: false,
default_move_on_expand: true,
line_scroll_offset: 120, /* px */
sticky_header_observer: new IntersectionObserver(
(entries, observer) => {
@ -255,7 +256,8 @@ const Headlines = {
// this would only work if there's enough space
App.byId("headlines-frame").scrollTop -= scroll_position_A-scroll_position_B;
Article.cdmMoveToId(id);
if (this.default_move_on_expand)
Article.cdmMoveToId(id);
}
} else if (in_body) {

View File

@ -29,6 +29,25 @@ function $$(query) {
return document.querySelectorAll(query);
}
// polyfill for safari https://raw.githubusercontent.com/pladaria/requestidlecallback-polyfill/master/index.js
window.requestIdleCallback =
window.requestIdleCallback ||
function (callback) {
const start = Date.now();
return setTimeout(() => {
callback({
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
},
);
}, 1);
};
window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
};
Element.prototype.hasClassName = function(className) {
return this.classList.contains(className);

View File

@ -15,7 +15,7 @@
"url": "https://github.com/dojo/dijit.git"
},
"dependencies": {
"dojo": "1.16.4"
"dojo": "1.16.5"
},
"devDependencies": {
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
{
"name": "dijit",
"version": "1.16.4",
"version": "1.16.5",
"directories": {
"lib": "."
},
"main": "main",
"dependencies": {
"dojo": "1.16.4"
"dojo": "1.16.5"
},
"description": "Dijit provides a complete collection of user interface controls based on Dojo, giving you the power to create web applications that are highly optimized for usability, performance, internationalization, accessibility, but above all deliver an incredible user experience.",
"license" : "BSD-3-Clause OR AFL-2.1",

View File

@ -7,7 +7,7 @@
# Dojo requires Java runtime to build. Further information on rebuilding Dojo
# is available here: http://dojotoolkit.org/reference-guide/build/index.html
VERSION=1.16.4
VERSION=1.16.5
# Download and extract dojo src code if it doesn't already exist
if [ ! -d "dojo" ]; then

View File

@ -5,4 +5,4 @@
*/
//>>built
define("dojo/_base/kernel",["../global","../has","./config","require","module"],function(_1,_2,_3,_4,_5){var i,p,_6={},_7={},_8={config:_3,global:_1,dijit:_6,dojox:_7};var _9={dojo:["dojo",_8],dijit:["dijit",_6],dojox:["dojox",_7]},_a=(_4.map&&_4.map[_5.id.match(/[^\/]+/)[0]]),_b;for(p in _a){if(_9[p]){_9[p][0]=_a[p];}else{_9[p]=[_a[p],{}];}}for(p in _9){_b=_9[p];_b[1]._scopeName=_b[0];if(!_3.noGlobals){_1[_b[0]]=_b[1];}}_8.scopeMap=_9;_8.baseUrl=_8.config.baseUrl=_4.baseUrl;_8.isAsync=!1||_4.async;_8.locale=_3.locale;var _c="$Rev:$".match(/[0-9a-f]{7,}/);_8.version={major:1,minor:16,patch:4,flag:"",revision:_c?_c[0]:NaN,toString:function(){var v=_8.version;return v.major+"."+v.minor+"."+v.patch+v.flag+" ("+v.revision+")";}};1||_2.add("extend-dojo",1);if(!_2("csp-restrictions")){(Function("d","d.eval = function(){return d.global.eval ? d.global.eval(arguments[0]) : eval(arguments[0]);}"))(_8);}if(0){_8.exit=function(_d){quit(_d);};}else{_8.exit=function(){};}if(!_2("host-webworker")){1||_2.add("dojo-guarantee-console",1);}if(1){_2.add("console-as-object",function(){return Function.prototype.bind&&console&&typeof console.log==="object";});typeof console!="undefined"||(console={});var cn=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","profile","profileEnd","time","timeEnd","trace","warn","log"];var tn;i=0;while((tn=cn[i++])){if(!console[tn]){(function(){var _e=tn+"";console[_e]=("log" in console)?function(){var a=Array.prototype.slice.call(arguments);a.unshift(_e+":");console["log"](a.join(" "));}:function(){};console[_e]._fake=true;})();}else{if(_2("console-as-object")){console[tn]=Function.prototype.bind.call(console[tn],console);}}}}_2.add("dojo-debug-messages",!!_3.isDebug);_8.deprecated=_8.experimental=function(){};if(_2("dojo-debug-messages")){_8.deprecated=function(_f,_10,_11){var _12="DEPRECATED: "+_f;if(_10){_12+=" "+_10;}if(_11){_12+=" -- will be removed in version: "+_11;}console.warn(_12);};_8.experimental=function(_13,_14){var _15="EXPERIMENTAL: "+_13+" -- APIs subject to change without notice.";if(_14){_15+=" "+_14;}console.warn(_15);};}1||_2.add("dojo-modulePaths",1);if(1){if(_3.modulePaths){_8.deprecated("dojo.modulePaths","use paths configuration");var _16={};for(p in _3.modulePaths){_16[p.replace(/\./g,"/")]=_3.modulePaths[p];}_4({paths:_16});}}1||_2.add("dojo-moduleUrl",1);if(1){_8.moduleUrl=function(_17,url){_8.deprecated("dojo.moduleUrl()","use require.toUrl","2.0");var _18=null;if(_17){_18=_4.toUrl(_17.replace(/\./g,"/")+(url?("/"+url):"")+"/*.*").replace(/\/\*\.\*/,"")+(url?"":"/");}return _18;};}_8._hasResource={};return _8;});
define("dojo/_base/kernel",["../global","../has","./config","require","module"],function(_1,_2,_3,_4,_5){var i,p,_6={},_7={},_8={config:_3,global:_1,dijit:_6,dojox:_7};var _9={dojo:["dojo",_8],dijit:["dijit",_6],dojox:["dojox",_7]},_a=(_4.map&&_4.map[_5.id.match(/[^\/]+/)[0]]),_b;for(p in _a){if(_9[p]){_9[p][0]=_a[p];}else{_9[p]=[_a[p],{}];}}for(p in _9){_b=_9[p];_b[1]._scopeName=_b[0];if(!_3.noGlobals){_1[_b[0]]=_b[1];}}_8.scopeMap=_9;_8.baseUrl=_8.config.baseUrl=_4.baseUrl;_8.isAsync=!1||_4.async;_8.locale=_3.locale;var _c="$Rev:$".match(/[0-9a-f]{7,}/);_8.version={major:1,minor:16,patch:5,flag:"",revision:_c?_c[0]:NaN,toString:function(){var v=_8.version;return v.major+"."+v.minor+"."+v.patch+v.flag+" ("+v.revision+")";}};1||_2.add("extend-dojo",1);if(!_2("csp-restrictions")){(Function("d","d.eval = function(){return d.global.eval ? d.global.eval(arguments[0]) : eval(arguments[0]);}"))(_8);}if(0){_8.exit=function(_d){quit(_d);};}else{_8.exit=function(){};}if(!_2("host-webworker")){1||_2.add("dojo-guarantee-console",1);}if(1){_2.add("console-as-object",function(){return Function.prototype.bind&&console&&typeof console.log==="object";});typeof console!="undefined"||(console={});var cn=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","profile","profileEnd","time","timeEnd","trace","warn","log"];var tn;i=0;while((tn=cn[i++])){if(!console[tn]){(function(){var _e=tn+"";console[_e]=("log" in console)?function(){var a=Array.prototype.slice.call(arguments);a.unshift(_e+":");console["log"](a.join(" "));}:function(){};console[_e]._fake=true;})();}else{if(_2("console-as-object")){console[tn]=Function.prototype.bind.call(console[tn],console);}}}}_2.add("dojo-debug-messages",!!_3.isDebug);_8.deprecated=_8.experimental=function(){};if(_2("dojo-debug-messages")){_8.deprecated=function(_f,_10,_11){var _12="DEPRECATED: "+_f;if(_10){_12+=" "+_10;}if(_11){_12+=" -- will be removed in version: "+_11;}console.warn(_12);};_8.experimental=function(_13,_14){var _15="EXPERIMENTAL: "+_13+" -- APIs subject to change without notice.";if(_14){_15+=" "+_14;}console.warn(_15);};}1||_2.add("dojo-modulePaths",1);if(1){if(_3.modulePaths){_8.deprecated("dojo.modulePaths","use paths configuration");var _16={};for(p in _3.modulePaths){_16[p.replace(/\./g,"/")]=_3.modulePaths[p];}_4({paths:_16});}}1||_2.add("dojo-moduleUrl",1);if(1){_8.moduleUrl=function(_17,url){_8.deprecated("dojo.moduleUrl()","use require.toUrl","2.0");var _18=null;if(_17){_18=_4.toUrl(_17.replace(/\./g,"/")+(url?("/"+url):"")+"/*.*").replace(/\/\*\.\*/,"")+(url?"":"/");}return _18;};}_8._hasResource={};return _8;});

View File

@ -5,4 +5,4 @@
*/
//>>built
define("dojo/_base/lang",["./kernel","../has","../sniff"],function(_1,_2){_2.add("bug-for-in-skips-shadowed",function(){for(var i in {toString:1}){return 0;}return 1;});var _3=_2("bug-for-in-skips-shadowed")?"hasOwnProperty.valueOf.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.constructor".split("."):[],_4=_3.length,_5=function(_6,_7,_8){if(!_8){if(_6[0]&&_1.scopeMap[_6[0]]){_8=_1.scopeMap[_6.shift()][1];}else{_8=_1.global;}}try{for(var i=0;i<_6.length;i++){var p=_6[i];if(!(p in _8)){if(_7){_8[p]={};}else{return;}}_8=_8[p];}return _8;}catch(e){}},_9=Object.prototype.toString,_a=function(_b,_c,_d){return (_d||[]).concat(Array.prototype.slice.call(_b,_c||0));},_e=/\{([^\}]+)\}/g;var _f={_extraNames:_3,_mixin:function(_10,_11,_12){var _13,s,i,_14={};for(_13 in _11){s=_11[_13];if(!(_13 in _10)||(_10[_13]!==s&&(!(_13 in _14)||_14[_13]!==s))){_10[_13]=_12?_12(s):s;}}if(_2("bug-for-in-skips-shadowed")){if(_11){for(i=0;i<_4;++i){_13=_3[i];s=_11[_13];if(!(_13 in _10)||(_10[_13]!==s&&(!(_13 in _14)||_14[_13]!==s))){_10[_13]=_12?_12(s):s;}}}}return _10;},mixin:function(_15,_16){if(!_15){_15={};}for(var i=1,l=arguments.length;i<l;i++){_f._mixin(_15,arguments[i]);}return _15;},setObject:function(_17,_18,_19){var _1a=_17.split("."),p=_1a.pop(),obj=_5(_1a,true,_19);return obj&&p?(obj[p]=_18):undefined;},getObject:function(_1b,_1c,_1d){return !_1b?_1d:_5(_1b.split("."),_1c,_1d);},exists:function(_1e,obj){return _f.getObject(_1e,false,obj)!==undefined;},isString:function(it){return (typeof it=="string"||it instanceof String);},isArray:Array.isArray||function(it){return _9.call(it)=="[object Array]";},isFunction:function(it){return _9.call(it)==="[object Function]";},isObject:function(it){return it!==undefined&&(it===null||typeof it=="object"||_f.isArray(it)||_f.isFunction(it));},isArrayLike:function(it){return !!it&&!_f.isString(it)&&!_f.isFunction(it)&&!(it.tagName&&it.tagName.toLowerCase()=="form")&&(_f.isArray(it)||isFinite(it.length));},isAlien:function(it){return it&&!_f.isFunction(it)&&/\{\s*\[native code\]\s*\}/.test(String(it));},extend:function(_1f,_20){for(var i=1,l=arguments.length;i<l;i++){_f._mixin(_1f.prototype,arguments[i]);}return _1f;},_hitchArgs:function(_21,_22){var pre=_f._toArray(arguments,2);var _23=_f.isString(_22);return function(){var _24=_f._toArray(arguments);var f=_23?(_21||_1.global)[_22]:_22;return f&&f.apply(_21||this,pre.concat(_24));};},hitch:function(_25,_26){if(arguments.length>2){return _f._hitchArgs.apply(_1,arguments);}if(!_26){_26=_25;_25=null;}if(_f.isString(_26)){_25=_25||_1.global;if(!_25[_26]){throw (["lang.hitch: scope[\"",_26,"\"] is null (scope=\"",_25,"\")"].join(""));}return function(){return _25[_26].apply(_25,arguments||[]);};}return !_25?_26:function(){return _26.apply(_25,arguments||[]);};},delegate:(function(){function TMP(){};return function(obj,_27){TMP.prototype=obj;var tmp=new TMP();TMP.prototype=null;if(_27){_f._mixin(tmp,_27);}return tmp;};})(),_toArray:_2("ie")?(function(){function _28(obj,_29,_2a){var arr=_2a||[];for(var x=_29||0;x<obj.length;x++){arr.push(obj[x]);}return arr;};return function(obj){return ((obj.item)?_28:_a).apply(this,arguments);};})():_a,partial:function(_2b){var arr=[null];return _f.hitch.apply(_1,arr.concat(_f._toArray(arguments)));},clone:function(src){if(!src||typeof src!="object"||_f.isFunction(src)){return src;}if(src.nodeType&&"cloneNode" in src){return src.cloneNode(true);}if(src instanceof Date){return new Date(src.getTime());}if(src instanceof RegExp){return new RegExp(src);}var r,i,l;if(_f.isArray(src)){r=[];for(i=0,l=src.length;i<l;++i){if(i in src){r[i]=_f.clone(src[i]);}}}else{r=src.constructor?new src.constructor():{};}return _f._mixin(r,src,_f.clone);},trim:String.prototype.trim?function(str){return str.trim();}:function(str){return str.replace(/^\s\s*/,"").replace(/\s\s*$/,"");},replace:function(_2c,map,_2d){return _2c.replace(_2d||_e,_f.isFunction(map)?map:function(_2e,k){return _f.getObject(k,false,map);});}};1&&_f.mixin(_1,_f);return _f;});
define("dojo/_base/lang",["./kernel","../has","../sniff"],function(_1,_2){_2.add("bug-for-in-skips-shadowed",function(){for(var i in {toString:1}){return 0;}return 1;});var _3=_2("bug-for-in-skips-shadowed")?"hasOwnProperty.valueOf.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.constructor".split("."):[],_4=_3.length,_5=function(_6,_7,_8){if(!_8){if(_6[0]&&_1.scopeMap[_6[0]]){_8=_1.scopeMap[_6.shift()][1];}else{_8=_1.global;}}try{for(var i=0;i<_6.length;i++){var p=_6[i];if(p==="__proto__"||p==="constructor"){return;}if(!(p in _8)){if(_7){_8[p]={};}else{return;}}_8=_8[p];}return _8;}catch(e){}},_9=Object.prototype.toString,_a=function(_b,_c,_d){return (_d||[]).concat(Array.prototype.slice.call(_b,_c||0));},_e=/\{([^\}]+)\}/g;var _f={_extraNames:_3,_mixin:function(_10,_11,_12){var _13,s,i,_14={};for(_13 in _11){s=_11[_13];if(!(_13 in _10)||(_10[_13]!==s&&(!(_13 in _14)||_14[_13]!==s))){_10[_13]=_12?_12(s):s;}}if(_2("bug-for-in-skips-shadowed")){if(_11){for(i=0;i<_4;++i){_13=_3[i];s=_11[_13];if(!(_13 in _10)||(_10[_13]!==s&&(!(_13 in _14)||_14[_13]!==s))){_10[_13]=_12?_12(s):s;}}}}return _10;},mixin:function(_15,_16){if(!_15){_15={};}for(var i=1,l=arguments.length;i<l;i++){_f._mixin(_15,arguments[i]);}return _15;},setObject:function(_17,_18,_19){var _1a=_17.split("."),p=_1a.pop(),obj=_5(_1a,true,_19);return obj&&p?(obj[p]=_18):undefined;},getObject:function(_1b,_1c,_1d){return !_1b?_1d:_5(_1b.split("."),_1c,_1d);},exists:function(_1e,obj){return _f.getObject(_1e,false,obj)!==undefined;},isString:function(it){return (typeof it=="string"||it instanceof String);},isArray:Array.isArray||function(it){return _9.call(it)=="[object Array]";},isFunction:function(it){return _9.call(it)==="[object Function]";},isObject:function(it){return it!==undefined&&(it===null||typeof it=="object"||_f.isArray(it)||_f.isFunction(it));},isArrayLike:function(it){return !!it&&!_f.isString(it)&&!_f.isFunction(it)&&!(it.tagName&&it.tagName.toLowerCase()=="form")&&(_f.isArray(it)||isFinite(it.length));},isAlien:function(it){return it&&!_f.isFunction(it)&&/\{\s*\[native code\]\s*\}/.test(String(it));},extend:function(_1f,_20){for(var i=1,l=arguments.length;i<l;i++){_f._mixin(_1f.prototype,arguments[i]);}return _1f;},_hitchArgs:function(_21,_22){var pre=_f._toArray(arguments,2);var _23=_f.isString(_22);return function(){var _24=_f._toArray(arguments);var f=_23?(_21||_1.global)[_22]:_22;return f&&f.apply(_21||this,pre.concat(_24));};},hitch:function(_25,_26){if(arguments.length>2){return _f._hitchArgs.apply(_1,arguments);}if(!_26){_26=_25;_25=null;}if(_f.isString(_26)){_25=_25||_1.global;if(!_25[_26]){throw (["lang.hitch: scope[\"",_26,"\"] is null (scope=\"",_25,"\")"].join(""));}return function(){return _25[_26].apply(_25,arguments||[]);};}return !_25?_26:function(){return _26.apply(_25,arguments||[]);};},delegate:(function(){function TMP(){};return function(obj,_27){TMP.prototype=obj;var tmp=new TMP();TMP.prototype=null;if(_27){_f._mixin(tmp,_27);}return tmp;};})(),_toArray:_2("ie")?(function(){function _28(obj,_29,_2a){var arr=_2a||[];for(var x=_29||0;x<obj.length;x++){arr.push(obj[x]);}return arr;};return function(obj){return ((obj.item)?_28:_a).apply(this,arguments);};})():_a,partial:function(_2b){var arr=[null];return _f.hitch.apply(_1,arr.concat(_f._toArray(arguments)));},clone:function(src){if(!src||typeof src!="object"||_f.isFunction(src)){return src;}if(src.nodeType&&"cloneNode" in src){return src.cloneNode(true);}if(src instanceof Date){return new Date(src.getTime());}if(src instanceof RegExp){return new RegExp(src);}var r,i,l;if(_f.isArray(src)){r=[];for(i=0,l=src.length;i<l;++i){if(i in src){r[i]=_f.clone(src[i]);}}}else{r=src.constructor?new src.constructor():{};}return _f._mixin(r,src,_f.clone);},trim:String.prototype.trim?function(str){return str.trim();}:function(str){return str.replace(/^\s\s*/,"").replace(/\s\s*$/,"");},replace:function(_2c,map,_2d){return _2c.replace(_2d||_e,_f.isFunction(map)?map:function(_2e,k){return _f.getObject(k,false,map);});}};1&&_f.mixin(_1,_f);return _f;});

2
lib/dojo/dojo.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "dojo",
"version": "1.16.4",
"version": "1.16.5",
"directories": {
"lib": "."
},
@ -11,7 +11,7 @@
"jsgi-node": "0.3.1",
"formidable": "1.0.17",
"sinon": "1.12.2",
"dojo": "1.16.4"
"dojo": "1.16.5"
},
"main": "main",
"scripts": {

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,10 @@ echo PWD: $(pwd)
while true; do
inotifywait . -e close_write -r -t 300 | grep -q .php && \
(
MODIFIED=$(git ls-files -m | grep .php)
docker run --rm -v $(pwd):/app -v /tmp/phpstan-8.1:/tmp/phpstan \
--workdir /app php:${PHP_VERSION}-cli php -d memory_limit=-1 ./vendor/bin/phpstan --memory-limit=2G --error-format=raw
--workdir /app registry.fakecake.org/cthulhoo/ci-alpine:3.16 php81 -d memory_limit=-1 ./vendor/bin/phpstan --memory-limit=2G --error-format=raw analyze ${MODIFIED}
echo All done, RC=$?.
)
sleep 1

18
vendor/autoload.php vendored
View File

@ -2,24 +2,6 @@
// 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';
return ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056::getLoader();

View File

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

View File

@ -18,29 +18,459 @@ use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
* To require it's presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @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
*/
private static $installed;
/**
* @var bool|null
*/
private static $installed = array (
'root' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => 'a37eab2610a0a2bcb655258781c1c7e925dc94c0',
'name' => '__root__',
),
'versions' =>
array (
'__root__' =>
array (
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'aliases' =>
array (
),
'reference' => 'a37eab2610a0a2bcb655258781c1c7e925dc94c0',
),
'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',
),
'jonahgeorge/jaeger-client-php' =>
array (
'pretty_version' => 'v1.4.4',
'version' => '1.4.4.0',
'aliases' =>
array (
),
'reference' => '3173d9c68ad8cea16058f25337982b00cc3d1c2b',
),
'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',
),
'opentracing/opentracing' =>
array (
'pretty_version' => '1.0.2',
'version' => '1.0.2.0',
'aliases' =>
array (
),
'reference' => 'cd60bd1fb2a25280600bc74c7f9e0c13881a9116',
),
'packaged/thrift' =>
array (
'pretty_version' => '0.13.01',
'version' => '0.13.01.0',
'aliases' =>
array (
),
'reference' => 'e3dbcfb79e319971d64264ffe9c340590cc8a228',
),
'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.10.3',
'version' => '1.10.3.0',
'aliases' =>
array (
),
'reference' => '5419375b5891add97dc74be71e6c1c34baaddf64',
),
'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',
),
'psr/cache' =>
array (
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'aliases' =>
array (
),
'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf',
),
'psr/log' =>
array (
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'aliases' =>
array (
),
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
),
'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;
/**
* @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();
/**
@ -56,6 +486,7 @@ class InstalledVersions
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
@ -63,42 +494,19 @@ class InstalledVersions
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
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
public static function isInstalled($packageName)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
return true;
}
}
@ -112,9 +520,10 @@ class InstalledVersions
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @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 VersionParser $parser Install composer/semver to have access to this class and functionality
* @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
*
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
@ -224,26 +633,9 @@ class InstalledVersions
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
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}
*/
public static function getRootPackage()
{
@ -255,38 +647,14 @@ class InstalledVersions
/**
* 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[]
* @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[]}>}
* @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[]}>}
*/
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;
}
/**
* 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
*
@ -303,7 +671,7 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @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
* @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
*/
public static function reload($data)
{
@ -313,7 +681,6 @@ class InstalledVersions
/**
* @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()
{
@ -324,27 +691,16 @@ class InstalledVersions
$installed = array();
if (self::$canGetVendors) {
// @phpstan-ignore-next-line
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($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;
return $installed;

View File

@ -1,4 +1,3 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
@ -18,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
THE SOFTWARE.

View File

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

View File

@ -2,10 +2,12 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
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',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'51fcf4e06c07cc00c920b44bcd900e7a' => $vendorDir . '/thecodingmachine/safe/deprecated/apc.php',
@ -95,6 +97,7 @@ return array(
'4af1dca6db8c527c6eed27bff85ff0e5' => $vendorDir . '/thecodingmachine/safe/generated/yaz.php',
'fe43ca06499ac37bc2dedd823af71eb5' => $vendorDir . '/thecodingmachine/safe/generated/zip.php',
'356736db98a6834f0a886b8d509b0ecd' => $vendorDir . '/thecodingmachine/safe/generated/zlib.php',
'9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'ff1b7935a93a4a9517db3ebe0533892a' => $vendorDir . '/opentracing/opentracing/src/OpenTracing/Tags.php',
'0db36546c71c357f5ee70c39bb03966f' => $vendorDir . '/opentracing/opentracing/src/OpenTracing/Formats.php',
'822502b10d2c1dae82956ef999e8b4be' => $vendorDir . '/jonahgeorge/jaeger-client-php/src/Jaeger/Constants.php',
);

View File

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

View File

@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
@ -10,10 +10,15 @@ return array(
'chillerlan\\Settings\\' => array($vendorDir . '/chillerlan/php-settings-container/src'),
'chillerlan\\QRCode\\' => array($vendorDir . '/chillerlan/php-qrcode/src'),
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
'Thrift\\' => array($vendorDir . '/packaged/thrift/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
'OpenTracing\\' => array($vendorDir . '/opentracing/opentracing/src/OpenTracing'),
'OTPHP\\' => array($vendorDir . '/spomky-labs/otphp/src'),
'Jaeger\\' => array($vendorDir . '/jonahgeorge/jaeger-client-php/src/Jaeger'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Assert\\' => array($vendorDir . '/beberlei/assert/lib/Assert'),

View File

@ -23,26 +23,51 @@ class ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056
}
spl_autoload_register(array('ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit19fc2ff1c0f9a92279c7979386bb2056', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::getInitializer($loader));
$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';
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);
$filesToLoad = \Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire19fc2ff1c0f9a92279c7979386bb2056($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire19fc2ff1c0f9a92279c7979386bb2056($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@ -7,6 +7,8 @@ namespace Composer\Autoload;
class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
{
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',
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'51fcf4e06c07cc00c920b44bcd900e7a' => __DIR__ . '/..' . '/thecodingmachine/safe/deprecated/apc.php',
@ -96,8 +98,9 @@ class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
'4af1dca6db8c527c6eed27bff85ff0e5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/yaz.php',
'fe43ca06499ac37bc2dedd823af71eb5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zip.php',
'356736db98a6834f0a886b8d509b0ecd' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zlib.php',
'9b38cf48e83f5d8f60375221cd213eee' => __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'ff1b7935a93a4a9517db3ebe0533892a' => __DIR__ . '/..' . '/opentracing/opentracing/src/OpenTracing/Tags.php',
'0db36546c71c357f5ee70c39bb03966f' => __DIR__ . '/..' . '/opentracing/opentracing/src/OpenTracing/Formats.php',
'822502b10d2c1dae82956ef999e8b4be' => __DIR__ . '/..' . '/jonahgeorge/jaeger-client-php/src/Jaeger/Constants.php',
);
public static $prefixLengthsPsr4 = array (
@ -114,16 +117,27 @@ class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
array (
'Webmozart\\Assert\\' => 17,
),
'T' =>
array (
'Thrift\\' => 7,
),
'P' =>
array (
'Psr\\Log\\' => 8,
'Psr\\Cache\\' => 10,
'Prophecy\\' => 9,
'PhpParser\\' => 10,
'ParagonIE\\ConstantTime\\' => 23,
),
'O' =>
array (
'OpenTracing\\' => 12,
'OTPHP\\' => 6,
),
'J' =>
array (
'Jaeger\\' => 7,
),
'D' =>
array (
'Doctrine\\Instantiator\\' => 22,
@ -154,6 +168,18 @@ class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
array (
0 => __DIR__ . '/..' . '/webmozart/assert/src',
),
'Thrift\\' =>
array (
0 => __DIR__ . '/..' . '/packaged/thrift/src',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/src',
),
'Psr\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/cache/src',
),
'Prophecy\\' =>
array (
0 => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy',
@ -166,10 +192,18 @@ class ComposerStaticInit19fc2ff1c0f9a92279c7979386bb2056
array (
0 => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src',
),
'OpenTracing\\' =>
array (
0 => __DIR__ . '/..' . '/opentracing/opentracing/src/OpenTracing',
),
'OTPHP\\' =>
array (
0 => __DIR__ . '/..' . '/spomky-labs/otphp/src',
),
'Jaeger\\' =>
array (
0 => __DIR__ . '/..' . '/jonahgeorge/jaeger-client-php/src/Jaeger',
),
'Doctrine\\Instantiator\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',

View File

@ -359,6 +359,77 @@
},
"install-path": "../j4mie/idiorm"
},
{
"name": "jonahgeorge/jaeger-client-php",
"version": "v1.4.4",
"version_normalized": "1.4.4.0",
"source": {
"type": "git",
"url": "https://github.com/jonahgeorge/jaeger-client-php.git",
"reference": "3173d9c68ad8cea16058f25337982b00cc3d1c2b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jonahgeorge/jaeger-client-php/zipball/3173d9c68ad8cea16058f25337982b00cc3d1c2b",
"reference": "3173d9c68ad8cea16058f25337982b00cc3d1c2b",
"shasum": ""
},
"require": {
"ext-sockets": "*",
"opentracing/opentracing": "^1.0",
"packaged/thrift": "^0.13",
"php": "^7.1 || ^8.0 || ^8.1",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/log": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"cache/array-adapter": "^1.0",
"phpunit/phpunit": "^7 || ^8 || ^9",
"squizlabs/php_codesniffer": "3.*",
"symfony/polyfill-php73": "^1.10"
},
"time": "2023-01-31T13:40:20+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"./src/Jaeger/Constants.php"
],
"psr-4": {
"Jaeger\\": "src/Jaeger/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonah George",
"homepage": "http://twitter.com/jonahgeorge"
},
{
"name": "José Carlos Chávez",
"email": "jcchavezs@gmail.com"
},
{
"name": "Contributors",
"homepage": "https://github.com/jonahgeorge/jaeger-client-php/graphs/contributors"
}
],
"description": "Jaeger Bindings for PHP OpenTracing API",
"keywords": [
"jaeger",
"opentracing",
"trace",
"tracing"
],
"support": {
"issues": "https://github.com/jonahgeorge/jaeger-client-php/issues",
"source": "https://github.com/jonahgeorge/jaeger-client-php/tree/v1.4.4"
},
"install-path": "../jonahgeorge/jaeger-client-php"
},
{
"name": "mervick/material-design-icons",
"version": "2.2.0",
@ -526,6 +597,100 @@
},
"install-path": "../nikic/php-parser"
},
{
"name": "opentracing/opentracing",
"version": "1.0.2",
"version_normalized": "1.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/opentracing/opentracing-php.git",
"reference": "cd60bd1fb2a25280600bc74c7f9e0c13881a9116"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentracing/opentracing-php/zipball/cd60bd1fb2a25280600bc74c7f9e0c13881a9116",
"reference": "cd60bd1fb2a25280600bc74c7f9e0c13881a9116",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "~0.12",
"phpunit/phpunit": "^7.0 || ^9.0",
"squizlabs/php_codesniffer": "3.*"
},
"time": "2022-01-27T19:59:21+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"src/OpenTracing/Tags.php",
"src/OpenTracing/Formats.php"
],
"psr-4": {
"OpenTracing\\": "src/OpenTracing/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "José Carlos Chávez",
"email": "jcchavezs@gmail.com"
}
],
"description": "OpenTracing API for PHP",
"support": {
"issues": "https://github.com/opentracing/opentracing-php/issues",
"source": "https://github.com/opentracing/opentracing-php/tree/1.0.2"
},
"install-path": "../opentracing/opentracing"
},
{
"name": "packaged/thrift",
"version": "0.13.01",
"version_normalized": "0.13.01.0",
"source": {
"type": "git",
"url": "https://github.com/packaged/thrift.git",
"reference": "e3dbcfb79e319971d64264ffe9c340590cc8a228"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/packaged/thrift/zipball/e3dbcfb79e319971d64264ffe9c340590cc8a228",
"reference": "e3dbcfb79e319971d64264ffe9c340590cc8a228",
"shasum": ""
},
"require": {
"php": "^5.5 || ^7.0 || ^8.0"
},
"time": "2021-01-25T13:32:28+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Thrift\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "Apache Thrift",
"homepage": "http://thrift.apache.org/",
"keywords": [
"apache",
"thrift"
],
"support": {
"issues": "https://github.com/packaged/thrift/issues",
"source": "https://github.com/packaged/thrift/tree/0.13.01"
},
"install-path": "../packaged/thrift"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.6.3",
@ -1453,6 +1618,111 @@
],
"install-path": "../phpunit/phpunit"
},
{
"name": "psr/cache",
"version": "3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"time": "2021-02-03T23:26:27+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"support": {
"source": "https://github.com/php-fig/cache/tree/3.0.0"
},
"install-path": "../psr/cache"
},
{
"name": "psr/log",
"version": "3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"time": "2021-07-14T16:46:02+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
},
"install-path": "../psr/log"
},
{
"name": "sebastian/cli-parser",
"version": "1.0.1",

View File

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

View File

@ -0,0 +1,16 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,44 @@
name: Test
on: [push, pull_request]
jobs:
run:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
name: PHP ${{ matrix.php-versions }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: xdebug
- name: Get composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: |
composer install \
--no-interaction \
--no-ansi \
--no-progress
- name: Run lint
run: composer lint
- name: Run test
run: composer test

View File

@ -0,0 +1,17 @@
# Please do not use this ignore file to define platform specific files.
#
# For these purposes create a global .gitignore file, which is a list of rules
# for ignoring files in every Git repository on your computer.
#
# https://help.github.com/articles/ignoring-files/#create-a-global-gitignore
jaeger-idl
vendor
composer.lock
composer.phar
jaeger-client-php.iml
phpunit.xml
phpcs.xml

View File

@ -0,0 +1,3 @@
# How to Contribute to Jaeger
We'd love your help!

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Jonah George, José Carlos Chávez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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 THE
SOFTWARE.

View File

@ -0,0 +1,219 @@
![Build Status](https://github.com/jonahgeorge/jaeger-client-php/workflows/Test/badge.svg) [![PHP version][packagist-img]][packagist]
# Jaeger Bindings for PHP OpenTracing API
This is a client-side library that can be used to instrument PHP apps for distributed trace collection,
and to send those traces to Jaeger. See the [OpenTracing PHP API](https://github.com/opentracing/opentracing-php)
for additional detail.
## Contributing and Developing
Please see [CONTRIBUTING.md](./CONTRIBUTING.md).
## Installation
Jaeger client can be installed via Composer:
```bash
composer require jonahgeorge/jaeger-client-php
```
## Getting Started
```php
<?php
require_once 'vendor/autoload.php';
use Jaeger\Config;
use OpenTracing\GlobalTracer;
$config = new Config(
[
'sampler' => [
'type' => Jaeger\SAMPLER_TYPE_CONST,
'param' => true,
],
'logging' => true,
],
'your-app-name'
);
$config->initializeTracer();
$tracer = GlobalTracer::get();
$scope = $tracer->startActiveSpan('TestSpan', []);
$scope->close();
$tracer->flush();
```
### Samplers
List of supported samplers, for more info about samplers, please read [Jaeger Sampling](https://www.jaegertracing.io/docs/1.9/sampling/) guide.
#### Const sampler
This sampler either samples everything, or nothing.
##### Configuration
```
'sampler' => [
'type' => Jaeger\SAMPLER_TYPE_CONST,
'param' => true, // boolean wheter to trace or not
],
```
#### Probabilistic sampler
This sampler samples request by given rate.
##### Configuration
```
'sampler' => [
'type' => Jaeger\SAMPLER_TYPE_PROBABILISTIC,
'param' => 0.5, // float [0.0, 1.0]
],
```
#### Rate limiting sampler
Samples maximum specified number of traces (requests) per second.
##### Requirements
* `psr/cache` PSR-6 cache component to store and retrieve sampler state between requests.
Cache component is passed to `Jaeger\Config` trough its constructor.
* `hrtime()` function, that can retrieve time in nanoseconds. You need either `php 7.3` or [PECL/hrtime](http://pecl.php.net/package/hrtime) extension.
##### Configuration
```
'sampler' => [
'type' => Jaeger\SAMPLER_TYPE_RATE_LIMITING,
'param' => 100 // integer maximum number of traces per second,
'cache' => [
'currentBalanceKey' => 'rate.currentBalance' // string
'lastTickKey' => 'rate.lastTick' // string
]
],
```
## Dispatch mode
The library supports 3 ways of sending data to Jaeger Agent:
1. `Zipkin.thrift` over Compact protocol (socket - UDP) - default
2. `Jaeger.thrift` over Binary protocol (socket - UDP)
2. `Jaeger.thrift` over Binary protocol (HTTP)
If you want to enable "`Jaeger.thrift` over Binary protocol" one or other, than
you need to set `dispatch_mode` config option or `JAEGER_DISPATCH_MODE` env
variable.
Allowed values for `dispatch_mode` are:
- `jaeger_over_binary_udp`
- `jaeger_over_binary_http`
- `zipkin_over_compact_udp`
There are 3 constants available, so it is better to use them:
```php
class Config
{
const ZIPKIN_OVER_COMPACT_UDP = "zipkin_over_compact_udp";
const JAEGER_OVER_BINARY_UDP = "jaeger_over_binary_udp";
const JAEGER_OVER_BINARY_HTTP = "jaeger_over_binary_http";
...
}
```
A possible config with custom `dispatch_mode` can look like this:
```php
// config.php
use Jaeger\Config;
return [
'sampler' => [
'type' => Jaeger\SAMPLER_TYPE_CONST,
'param' => true,
],
'logging' => true,
"tags" => [
// process. prefix works only with JAEGER_OVER_HTTP, JAEGER_OVER_BINARY
// otherwise it will be shown as simple global tag
"process.process-tag-key-1" => "process-value-1", // all tags with `process.` prefix goes to process section
"process.process-tag-key-2" => "process-value-2", // all tags with `process.` prefix goes to process section
"global-tag-key-1" => "global-tag-value-1", // this tag will be appended to all spans
"global-tag-key-2" => "global-tag-value-2", // this tag will be appended to all spans
],
"local_agent" => [
"reporting_host" => "localhost",
// You can override port by setting local_agent.reporting_port value
"reporting_port" => 6832
],
// Different ways to send data to Jaeger. Config::ZIPKIN_OVER_COMPACT - default):
'dispatch_mode' => Config::JAEGER_OVER_BINARY_UDP,
];
```
The full example you can see at `examples` directory.
By default, for each `dispatch_mode` there is default `reporting_port` config value. Table with
default values you can see below:
`dispatch_mode` | default `reporting_port`
------------------------ | ----------------
ZIPKIN_OVER_COMPACT_UDP | 5775
JAEGER_OVER_BINARY_UDP | 6832
JAEGER_OVER_BINARY_HTTP | 14268
## IPv6
In case you need IPv6 support you need to set `ip_version` Config variable.
By default, IPv4 is used. There is an alias `Config::IP_VERSION` which you can use
as an alternative to raw `ip_version`.
Example:
```php
use Jaeger\Config;
$config = new Config(
[
"ip_version" => Config::IPV6, // <-- or use Config::IP_VERSION constant
'logging' => true,
'dispatch_mode' => Config::JAEGER_OVER_BINARY_UDP,
],
'serviceNameExample',
);
$config->initializeTracer();
```
or
```php
use Jaeger\Config;
$config = new Config(
[
Config::IP_VERSION => Config::IPV6, // <--
'logging' => true,
'dispatch_mode' => Config::JAEGER_OVER_BINARY_UDP,
],
'serviceNameExample',
);
$config->initializeTracer();
```
## Testing
Tests are located in the `tests` directory. See [tests/README.md](./tests/README.md).
## Roadmap
- [Support Span baggage](https://github.com/jonahgeorge/jaeger-client-php/issues/5)
- [Support Tracer metrics](https://github.com/jonahgeorge/jaeger-client-php/issues/12)
- [Support Tracer error reporting](https://github.com/jonahgeorge/jaeger-client-php/issues/13)
## License
[MIT License](./LICENSE).
[ci-img]: https://travis-ci.org/jonahgeorge/jaeger-client-php.svg?branch=travis
[ci]: https://travis-ci.org/jonahgeorge/jaeger-client-php
[packagist-img]: https://badge.fury.io/ph/jonahgeorge%2Fjaeger-client-php.svg
[packagist]: https://badge.fury.io/ph/jonahgeorge%2Fjaeger-client-php

View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,62 @@
{
"name": "jonahgeorge/jaeger-client-php",
"description": "Jaeger Bindings for PHP OpenTracing API",
"keywords": [
"jaeger",
"opentracing",
"trace",
"tracing"
],
"license": "MIT",
"authors": [
{
"name": "Jonah George",
"homepage": "http://twitter.com/jonahgeorge"
},
{
"name": "José Carlos Chávez",
"email": "jcchavezs@gmail.com"
},
{
"name": "Contributors",
"homepage": "https://github.com/jonahgeorge/jaeger-client-php/graphs/contributors"
}
],
"require": {
"php": "^7.1 || ^8.0 || ^8.1",
"ext-sockets": "*",
"opentracing/opentracing": "^1.0",
"packaged/thrift": "^0.13",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/log": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"phpunit/phpunit": "^7 || ^8 || ^9",
"squizlabs/php_codesniffer": "3.*",
"cache/array-adapter": "^1.0",
"symfony/polyfill-php73": "^1.10"
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"autoload": {
"psr-4": {
"Jaeger\\": "src/Jaeger/"
},
"files": [
"./src/Jaeger/Constants.php"
]
},
"autoload-dev": {
"Jaeger\\Tests\\": "tests/Jaeger/"
},
"minimum-stability": "dev",
"prefer-stable": true,
"scripts": {
"fix-lint": "./vendor/bin/phpcbf",
"lint": "./vendor/bin/phpcs",
"test": "./vendor/bin/phpunit"
}
}

View File

@ -0,0 +1,26 @@
<?php
use Jaeger\Config;
return [
'sampler' => [
'type' => Jaeger\SAMPLER_TYPE_CONST,
'param' => true,
],
'logging' => true,
"tags" => [
// process. prefix works only with JAEGER_OVER_HTTP, JAEGER_OVER_BINARY
// otherwise it will be shown as simple global tag
"process.process-tag-key-1" => "process-value-1", // all tags with `process.` prefix goes to process section
"process.process-tag-key-2" => "process-value-2", // all tags with `process.` prefix goes to process section
"global-tag-key-1" => "global-tag-value-1", // this tag will be appended to all spans
"global-tag-key-2" => "global-tag-value-2", // this tag will be appended to all spans
],
"local_agent" => [
"reporting_host" => "localhost",
// You can override port by setting local_agent.reporting_port value
// "reporting_port" => 6832
],
// Different ways to send data to Jaeger. Config::ZIPKIN_OVER_COMPACT - default):
'dispatch_mode' => Config::JAEGER_OVER_BINARY_UDP,
];

View File

@ -0,0 +1,51 @@
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Jaeger\Config;
use OpenTracing\GlobalTracer;
$config = new Config(
require_once __DIR__.'/config.php',
'your-app-name'
);
$config->initializeTracer();
$tracer = GlobalTracer::get();
$scope = $tracer->startActiveSpan('JaegerSpan', []);
$scope->getSpan()->setTag("tag1", "value1");
$scope->getSpan()->setTag("tag2", "value2");
$scope->getSpan()->setTag("tag3", "value2");
$scope->getSpan()->log([
"key1" => "value1",
"key2" => 2,
"key3" => true
]);
$scope->getSpan()->addBaggageItem("baggage-item1", "baggage-value1");
$scope->getSpan()->addBaggageItem("baggage-item2", "baggage-value2");
$scope->getSpan()->addBaggageItem("baggage-item3", "baggage-value3");
$nestedSpanScope = $tracer->startActiveSpan("Nested1");
$nestedSpanScope->getSpan()->setTag("tag1", "value1");
$nestedSpanScope->getSpan()->setTag("tag2", "value2");
$nestedSpanScope->getSpan()->setTag("tag3", "value2");
$nestedSpanScope->getSpan()->log([
"key1" => "value1",
"key2" => 2,
"key3" => true
]);
$nestedSpanScope->getSpan()->addBaggageItem("baggage-item1", "baggage-value1");
$nestedSpanScope->getSpan()->addBaggageItem("baggage-item2", "baggage-value2");
$nestedSpanScope->getSpan()->addBaggageItem("baggage-item3", "baggage-value3");
sleep(1);
$nestedSpanScope->close();
sleep(1);
$scope->close();
$tracer->flush();

View File

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<ruleset name="Jaeger">
<!--
The name attribute of the ruleset tag is displayed
when running PHP_CodeSniffer with the -v command line
argument. The description tag below is not displayed anywhere
except in this file, so it can contain information for
developers who may change this file in the future.
-->
<description>The coding standard for Jaeger Client</description>
<!-- Show sniff codes in all reports -->
<arg value="s"/>
<!-- Use PSR-2 as a base -->
<rule ref="PSR2"/>
<!-- Uncomment to use colors in progress or report -->
<!-- arg name="colors" / -->
<!--
If no files or directories are specified on the command line
your custom standard can specify what files should be checked
instead.
Note that specifying any file or directory path
on the command line will ignore all file tags.
-->
<file>src</file>
<!--
You can hard-code ignore patterns directly into your
custom standard so you don't have to specify the
patterns on the command line.
-->
<exclude-pattern>src/Jaeger/Thrift/*</exclude-pattern>
</ruleset>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<filter>
<whitelist>
<directory suffix=".php">src/Jaeger</directory>
<exclude>
<directory suffix="Interface.php">src/</directory>
<file>src/Jaeger/Constants.php</file>
<file>src/Jaeger/Thrift/Agent/AgentIf.php</file>
<file>src/Jaeger/Thrift/Agent/AggregationValidatorIf.php</file>
<file>src/Jaeger/Thrift/Agent/BaggageRestrictionManagerIf.php</file>
<file>src/Jaeger/Thrift/Agent/DependencyIf.php</file>
<file>src/Jaeger/Thrift/Agent/SamplingManagerIf.php</file>
<file>src/Jaeger/Thrift/CollectorIf.php</file>
<file>src/Jaeger/Thrift/Crossdock/TracedServiceIf.php</file>
</exclude>
</whitelist>
</filter>
<testsuites>
<testsuite name="Jaeger Test Suite">
<directory suffix=".php">tests/Jaeger</directory>
</testsuite>
</testsuites>
<php>
<ini name="date.timezone" value="UTC"/>
<ini name="display_errors" value="on"/>
<ini name="display_startup_errors" value="on"/>
</php>
<logging>
<log
type="coverage-text"
target="php://stdout"
lowUpperBound="60"
highLowerBound="90"/>
<log
type="coverage-clover"
target="build/coverage.xml"/>
</logging>
</phpunit>

View File

@ -0,0 +1,27 @@
#!/bin/sh
set -e
cd "$(dirname "$0")/.."
# checkout jaeger thrift files
rm -rf jaeger-idl
git clone https://github.com/jaegertracing/jaeger-idl
# define thrift cmd
THRIFT="docker run -u $(id -u) -v '${PWD}:/data' thrift:0.11.0 thrift -o /data/jaeger-idl"
THRIFT_CMD="${THRIFT} --gen php:psr4,oop"
# generate php files
FILES=$(find jaeger-idl/thrift -type f -name \*.thrift)
for f in ${FILES}; do
echo "${THRIFT_CMD} "/data/${f}""
eval $THRIFT_CMD "/data/${f}"
done
# move generated files
rm -rf src/Jaeger/Thrift
mv jaeger-idl/gen-php/Jaeger/Thrift src/Jaeger/Thrift
# remove thrift files
rm -rf jaeger-idl

View File

@ -0,0 +1,27 @@
<?php
namespace Jaeger\AgentClient;
class HttpAgentClient implements \Jaeger\Thrift\Agent\AgentIf
{
protected $input_ = null;
protected $output_ = null;
protected $seqid_ = 0;
public function __construct($input, $output = null)
{
$this->input_ = $input;
$this->output_ = $output ? $output : $input;
}
public function emitZipkinBatch(array $spans)
{
}
public function emitBatch(\Jaeger\Thrift\Batch $batch)
{
$batch->write($this->output_);
$this->output_->getTransport()->flush();
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Jaeger\Codec;
use Jaeger\SpanContext;
use OpenTracing\UnsupportedFormatException;
class BinaryCodec implements CodecInterface
{
/**
* {@inheritdoc}
*
* @see \Jaeger\Tracer::inject
*
* @param SpanContext $spanContext
* @param mixed $carrier
*
* @return void
*/
public function inject(SpanContext $spanContext, &$carrier)
{
throw new UnsupportedFormatException('Binary encoding not implemented');
}
/**
* {@inheritdoc}
*
* @see \Jaeger\Tracer::extract
*
* @param mixed $carrier
* @return SpanContext|null
*
* @throws UnsupportedFormatException
*/
public function extract($carrier)
{
throw new UnsupportedFormatException('Binary encoding not implemented');
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Jaeger\Codec;
use Jaeger\SpanContext;
interface CodecInterface
{
/**
* Handle the logic behind injecting propagation scheme specific information into the carrier
* (e.g. http request headers, amqp message headers, etc.).
*
* This method can modify the carrier.
*
* @see \Jaeger\Tracer::inject
*
* @param SpanContext $spanContext
* @param mixed $carrier
*
* @return void
*/
public function inject(SpanContext $spanContext, &$carrier);
/**
* Handle the logic behind extracting propagation-scheme specific information from carrier
* (e.g. http request headers, amqp message headers, etc.).
*
* This method must not modify the carrier.
*
* @see \Jaeger\Tracer::extract
*
* @param mixed $carrier
* @return SpanContext|null
*/
public function extract($carrier);
}

View File

@ -0,0 +1,41 @@
<?php
namespace Jaeger\Codec;
class CodecUtility
{
/**
* Incoming trace/span IDs are hex representations of 64-bit values. PHP
* represents ints internally as signed 32- or 64-bit values, but base_convert
* converts to string representations of arbitrarily large positive numbers.
* This means at least half the incoming IDs will be larger than PHP_INT_MAX.
*
* Thrift, while building a binary representation of the IDs, performs bitwise
* operations on the string values, implicitly casting to int and capping them
* at PHP_INT_MAX. So, incoming IDs larger than PHP_INT_MAX will be serialized
* and sent to the agent as PHP_INT_MAX, breaking trace/span correlation.
*
* This method therefore, on 64-bit architectures, splits the hex string into
* high and low values, converts them separately to ints, and manually combines
* them into a proper signed int. This int is then handled properly by the
* Thrift package.
*
* On 32-bit architectures, it falls back to base_convert.
*
* @param string $hex
* @return string|int
*/
public static function hexToInt64($hex)
{
// If we're on a 32-bit architecture, fall back to base_convert.
if (PHP_INT_SIZE === 4) {
return base_convert($hex, 16, 10);
}
$hi = intval(substr($hex, -16, -8), 16);
$lo = intval(substr($hex, -8, 8), 16);
return $hi << 32 | $lo;
}
}

View File

@ -0,0 +1,184 @@
<?php
namespace Jaeger\Codec;
use Exception;
use Jaeger\SpanContext;
use const Jaeger\TRACE_ID_HEADER;
use const Jaeger\BAGGAGE_HEADER_PREFIX;
use const Jaeger\DEBUG_ID_HEADER_KEY;
class TextCodec implements CodecInterface
{
private $urlEncoding;
private $traceIdHeader;
private $baggagePrefix;
private $debugIdHeader;
private $prefixLength;
/**
* @param bool $urlEncoding
* @param string $traceIdHeader
* @param string $baggageHeaderPrefix
* @param string $debugIdHeader
*/
public function __construct(
bool $urlEncoding = false,
string $traceIdHeader = TRACE_ID_HEADER,
string $baggageHeaderPrefix = BAGGAGE_HEADER_PREFIX,
string $debugIdHeader = DEBUG_ID_HEADER_KEY
) {
$this->urlEncoding = $urlEncoding;
$this->traceIdHeader = str_replace('_', '-', strtolower($traceIdHeader));
$this->baggagePrefix = str_replace('_', '-', strtolower($baggageHeaderPrefix));
$this->debugIdHeader = str_replace('_', '-', strtolower($debugIdHeader));
$this->prefixLength = strlen($baggageHeaderPrefix);
}
/**
* {@inheritdoc}
*
* @see \Jaeger\Tracer::inject
*
* @param SpanContext $spanContext
* @param mixed $carrier
*
* @return void
*/
public function inject(SpanContext $spanContext, &$carrier)
{
$carrier[$this->traceIdHeader] = $this->spanContextToString(
$spanContext->getTraceId(),
$spanContext->getSpanId(),
$spanContext->getParentId(),
$spanContext->getFlags()
);
$baggage = $spanContext->getBaggage();
if (empty($baggage)) {
return;
}
foreach ($baggage as $key => $value) {
$encodedValue = $value;
if ($this->urlEncoding) {
$encodedValue = urlencode($value);
}
$carrier[$this->baggagePrefix . $key] = $encodedValue;
}
}
/**
* {@inheritdoc}
*
* @see \Jaeger\Tracer::extract
*
* @param mixed $carrier
* @return SpanContext|null
*
* @throws Exception
*/
public function extract($carrier)
{
$traceId = null;
$spanId = null;
$parentId = null;
$flags = null;
$baggage = null;
$debugId = null;
foreach ($carrier as $key => $value) {
$ucKey = strtolower($key);
if ($ucKey === $this->traceIdHeader) {
if ($this->urlEncoding) {
$value = urldecode($value);
}
list($traceId, $spanId, $parentId, $flags) =
$this->spanContextFromString($value);
} elseif ($this->startsWith($ucKey, $this->baggagePrefix)) {
if ($this->urlEncoding) {
$value = urldecode($value);
}
$attrKey = substr($key, $this->prefixLength);
if ($baggage === null) {
$baggage = [strtolower($attrKey) => $value];
} else {
$baggage[strtolower($attrKey)] = $value;
}
} elseif ($ucKey === $this->debugIdHeader) {
if ($this->urlEncoding) {
$value = urldecode($value);
}
$debugId = $value;
}
}
if ($traceId === null && $baggage !== null) {
throw new Exception('baggage without trace ctx');
}
if ($traceId === null) {
if ($debugId !== null) {
return new SpanContext(null, null, null, null, [], $debugId);
}
return null;
}
return new SpanContext($traceId, $spanId, $parentId, $flags, $baggage);
}
/**
* Store a span context to a string.
*
* @param int $traceId
* @param int $spanId
* @param int $parentId
* @param int $flags
* @return string
*/
private function spanContextToString($traceId, $spanId, $parentId, $flags)
{
$parentId = $parentId ?? 0;
return sprintf('%x:%x:%x:%x', $traceId, $spanId, $parentId, $flags);
}
/**
* Create a span context from a string.
*
* @param string $value
* @return array
*
* @throws Exception
*/
private function spanContextFromString($value): array
{
$parts = explode(':', $value);
if (count($parts) != 4) {
throw new Exception('Malformed tracer state string.');
}
return [
CodecUtility::hexToInt64($parts[0]),
CodecUtility::hexToInt64($parts[1]),
CodecUtility::hexToInt64($parts[2]),
$parts[3],
];
}
/**
* Checks that a string ($haystack) starts with a given prefix ($needle).
*
* @param string $haystack
* @param string $needle
* @return bool
*/
private function startsWith(string $haystack, string $needle): bool
{
return substr($haystack, 0, strlen($needle)) == $needle;
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace Jaeger\Codec;
use Jaeger\SpanContext;
use const Jaeger\DEBUG_FLAG;
use const Jaeger\SAMPLED_FLAG;
class ZipkinCodec implements CodecInterface
{
const SAMPLED_NAME = 'X-B3-Sampled';
const TRACE_ID_NAME = 'X-B3-TraceId';
const SPAN_ID_NAME = 'X-B3-SpanId';
const PARENT_ID_NAME = 'X-B3-ParentSpanId';
const FLAGS_NAME = 'X-B3-Flags';
/**
* {@inheritdoc}
*
* @see \Jaeger\Tracer::inject
*
* @param SpanContext $spanContext
* @param mixed $carrier
*
* @return void
*/
public function inject(SpanContext $spanContext, &$carrier)
{
$carrier[self::TRACE_ID_NAME] = dechex($spanContext->getTraceId());
$carrier[self::SPAN_ID_NAME] = dechex($spanContext->getSpanId());
if ($spanContext->getParentId() != null) {
$carrier[self::PARENT_ID_NAME] = dechex($spanContext->getParentId());
}
$carrier[self::FLAGS_NAME] = (int) $spanContext->getFlags();
}
/**
* {@inheritdoc}
*
* @see \Jaeger\Tracer::extract
*
* @param mixed $carrier
* @return SpanContext|null
*/
public function extract($carrier)
{
$traceId = "0";
$spanId = "0";
$parentId = "0";
$flags = 0;
if (isset($carrier[strtolower(self::SAMPLED_NAME)])) {
if ($carrier[strtolower(self::SAMPLED_NAME)] === "1" ||
strtolower($carrier[strtolower(self::SAMPLED_NAME)] === "true")
) {
$flags = $flags | SAMPLED_FLAG;
}
}
if (isset($carrier[strtolower(self::TRACE_ID_NAME)])) {
$traceId = CodecUtility::hexToInt64($carrier[strtolower(self::TRACE_ID_NAME)], 16, 10);
}
if (isset($carrier[strtolower(self::PARENT_ID_NAME)])) {
$parentId = CodecUtility::hexToInt64($carrier[strtolower(self::PARENT_ID_NAME)], 16, 10);
}
if (isset($carrier[strtolower(self::SPAN_ID_NAME)])) {
$spanId = CodecUtility::hexToInt64($carrier[strtolower(self::SPAN_ID_NAME)], 16, 10);
}
if (isset($carrier[strtolower(self::FLAGS_NAME)])) {
if ($carrier[strtolower(self::FLAGS_NAME)] === "1") {
$flags = $flags | DEBUG_FLAG;
}
}
if ($traceId != "0" && $spanId != "0") {
return new SpanContext($traceId, $spanId, $parentId, $flags);
}
return null;
}
}

View File

@ -0,0 +1,390 @@
<?php
namespace Jaeger;
use Exception;
use Jaeger\Reporter\CompositeReporter;
use Jaeger\Reporter\LoggingReporter;
use Jaeger\Reporter\ReporterInterface;
use Jaeger\ReporterFactory\JaegerHttpReporterFactory;
use Jaeger\ReporterFactory\JaegerReporterFactory;
use Jaeger\ReporterFactory\ZipkinReporterFactory;
use Jaeger\Sampler\ConstSampler;
use Jaeger\Sampler\ProbabilisticSampler;
use Jaeger\Sampler\RateLimitingSampler;
use Jaeger\Sampler\SamplerInterface;
use Jaeger\Util\RateLimiter;
use OpenTracing\GlobalTracer;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
class Config
{
const IP_VERSION = "ip_version";
const ZIPKIN_OVER_COMPACT_UDP = "zipkin_over_compact_udp";
const JAEGER_OVER_BINARY_UDP = "jaeger_over_binary_udp";
const JAEGER_OVER_BINARY_HTTP = "jaeger_over_binary_http";
const IPV6 = "IPv6";
const IPV4 = "IPv4";
/**
* @return string[]
*/
public static function getAvailableDispatchModes()
{
return [self::ZIPKIN_OVER_COMPACT_UDP, self::JAEGER_OVER_BINARY_UDP, self::JAEGER_OVER_BINARY_HTTP];
}
/**
* @var array
*/
private $config;
/**
* @var string
*/
private $serviceName;
/**
* @var bool
*/
private $initialized = false;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @return LoggerInterface
*/
public function getLogger()
{
return $this->logger;
}
/**
* @var CacheItemPoolInterface
*/
private $cache;
/**
* Config constructor.
* @param array $config
* @param string|null $serviceName
* @param LoggerInterface|null $logger
* @param CacheItemPoolInterface|null $cache
* @throws Exception
*/
public function __construct(
array $config,
string $serviceName = null,
LoggerInterface $logger = null,
CacheItemPoolInterface $cache = null
) {
$this->config = $config;
$this->setConfigFromEnv();
if (empty($this->config["dispatch_mode"])) {
$this->config["dispatch_mode"] = self::ZIPKIN_OVER_COMPACT_UDP;
}
if (empty($this->config[Config::IP_VERSION])) {
$this->config[Config::IP_VERSION] = self::IPV4;
}
$this->serviceName = $this->config['service_name'] ?? $serviceName;
if ($this->serviceName === null) {
throw new Exception('service_name required in the config or param.');
}
$this->logger = $logger ?: new NullLogger();
$this->cache = $cache;
}
/**
* @return Tracer|null
* @throws Exception
*/
public function initializeTracer()
{
if ($this->initialized) {
$this->logger->warning('Jaeger tracer already initialized, skipping');
return null;
}
$reporter = $this->getReporter();
$sampler = $this->getSampler();
$tracer = $this->createTracer($reporter, $sampler);
$this->initializeGlobalTracer($tracer);
return $tracer;
}
/**
* @param ReporterInterface $reporter
* @param SamplerInterface $sampler
* @return Tracer
*/
public function createTracer(ReporterInterface $reporter, SamplerInterface $sampler): Tracer
{
return new Tracer(
$this->serviceName,
$reporter,
$sampler,
$this->shouldUseOneSpanPerRpc(),
$this->logger,
null,
$this->getTraceIdHeader(),
$this->getBaggageHeaderPrefix(),
$this->getDebugIdHeaderKey(),
$this->getConfiguredTags()
);
}
/**
* @return string
*/
public function getServiceName(): string
{
return $this->serviceName;
}
/**
* @param Tracer $tracer
*/
private function initializeGlobalTracer(Tracer $tracer)
{
GlobalTracer::set($tracer);
$this->logger->debug('OpenTracing\GlobalTracer initialized to ' . $tracer->getServiceName());
}
/**
* @return bool
*/
private function getLogging(): bool
{
return (bool)($this->config['logging'] ?? false);
}
/**
* @return ReporterInterface
*/
private function getReporter(): ReporterInterface
{
switch ($this->config["dispatch_mode"]) {
case self::JAEGER_OVER_BINARY_UDP:
$reporter = (new JaegerReporterFactory($this))->createReporter();
break;
case self::ZIPKIN_OVER_COMPACT_UDP:
$reporter = (new ZipkinReporterFactory($this))->createReporter();
break;
case self::JAEGER_OVER_BINARY_HTTP:
$reporter = (new JaegerHttpReporterFactory($this))->createReporter();
break;
default:
throw new \RuntimeException(
sprintf(
"Unsupported `dispatch_mode` value: %s. Allowed values are: %s",
$this->config["dispatch_mode"],
implode(", ", Config::getAvailableDispatchModes())
)
);
}
if ($this->getLogging()) {
$reporter = new CompositeReporter($reporter, new LoggingReporter($this->logger));
}
return $reporter;
}
/**
* @return SamplerInterface
* @throws \Psr\Cache\InvalidArgumentException
* @throws Exception
*/
private function getSampler(): SamplerInterface
{
$samplerConfig = $this->config['sampler'] ?? [];
$samplerType = $samplerConfig['type'] ?? null;
$samplerParam = $samplerConfig['param'] ?? null;
if ($samplerType === null || $samplerType === SAMPLER_TYPE_REMOTE) {
// todo: implement remote sampling
return new ProbabilisticSampler((float)$samplerParam);
} elseif ($samplerType === SAMPLER_TYPE_CONST) {
return new ConstSampler($samplerParam ?? false);
} elseif ($samplerType === SAMPLER_TYPE_PROBABILISTIC) {
return new ProbabilisticSampler((float)$samplerParam);
} elseif ($samplerType === SAMPLER_TYPE_RATE_LIMITING) {
if (!$this->cache) {
throw new Exception('You cannot use RateLimitingSampler without cache component');
}
$cacheConfig = $samplerConfig['cache'] ?? [];
return new RateLimitingSampler(
$samplerParam ?? 0,
new RateLimiter(
$this->cache,
$cacheConfig['currentBalanceKey'] ?? 'rate.currentBalance',
$cacheConfig['lastTickKey'] ?? 'rate.lastTick'
)
);
}
throw new Exception('Unknown sampler type ' . $samplerType);
}
/**
* The UDP max buffer length.
*
* @return int
*/
public function getMaxBufferLength(): int
{
return (int)($this->config['max_buffer_length'] ?? 64000);
}
/**
* @return string
*/
public function getLocalAgentReportingHost(): string
{
return $this->getLocalAgentGroup()['reporting_host'] ?? DEFAULT_REPORTING_HOST;
}
/**
* @return int
*/
public function getLocalAgentReportingPort(): int
{
$port = $this->getLocalAgentGroup()['reporting_port'] ?? null;
if (empty($this->getLocalAgentGroup()['reporting_port'])) {
switch ($this->config['dispatch_mode']) {
case self::JAEGER_OVER_BINARY_UDP:
$port = DEFAULT_JAEGER_UDP_BINARY_REPORTING_PORT;
break;
case self::JAEGER_OVER_BINARY_HTTP:
$port = DEFAULT_JAEGER_HTTP_BINARY_REPORTING_PORT;
break;
default:
$port = DEFAULT_ZIPKIN_UDP_COMPACT_REPORTING_PORT;
}
}
return (int)$port;
}
/**
* @return array
*/
private function getLocalAgentGroup(): array
{
return $this->config['local_agent'] ?? [];
}
/**
* @return string
*/
private function getTraceIdHeader(): string
{
return $this->config['trace_id_header'] ?? TRACE_ID_HEADER;
}
/**
* @return string
*/
private function getBaggageHeaderPrefix(): string
{
return $this->config['baggage_header_prefix'] ?? BAGGAGE_HEADER_PREFIX;
}
/**
* @return string
*/
private function getDebugIdHeaderKey(): string
{
return $this->config['debug_id_header_key'] ?? DEBUG_ID_HEADER_KEY;
}
/**
* Get a list of user-defined tags to be added to each span created by the tracer initialized by this config.
* @return string[]
*/
private function getConfiguredTags(): array
{
return $this->config['tags'] ?? [];
}
/**
* Whether to follow the Zipkin model of using one span per RPC,
* as opposed to the model of using separate spans on the RPC client and server.
* Defaults to true.
*
* @return bool
*/
private function shouldUseOneSpanPerRpc(): bool
{
return $this->config['one_span_per_rpc'] ?? true;
}
public function ipProtocolVersion(): string
{
return $this->config[self::IP_VERSION] ?? self::IPV4;
}
/**
* Sets values from env vars into config props, unless ones has been already set.
*/
private function setConfigFromEnv()
{
// general
if (isset($_ENV['JAEGER_SERVICE_NAME']) && !isset($this->config['service_name'])) {
$this->config['service_name'] = $_ENV['JAEGER_SERVICE_NAME'];
}
if (isset($_ENV['JAEGER_TAGS']) && !isset($this->config["tags"])) {
$this->config['tags'] = $_ENV['JAEGER_TAGS'];
}
if (isset($_ENV['JAEGER_DISPATCH_MODE']) && !isset($this->config['dispatch_mode'])) {
$this->config['dispatch_mode'] = $_ENV['JAEGER_DISPATCH_MODE'];
}
// reporting
if (isset($_ENV['JAEGER_AGENT_HOST']) && !isset($this->config['local_agent']['reporting_host'])) {
$this->config['local_agent']['reporting_host'] = $_ENV['JAEGER_AGENT_HOST'];
}
if (isset($_ENV['JAEGER_AGENT_PORT']) && !isset($this->config['local_agent']['reporting_port'])) {
$this->config['local_agent']['reporting_port'] = intval($_ENV['JAEGER_AGENT_PORT']);
}
if (isset($_ENV['JAEGER_REPORTER_LOG_SPANS']) && !isset($this->config['logging'])) {
$this->config['logging'] = filter_var($_ENV['JAEGER_REPORTER_LOG_SPANS'], FILTER_VALIDATE_BOOLEAN);
}
if (isset($_ENV['JAEGER_REPORTER_MAX_QUEUE_SIZE']) && !isset($this->config['max_buffer_length'])) {
$this->config['max_buffer_length'] = intval($_ENV['JAEGER_REPORTER_MAX_QUEUE_SIZE']);
}
// sampling
if (isset($_ENV['JAEGER_SAMPLER_TYPE']) && !isset($this->config['sampler']['type'])) {
$this->config['sampler']['type'] = $_ENV['JAEGER_SAMPLER_TYPE'];
}
if (isset($_ENV['JAEGER_SAMPLER_PARAM']) && !isset($this->config['sampler']['param'])) {
$this->config['sampler']['param'] = $_ENV['JAEGER_SAMPLER_PARAM'];
}
if (isset($_ENV['IP_VERSION']) && !isset($this->config[Config::IP_VERSION])) {
$this->config[Config::IP_VERSION] = $_ENV['IP_VERSION'];
}
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace Jaeger;
// Max number of bits to use when generating random ID
const MAX_ID_BITS = 64;
// How often remotely controller sampler polls for sampling strategy
const DEFAULT_SAMPLING_INTERVAL = 60;
// How often remote reporter does a preemptive flush of its buffers
const DEFAULT_FLUSH_INTERVAL = 1;
// Name of the HTTP header used to encode trace ID
const TRACE_ID_HEADER = 'uber-trace-id';
// Prefix for HTTP headers used to record baggage items
const BAGGAGE_HEADER_PREFIX = 'uberctx-';
// The name of HTTP header or a TextMap carrier key which, if found in the
// carrier, forces the trace to be sampled as "debug" trace. The value of the
// header is recorded as the tag on the # root span, so that the trace can
// be found in the UI using this value as a correlation ID.
const DEBUG_ID_HEADER_KEY = 'jaeger-debug-id';
const JAEGER_CLIENT_VERSION = 'PHP-' . PHP_VERSION;
// Tracer-scoped tag that tells the version of Jaeger client library
const JAEGER_VERSION_TAG_KEY = 'jaeger.version';
// Tracer-scoped tag that contains the hostname
const JAEGER_HOSTNAME_TAG_KEY = 'jaeger.hostname';
const SAMPLER_TYPE_TAG_KEY = 'sampler.type';
const SAMPLER_PARAM_TAG_KEY = 'sampler.param';
const DEFAULT_SAMPLING_PROBABILITY = 0.001;
const DEFAULT_LOWER_BOUND = 1.0 / (10.0 * 60.0); # sample once every 10 minutes
const DEFAULT_MAX_OPERATIONS = 2000;
const STRATEGIES_STR = 'perOperationStrategies';
const OPERATION_STR = 'operation';
const DEFAULT_LOWER_BOUND_STR = 'defaultLowerBoundTracesPerSecond';
const PROBABILISTIC_SAMPLING_STR = 'probabilisticSampling';
const SAMPLING_RATE_STR = 'samplingRate';
const DEFAULT_SAMPLING_PROBABILITY_STR = 'defaultSamplingProbability';
const OPERATION_SAMPLING_STR = 'operationSampling';
const MAX_TRACES_PER_SECOND_STR = 'maxTracesPerSecond';
const RATE_LIMITING_SAMPLING_STR = 'rateLimitingSampling';
const STRATEGY_TYPE_STR = 'strategyType';
// the type of sampler that always makes the same decision.
const SAMPLER_TYPE_CONST = 'const';
// the type of sampler that polls Jaeger agent for sampling strategy.
const SAMPLER_TYPE_REMOTE = 'remote';
// the type of sampler that samples traces with a certain fixed probability.
const SAMPLER_TYPE_PROBABILISTIC = 'probabilistic';
// the type of sampler that samples only up to a fixed number
// of traces per second.
// noinspection SpellCheckingInspection
const SAMPLER_TYPE_RATE_LIMITING = 'ratelimiting';
// the type of sampler that samples only up to a fixed number
// of traces per second.
// noinspection SpellCheckingInspection
const SAMPLER_TYPE_LOWER_BOUND = 'lowerbound';
const DEFAULT_REPORTING_HOST = 'localhost';
/** @deprecated */
const DEFAULT_REPORTING_PORT = 5775;
const DEFAULT_ZIPKIN_UDP_COMPACT_REPORTING_PORT = 5775;
const DEFAULT_JAEGER_UDP_BINARY_REPORTING_PORT = 6832;
const DEFAULT_JAEGER_HTTP_BINARY_REPORTING_PORT = 14268;
const DEFAULT_SAMPLING_PORT = 5778;
const LOCAL_AGENT_DEFAULT_ENABLED = true;
const ZIPKIN_SPAN_FORMAT = 'zipkin-span-format';
const SAMPLED_FLAG = 0x01;
const DEBUG_FLAG = 0x02;

View File

@ -0,0 +1,171 @@
<?php
namespace Jaeger\Mapper;
use Jaeger\Span;
use Jaeger\Thrift\Agent\Zipkin\AnnotationType;
use Jaeger\Thrift\Agent\Zipkin\BinaryAnnotation;
use Jaeger\Thrift\Log;
use Jaeger\Thrift\Span as JaegerThriftSpan;
use Jaeger\Thrift\Tag;
use Jaeger\Thrift\TagType;
use const OpenTracing\Tags\COMPONENT;
use const OpenTracing\Tags\PEER_HOST_IPV4;
use const OpenTracing\Tags\PEER_PORT;
use const OpenTracing\Tags\PEER_SERVICE;
use const OpenTracing\Tags\SPAN_KIND;
class SpanToJaegerMapper
{
private $specialSpanTags = ["jaeger.hostname", "jaeger.version"];
private $processTagsPrefix = "process.";
/**
* @return string[]
*/
public function getSpecialSpanTags(): array
{
return $this->specialSpanTags;
}
/**
* @return string
*/
public function getProcessTagsPrefix(): string
{
return $this->processTagsPrefix;
}
public function mapSpanToJaeger(Span $span) : JaegerThriftSpan
{
$timestamp = $span->getStartTime();
$duration = $span->getEndTime() - $span->getStartTime();
/** @var Tag[] $tags */
$tags = [];
$tags[] = new Tag([
"key" => COMPONENT,
"vType" => TagType::STRING,
"vStr" => $span->getComponent() ?? $span->getTracer()->getServiceName(),
]);
// Handle special tags
$peerService = $span->peer['service_name'] ?? null;
if ($peerService !== null) {
$tags[] = new Tag([
"key" => PEER_SERVICE,
"vType" => TagType::STRING,
"vStr" => $peerService,
]);
}
$peerHostIpv4 = $span->peer['ipv4'] ?? null;
if ($peerHostIpv4 !== null) {
$tags[] = new Tag([
"key" => PEER_HOST_IPV4,
"vType" => TagType::STRING,
"vStr" => $peerHostIpv4,
]);
}
$peerPort = $span->peer['port'] ?? null;
if ($peerPort !== null) {
$tags[] = new Tag([
"key" => PEER_PORT,
"vType" => TagType::LONG,
"vLong" => $peerPort,
]);
}
$spanKind = $span->getKind();
if ($spanKind !== null) {
$tags[] = new Tag([
"key" => SPAN_KIND,
"vType" => TagType::STRING,
"vStr" => $spanKind,
]);
}
/** @var BinaryAnnotation[] $binaryAnnotationTags */
$binaryAnnotationTags = $span->getTags();
foreach ($binaryAnnotationTags as $binaryAnnotationTag) {
if (in_array($binaryAnnotationTag->key, $this->specialSpanTags, true)) {
continue ;
}
if (strpos($binaryAnnotationTag->key, $this->processTagsPrefix) === 0) {
continue;
}
$type = "";
$vkey = "";
switch ($binaryAnnotationTag->annotation_type) {
case AnnotationType::BOOL:
$type = TagType::BOOL;
$vkey = "vBool";
break;
case AnnotationType::BYTES:
$type = TagType::BINARY;
$vkey = "vBinary";
break;
case AnnotationType::DOUBLE:
$type = TagType::DOUBLE;
$vkey = "vDouble";
break;
case AnnotationType::I16:
case AnnotationType::I32:
case AnnotationType::I64:
$type = TagType::LONG;
$vkey = "vLong";
break;
default:
$type = TagType::STRING;
$vkey = "vStr";
}
$tags[] = new Tag([
"key" => $binaryAnnotationTag->key,
"vType" => $type,
$vkey => $binaryAnnotationTag->value,
]);
}
/** @var Log[] $logs */
$logs = [];
$spanLogs = $span->getLogs();
foreach ($spanLogs as $spanLog) {
/** @var Tag $fields */
$fields = [];
if (!empty($spanLog["fields"])) {
$fields[] = new Tag([
"key" => "event",
"vType" => TagType::STRING,
"vStr" => json_encode($spanLog["fields"])
]);
}
$logs[] = new Log([
"timestamp" => $spanLog["timestamp"],
"fields" => $fields
]);
}
return new JaegerThriftSpan([
"traceIdLow" => (int)$span->getContext()->getTraceId(),
"traceIdHigh" => 0,
"spanId" => (int)$span->getContext()->getSpanId(),
"parentSpanId" => (int)$span->getContext()->getParentId(),
"operationName" => $span->getOperationName(),
"startTime" => $timestamp,
"duration" => $duration,
"flags" => (int)$span->isDebug(),
"tags" => $tags,
"logs" => $logs
]);
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Span;
/**
* CompositeReporter delegates reporting to one or more underlying reporters.
*/
class CompositeReporter implements ReporterInterface
{
/**
* @var ReporterInterface[]
*/
private $reporters;
/**
* CompositeReporter constructor.
*
* @param ReporterInterface ...$reporters
*/
public function __construct(ReporterInterface ...$reporters)
{
$this->reporters = $reporters;
}
/**
* {@inheritdoc}
*
* @param Span $span
* @return void
*/
public function reportSpan(Span $span)
{
foreach ($this->reporters as $reporter) {
$reporter->reportSpan($span);
}
}
/**
* {@inheritdoc}
*
* @return void
*/
public function close()
{
foreach ($this->reporters as $reporter) {
$reporter->close();
}
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Span;
/**
* InMemoryReporter stores spans in memory and returns them via getSpans().
*/
class InMemoryReporter implements ReporterInterface
{
/**
* @var Span[]
*/
private $spans = [];
/**
* {@inheritdoc}
*
* @param Span $span
* @return void
*/
public function reportSpan(Span $span)
{
$this->spans[] = $span;
}
/**
* @return Span[]
*/
public function getSpans(): array
{
return $this->spans;
}
/**
* {@inheritdoc}
*
* Only implemented to satisfy the sampler interface.
*
* @return void
*/
public function close()
{
// nothing to do
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Sender\SenderInterface;
use Jaeger\Span;
class JaegerReporter implements ReporterInterface
{
/**
* @var SenderInterface
*/
private $sender;
/**
* RemoteReporter constructor.
*
* @param SenderInterface $sender
*/
public function __construct(SenderInterface $sender)
{
$this->sender = $sender;
}
public function reportSpan(Span $span)
{
$this->sender->append($span);
}
public function close()
{
$this->sender->flush();
$this->sender->close();
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Span;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
/**
* LoggingReporter logs all spans.
*/
class LoggingReporter implements ReporterInterface
{
/**
* @var LoggerInterface
*/
private $logger;
/**
* LoggingReporter constructor.
*
* @param LoggerInterface|null $logger
*/
public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger ?? new NullLogger();
}
/**
* {@inheritdoc}
*
* @param Span $span
* @return void
*/
public function reportSpan(Span $span)
{
$this->logger->debug('Reporting span ' . $span->getOperationName());
}
/**
* {@inheritdoc}
*
* Only implemented to satisfy the sampler interface.
*
* @return void
*/
public function close()
{
// nothing to do
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Span;
/**
* NullReporter ignores all spans.
*/
class NullReporter implements ReporterInterface
{
/**
* {@inheritdoc}
*
* @param Span $span
* @return void
*/
public function reportSpan(Span $span)
{
// nothing to do
}
/**
* {@inheritdoc}
*
* @return void
*/
public function close()
{
// nothing to do
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Sender\UdpSender;
use Jaeger\Span;
class RemoteReporter implements ReporterInterface
{
/**
* @var UdpSender
*/
private $transport;
/**
* RemoteReporter constructor.
*
* @param UdpSender $transport
*/
public function __construct(UdpSender $transport)
{
$this->transport = $transport;
}
/**
* {@inheritdoc}
*
* @param Span $span
* @return void
*/
public function reportSpan(Span $span)
{
$this->transport->append($span);
}
/**
* {@inheritdoc}
*
* @return void
*/
public function close()
{
$this->transport->flush();
$this->transport->close();
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Jaeger\Reporter;
use Jaeger\Span;
/**
* Uses to report finished span to something that collects those spans.
*
* @package Jaeger\Reporter
*/
interface ReporterInterface
{
/**
* Report finished span.
*
* @param Span $span
* @return void
*/
public function reportSpan(Span $span);
/**
* Release any resources used by the reporter and flushes/sends the data.
*
* @return void
*/
public function close();
}

View File

@ -0,0 +1,23 @@
<?php
namespace Jaeger\ReporterFactory;
use Jaeger\Config;
use Thrift\Transport\TTransport;
abstract class AbstractReporterFactory implements ReporterFactoryInterface
{
/**
* @var Config
*/
protected $config;
/**
* AbstractReporterFactory constructor.
* @param Config $config
*/
public function __construct($config)
{
$this->config = $config;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Jaeger\ReporterFactory;
use Jaeger\AgentClient\HttpAgentClient;
use Jaeger\Reporter\JaegerReporter;
use Jaeger\Reporter\ReporterInterface;
use Jaeger\Sender\JaegerSender;
use Thrift\Exception\TTransportException;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\THttpClient;
class JaegerHttpReporterFactory extends AbstractReporterFactory implements ReporterFactoryInterface
{
public function createReporter() : ReporterInterface
{
$transport = new THttpClient(
$this->config->getLocalAgentReportingHost(),
$this->config->getLocalAgentReportingPort(),
"/api/traces"
);
try {
$transport->open();
} catch (TTransportException $e) {
$this->config->getLogger()->warning($e->getMessage());
}
$protocol = new TBinaryProtocol($transport);
$client = new HttpAgentClient($protocol);
$this->config->getLogger()->debug('Initializing HTTP Jaeger Tracer with Jaeger.Thrift over Binary protocol');
$sender = new JaegerSender($client, $this->config->getLogger());
$sender->setMaxBufferLength($this->config->getMaxBufferLength());
return new JaegerReporter($sender);
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Jaeger\ReporterFactory;
use Jaeger\AgentClient\HttpAgentClient;
use Jaeger\Reporter\JaegerReporter;
use Jaeger\Reporter\ReporterInterface;
use Jaeger\Sender\JaegerSender;
use Jaeger\Thrift\Agent\AgentClient;
use Jaeger\ThriftUdpTransport;
use Thrift\Exception\TTransportException;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TBufferedTransport;
class JaegerReporterFactory extends AbstractReporterFactory implements ReporterFactoryInterface
{
public function createReporter() : ReporterInterface
{
$udp = new ThriftUdpTransport(
$this->config->getLocalAgentReportingHost(),
$this->config->getLocalAgentReportingPort(),
$this->config->getLogger(),
$this->config
);
$transport = new TBufferedTransport(
$udp,
$this->config->getMaxBufferLength(),
$this->config->getMaxBufferLength()
);
try {
$transport->open();
} catch (TTransportException $e) {
$this->config->getLogger()->warning($e->getMessage());
}
$protocol = new TBinaryProtocol($transport);
$client = new AgentClient($protocol);
$this->config->getLogger()->debug('Initializing UDP Jaeger Tracer with Jaeger.Thrift over Binary protocol');
$sender = new JaegerSender($client, $this->config->getLogger());
$sender->setMaxBufferLength($this->config->getMaxBufferLength());
return new JaegerReporter($sender);
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Jaeger\ReporterFactory;
use Jaeger\Reporter\ReporterInterface;
interface ReporterFactoryInterface
{
public function createReporter() : ReporterInterface;
}

View File

@ -0,0 +1,46 @@
<?php
namespace Jaeger\ReporterFactory;
use Jaeger\Reporter\JaegerReporter;
use Jaeger\Reporter\RemoteReporter;
use Jaeger\Reporter\ReporterInterface;
use Jaeger\Sender\JaegerSender;
use Jaeger\Sender\UdpSender;
use Jaeger\Thrift\Agent\AgentClient;
use Jaeger\ThriftUdpTransport;
use Thrift\Exception\TTransportException;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Protocol\TCompactProtocol;
use Thrift\Transport\TBufferedTransport;
use Thrift\Transport\TTransport;
class ZipkinReporterFactory extends AbstractReporterFactory implements ReporterFactoryInterface
{
public function createReporter() : ReporterInterface
{
$udp = new ThriftUdpTransport(
$this->config->getLocalAgentReportingHost(),
$this->config->getLocalAgentReportingPort(),
$this->config->getLogger(),
$this->config
);
$transport = new TBufferedTransport(
$udp,
$this->config->getMaxBufferLength(),
$this->config->getMaxBufferLength()
);
try {
$transport->open();
} catch (TTransportException $e) {
$this->config->getLogger()->warning($e->getMessage());
}
$protocol = new TCompactProtocol($transport);
$client = new AgentClient($protocol);
$this->config->getLogger()->debug('Initializing UDP Jaeger Tracer with Zipkin.Thrift over Compact protocol');
$sender = new UdpSender($client, $this->config->getMaxBufferLength(), $this->config->getLogger());
return new RemoteReporter($sender);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace Jaeger\Sampler;
use const Jaeger\SAMPLER_PARAM_TAG_KEY;
use const Jaeger\SAMPLER_TYPE_CONST;
use const Jaeger\SAMPLER_TYPE_TAG_KEY;
/**
* ConstSampler always returns the same decision.
*
* @package Jaeger\Sampler
*/
class ConstSampler implements SamplerInterface
{
/**
* Whether or not the new trace should be sampled.
*
* @var bool
*/
private $decision;
/**
* A list of the sampler tags.
*
* @var array
*/
private $tags = [];
/**
* ConstSampler constructor.
*
* @param bool $decision
*/
public function __construct(bool $decision = true)
{
$this->tags = [
SAMPLER_TYPE_TAG_KEY => SAMPLER_TYPE_CONST,
SAMPLER_PARAM_TAG_KEY => $decision,
];
$this->decision = $decision;
}
/**
* {@inheritdoc}
*
* @param string $traceId The traceId on the span.
* @param string $operation The operation name set on the span.
* @return array
*/
public function isSampled(string $traceId, string $operation = ''): array
{
return [$this->decision, $this->tags];
}
/**
* {@inheritdoc}
*
* Only implemented to satisfy the sampler interface.
*
* @return void
*/
public function close()
{
// nothing to do
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace Jaeger\Sampler;
use OutOfBoundsException;
use const Jaeger\SAMPLER_PARAM_TAG_KEY;
use const Jaeger\SAMPLER_TYPE_PROBABILISTIC;
use const Jaeger\SAMPLER_TYPE_TAG_KEY;
/**
* A sampler that randomly samples a certain percentage of traces specified
* by the samplingRate, in the range between 0.0 and 1.0.
*
* @package Jaeger\Sampler
*/
class ProbabilisticSampler implements SamplerInterface
{
/**
* The sampling rate rate between 0.0 and 1.0.
*
* @var float
*/
private $rate;
/**
* A list of the sampler tags.
*
* @var array
*/
private $tags = [];
/**
* The boundary of the sample sampling rate.
*
* @var float
*/
private $boundary;
/**
* ProbabilisticSampler constructor.
*
* @param float $rate
* @throws OutOfBoundsException
*/
public function __construct(float $rate)
{
$this->tags = [
SAMPLER_TYPE_TAG_KEY => SAMPLER_TYPE_PROBABILISTIC,
SAMPLER_PARAM_TAG_KEY => $rate,
];
if ($rate < 0.0 || $rate > 1.0) {
throw new OutOfBoundsException('Sampling rate must be between 0.0 and 1.0.');
}
$this->rate = $rate;
if ($rate < 0.5) {
$this->boundary = (int)($rate * PHP_INT_MAX);
} else {
// more precise calculation due to int and float having different precision near PHP_INT_MAX
$this->boundary = PHP_INT_MAX - (int)((1 - $rate) * PHP_INT_MAX);
}
}
/**
* {@inheritdoc}
*
* @param string $traceId The traceId on the span.
* @param string $operation The operation name set on the span.
* @return array
*/
public function isSampled(string $traceId, string $operation = ''): array
{
return [($traceId < $this->boundary), $this->tags];
}
/**
* {@inheritdoc}
*
* Only implemented to satisfy the sampler interface.
*
* @return void
*/
public function close()
{
// nothing to do
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace Jaeger\Sampler;
use Jaeger\Util\RateLimiter;
use const Jaeger\SAMPLER_PARAM_TAG_KEY;
use const Jaeger\SAMPLER_TYPE_RATE_LIMITING;
use const Jaeger\SAMPLER_TYPE_TAG_KEY;
class RateLimitingSampler implements SamplerInterface
{
/**
* @var RateLimiter
*/
private $rateLimiter;
/**
* A list of the sampler tags.
*
* @var array
*/
private $tags = [];
public function __construct($maxTracesPerSecond, RateLimiter $rateLimiter)
{
$this->tags = [
SAMPLER_TYPE_TAG_KEY => SAMPLER_TYPE_RATE_LIMITING,
SAMPLER_PARAM_TAG_KEY => $maxTracesPerSecond,
];
$maxTracesPerNanosecond = $maxTracesPerSecond / 1000000000.0;
$this->rateLimiter = $rateLimiter;
$this->rateLimiter->initialize($maxTracesPerNanosecond, $maxTracesPerSecond > 1.0 ? 1.0 : $maxTracesPerSecond);
}
/**
* Whether or not the new trace should be sampled.
*
* Implementations should return an array in the format [$decision, $tags].
*
* @param string $traceId The traceId on the span.
* @param string $operation The operation name set on the span.
* @return array
*/
public function isSampled(string $traceId = '', string $operation = '')
{
return [$this->rateLimiter->checkCredit(1.0), $this->tags];
}
/**
* {@inheritdoc}
*
* Only implemented to satisfy the sampler interface.
*
* @return void
*/
public function close()
{
// nothing to do
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Jaeger\Sampler;
/**
* Sampler is responsible for deciding if a new trace should be sampled and captured for storage.
*
* @package Jaeger\Sampler
*/
interface SamplerInterface
{
/**
* Whether or not the new trace should be sampled.
*
* Implementations should return an array in the format [$decision, $tags].
*
* @param string $traceId The traceId on the span.
* @param string $operation The operation name set on the span.
* @return array
*/
public function isSampled(string $traceId, string $operation);
/**
* Release any resources used by the sampler.
*
* @return void
*/
public function close();
}

View File

@ -0,0 +1,71 @@
<?php
namespace Jaeger;
use OpenTracing\Scope as OTScope;
use OpenTracing\Span as OTSpan;
/**
* {@inheritdoc}
*/
class Scope implements OTScope
{
/**
* @var ScopeManager
*/
private $scopeManager;
/**
* @var OTSpan
*/
private $wrapped;
/**
* @var OTScope|null
*/
private $toRestore;
/**
* @var bool
*/
private $finishSpanOnClose;
/**
* Scope constructor.
* @param ScopeManager $scopeManager
* @param OTSpan $wrapped
* @param bool $finishSpanOnClose
*/
public function __construct(ScopeManager $scopeManager, OTSpan $wrapped, bool $finishSpanOnClose)
{
$this->scopeManager = $scopeManager;
$this->wrapped = $wrapped;
$this->finishSpanOnClose = $finishSpanOnClose;
$this->toRestore = $scopeManager->getActive();
}
/**
* {@inheritdoc}
*/
public function close(): void
{
if ($this->scopeManager->getActive() !== $this) {
// This shouldn't happen if users call methods in expected order
return;
}
if ($this->finishSpanOnClose) {
$this->wrapped->finish();
}
$this->scopeManager->setActive($this->toRestore);
}
/**
* {@inheritdoc}
*/
public function getSpan(): OTSpan
{
return $this->wrapped;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Jaeger;
use OpenTracing\ScopeManager as OTScopeManager;
use OpenTracing\Span as OTSpan;
use OpenTracing\Scope as OTScope;
/**
* {@inheritdoc}
*/
class ScopeManager implements OTScopeManager
{
/**
* @var OTScope
*/
private $active;
/**
* {@inheritdoc}
*/
public function activate(OTSpan $span, bool $finishSpanOnClose = self::DEFAULT_FINISH_SPAN_ON_CLOSE): OTScope
{
$this->active = new Scope($this, $span, $finishSpanOnClose);
return $this->active;
}
/**
* {@inheritdoc}
*/
public function getActive(): ?OTScope
{
return $this->active;
}
/**
* Sets the scope as active.
* @param OTScope|null $scope
*/
public function setActive(OTScope $scope = null)
{
$this->active = $scope;
}
}

View File

@ -0,0 +1,236 @@
<?php
namespace Jaeger\Sender;
use Jaeger\Mapper\SpanToJaegerMapper;
use Jaeger\Span as JaegerSpan;
use Jaeger\Thrift\Agent\AgentClient;
use Jaeger\Thrift\Agent\AgentIf;
use Jaeger\Thrift\Batch;
use Jaeger\Thrift\Process;
use Jaeger\Thrift\Span as JaegerThriftSpan;
use Jaeger\Thrift\Tag;
use Jaeger\Thrift\TagType;
use Jaeger\Tracer;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Protocol\TCompactProtocol;
use Thrift\Transport\TMemoryBuffer;
use const Jaeger\JAEGER_HOSTNAME_TAG_KEY;
class JaegerSender implements SenderInterface
{
/**
* @var JaegerSpan[]
*/
private $spans = [];
/**
* @var AgentIf
*/
private $agentClient;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var Tracer
*/
private $tracer;
/**
* @var SpanToJaegerMapper
*/
private $mapper;
/**
* @var int
*/
private $jaegerBatchOverheadLength = 512;
/**
* The maximum length of the thrift-objects for a jaeger-batch.
*
* @var int
*/
private $maxBufferLength = 64000;
/**
* @param AgentIf $agentClient
* @param LoggerInterface|null $logger
* @param SpanToJaegerMapper|null $mapper
*/
public function __construct(
AgentIf $agentClient,
LoggerInterface $logger = null,
SpanToJaegerMapper $mapper = null
) {
$this->agentClient = $agentClient;
$this->logger = $logger ?? new NullLogger();
$this->mapper = $mapper ?? new SpanToJaegerMapper();
}
public function flush(): int
{
$count = count($this->spans);
if ($count === 0) {
return 0;
}
$jaegerThriftSpans = $this->makeJaegerBatch($this->spans);
try {
$this->send($jaegerThriftSpans);
} catch (\Exception $e) {
$this->logger->warning($e->getMessage());
}
$this->spans = [];
return $count;
}
public function setMaxBufferLength($maxBufferLength)
{
$this->maxBufferLength = $maxBufferLength;
}
/**
* @param JaegerSpan[] $spans
* @return array
*/
private function makeJaegerBatch(array $spans) : array
{
/** @var JaegerThriftSpan[] $jaegerSpans */
$jaegerSpans = [];
foreach ($spans as $span) {
if (empty($this->tracer)) {
$this->tracer = $span->getTracer();
}
$jaegerSpans[] = $this->mapper->mapSpanToJaeger($span);
}
return $jaegerSpans;
}
/**
* @param JaegerThriftSpan[] $spans
*/
private function send(array $spans)
{
if (empty($this->tracer)) {
return ;
}
$chunks = $this->chunkSplit($spans);
foreach ($chunks as $chunk) {
/** @var JaegerThriftSpan[] $chunk */
$this->emitJaegerBatch($chunk);
}
}
/**
* @param JaegerThriftSpan $span
*/
private function getBufferLength($span)
{
$memoryBuffer = new TMemoryBuffer();
$span->write(new TBinaryProtocol($memoryBuffer));
return $memoryBuffer->available();
}
private function chunkSplit(array $spans): array
{
$actualBufferSize = $this->jaegerBatchOverheadLength;
$chunkId = 0;
$chunks = [];
foreach ($spans as $span) {
$spanBufferLength = $this->getBufferLength($span);
if (!empty($chunks[$chunkId]) && ($actualBufferSize + $spanBufferLength) > $this->maxBufferLength) {
// point to next chunk
++$chunkId;
// reset buffer size
$actualBufferSize = $this->jaegerBatchOverheadLength;
}
if (!isset($chunks[$chunkId])) {
$chunks[$chunkId] = [];
}
$chunks[$chunkId][] = $span;
$actualBufferSize += $spanBufferLength;
}
return $chunks;
}
protected function emitJaegerBatch(array $spans)
{
/** @var Tag[] $tags */
$tags = [];
foreach ($this->tracer->getTags() as $k => $v) {
if (!in_array($k, $this->mapper->getSpecialSpanTags())) {
if (strpos($k, $this->mapper->getProcessTagsPrefix()) !== 0) {
continue ;
}
$quoted = preg_quote($this->mapper->getProcessTagsPrefix());
$k = preg_replace(sprintf('/^%s/', $quoted), '', $k);
}
if ($k === JAEGER_HOSTNAME_TAG_KEY) {
$k = "hostname";
}
$tags[] = new Tag([
"key" => $k,
"vType" => TagType::STRING,
"vStr" => $v
]);
}
$tags[] = new Tag([
"key" => "format",
"vType" => TagType::STRING,
"vStr" => "jaeger.thrift"
]);
$tags[] = new Tag([
"key" => "ip",
"vType" => TagType::STRING,
"vStr" => $this->tracer->getIpAddress()
]);
$batch = new Batch([
"spans" => $spans,
"process" => new Process([
"serviceName" => $this->tracer->getServiceName(),
"tags" => $tags
])
]);
$this->agentClient->emitBatch($batch);
}
/**
* @param JaegerSpan $span
*/
public function append(JaegerSpan $span)
{
$this->spans[] = $span;
}
public function close()
{
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace Jaeger\Sender;
use Jaeger\Span as JaegerSpan;
interface SenderInterface
{
public function flush(): int;
public function append(JaegerSpan $span);
public function close();
}

View File

@ -0,0 +1,305 @@
<?php
namespace Jaeger\Sender;
use Exception;
use Jaeger\Thrift\Agent\AgentClient;
use Jaeger\Thrift\Agent\Zipkin\Annotation;
use Jaeger\Thrift\Agent\Zipkin\AnnotationType;
use Jaeger\Thrift\Agent\Zipkin\BinaryAnnotation;
use Jaeger\Thrift\Agent\Zipkin\Endpoint;
use Jaeger\Thrift\Agent\Zipkin\Span as ThriftSpan;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Thrift\Base\TBase;
use Thrift\Protocol\TCompactProtocol;
use Thrift\Transport\TMemoryBuffer;
use Jaeger\Span as JaegerSpan;
use const OpenTracing\Tags\COMPONENT;
class UdpSender
{
const CLIENT_ADDR = "ca";
const SERVER_ADDR = "sa";
/**
* @var JaegerSpan[]
*/
private $spans = [];
/**
* @var AgentClient
*/
private $client;
/**
* @var LoggerInterface
*/
private $logger;
/**
* The maximum length of the thrift-objects for a zipkin-batch.
*
* @var int
*/
private $maxBufferLength;
/**
* The length of the zipkin-batch overhead.
*
* @var int
*/
private $zipkinBatchOverheadLength = 30;
/**
* UdpSender constructor.
*
* @param AgentClient $client
* @param int $maxBufferLength
* @param LoggerInterface|null $logger
*/
public function __construct(
AgentClient $client,
int $maxBufferLength,
LoggerInterface $logger = null
) {
$this->client = $client;
$this->maxBufferLength = $maxBufferLength;
$this->logger = $logger ?? new NullLogger();
}
/**
* @param JaegerSpan $span
*/
public function append(JaegerSpan $span)
{
$this->spans[] = $span;
}
/**
* @return int the number of flushed spans
*/
public function flush(): int
{
$count = count($this->spans);
if ($count === 0) {
return 0;
}
$zipkinSpans = $this->makeZipkinBatch($this->spans);
try {
$this->send($zipkinSpans);
} catch (Exception $e) {
$this->logger->warning($e->getMessage());
}
$this->spans = [];
return $count;
}
public function close()
{
}
/**
* Emits the thrift-objects.
*
* @param array|ThriftSpan[]|TBase[] $thrifts
*/
private function send(array $thrifts)
{
foreach ($this->chunkSplit($thrifts) as $chunk) {
/* @var $chunk ThriftSpan[] */
$this->client->emitZipkinBatch($chunk);
}
}
/**
* @param JaegerSpan[] $spans
* @return ThriftSpan[]
*/
private function makeZipkinBatch(array $spans): array
{
/** @var ThriftSpan[] */
$zipkinSpans = [];
foreach ($spans as $span) {
/** @var JaegerSpan $span */
$endpoint = $this->makeEndpoint(
$span->getTracer()->getIpAddress(),
0, // span.port,
$span->getTracer()->getServiceName()
);
$timestamp = $span->getStartTime();
$duration = $span->getEndTime() - $span->getStartTime();
$this->addZipkinAnnotations($span, $endpoint);
$zipkinSpan = new ThriftSpan([
'name' => $span->getOperationName(),
'id' => $span->getContext()->getSpanId(),
'parent_id' => $span->getContext()->getParentId() ?? null,
'trace_id' => $span->getContext()->getTraceId(),
'annotations' => $this->createAnnotations($span, $endpoint),
'binary_annotations' => $span->getTags(),
'debug' => $span->isDebug(),
'timestamp' => $timestamp,
'duration' => $duration,
]);
$zipkinSpans[] = $zipkinSpan;
}
return $zipkinSpans;
}
private function addZipkinAnnotations(JaegerSpan $span, Endpoint $endpoint)
{
if ($span->isRpc() && $span->peer) {
$isClient = $span->isRpcClient();
$host = $this->makeEndpoint(
$span->peer['ipv4'] ?? 0,
$span->peer['port'] ?? 0,
$span->peer['service_name'] ?? ''
);
$key = ($isClient) ? self::SERVER_ADDR : self::CLIENT_ADDR;
$peer = $this->makePeerAddressTag($key, $host);
$span->tags[$key] = $peer;
} else {
$tag = $this->makeLocalComponentTag(
$span->getComponent() ?? $span->getTracer()->getServiceName(),
$endpoint
);
$span->tags[COMPONENT] = $tag;
}
}
private function makeLocalComponentTag(string $componentName, Endpoint $endpoint): BinaryAnnotation
{
return new BinaryAnnotation([
'key' => "lc",
'value' => $componentName,
'annotation_type' => AnnotationType::STRING,
'host' => $endpoint,
]);
}
private function makeEndpoint(string $ipv4, int $port, string $serviceName): Endpoint
{
$ipv4 = $this->ipv4ToInt($ipv4);
return new Endpoint([
'ipv4' => $ipv4,
'port' => $port,
'service_name' => $serviceName,
]);
}
private function ipv4ToInt(string $ipv4): int
{
if ($ipv4 == 'localhost') {
$ipv4 = '127.0.0.1';
} elseif ($ipv4 == '::1') {
$ipv4 = '127.0.0.1';
}
$long = ip2long($ipv4);
if (PHP_INT_SIZE === 8) {
return $long >> 31 ? $long - (1 << 32) : $long;
}
return $long;
}
// Used for Zipkin binary annotations like CA/SA (client/server address).
// They are modeled as Boolean type with '0x01' as the value.
private function makePeerAddressTag(string $key, Endpoint $host): BinaryAnnotation
{
return new BinaryAnnotation([
"key" => $key,
"value" => '0x01',
"annotation_type" => AnnotationType::BOOL,
"host" => $host,
]);
}
/**
* Splits an array of thrift-objects into several chunks when the buffer limit has been reached.
*
* @param array|ThriftSpan[]|TBase[] $thrifts
*
* @return array
*/
private function chunkSplit(array $thrifts): array
{
$actualBufferSize = $this->zipkinBatchOverheadLength;
$chunkId = 0;
$chunks = [];
foreach ($thrifts as $thrift) {
$spanBufferLength = $this->getBufferLength($thrift);
if (!empty($chunks[$chunkId]) && ($actualBufferSize + $spanBufferLength) > $this->maxBufferLength) {
// point to next chunk
++$chunkId;
// reset buffer size
$actualBufferSize = $this->zipkinBatchOverheadLength;
}
if (!isset($chunks[$chunkId])) {
$chunks[$chunkId] = [];
}
$chunks[$chunkId][] = $thrift;
$actualBufferSize += $spanBufferLength;
}
return $chunks;
}
/**
* Returns the length of a thrift-object.
*
* @param ThriftSpan|TBase $thrift
*
* @return int
*/
private function getBufferLength($thrift): int
{
$memoryBuffer = new TMemoryBuffer();
$thrift->write(new TCompactProtocol($memoryBuffer));
return $memoryBuffer->available();
}
/*
* @param JaegerSpan $span
* @param Endpoint $endpoint
*
* @return array|Annotation[]
*/
private function createAnnotations(JaegerSpan $span, Endpoint $endpoint): array
{
$annotations = [];
foreach ($span->getLogs() as $values) {
$annotations[] = new Annotation([
'timestamp' => $values['timestamp'],
'value' => json_encode($values['fields']),
'host' => $endpoint,
]);
}
return $annotations;
}
}

View File

@ -0,0 +1,476 @@
<?php
namespace Jaeger;
use Jaeger\Thrift\Agent\Zipkin\AnnotationType;
use Jaeger\Thrift\Agent\Zipkin\BinaryAnnotation;
use OpenTracing\Span as OTSpan;
use DateTime;
use DateTimeInterface;
use OpenTracing\SpanContext as OTSpanContext;
use const OpenTracing\Tags\COMPONENT;
use const OpenTracing\Tags\PEER_HOST_IPV4;
use const OpenTracing\Tags\PEER_PORT;
use const OpenTracing\Tags\PEER_SERVICE;
use const OpenTracing\Tags\SPAN_KIND;
use const OpenTracing\Tags\SPAN_KIND_MESSAGE_BUS_CONSUMER;
use const OpenTracing\Tags\SPAN_KIND_MESSAGE_BUS_PRODUCER;
use const OpenTracing\Tags\SPAN_KIND_RPC_CLIENT;
use const OpenTracing\Tags\SPAN_KIND_RPC_SERVER;
class Span implements OTSpan
{
/**
* @var Tracer
*/
private $tracer;
/**
* @var SpanContext
*/
private $context;
/**
* @var string
*/
private $operationName;
/**
* @var int|float|DateTime|null
*/
private $startTime;
/**
* @var int|float|DateTime|null
*/
private $endTime;
/**
* SPAN_RPC_CLIENT
* @var null|string
*/
private $kind;
/**
* @var array|null
*/
public $peer;
/**
* @var string|null
*/
private $component;
/**
* @var array
*/
private $logs = [];
/**
* @var BinaryAnnotation[]
*/
public $tags = [];
/**
* @var bool
*/
private $debug = false;
/**
* Span constructor.
* @param SpanContext $context
* @param Tracer $tracer
* @param string $operationName
* @param array $tags
* @param int|float|DateTime|null $startTime
*/
public function __construct(
SpanContext $context,
Tracer $tracer,
string $operationName,
array $tags = [],
$startTime = null
) {
$this->context = $context;
$this->tracer = $tracer;
$this->operationName = $operationName;
$this->startTime = $this->microTime($startTime);
$this->endTime = null;
$this->kind = null;
$this->peer = null;
$this->component = null;
foreach ($tags as $key => $value) {
$this->setTag($key, $value);
}
}
/**
* Converts time to microtime int
* - int represents microseconds
* - float represents seconds
*
* @param int|float|DateTime|null $time
* @return int
*/
protected function microTime($time): int
{
if ($time === null) {
return $this->timestampMicro();
}
if ($time instanceof \DateTimeInterface) {
return (int)round($time->format('U.u') * 1000000, 0);
}
if (is_int($time)) {
return $time;
}
if (is_float($time)) {
return (int)round($time * 1000000, 0);
}
throw new \InvalidArgumentException(sprintf(
'Time should be one of the types int|float|DateTime|null, got %s.',
gettype($time)
));
}
/**
* @return Tracer
*/
public function getTracer(): Tracer
{
return $this->tracer;
}
/**
* @return bool
*/
public function isDebug(): bool
{
return $this->debug;
}
/**
* @return int
*/
public function getStartTime(): int
{
return $this->startTime;
}
/**
* @return int|null
*/
public function getEndTime()
{
return $this->endTime;
}
/**
* @return string
*/
public function getOperationName(): string
{
return $this->operationName;
}
/**
* @return mixed
*/
public function getComponent()
{
// TODO
return $this->component;
}
/**
* {@inheritdoc}
*
* @return SpanContext
*/
public function getContext(): OTSpanContext
{
return $this->context;
}
/**
* {@inheritdoc}
*/
public function finish($finishTime = null, array $logRecords = []): void
{
if (!$this->isSampled()) {
return;
}
foreach ($logRecords as $logRecord) {
$this->log($logRecord);
}
$this->endTime = $this->microTime($finishTime);
$this->tracer->reportSpan($this);
}
/**
* Returns true if the trace should be measured.
*
* @return bool
*/
public function isSampled(): bool
{
$context = $this->getContext();
return ($context->getFlags() & SAMPLED_FLAG) == SAMPLED_FLAG;
}
/**
* {@inheritdoc}
*/
public function overwriteOperationName(string $newOperationName): void
{
// TODO log warning
$this->operationName = $newOperationName;
}
/**
* {@inheritdoc}
*
* @param array $tags
* @return void
*/
public function setTags($tags)
{
foreach ($tags as $key => $value) {
$this->setTag($key, $value);
}
}
/**
* {@inheritdoc}
*/
public function setTag(string $key, $value): void
{
if ($this->isSampled()) {
$special = self::SPECIAL_TAGS[$key] ?? null;
$handled = false;
if ($special !== null && is_callable([$this, $special])) {
$handled = $this->$special($value);
}
if (!$handled) {
$tag = $this->makeTag($key, $value);
$this->tags[$key] = $tag;
}
}
}
const SPECIAL_TAGS = [
PEER_SERVICE => 'setPeerService',
PEER_HOST_IPV4 => 'setPeerHostIpv4',
PEER_PORT => 'setPeerPort',
SPAN_KIND => 'setSpanKind',
COMPONENT => 'setComponent',
];
/**
* Sets a low-cardinality identifier of the module, library,
* or package that is generating a span.
*
* @see Span::setTag()
*
* @param string $value
* @return bool
*/
private function setComponent($value): bool
{
$this->component = $value;
return true;
}
/**
* @return bool
*/
private function setSpanKind($value): bool
{
$validSpanKinds = [
SPAN_KIND_RPC_CLIENT,
SPAN_KIND_RPC_SERVER,
SPAN_KIND_MESSAGE_BUS_CONSUMER,
SPAN_KIND_MESSAGE_BUS_PRODUCER,
];
if ($value === null || in_array($value, $validSpanKinds, true)) {
$this->kind = $value;
return true;
}
return false;
}
/**
* @return string|null
*/
public function getKind(): ?string
{
return $this->kind;
}
/**
* @return bool
*/
private function setPeerPort($value): bool
{
if ($this->peer === null) {
$this->peer = ['port' => $value];
} else {
$this->peer['port'] = $value;
}
return true;
}
/**
* @return bool
*/
private function setPeerHostIpv4($value): bool
{
if ($this->peer === null) {
$this->peer = ['ipv4' => $value];
} else {
$this->peer['ipv4'] = $value;
}
return true;
}
/**
* @return bool
*/
private function setPeerService($value): bool
{
if ($this->peer === null) {
$this->peer = ['service_name' => $value];
} else {
$this->peer['service_name'] = $value;
}
return true;
}
/**
* @return bool
*/
public function isRpc(): bool
{
return $this->kind == SPAN_KIND_RPC_CLIENT || $this->kind == SPAN_KIND_RPC_SERVER;
}
/**
* @return bool
*/
public function isRpcClient(): bool
{
return $this->kind == SPAN_KIND_RPC_CLIENT;
}
/**
* {@inheritdoc}
*/
public function log(array $fields = [], $timestamp = null): void
{
$timestamp = $this->microTime($timestamp);
if ($timestamp < $this->getStartTime()) {
$timestamp = $this->timestampMicro();
}
$this->logs[] = [
'fields' => $fields,
'timestamp' => $timestamp,
];
}
/**
* Returns the logs.
*
* [
* [
* 'timestamp' => timestamp in microsecond,
* 'fields' => [
* 'error' => 'message',
* ]
* ]
* ]
*
* @return array
*/
public function getLogs(): array
{
return $this->logs;
}
/**
* {@inheritdoc}
*/
public function addBaggageItem(string $key, string $value): void
{
$this->context = $this->context->withBaggageItem($key, $value);
}
/**
* {@inheritdoc}
*/
public function getBaggageItem(string $key): ?string
{
return $this->context->getBaggageItem($key);
}
/**
* @return array
*/
public function getTags(): array
{
return $this->tags;
}
/**
* @return int
*/
private function timestampMicro(): int
{
return round(microtime(true) * 1000000);
}
/**
* @param string $key
* @param mixed $value
* @return BinaryAnnotation
*/
private function makeTag(string $key, $value): BinaryAnnotation
{
$valueType = gettype($value);
$annotationType = null;
switch ($valueType) {
case "boolean":
$annotationType = AnnotationType::BOOL;
break;
case "integer":
$annotationType = AnnotationType::I64;
break;
case "double":
$annotationType = AnnotationType::DOUBLE;
break;
default:
$annotationType = AnnotationType::STRING;
$value = (string)$value;
if (strlen($value) > 1024) {
$value = substr($value, 0, 1024);
}
}
return new BinaryAnnotation([
'key' => $key,
'value' => $value,
'annotation_type' => $annotationType,
]);
}
}

View File

@ -0,0 +1,120 @@
<?php
namespace Jaeger;
use ArrayIterator;
use OpenTracing\SpanContext as OTSpanContext;
class SpanContext implements OTSpanContext
{
private $traceId;
private $spanId;
private $parentId;
private $flags;
/**
* @var array
*/
private $baggage;
private $debugId;
/**
* SpanContext constructor.
*
* @param string $traceId
* @param string $spanId
* @param string $parentId
* @param int|null $flags
* @param array $baggage
* @param int|null $debugId
*/
public function __construct($traceId, $spanId, $parentId, $flags = null, $baggage = [], $debugId = null)
{
$this->traceId = $traceId;
$this->spanId = $spanId;
$this->parentId = $parentId;
$this->flags = $flags;
$this->baggage = is_array($baggage) ? $baggage : [];
$this->debugId = $debugId;
}
/**
* {@inheritdoc}
* @return ArrayIterator
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new ArrayIterator($this->baggage);
}
/**
* {@inheritdoc}
*/
public function getBaggageItem(string $key): ?string
{
return array_key_exists($key, $this->baggage) ? $this->baggage[$key] : null;
}
/**
* {@inheritdoc}
*
* @param string $key
* @param string $value
* @return SpanContext
*/
public function withBaggageItem(string $key, string $value): OTSpanContext
{
return new self(
$this->traceId,
$this->spanId,
$this->parentId,
$this->flags,
[$key => $value] + $this->baggage
);
}
public function getTraceId()
{
return $this->traceId;
}
public function getParentId()
{
return $this->parentId;
}
public function getSpanId()
{
return $this->spanId;
}
/**
* Get the span context flags.
*
* @return int|null
*/
public function getFlags()
{
return $this->flags;
}
public function getBaggage()
{
return $this->baggage;
}
public function getDebugId()
{
return $this->debugId;
}
public function isDebugIdContainerOnly(): bool
{
return ($this->traceId === null) && ($this->debugId !== null);
}
}

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