diff --git a/.github/workflows/close_old_issues_and_prs.yml b/.github/workflows/close_old_issues_and_prs.yml index f3306b7df..e55b27068 100644 --- a/.github/workflows/close_old_issues_and_prs.yml +++ b/.github/workflows/close_old_issues_and_prs.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Mark/Close Stale Issues and Pull Requests 🗑️ - uses: actions/stale@v10.1.1 + uses: actions/stale@v10.2.0 with: repo-token: ${{ secrets.STALE_ACTION_PAT }} days-before-stale: 60 diff --git a/.github/workflows/rebuild_backup_image.yml b/.github/workflows/rebuild_backup_image.yml index 10edd5cd0..b18d339ba 100644 --- a/.github/workflows/rebuild_backup_image.yml +++ b/.github/workflows/rebuild_backup_image.yml @@ -16,21 +16,21 @@ jobs: uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to GHCR if: github.event_name != 'pull_request' - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/.gitignore b/.gitignore index a06c3da7f..416af9f7d 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ data/conf/sogo/cron.creds data/conf/sogo/custom-fulllogo.svg data/conf/sogo/custom-shortlogo.svg data/conf/sogo/custom-fulllogo.png +data/conf/acme/dns-01.conf data/gitea/ data/gogs/ data/hooks/dovecot/* diff --git a/_modules/scripts/new_options.sh b/_modules/scripts/new_options.sh index 30c747b70..2c39c8a7c 100644 --- a/_modules/scripts/new_options.sh +++ b/_modules/scripts/new_options.sh @@ -57,11 +57,14 @@ adapt_new_options() { "DISABLE_NETFILTER_ISOLATION_RULE" "HTTP_REDIRECT" "ENABLE_IPV6" + "ACME_DNS_CHALLENGE" + "ACME_DNS_PROVIDER" + "ACME_ACCOUNT_EMAIL" ) sed -i --follow-symlinks '$a\' mailcow.conf for option in ${CONFIG_ARRAY[@]}; do - if grep -q "${option}" mailcow.conf; then + if grep -q "^#\?${option}=" mailcow.conf; then continue fi @@ -292,6 +295,20 @@ adapt_new_options() { echo '# This key is used to encrypt email addresses within SOGo URLs' >> mailcow.conf echo "SOGO_URL_ENCRYPTION_KEY=$(LC_ALL=C /dev/null | head -c 16)" >> mailcow.conf ;; + ACME_DNS_CHALLENGE) + echo '# Enable DNS-01 challenge for ACME (acme-mailcow) - y/n' >> mailcow.conf + echo '# This requires you to set ACME_DNS_PROVIDER and ACME_ACCOUNT_EMAIL below' >> mailcow.conf + echo 'ACME_DNS_CHALLENGE=n' >> mailcow.conf + ;; + ACME_DNS_PROVIDER) + echo '# DNS provider for DNS-01 challenge (e.g. dns_cf, dns_azure, dns_gd, etc.)' >> mailcow.conf + echo '# See the dns-01 provider documentation for more information.' >> mailcow.conf + echo 'ACME_DNS_PROVIDER=dns_xxx' >> mailcow.conf + ;; + ACME_ACCOUNT_EMAIL) + echo '# Account email for ACME DNS-01 challenge registration' >> mailcow.conf + echo 'ACME_ACCOUNT_EMAIL=me@example.com' >> mailcow.conf + ;; *) echo "${option}=" >> mailcow.conf ;; diff --git a/data/Dockerfiles/acme/Dockerfile b/data/Dockerfiles/acme/Dockerfile index f6e990e57..193ca7e00 100644 --- a/data/Dockerfiles/acme/Dockerfile +++ b/data/Dockerfiles/acme/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.21 +FROM alpine:3.23 LABEL maintainer = "The Infrastructure Company GmbH " @@ -14,11 +14,22 @@ RUN apk upgrade --no-cache \ tini \ tzdata \ python3 \ - acme-tiny + acme-tiny \ + git \ + socat \ + && git clone --depth 1 https://github.com/acmesh-official/acme.sh.git /opt/acme.sh \ + && chmod +x /opt/acme.sh/acme.sh \ + && mkdir -p /var/lib/acme/acme-sh + +ENV ACME_SH_BIN=/opt/acme.sh/acme.sh \ + ACME_SH_HOME=/opt/acme.sh \ + ACME_SH_CONFIG_HOME=/var/lib/acme/acme-sh COPY acme.sh /srv/acme.sh COPY functions.sh /srv/functions.sh COPY obtain-certificate.sh /srv/obtain-certificate.sh +COPY obtain-certificate-dns.sh /srv/obtain-certificate-dns.sh +COPY load-dns-config.sh /srv/load-dns-config.sh COPY reload-configurations.sh /srv/reload-configurations.sh COPY expand6.sh /srv/expand6.sh diff --git a/data/Dockerfiles/acme/acme.sh b/data/Dockerfiles/acme/acme.sh index 2aab8e7e3..c09569cdc 100755 --- a/data/Dockerfiles/acme/acme.sh +++ b/data/Dockerfiles/acme/acme.sh @@ -14,6 +14,17 @@ until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do sleep 2 done +# Create DNS-01 configuration template if it doesn't exist +if [[ ! -f /etc/acme/dns-01.conf ]]; then + mkdir -p /etc/acme + cat > /etc/acme/dns-01.conf <<'EOF' +# Add here your DNS-01 challenge configuration +# For more information, visit the acme.sh documentation: +# https://github.com/acmesh-official/acme.sh/wiki/dnsapi +EOF + echo "Created DNS-01 configuration template at /etc/acme/dns-01.conf" +fi + source /srv/functions.sh # Thanks to https://github.com/cvmiller -> https://github.com/cvmiller/expand6 source /srv/expand6.sh @@ -42,6 +53,10 @@ if [[ "${ENABLE_SSL_SNI}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then ENABLE_SSL_SNI=y fi +if [[ "${ACME_DNS_CHALLENGE}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + ACME_DNS_CHALLENGE=y +fi + if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then log_f "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..." sleep 365d diff --git a/data/Dockerfiles/acme/functions.sh b/data/Dockerfiles/acme/functions.sh index 183be01b0..9db832910 100644 --- a/data/Dockerfiles/acme/functions.sh +++ b/data/Dockerfiles/acme/functions.sh @@ -80,6 +80,11 @@ check_domain(){ return 1 fi fi + + if [[ ${ACME_DNS_CHALLENGE} == "y" ]]; then + log_f "ACME_DNS_CHALLENGE=y - skipping IP and HTTP validation for ${DOMAIN}" + return 0 + fi # Check if CNAME without v6 enabled target if [[ ! -z ${AAAA_DOMAIN} ]] && [[ -z $(echo ${AAAA_DOMAIN} | grep "^\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}$") ]]; then AAAA_DOMAIN= diff --git a/data/Dockerfiles/acme/load-dns-config.sh b/data/Dockerfiles/acme/load-dns-config.sh new file mode 100755 index 000000000..af9caeaf6 --- /dev/null +++ b/data/Dockerfiles/acme/load-dns-config.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +SCRIPT_SOURCE="${BASH_SOURCE[0]:-${0}}" +if [[ "${SCRIPT_SOURCE}" == "${0}" ]]; then + __dns_loader_standalone=1 +else + __dns_loader_standalone=0 +fi + +CONFIG_PATH="${ACME_DNS_CONFIG_FILE:-/etc/acme/dns-01.conf}" + +if [[ ! -f "${CONFIG_PATH}" ]]; then + if [[ $__dns_loader_standalone -eq 1 ]]; then + exit 0 + else + return 0 + fi +fi + +source /srv/functions.sh + +log_f "Loading DNS-01 configuration from ${CONFIG_PATH}" + +LINE_NO=0 +while IFS= read -r line || [[ -n "${line}" ]]; do + LINE_NO=$((LINE_NO+1)) + line="${line%$'\r'}" + line_trimmed="$(printf '%s' "${line}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + [[ -z "${line_trimmed}" ]] && continue + [[ "${line_trimmed:0:1}" == "#" ]] && continue + if [[ "${line_trimmed}" != *=* ]]; then + log_f "Skipping invalid DNS config line ${LINE_NO} (missing key=value)" + continue + fi + KEY="${line_trimmed%%=*}" + VALUE="${line_trimmed#*=}" + KEY="$(printf '%s' "${KEY}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + VALUE="$(printf '%s' "${VALUE}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + if [[ -z "${KEY}" ]]; then + log_f "Skipping invalid DNS config line ${LINE_NO} (empty key)" + continue + fi + if [[ "${VALUE}" =~ ^\".*\"$ ]]; then + VALUE="${VALUE:1:-1}" + elif [[ "${VALUE}" =~ ^\'.*\'$ ]]; then + VALUE="${VALUE:1:-1}" + fi + export "${KEY}"="${VALUE}" + log_f "Exported DNS config key ${KEY}" + +done < "${CONFIG_PATH}" + +if [[ $__dns_loader_standalone -eq 1 ]]; then + exit 0 +else + return 0 +fi diff --git a/data/Dockerfiles/acme/obtain-certificate-dns.sh b/data/Dockerfiles/acme/obtain-certificate-dns.sh new file mode 100644 index 000000000..f88089f01 --- /dev/null +++ b/data/Dockerfiles/acme/obtain-certificate-dns.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +# Return values / exit codes +# 0 = cert created successfully +# 1 = cert renewed successfully +# 2 = cert not due for renewal +# * = errors + +source /srv/functions.sh + +CERT_DOMAINS=(${DOMAINS[@]}) +CERT_DOMAIN=${CERT_DOMAINS[0]} +ACME_BASE=/var/lib/acme + +# Load optional DNS provider secrets from /etc/acme/dns-01.conf +if [[ -f /srv/load-dns-config.sh ]]; then + source /srv/load-dns-config.sh + if declare -F log_f >/dev/null; then + log_f "ACME_DNS_CHALLENGE is enabled, DNS provider secrets loaded" + fi +fi + +TYPE=${1} +PREFIX="" +# only support rsa certificates for now +if [[ "${TYPE}" != "rsa" ]]; then + log_f "Unknown certificate type '${TYPE}' requested" + exit 5 +fi + +if [[ -z "${ACME_DNS_PROVIDER}" ]]; then + log_f "ACME_DNS_PROVIDER is required when ACME_DNS_CHALLENGE is enabled" + exit 6 +fi + +DOMAINS_FILE=${ACME_BASE}/${CERT_DOMAIN}/domains +CERT=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}cert.pem +SHARED_KEY=${ACME_BASE}/acme/${PREFIX}key.pem # must already exist +KEY=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}key.pem +CSR=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}acme.csr + +if [[ -z ${CERT_DOMAINS[*]} ]]; then + log_f "Missing CERT_DOMAINS to obtain a certificate" + exit 3 +fi + +if [[ "${LE_STAGING}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + if [[ ! -z "${DIRECTORY_URL}" ]]; then + log_f "Cannot use DIRECTORY_URL with LE_STAGING=y - ignoring DIRECTORY_URL" + fi + log_f "Using Let's Encrypt staging servers" + ACME_SH_SERVER_ARGS=("--staging") +elif [[ ! -z "${DIRECTORY_URL}" ]]; then + log_f "Using custom directory URL ${DIRECTORY_URL}" + ACME_SH_SERVER_ARGS=("--server" "${DIRECTORY_URL}") +else + log_f "Using Let's Encrypt production servers" + ACME_SH_SERVER_ARGS=("--server" "letsencrypt") +fi + +if [[ -f ${DOMAINS_FILE} && "$(cat ${DOMAINS_FILE})" == "${CERT_DOMAINS[*]}" ]]; then + if [[ ! -f ${CERT} || ! -f "${KEY}" || -f "${ACME_BASE}/force_renew" ]]; then + log_f "Certificate ${CERT} doesn't exist yet or forced renewal - start obtaining" + elif ! openssl x509 -checkend 2592000 -noout -in ${CERT} > /dev/null; then + log_f "Certificate ${CERT} is due for renewal (< 30 days) - start renewing" + else + log_f "Certificate ${CERT} validation done, neither changed nor due for renewal." + exit 2 + fi +else + log_f "Certificate ${CERT} missing or changed domains '${CERT_DOMAINS[*]}' - start obtaining" +fi + +# Make backup +if [[ -f ${CERT} ]]; then + DATE=$(date +%Y-%m-%d_%H_%M_%S) + BACKUP_DIR=${ACME_BASE}/backups/${CERT_DOMAIN}/${PREFIX}${DATE} + log_f "Creating backups in ${BACKUP_DIR} ..." + mkdir -p ${BACKUP_DIR}/ + [[ -f ${DOMAINS_FILE} ]] && cp ${DOMAINS_FILE} ${BACKUP_DIR}/ + [[ -f ${CERT} ]] && cp ${CERT} ${BACKUP_DIR}/ + [[ -f ${KEY} ]] && cp ${KEY} ${BACKUP_DIR}/ + [[ -f ${CSR} ]] && cp ${CSR} ${BACKUP_DIR}/ +fi + +mkdir -p ${ACME_BASE}/${CERT_DOMAIN} +if [[ ! -f ${KEY} ]]; then + log_f "Copying shared private key for this certificate..." + cp ${SHARED_KEY} ${KEY} + chmod 600 ${KEY} +fi + +# Generating CSR to keep layout parity with HTTP challenge flow +printf "[SAN]\nsubjectAltName=" > /tmp/_SAN +printf "DNS:%s," "${CERT_DOMAINS[@]}" >> /tmp/_SAN +sed -i '$s/,$//' /tmp/_SAN +openssl req -new -sha256 -key ${KEY} -subj "/" -reqexts SAN -config <(cat "$(openssl version -d | sed 's/.*\"\(.*\)\"/\1/g')/openssl.cnf" /tmp/_SAN) > ${CSR} + +log_f "Checking resolver..." +until dig letsencrypt.org +time=3 +tries=1 @unbound > /dev/null; do + sleep 2 +done +log_f "Resolver OK" + +ACME_SH_BIN_PATH=${ACME_SH_BIN:-/opt/acme.sh/acme.sh} +ACME_SH_WORK_HOME=${ACME_SH_CONFIG_HOME:-/var/lib/acme/acme-sh} +mkdir -p ${ACME_SH_WORK_HOME} + +if [[ ! -x ${ACME_SH_BIN_PATH} ]]; then + log_f "acme.sh binary not found at ${ACME_SH_BIN_PATH}" + exit 7 +fi + +if [[ ! -f ${ACME_SH_WORK_HOME}/account.conf ]]; then + if [[ -z "${ACME_ACCOUNT_EMAIL}" ]]; then + log_f "ACME_ACCOUNT_EMAIL is required to register a new acme.sh account" + exit 8 + fi + log_f "Registering acme.sh account for ${ACME_ACCOUNT_EMAIL}" + REGISTER_CMD=("${ACME_SH_BIN_PATH}" "--home" "${ACME_SH_WORK_HOME}" "--config-home" "${ACME_SH_WORK_HOME}" "--cert-home" "${ACME_SH_WORK_HOME}" "--register-account" "-m" "${ACME_ACCOUNT_EMAIL}") + REGISTER_CMD+=("${ACME_SH_SERVER_ARGS[@]}") + REGISTER_RESPONSE=$("${REGISTER_CMD[@]}" 2>&1) + if [[ $? -ne 0 ]]; then + log_f "Failed to register acme.sh account: ${REGISTER_RESPONSE}" + exit 9 + fi +fi + +TMP_CERT=$(mktemp /tmp/acme-cert.XXXXXX) +TMP_FULLCHAIN=$(mktemp /tmp/acme-fullchain.XXXXXX) + +ACME_CMD=("${ACME_SH_BIN_PATH}" "--home" "${ACME_SH_WORK_HOME}" "--config-home" "${ACME_SH_WORK_HOME}" "--cert-home" "${ACME_SH_WORK_HOME}") +ACME_CMD+=("${ACME_SH_SERVER_ARGS[@]}") +ACME_CMD+=("--issue" "--dns" "${ACME_DNS_PROVIDER}" "--key-file" "${KEY}" "--cert-file" "${TMP_CERT}" "--fullchain-file" "${TMP_FULLCHAIN}" "--force") +for domain in "${CERT_DOMAINS[@]}"; do + ACME_CMD+=("-d" "${domain}") +done + +log_f "Using command ${ACME_CMD[*]}" +if [[ -n "${ACME_DNS_PROVIDER}" ]]; then + log_f "DNS provider: ${ACME_DNS_PROVIDER}" +fi +if compgen -A variable | grep -Eq "^DNS_|^ACME_"; then + LOG_KEYS=$(env | grep -E "^(DNS_|ACME_)" | cut -d= -f1 | tr '\n' ' ') + log_f "Available DNS/ACME env keys: ${LOG_KEYS}" redis_only +fi +ACME_RESPONSE=$("${ACME_CMD[@]}" 2>&1 | tee /dev/fd/5; exit ${PIPESTATUS[0]}) +SUCCESS="$?" +ACME_RESPONSE_B64=$(echo "${ACME_RESPONSE}" | openssl enc -e -A -base64) +log_f "${ACME_RESPONSE_B64}" redis_only b64 + +case "$SUCCESS" in + 0) + log_f "Deploying certificate ${CERT}..." + if verify_hash_match ${TMP_FULLCHAIN} ${KEY}; then + RETURN=0 + if [[ -f ${CERT} ]]; then + RETURN=1 + fi + mv -f ${TMP_FULLCHAIN} ${CERT} + rm -f ${TMP_CERT} + echo -n ${CERT_DOMAINS[*]} > ${DOMAINS_FILE} + log_f "Certificate successfully obtained via DNS challenge" + exit ${RETURN} + else + log_f "Certificate was requested, but key and certificate hashes do not match" + rm -f ${TMP_CERT} ${TMP_FULLCHAIN} + exit 4 + fi + ;; + *) + log_f "Failed to obtain certificate ${CERT} for domains '${CERT_DOMAINS[*]}' via DNS challenge" + redis-cli -h redis -a ${REDISPASS} --no-auth-warning SET ACME_FAIL_TIME "$(date +%s)" + rm -f ${TMP_CERT} ${TMP_FULLCHAIN} + exit 100${SUCCESS} + ;; +esac diff --git a/data/Dockerfiles/acme/obtain-certificate.sh b/data/Dockerfiles/acme/obtain-certificate.sh index f476bf666..9d727a202 100644 --- a/data/Dockerfiles/acme/obtain-certificate.sh +++ b/data/Dockerfiles/acme/obtain-certificate.sh @@ -20,6 +20,10 @@ if [[ "${TYPE}" != "rsa" ]]; then log_f "Unknown certificate type '${TYPE}' requested" exit 5 fi + +if [[ "${ACME_DNS_CHALLENGE}" == "y" ]]; then + exec /srv/obtain-certificate-dns.sh "$@" +fi DOMAINS_FILE=${ACME_BASE}/${CERT_DOMAIN}/domains CERT=${ACME_BASE}/${CERT_DOMAIN}/${PREFIX}cert.pem SHARED_KEY=${ACME_BASE}/acme/${PREFIX}key.pem # must already exist diff --git a/data/Dockerfiles/dockerapi/Dockerfile b/data/Dockerfiles/dockerapi/Dockerfile index 872764317..c27f6154b 100644 --- a/data/Dockerfiles/dockerapi/Dockerfile +++ b/data/Dockerfiles/dockerapi/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.21 +FROM alpine:3.23 LABEL maintainer = "The Infrastructure Company GmbH " diff --git a/data/Dockerfiles/dockerapi/main.py b/data/Dockerfiles/dockerapi/main.py index 57e262864..bf197bd61 100644 --- a/data/Dockerfiles/dockerapi/main.py +++ b/data/Dockerfiles/dockerapi/main.py @@ -110,12 +110,12 @@ async def get_container(container_id : str): return Response(content=json.dumps(res, indent=4), media_type="application/json") @app.get("/containers/json") -async def get_containers(): +async def get_containers(all: bool = False): global dockerapi containers = {} try: - for container in (await dockerapi.async_docker_client.containers.list()): + for container in (await dockerapi.async_docker_client.containers.list(all=all)): container_info = await container.show() containers.update({container_info['Id']: container_info}) return Response(content=json.dumps(containers, indent=4), media_type="application/json") diff --git a/data/Dockerfiles/netfilter/Dockerfile b/data/Dockerfiles/netfilter/Dockerfile index 57dbd6e94..70cd49c1a 100644 --- a/data/Dockerfiles/netfilter/Dockerfile +++ b/data/Dockerfiles/netfilter/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.21 +FROM alpine:3.23 LABEL maintainer = "The Infrastructure Company GmbH " @@ -40,4 +40,4 @@ COPY ./docker-entrypoint.sh /app/ RUN chmod +x /app/docker-entrypoint.sh -CMD ["/bin/sh", "-c", "/app/docker-entrypoint.sh"] \ No newline at end of file +CMD ["/bin/sh", "-c", "/app/docker-entrypoint.sh"] diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile index f0971ff5e..7917d7f7d 100644 --- a/data/Dockerfiles/phpfpm/Dockerfile +++ b/data/Dockerfiles/phpfpm/Dockerfile @@ -13,7 +13,7 @@ ARG MEMCACHED_PECL_VERSION=3.4.0 # renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?.*)$ ARG REDIS_PECL_VERSION=6.3.0 # renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?.*)$ -ARG COMPOSER_VERSION=2.8.6 +ARG COMPOSER_VERSION=2.9.5 RUN apk add -U --no-cache autoconf \ aspell-dev \ diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile index 55bcc9572..d0c710428 100644 --- a/data/Dockerfiles/rspamd/Dockerfile +++ b/data/Dockerfiles/rspamd/Dockerfile @@ -2,7 +2,7 @@ FROM debian:trixie-slim LABEL maintainer="The Infrastructure Company GmbH " ARG DEBIAN_FRONTEND=noninteractive -ARG RSPAMD_VER=rspamd_3.14.2-82~90302bc +ARG RSPAMD_VER=rspamd_3.14.3-1~236eb65 ARG CODENAME=trixie ENV LC_ALL=C diff --git a/data/Dockerfiles/sogo/Dockerfile b/data/Dockerfiles/sogo/Dockerfile index ed7e07ed6..d118b6492 100644 --- a/data/Dockerfiles/sogo/Dockerfile +++ b/data/Dockerfiles/sogo/Dockerfile @@ -1,47 +1,161 @@ -FROM debian:bookworm-slim +# SOGo built from source to enable security patch application +# Repository: https://github.com/Alinto/sogo +# Version: SOGo-5.12.4 +# +# Applied security patches: +# - +# +# To add new patches, modify SOGO_SECURITY_PATCHES ARG below with space-separated commit hashes + +FROM debian:bookworm LABEL maintainer="The Infrastructure Company GmbH " ARG DEBIAN_FRONTEND=noninteractive -ARG DEBIAN_VERSION=bookworm -ARG SOGO_DEBIAN_REPOSITORY=https://packagingv2.sogo.nu/sogo-nightly-debian/ +ARG SOGO_VERSION=SOGo-5.12.5 +ARG SOPE_VERSION=SOPE-5.12.5 +# Security patches to apply (space-separated commit hashes) +ARG SOGO_SECURITY_PATCHES="" # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?.*)$ ARG GOSU_VERSION=1.19 ENV LC_ALL=C -# Prerequisites -RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \ - && apt-get update && apt-get install -y --no-install-recommends \ - apt-transport-https \ - ca-certificates \ - gettext \ - gnupg \ - mariadb-client \ - rsync \ - supervisor \ - syslog-ng \ - syslog-ng-core \ - syslog-ng-mod-redis \ - dirmngr \ - netcat-traditional \ - psmisc \ - wget \ - patch \ +# Install dependencies, build SOPE and SOGo, then clean up (all in one layer to minimize image size) +RUN apt-get update && apt-get install -y --no-install-recommends \ + # Build dependencies + git \ + build-essential \ + gobjc \ + gnustep-make \ + gnustep-base-runtime \ + libgnustep-base-dev \ + libxml2-dev \ + libldap2-dev \ + libssl-dev \ + zlib1g-dev \ + libpq-dev \ + libmariadb-dev-compat \ + libmemcached-dev \ + libsodium-dev \ + libcurl4-openssl-dev \ + libzip-dev \ + libytnef0-dev \ + curl \ + ca-certificates \ + # Runtime dependencies + apt-transport-https \ + gettext \ + gnupg \ + mariadb-client \ + rsync \ + supervisor \ + syslog-ng \ + syslog-ng-core \ + syslog-ng-mod-redis \ + dirmngr \ + netcat-traditional \ + psmisc \ + wget \ + patch \ + libobjc4 \ + libxml2 \ + libldap-2.5-0 \ + libssl3 \ + zlib1g \ + libmariadb3 \ + libmemcached11 \ + libsodium23 \ + libcurl4 \ + libzip4 \ + libytnef0 \ + # Download gosu && dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \ && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \ && chmod +x /usr/local/bin/gosu \ && gosu nobody true \ - && mkdir /usr/share/doc/sogo \ - && touch /usr/share/doc/sogo/empty.sh \ - && wget -O- https://keys.openpgp.org/vks/v1/by-fingerprint/74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 | gpg --dearmor | apt-key add - \ - && echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} main" > /etc/apt/sources.list.d/sogo.list \ - && apt-get update && apt-get install -y --no-install-recommends \ - sogo \ - sogo-activesync \ - && apt-get autoclean \ + # Build SOPE + && git clone --depth 1 --branch ${SOPE_VERSION} https://github.com/Alinto/sope.git /tmp/sope \ + && cd /tmp/sope \ + && rm -rf .git \ + && . /usr/share/GNUstep/Makefiles/GNUstep.sh \ + && ./configure --prefix=/usr --disable-debug --disable-strip \ + && make -j$(nproc) \ + && make install \ + && cd / \ + && rm -rf /tmp/sope \ + # Build SOGo with security patches + && git clone --depth 1 --branch ${SOGO_VERSION} https://github.com/Alinto/sogo.git /tmp/sogo \ + && cd /tmp/sogo \ + && git config user.email "builder@mailcow.local" \ + && git config user.name "SOGo Builder" \ + && for patch in ${SOGO_SECURITY_PATCHES}; do \ + echo "Applying security patch: ${patch}"; \ + git fetch origin ${patch} && git cherry-pick ${patch}; \ + done \ + && rm -rf .git \ + && . /usr/share/GNUstep/Makefiles/GNUstep.sh \ + && ./configure --disable-debug --disable-strip \ + && make -j$(nproc) \ + && make install \ + && cd / \ + && rm -rf /tmp/sogo \ + # Strip binaries + && strip --strip-unneeded /usr/local/sbin/sogod 2>/dev/null || true \ + && strip --strip-unneeded /usr/local/sbin/sogo-tool 2>/dev/null || true \ + && strip --strip-unneeded /usr/local/sbin/sogo-ealarms-notify 2>/dev/null || true \ + && strip --strip-unneeded /usr/local/sbin/sogo-slapd-sockd 2>/dev/null || true \ + # Remove build dependencies and clean up + && apt-get purge -y --auto-remove \ + git \ + build-essential \ + gobjc \ + gnustep-make \ + libgnustep-base-dev \ + libxml2-dev \ + libldap2-dev \ + libssl-dev \ + zlib1g-dev \ + libpq-dev \ + libmariadb-dev-compat \ + libmemcached-dev \ + libsodium-dev \ + libcurl4-openssl-dev \ + libzip-dev \ + libytnef0-dev \ + curl \ + && apt-get autoremove -y \ + && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ + && rm -rf /usr/share/doc/* \ + && rm -rf /usr/share/man/* \ + && rm -rf /var/cache/debconf/* \ + && rm -rf /tmp/* \ + && rm -rf /root/.cache \ + && find /usr/local/lib -name '*.a' -delete \ + && find /usr/lib -name '*.a' -delete \ + && mkdir -p /usr/share/doc/sogo \ + && touch /usr/share/doc/sogo/empty.sh \ && touch /etc/default/locale +# Configure library paths +RUN echo "/usr/lib64" > /etc/ld.so.conf.d/sogo.conf \ + && echo "/usr/local/lib/sogo" >> /etc/ld.so.conf.d/sogo.conf \ + && echo "/usr/local/lib/GNUstep/Frameworks/SOGo.framework/Versions/5/sogo" >> /etc/ld.so.conf.d/sogo.conf \ + && ldconfig + +# Create sogo user and group +RUN groupadd -r -g 999 sogo \ + && useradd -r -u 999 -g sogo -d /var/lib/sogo -s /bin/bash -c "SOGo Daemon" sogo \ + && mkdir -p /var/lib/sogo /var/run/sogo /var/log/sogo \ + && chown -R sogo:sogo /var/lib/sogo /var/run/sogo /var/log/sogo + +# Create symlinks for SOGo binaries +RUN ln -s /usr/local/sbin/sogod /usr/sbin/sogod \ + && ln -s /usr/local/sbin/sogo-tool /usr/sbin/sogo-tool \ + && ln -s /usr/local/sbin/sogo-ealarms-notify /usr/sbin/sogo-ealarms-notify \ + && ln -s /usr/local/sbin/sogo-slapd-sockd /usr/sbin/sogo-slapd-sockd + +# Copy configuration files and scripts COPY ./bootstrap-sogo.sh /bootstrap-sogo.sh COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf COPY syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng-redis_slave.conf @@ -56,4 +170,4 @@ RUN chmod +x /bootstrap-sogo.sh \ ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] \ No newline at end of file +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/data/Dockerfiles/sogo/acl.diff b/data/Dockerfiles/sogo/acl.diff index 513700388..e6b5d0520 100644 --- a/data/Dockerfiles/sogo/acl.diff +++ b/data/Dockerfiles/sogo/acl.diff @@ -1,5 +1,5 @@ ---- /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:57.987504204 +0200 -+++ /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:35.918291298 +0200 +--- /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:57.987504204 +0200 ++++ /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:35.918291298 +0200 @@ -46,7 +46,7 @@ diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 96d8a6919..1b2cd2ace 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -130,18 +130,22 @@ chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist # Patch ACLs #if [[ ${ACL_ANYONE} == 'allow' ]]; then # #enable any or authenticated targets for ACL -# if patch -R -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then -# patch -R /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff; +# if patch -R -sfN --dry-run /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then +# patch -R /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff; # fi #else # #disable any or authenticated targets for ACL -# if patch -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then -# patch /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff; +# if patch -sfN --dry-run /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then +# patch /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff; # fi #fi -if patch -R -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff > /dev/null; then - patch -R /usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff; +# Apply custom UI patch (reverse patch to ADD buttons) +if patch -R -sfN --dry-run /usr/local/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff > /dev/null; then + echo "Applying navMailcowBtns patch (reverse to add buttons)..." + patch -R /usr/local/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff; +else + echo "navMailcowBtns patch already applied or cannot be applied" fi # Rename custom logo, if any @@ -149,7 +153,7 @@ fi # Rsync web content echo "Syncing web content with named volume" -rsync -a /usr/lib/GNUstep/SOGo/. /sogo_web/ +rsync -a /usr/local/lib/GNUstep/SOGo/. /sogo_web/ # Chown backup path chown -R sogo:sogo /sogo_backup diff --git a/data/Dockerfiles/unbound/Dockerfile b/data/Dockerfiles/unbound/Dockerfile index 4903750ed..7c4921384 100644 --- a/data/Dockerfiles/unbound/Dockerfile +++ b/data/Dockerfiles/unbound/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.21 +FROM alpine:3.23 LABEL maintainer = "The Infrastructure Company GmbH " diff --git a/data/Dockerfiles/watchdog/Dockerfile b/data/Dockerfiles/watchdog/Dockerfile index 6d8541d79..831086733 100644 --- a/data/Dockerfiles/watchdog/Dockerfile +++ b/data/Dockerfiles/watchdog/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.21 +FROM alpine:3.23 LABEL maintainer = "The Infrastructure Company GmbH " diff --git a/data/Dockerfiles/watchdog/check_dns.sh b/data/Dockerfiles/watchdog/check_dns.sh index ce4cfa3b1..ba430fe95 100755 --- a/data/Dockerfiles/watchdog/check_dns.sh +++ b/data/Dockerfiles/watchdog/check_dns.sh @@ -19,19 +19,19 @@ if [ -z "$HOST" ]; then fi # run dig and measure the time it takes to run -START_TIME=$(date +%s%3N) +START_TIME=$(perl -MTime::HiRes -e 'print Time::HiRes::time') dig_output=$(dig +short +timeout=2 +tries=1 "$HOST" @"$SERVER" 2>/dev/null) dig_rc=$? +END_TIME=$(perl -MTime::HiRes -e 'print Time::HiRes::time') dig_output_ips=$(echo "$dig_output" | grep -E '^[0-9.]+$' | sort | paste -sd ',' -) -END_TIME=$(date +%s%3N) -ELAPSED_TIME=$((END_TIME - START_TIME)) +ELAPSED_TIME=$(perl -e "printf('%.3f', $END_TIME - $START_TIME)") # validate and perform nagios like output and exit codes if [ $dig_rc -ne 0 ] || [ -z "$dig_output" ]; then echo "Domain $HOST was not found by the server" exit 2 elif [ $dig_rc -eq 0 ]; then - echo "DNS OK: $ELAPSED_TIME ms response time. $HOST returns $dig_output_ips" + echo "DNS OK: $ELAPSED_TIME seconds response time. $HOST returns $dig_output_ips" exit 0 else echo "Unknown error" diff --git a/data/web/inc/lib/vendor/tightenco/collect/framework-.zip b/data/conf/acme/.gitkeep similarity index 100% rename from data/web/inc/lib/vendor/tightenco/collect/framework-.zip rename to data/conf/acme/.gitkeep diff --git a/data/conf/nginx/templates/sites-default.conf.j2 b/data/conf/nginx/templates/sites-default.conf.j2 index f3d734528..84bd8eae4 100644 --- a/data/conf/nginx/templates/sites-default.conf.j2 +++ b/data/conf/nginx/templates/sites-default.conf.j2 @@ -261,19 +261,19 @@ location ~* /sogo$ { } location /SOGo.woa/WebServerResources/ { - alias /usr/lib/GNUstep/SOGo/WebServerResources/; + alias /usr/local/lib/GNUstep/SOGo/WebServerResources/; } location /.woa/WebServerResources/ { - alias /usr/lib/GNUstep/SOGo/WebServerResources/; + alias /usr/local/lib/GNUstep/SOGo/WebServerResources/; } location /SOGo/WebServerResources/ { - alias /usr/lib/GNUstep/SOGo/WebServerResources/; + alias /usr/local/lib/GNUstep/SOGo/WebServerResources/; } location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) { - alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; + alias /usr/local/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; } {% endif %} diff --git a/data/conf/postfix/postscreen_access.cidr b/data/conf/postfix/postscreen_access.cidr index 9ff9f6265..de38fe39e 100644 --- a/data/conf/postfix/postscreen_access.cidr +++ b/data/conf/postfix/postscreen_access.cidr @@ -1,6 +1,6 @@ -# Whitelist generated by Postwhite v3.4 on Thu Jan 1 00:24:01 UTC 2026 +# Whitelist generated by Postwhite v3.4 on Sun Mar 1 00:29:01 UTC 2026 # https://github.com/stevejenkins/postwhite/ -# 2105 total rules +# 2174 total rules 2a00:1450:4000::/36 permit 2a01:111:f400::/48 permit 2a01:111:f403:2800::/53 permit @@ -52,10 +52,14 @@ 8.25.194.0/23 permit 8.25.196.0/23 permit 8.36.116.0/24 permit +8.39.54.0/23 permit +8.39.54.250/31 permit 8.39.144.0/24 permit +8.40.222.0/23 permit +8.40.222.250/31 permit 12.130.86.238 permit -13.107.213.38 permit -13.107.246.38 permit +13.107.213.51 permit +13.107.246.51 permit 13.108.16.0/20 permit 13.110.208.0/21 permit 13.110.209.0/24 permit @@ -65,6 +69,7 @@ 13.111.191.0/24 permit 13.216.7.111 permit 13.216.54.180 permit +13.247.164.219 permit 15.200.21.50 permit 15.200.44.248 permit 15.200.201.185 permit @@ -168,6 +173,7 @@ 34.215.104.144 permit 34.218.115.239 permit 34.225.212.172 permit +34.241.242.183 permit 35.83.148.184 permit 35.155.198.111 permit 35.158.23.94 permit @@ -191,6 +197,7 @@ 40.233.64.216 permit 40.233.83.78 permit 40.233.88.28 permit +43.239.212.33 permit 44.206.138.57 permit 44.210.169.44 permit 44.217.45.156 permit @@ -272,6 +279,7 @@ 50.112.246.219 permit 52.1.14.157 permit 52.5.230.59 permit +52.6.74.205 permit 52.12.53.23 permit 52.13.214.179 permit 52.26.1.71 permit @@ -328,6 +336,7 @@ 54.244.54.130 permit 54.244.242.0/24 permit 54.255.61.23 permit +56.124.6.228 permit 57.103.64.0/18 permit 57.129.93.249 permit 62.13.128.0/24 permit @@ -393,6 +402,7 @@ 65.110.161.77 permit 65.123.29.213 permit 65.123.29.220 permit +65.154.166.0/24 permit 65.212.180.36 permit 66.102.0.0/20 permit 66.119.150.192/26 permit @@ -697,7 +707,9 @@ 87.248.117.205 permit 87.253.232.0/21 permit 89.22.108.0/24 permit -91.198.2.0/24 permit +91.198.2.177 permit +91.198.2.217 permit +91.198.2.222 permit 91.211.240.0/22 permit 94.236.119.0/26 permit 95.131.104.0/21 permit @@ -1194,6 +1206,9 @@ 99.78.197.208/28 permit 103.9.96.0/22 permit 103.28.42.0/24 permit +103.84.217.15 permit +103.84.217.238 permit +103.89.75.238 permit 103.151.192.0/23 permit 103.168.172.128/27 permit 103.237.104.0/22 permit @@ -1354,6 +1369,9 @@ 117.120.16.0/21 permit 119.42.242.52/31 permit 119.42.242.156 permit +121.244.91.48 permit +121.244.91.52 permit +122.15.156.182 permit 123.126.78.64/29 permit 124.108.96.24/31 permit 124.108.96.28/31 permit @@ -1419,7 +1437,21 @@ 134.170.141.64/26 permit 134.170.143.0/24 permit 134.170.174.0/24 permit +135.84.80.0/24 permit +135.84.81.0/24 permit +135.84.82.0/24 permit +135.84.83.0/24 permit 135.84.216.0/22 permit +136.143.160.0/24 permit +136.143.161.0/24 permit +136.143.162.0/24 permit +136.143.176.0/24 permit +136.143.177.0/24 permit +136.143.178.49 permit +136.143.182.0/23 permit +136.143.184.0/24 permit +136.143.188.0/24 permit +136.143.190.0/23 permit 136.146.128.0/20 permit 136.147.128.0/20 permit 136.147.135.0/24 permit @@ -1435,6 +1467,7 @@ 139.138.46.219 permit 139.138.57.55 permit 139.138.58.119 permit +139.167.79.86 permit 139.180.17.0/24 permit 140.238.148.191 permit 141.148.55.217 permit @@ -1523,8 +1556,10 @@ 159.135.224.0/20 permit 159.135.228.10 permit 159.183.0.0/16 permit +159.183.14.233 permit 159.183.68.71 permit 159.183.79.38 permit +159.183.121.182 permit 159.183.129.172 permit 160.1.62.192 permit 161.38.192.0/20 permit @@ -1550,6 +1585,10 @@ 164.152.23.32 permit 164.152.25.241 permit 164.177.132.168/30 permit +165.173.128.0/24 permit +165.173.180.1 permit +165.173.180.250/31 permit +165.173.182.250/31 permit 166.78.68.0/22 permit 166.78.68.221 permit 166.78.69.169 permit @@ -1579,6 +1618,18 @@ 168.245.12.252 permit 168.245.46.9 permit 168.245.127.231 permit +169.148.129.0/24 permit +169.148.131.0/24 permit +169.148.138.0/24 permit +169.148.142.10 permit +169.148.142.33 permit +169.148.144.0/25 permit +169.148.144.10 permit +169.148.146.0/23 permit +169.148.175.3 permit +169.148.179.3 permit +169.148.188.0/24 permit +169.148.188.182 permit 170.9.232.254 permit 170.10.128.0/24 permit 170.10.129.0/24 permit @@ -1612,8 +1663,7 @@ 182.50.78.64/28 permit 183.240.219.64/29 permit 185.4.120.0/22 permit -185.11.253.128/27 permit -185.11.255.0/24 permit +185.11.255.144 permit 185.12.80.0/22 permit 185.28.196.0/22 permit 185.58.84.93 permit @@ -1627,8 +1677,16 @@ 185.138.56.128/25 permit 185.189.236.0/22 permit 185.211.120.0/22 permit -185.233.188.0/23 permit -185.233.190.0/23 permit +185.233.188.68 permit +185.233.188.75 permit +185.233.188.84 permit +185.233.188.160 permit +185.233.188.176 permit +185.233.188.247 permit +185.233.189.44 permit +185.233.189.98 permit +185.233.189.122 permit +185.233.189.228 permit 185.250.236.0/22 permit 185.250.239.148 permit 185.250.239.168 permit @@ -1704,7 +1762,9 @@ 193.109.254.0/23 permit 193.122.128.100 permit 193.123.56.63 permit -193.142.157.0/24 permit +193.142.157.15 permit +193.142.157.125 permit +193.142.157.158 permit 193.142.157.191 permit 193.142.157.198 permit 194.19.134.0/25 permit @@ -1764,7 +1824,16 @@ 199.16.156.0/22 permit 199.33.145.1 permit 199.33.145.32 permit +199.34.22.36 permit 199.59.148.0/22 permit +199.67.80.2 permit +199.67.80.20 permit +199.67.82.2 permit +199.67.82.20 permit +199.67.84.0/24 permit +199.67.86.0/24 permit +199.67.88.0/24 permit +199.67.90.0/24 permit 199.101.161.130 permit 199.101.162.0/25 permit 199.122.120.0/21 permit @@ -1820,6 +1889,8 @@ 204.92.114.187 permit 204.92.114.203 permit 204.92.114.204/31 permit +204.141.32.0/23 permit +204.141.42.0/23 permit 204.216.164.202 permit 204.220.160.0/21 permit 204.220.168.0/21 permit @@ -1997,8 +2068,6 @@ 212.227.126.225 permit 212.227.126.226 permit 212.227.126.227 permit -213.95.19.64/27 permit -213.95.135.4 permit 213.199.128.139 permit 213.199.128.145 permit 213.199.138.181 permit @@ -2088,11 +2157,9 @@ 2001:748:400:3301::3 permit 2001:748:400:3301::4 permit 2404:6800:4000::/36 permit -2603:1010:3:3::5b permit -2603:1020:201:10::10f permit -2603:1030:20e:3::23c permit -2603:1030:b:3::152 permit -2603:1030:c02:8::14 permit +2607:13c0:0001:0000:0000:0000:0000:7000/116 permit +2607:13c0:0002:0000:0000:0000:0000:1000/116 permit +2607:13c0:0004:0000:0000:0000:0000:0000/116 permit 2607:f8b0:4000::/36 permit 2620:109:c003:104::/64 permit 2620:109:c003:104::215 permit @@ -2105,6 +2172,8 @@ 2620:10d:c09c:400::8:1 permit 2620:119:50c0:207::/64 permit 2620:119:50c0:207::215 permit +2620:1ec:46::51 permit +2620:1ec:bdf::51 permit 2800:3f0:4000::/36 permit 49.12.4.251 permit # checks.mailcow.email 2a01:4f8:c17:7906::10 permit # checks.mailcow.email diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index 503b41e6d..313ba7e90 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -392,6 +392,7 @@ rspamd_config:register_symbol({ local rspamd_http = require "rspamd_http" local rcpts = task:get_recipients('smtp') local lua_util = require "lua_util" + local tagged_rcpt = task:get_symbol("TAGGED_RCPT") local function remove_moo_tag() local moo_tag_header = task:get_header('X-Moo-Tag', false) @@ -416,12 +417,9 @@ rspamd_config:register_symbol({ -- Check if recipient has a tag (contains '+') local tag = nil - if rcpt_user:find('%+') then - local base_user, tag_part = rcpt_user:match('^(.-)%+(.+)$') - if base_user and tag_part then - tag = tag_part - rspamd_logger.infox("TAG_MOO: found tag in recipient: %s (base: %s, tag: %s)", rcpt_addr, base_user, tag) - end + if tagged_rcpt ~= nil then + tag = tagged_rcpt + rspamd_logger.infox("TAG_MOO: found tag in recipient: %s (base: %s, tag: %s)", rcpt_addr, base_user, tag) end if not tag then @@ -500,7 +498,8 @@ rspamd_config:register_symbol({ else rspamd_logger.infox("TAG_MOO: user wants subject modified for tagged mail") local sbj = task:get_header('Subject') or '' - new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?=' + local tag_value = tag[1] and tag[1].options and tag[1].options[1] or '' + new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag_value .. '] ' .. sbj)) .. '?=' task:set_milter_reply({ remove_headers = { ['Subject'] = 1, @@ -945,4 +944,4 @@ rspamd_config:register_symbol({ return true end, priority = 1 -}) \ No newline at end of file +}) diff --git a/data/web/admin/dashboard.php b/data/web/admin/dashboard.php index 443c2ea2a..2867770bc 100644 --- a/data/web/admin/dashboard.php +++ b/data/web/admin/dashboard.php @@ -2,18 +2,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.admin.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - header('Location: /user'); - exit(); -} -elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != "admin") { - header('Location: /admin'); - exit(); -} +protect_route(['admin']); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; diff --git a/data/web/admin/index.php b/data/web/admin/index.php index 9ae4a0380..9e0bf1bd7 100644 --- a/data/web/admin/index.php +++ b/data/web/admin/index.php @@ -3,8 +3,11 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.admin.inc.php'; if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { - header('Location: /admin/dashboard'); - exit(); + // Only redirect to dashboard if NO pending actions + if (empty($_SESSION['pending_tfa_setup']) && empty($_SESSION['pending_pw_update'])) { + header('Location: /admin/dashboard'); + exit(); + } } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { header('Location: /domainadmin/mailbox'); diff --git a/data/web/admin/mailbox.php b/data/web/admin/mailbox.php index d0073bbd6..7b07d6fa9 100644 --- a/data/web/admin/mailbox.php +++ b/data/web/admin/mailbox.php @@ -2,18 +2,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.admin.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - header('Location: /user'); - exit(); -} -elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != "admin") { - header('Location: /admin'); - exit(); -} +protect_route(['admin']); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; diff --git a/data/web/admin/queue.php b/data/web/admin/queue.php index 85ec59401..66682c7ae 100644 --- a/data/web/admin/queue.php +++ b/data/web/admin/queue.php @@ -2,19 +2,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.admin.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - header('Location: /user'); - exit(); -} -elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != "admin") { - header('Location: /admin'); - exit(); -} - +protect_route(['admin']); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; $js_minifier->add('/web/js/site/queue.js'); diff --git a/data/web/admin/system.php b/data/web/admin/system.php index 9fd44e0d8..4db40c753 100644 --- a/data/web/admin/system.php +++ b/data/web/admin/system.php @@ -2,18 +2,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.admin.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - header('Location: /user'); - exit(); -} -elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != "admin") { - header('Location: /admin'); - exit(); -} +protect_route(['admin']); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; diff --git a/data/web/api/openapi.yaml b/data/web/api/openapi.yaml index f207ee6a1..cf54f6add 100644 --- a/data/web/api/openapi.yaml +++ b/data/web/api/openapi.yaml @@ -2454,6 +2454,90 @@ paths: type: object type: object summary: Delete mails in Quarantine + /api/v1/edit/qitem: + post: + responses: + "401": + $ref: "#/components/responses/Unauthorized" + "200": + content: + application/json: + examples: + release: + value: + - log: + - quarantine + - edit + - id: + - "33" + action: release + msg: + - item_released + - "33" + type: success + learnham: + value: + - log: + - quarantine + - edit + - id: + - "34" + action: learnham + msg: + - item_learned + - "34" + type: success + schema: + properties: + log: + description: contains request object + items: {} + type: array + msg: + items: {} + type: array + type: + enum: + - success + - danger + - error + type: string + type: object + description: OK + headers: {} + tags: + - Quarantine + description: >- + Using this endpoint you can perform actions on quarantine items. It is possible to release + emails from quarantine into to the inbox, or learn them as ham to improve Rspamd filtering. + You must provide the quarantine item IDs. You can get the IDs using the GET method. + operationId: Edit mails in Quarantine + requestBody: + content: + application/json: + schema: + example: + items: + - "33" + - "34" + attr: + action: release + properties: + items: + description: contains list of quarantine item IDs to release or learn as ham + type: object + attr: + description: attributes for the action + type: object + properties: + action: + type: string + enum: + - release + - learnham + description: "release - return email to inbox; learnham - learn as ham to improve filtering" + type: object + summary: Edit mails in Quarantine /api/v1/delete/recipient_map: post: responses: diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php index d3cda4004..3e31dc3dd 100644 --- a/data/web/autodiscover.php +++ b/data/web/autodiscover.php @@ -60,101 +60,31 @@ $pdo = new PDO($dsn, $database_user, $database_pass, $opt); $iam_provider = identity_provider('init'); $iam_settings = identity_provider('get'); -$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER'])); -$login_pass = trim(htmlspecialchars_decode($_SERVER['PHP_AUTH_PW'])); +// Passwordless autodiscover - no authentication required +// Email will be extracted from the request body +$login_user = null; +$login_role = null; -if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])) { - $json = json_encode( - array( - "time" => time(), - "ua" => $_SERVER['HTTP_USER_AGENT'], - "user" => "none", - "ip" => $_SERVER['REMOTE_ADDR'], - "service" => "Error: must be authenticated" - ) - ); - $redis->lPush('AUTODISCOVER_LOG', $json); - header('WWW-Authenticate: Basic realm="' . $_SERVER['HTTP_HOST'] . '"'); - header('HTTP/1.0 401 Unauthorized'); - exit(0); -} - -$login_role = check_login($login_user, $login_pass, array('service' => 'EAS')); - -if ($login_role === "user") { - header("Content-Type: application/xml"); - echo '' . PHP_EOL; +header("Content-Type: application/xml"); +echo '' . PHP_EOL; ?> time(), - "ua" => $_SERVER['HTTP_USER_AGENT'], - "user" => $_SERVER['PHP_AUTH_USER'], - "ip" => $_SERVER['REMOTE_ADDR'], - "service" => "Error: invalid or missing request data" - ) - ); - $redis->lPush('AUTODISCOVER_LOG', $json); - $redis->lTrim('AUTODISCOVER_LOG', 0, 100); - } - catch (RedisException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'msg' => 'Redis: '.$e - ); - return false; - } - list($usec, $sec) = explode(' ', microtime()); -?> - - - 600 - Invalid Request - - - - -Request->EMailAddress; - } catch (Exception $e) { - $email = $_SERVER['PHP_AUTH_USER']; - } - - $username = trim($email); - try { - $stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username"); - $stmt->execute(array(':username' => $username)); - $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC); - } - catch(PDOException $e) { - die("Failed to determine name from SQL"); - } - if (!empty($MailboxData['name'])) { - $displayname = $MailboxData['name']; - } - else { - $displayname = $email; - } +if(!$data) { try { $json = json_encode( array( "time" => time(), "ua" => $_SERVER['HTTP_USER_AGENT'], - "user" => $_SERVER['PHP_AUTH_USER'], + "user" => "none", "ip" => $_SERVER['REMOTE_ADDR'], - "service" => $autodiscover_config['autodiscoverType'] + "service" => "Error: invalid or missing request data" ) ); $redis->lPush('AUTODISCOVER_LOG', $json); $redis->lTrim('AUTODISCOVER_LOG', 0, 100); + $redis->publish("F2B_CHANNEL", "Autodiscover: Invalid request by " . $_SERVER['REMOTE_ADDR']); + error_log("Autodiscover: Invalid request by " . $_SERVER['REMOTE_ADDR']); } catch (RedisException $e) { $_SESSION['return'][] = array( @@ -163,7 +93,143 @@ if ($login_role === "user") { ); return false; } - if ($autodiscover_config['autodiscoverType'] == 'imap') { + list($usec, $sec) = explode(' ', microtime()); +?> + + + 600 + Invalid Request + + + + +Request->EMailAddress; +} catch (Exception $e) { + // If parsing fails, return error + try { + $json = json_encode( + array( + "time" => time(), + "ua" => $_SERVER['HTTP_USER_AGENT'], + "user" => "none", + "ip" => $_SERVER['REMOTE_ADDR'], + "service" => "Error: could not parse email from request" + ) + ); + $redis->lPush('AUTODISCOVER_LOG', $json); + $redis->lTrim('AUTODISCOVER_LOG', 0, 100); + $redis->publish("F2B_CHANNEL", "Autodiscover: Malformed XML by " . $_SERVER['REMOTE_ADDR']); + error_log("Autodiscover: Malformed XML by " . $_SERVER['REMOTE_ADDR']); + } + catch (RedisException $e) { + // Silently fail + } + list($usec, $sec) = explode(' ', microtime()); +?> + + + 600 + Invalid Request + + + + +prepare("SELECT `mailbox`.`name`, `mailbox`.`active` FROM `mailbox` + INNER JOIN `domain` ON `mailbox`.`domain` = `domain`.`domain` + WHERE `mailbox`.`username` = :username + AND `mailbox`.`active` = '1' + AND `domain`.`active` = '1'"); + $stmt->execute(array(':username' => $username)); + $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC); +} +catch(PDOException $e) { + // Database error - return error response with complete XML + list($usec, $sec) = explode(' ', microtime()); +?> + + + 500 + Database Error + + + + + time(), + "ua" => $_SERVER['HTTP_USER_AGENT'], + "user" => $email, + "ip" => $_SERVER['REMOTE_ADDR'], + "service" => "Error: mailbox not found or inactive" + ) + ); + $redis->lPush('AUTODISCOVER_LOG', $json); + $redis->lTrim('AUTODISCOVER_LOG', 0, 100); + $redis->publish("F2B_CHANNEL", "Autodiscover: Invalid mailbox attempt by " . $_SERVER['REMOTE_ADDR']); + error_log("Autodiscover: Invalid mailbox attempt by " . $_SERVER['REMOTE_ADDR']); + } + catch (RedisException $e) { + // Silently fail + } + list($usec, $sec) = explode(' ', microtime()); +?> + + + 600 + Invalid Request + + + + + time(), + "ua" => $_SERVER['HTTP_USER_AGENT'], + "user" => $email, + "ip" => $_SERVER['REMOTE_ADDR'], + "service" => $autodiscover_config['autodiscoverType'] + ) + ); + $redis->lPush('AUTODISCOVER_LOG', $json); + $redis->lTrim('AUTODISCOVER_LOG', 0, 100); +} +catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'msg' => 'Redis: '.$e + ); + return false; +} +if ($autodiscover_config['autodiscoverType'] == 'imap') { ?> @@ -238,6 +304,3 @@ if ($login_role === "user") { } ?> - diff --git a/data/web/domainadmin/index.php b/data/web/domainadmin/index.php index 0d70ec3ae..07c62a7d2 100644 --- a/data/web/domainadmin/index.php +++ b/data/web/domainadmin/index.php @@ -3,8 +3,11 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.domainadmin.inc.php'; if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); + // Only redirect to mailbox if NO pending actions + if (empty($_SESSION['pending_tfa_setup']) && empty($_SESSION['pending_pw_update'])) { + header('Location: /domainadmin/mailbox'); + exit(); + } } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { header('Location: /admin/dashboard'); diff --git a/data/web/domainadmin/mailbox.php b/data/web/domainadmin/mailbox.php index bb2ef16f3..8beb5abb9 100644 --- a/data/web/domainadmin/mailbox.php +++ b/data/web/domainadmin/mailbox.php @@ -2,18 +2,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.domainadmin.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { - header('Location: /admin/dashboard'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - header('Location: /user'); - exit(); -} -elseif (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != "domainadmin") { - header('Location: /domainadmin'); - exit(); -} +protect_route(['domainadmin']); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; diff --git a/data/web/domainadmin/user.php b/data/web/domainadmin/user.php index 7f1b392e0..a45e00dfc 100644 --- a/data/web/domainadmin/user.php +++ b/data/web/domainadmin/user.php @@ -2,41 +2,28 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.domainadmin.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { +/* +/ DOMAIN ADMIN +*/ - /* - / DOMAIN ADMIN - */ +protect_route(['domainadmin']); - require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; - $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; - $tfa_data = get_tfa(); - $fido2_data = fido2(array("action" => "get_friendly_names")); - $username = $_SESSION['mailcow_cc_username']; +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; +$_SESSION['return_to'] = $_SERVER['REQUEST_URI']; +$tfa_data = get_tfa(); +$fido2_data = fido2(array("action" => "get_friendly_names")); +$username = $_SESSION['mailcow_cc_username']; - $template = 'domainadmin.twig'; - $template_data = [ - 'acl' => $_SESSION['acl'], - 'acl_json' => json_encode($_SESSION['acl']), - 'user_spam_score' => mailbox('get', 'spam_score', $username), - 'tfa_data' => $tfa_data, - 'fido2_data' => $fido2_data, - 'lang_user' => json_encode($lang['user']), - 'lang_datatables' => json_encode($lang['datatables']), - ]; -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { - header('Location: /admin/dashboard'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - header('Location: /user'); - exit(); -} -else { - header('Location: /domainadmin'); - exit(); -} +$template = 'domainadmin.twig'; +$template_data = [ + 'acl' => $_SESSION['acl'], + 'acl_json' => json_encode($_SESSION['acl']), + 'user_spam_score' => mailbox('get', 'spam_score', $username), + 'tfa_data' => $tfa_data, + 'fido2_data' => $fido2_data, + 'lang_user' => json_encode($lang['user']), + 'lang_datatables' => json_encode($lang['datatables']), +]; $js_minifier->add('/web/js/site/user.js'); $js_minifier->add('/web/js/site/pwgen.js'); diff --git a/data/web/edit.php b/data/web/edit.php index 57cf24bd2..48f2309c1 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -1,10 +1,8 @@ @$_SESSION['pending_tfa_methods'], 'pending_tfa_authmechs' => $pending_tfa_authmechs, 'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'], + 'pending_tfa_setup' => !empty($_SESSION['pending_tfa_setup']), + 'pending_pw_update_modal' => !empty($_SESSION['pending_pw_update']), 'lang_footer' => json_encode($lang['footer']), 'lang_acl' => json_encode($lang['acl']), 'lang_tfa' => json_encode($lang['tfa']), diff --git a/data/web/inc/functions.admin.inc.php b/data/web/inc/functions.admin.inc.php index 9f42fd721..7bd0af42f 100644 --- a/data/web/inc/functions.admin.inc.php +++ b/data/web/inc/functions.admin.inc.php @@ -121,34 +121,56 @@ function admin($_action, $_data = null) { continue; } } - if (!empty($password)) { - if (password_check($password, $password2) !== true) { - return false; + // Check if this is a self password change via forced update + if ($username == $_SESSION['mailcow_cc_username'] && !empty($_SESSION['pending_pw_update'])) { + // Forced password update: only change password and clear force_pw_update flag + if (!empty($password)) { + if (password_check($password, $_data['password2']) !== true) { + return false; + } + $password_hashed = hash_password($password); + $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed, + `attributes` = JSON_SET(COALESCE(`attributes`, '{}'), '$.force_pw_update', '0') + WHERE `username` = :username"); + $stmt->execute(array( + ':password_hashed' => $password_hashed, + ':username' => $username + )); + unset($_SESSION['pending_pw_update']); } - $password_hashed = hash_password($password); - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username"); - $stmt->execute(array( - ':password_hashed' => $password_hashed, - ':username_new' => $username_new, - ':username' => $username, - ':active' => $active - )); - if (isset($_data['disable_tfa'])) { - $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); - $stmt->execute(array(':username' => $username)); + } else { + // Normal admin edit: update all attributes + $force_tfa = intval($_data['force_tfa'] ?? 0) ? 1 : 0; + $force_pw_update = intval($_data['force_pw_update'] ?? 0) ? 1 : 0; + if (!empty($password)) { + if (password_check($password, $password2) !== true) { + return false; + } + $password_hashed = hash_password($password); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed, + `attributes` = JSON_SET(COALESCE(`attributes`, '{}'), '$.force_tfa', :force_tfa, '$.force_pw_update', :force_pw_update) + WHERE `username` = :username"); + $stmt->execute(array( + ':password_hashed' => $password_hashed, + ':username_new' => $username_new, + ':username' => $username, + ':active' => $active, + ':force_tfa' => strval($force_tfa), + ':force_pw_update' => strval($force_pw_update) + )); } else { - $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); - $stmt->execute(array(':username_new' => $username_new, ':username' => $username)); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, + `attributes` = JSON_SET(COALESCE(`attributes`, '{}'), '$.force_tfa', :force_tfa, '$.force_pw_update', :force_pw_update) + WHERE `username` = :username"); + $stmt->execute(array( + ':username_new' => $username_new, + ':username' => $username, + ':active' => $active, + ':force_tfa' => strval($force_tfa), + ':force_pw_update' => strval($force_pw_update) + )); } - } - else { - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username"); - $stmt->execute(array( - ':username_new' => $username_new, - ':username' => $username, - ':active' => $active - )); if (isset($_data['disable_tfa'])) { $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); $stmt->execute(array(':username' => $username)); @@ -223,7 +245,8 @@ function admin($_action, $_data = null) { `tfa`.`active` AS `tfa_active`, `admin`.`username`, `admin`.`created`, - `admin`.`active` AS `active` + `admin`.`active` AS `active`, + `admin`.`attributes` AS `attributes` FROM `admin` LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`admin`.`username` WHERE `admin`.`username`= :admin AND `superadmin` = '1'"); @@ -240,6 +263,7 @@ function admin($_action, $_data = null) { $admindata['active'] = $row['active']; $admindata['active_int'] = $row['active']; $admindata['created'] = $row['created']; + $admindata['attributes'] = json_decode($row['attributes'], true) ?? array('force_tfa' => '0', 'force_pw_update' => '0'); return $admindata; break; } diff --git a/data/web/inc/functions.auth.inc.php b/data/web/inc/functions.auth.inc.php index 3903ba642..91a8c55fa 100644 --- a/data/web/inc/functions.auth.inc.php +++ b/data/web/inc/functions.auth.inc.php @@ -82,7 +82,7 @@ function admin_login($user, $pass){ } $user = strtolower(trim($user)); - $stmt = $pdo->prepare("SELECT `password` FROM `admin` + $stmt = $pdo->prepare("SELECT `password`, `attributes` FROM `admin` WHERE `superadmin` = '1' AND `active` = '1' AND `username` = :user"); @@ -91,6 +91,13 @@ function admin_login($user, $pass){ // verify password if (verify_hash($row['password'], $pass)) { + $admin_attrs = json_decode($row['attributes'], true) ?? []; + + // Check force_pw_update + if (intval($admin_attrs['force_pw_update'] ?? 0) == 1) { + $_SESSION['pending_pw_update'] = true; + } + // check for tfa authenticators $authenticators = get_tfa($user); if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) { @@ -110,6 +117,10 @@ function admin_login($user, $pass){ // Reactivate TFA if it was set to "deactivate TFA for next login" $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); $stmt->execute(array(':user' => $user)); + // Check force_tfa: only force setup if NO TFA exists at all + if (intval($admin_attrs['force_tfa'] ?? 0) == 1 && !tfa_exists($user)) { + $_SESSION['pending_tfa_setup'] = true; + } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $user, '*'), @@ -135,7 +146,7 @@ function domainadmin_login($user, $pass){ return false; } - $stmt = $pdo->prepare("SELECT `password` FROM `admin` + $stmt = $pdo->prepare("SELECT `password`, `attributes` FROM `admin` WHERE `superadmin` = '0' AND `active`='1' AND `username` = :user"); @@ -144,6 +155,13 @@ function domainadmin_login($user, $pass){ // verify password if (verify_hash($row['password'], $pass) !== false) { + $admin_attrs = json_decode($row['attributes'], true) ?? []; + + // Check force_pw_update + if (intval($admin_attrs['force_pw_update'] ?? 0) == 1) { + $_SESSION['pending_pw_update'] = true; + } + // check for tfa authenticators $authenticators = get_tfa($user); if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) { @@ -163,6 +181,10 @@ function domainadmin_login($user, $pass){ // Reactivate TFA if it was set to "deactivate TFA for next login" $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); $stmt->execute(array(':user' => $user)); + // Check force_tfa: only force setup if NO TFA exists at all + if (intval($admin_attrs['force_tfa'] ?? 0) == 1 && !tfa_exists($user)) { + $_SESSION['pending_tfa_setup'] = true; + } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $user, '*'), @@ -286,6 +308,10 @@ function user_login($user, $pass, $extra = null){ // Reactivate TFA if it was set to "deactivate TFA for next login" $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); $stmt->execute(array(':user' => $user)); + // Check force_tfa: only force setup if NO TFA exists at all + if (intval($row['attributes']['force_tfa']) == 1 && !tfa_exists($user)) { + $_SESSION['pending_tfa_setup'] = true; + } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $user, '*', 'Provider: Keycloak'), @@ -338,6 +364,10 @@ function user_login($user, $pass, $extra = null){ // Reactivate TFA if it was set to "deactivate TFA for next login" $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); $stmt->execute(array(':user' => $user)); + // Check force_tfa: only force setup if NO TFA exists at all + if (intval($row['attributes']['force_tfa']) == 1 && !tfa_exists($user)) { + $_SESSION['pending_tfa_setup'] = true; + } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $user, '*', 'Provider: LDAP'), @@ -381,6 +411,10 @@ function user_login($user, $pass, $extra = null){ // Reactivate TFA if it was set to "deactivate TFA for next login" $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); $stmt->execute(array(':user' => $user)); + // Check force_tfa: only force setup if NO TFA exists at all + if (intval($row['attributes']['force_tfa']) == 1 && !tfa_exists($user)) { + $_SESSION['pending_tfa_setup'] = true; + } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $user, '*', 'Provider: mailcow'), diff --git a/data/web/inc/functions.docker.inc.php b/data/web/inc/functions.docker.inc.php index 5b5b7ace1..daed17c63 100644 --- a/data/web/inc/functions.docker.inc.php +++ b/data/web/inc/functions.docker.inc.php @@ -63,7 +63,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex break; case 'info': if (empty($service_name)) { - curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json'); + curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json?all=true'); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 0); curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT); diff --git a/data/web/inc/functions.domain_admin.inc.php b/data/web/inc/functions.domain_admin.inc.php index bb88ea34c..46b651b85 100644 --- a/data/web/inc/functions.domain_admin.inc.php +++ b/data/web/inc/functions.domain_admin.inc.php @@ -195,17 +195,23 @@ function domain_admin($_action, $_data = null) { )); } } + $force_tfa = intval($_data['force_tfa'] ?? 0) ? 1 : 0; + $force_pw_update = intval($_data['force_pw_update'] ?? 0) ? 1 : 0; if (!empty($password)) { if (password_check($password, $password2) !== true) { return false; } $password_hashed = hash_password($password); - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username"); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed, + `attributes` = JSON_SET(COALESCE(`attributes`, '{}'), '$.force_tfa', :force_tfa, '$.force_pw_update', :force_pw_update) + WHERE `username` = :username"); $stmt->execute(array( ':password_hashed' => $password_hashed, ':username_new' => $username_new, ':username' => $username, - ':active' => $active + ':active' => $active, + ':force_tfa' => strval($force_tfa), + ':force_pw_update' => strval($force_pw_update) )); if (isset($_data['disable_tfa'])) { $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); @@ -217,11 +223,15 @@ function domain_admin($_action, $_data = null) { } } else { - $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username"); + $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, + `attributes` = JSON_SET(COALESCE(`attributes`, '{}'), '$.force_tfa', :force_tfa, '$.force_pw_update', :force_pw_update) + WHERE `username` = :username"); $stmt->execute(array( ':username_new' => $username_new, ':username' => $username, - ':active' => $active + ':active' => $active, + ':force_tfa' => strval($force_tfa), + ':force_pw_update' => strval($force_pw_update) )); if (isset($_data['disable_tfa'])) { $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); @@ -244,31 +254,37 @@ function domain_admin($_action, $_data = null) { // Can only edit itself elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") { $username = $_SESSION['mailcow_cc_username']; - $password_old = $_data['user_old_pass']; + $password_old = $_data['user_old_pass'] ?? ''; $password_new = $_data['user_new_pass']; $password_new2 = $_data['user_new_pass2']; - $stmt = $pdo->prepare("SELECT `password` FROM `admin` - WHERE `username` = :user"); - $stmt->execute(array(':user' => $username)); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - if (!verify_hash($row['password'], $password_old)) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_action, $_data_log), - 'msg' => 'access_denied' - ); - return false; + // Only verify old password if this is NOT a forced password update + if (empty($_SESSION['pending_pw_update'])) { + $stmt = $pdo->prepare("SELECT `password` FROM `admin` + WHERE `username` = :user"); + $stmt->execute(array(':user' => $username)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (!verify_hash($row['password'], $password_old)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ); + return false; + } } if (password_check($password_new, $password_new2) !== true) { return false; } $password_hashed = hash_password($password_new); - $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username"); + $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed, + `attributes` = JSON_SET(COALESCE(`attributes`, '{}'), '$.force_pw_update', '0') + WHERE `username` = :username"); $stmt->execute(array( ':password_hashed' => $password_hashed, ':username' => $username )); + unset($_SESSION['pending_pw_update']); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_action, $_data_log), @@ -360,9 +376,11 @@ function domain_admin($_action, $_data = null) { `tfa`.`active` AS `tfa_active`, `domain_admins`.`username`, `domain_admins`.`created`, - `domain_admins`.`active` AS `active` + `domain_admins`.`active` AS `active`, + `admin`.`attributes` AS `attributes` FROM `domain_admins` LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username` + LEFT OUTER JOIN `admin` ON `admin`.`username`=`domain_admins`.`username` WHERE `domain_admins`.`username`= :domain_admin"); $stmt->execute(array( ':domain_admin' => $_data @@ -377,6 +395,7 @@ function domain_admin($_action, $_data = null) { $domainadmindata['active'] = $row['active']; $domainadmindata['active_int'] = $row['active']; $domainadmindata['created'] = $row['created']; + $domainadmindata['attributes'] = json_decode($row['attributes'], true) ?? array('force_tfa' => '0', 'force_pw_update' => '0'); // GET SELECTED $stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain` IN ( diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 23b8d701d..89f14b574 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -1033,20 +1033,24 @@ function edit_user_account($_data) { } // edit password - if (!empty($password_old) && !empty($_data['user_new_pass']) && !empty($_data['user_new_pass2'])) { - $stmt = $pdo->prepare("SELECT `password` FROM `mailbox` - WHERE `kind` NOT REGEXP 'location|thing|group' - AND `username` = :user AND authsource = 'mailcow'"); - $stmt->execute(array(':user' => $username)); - $row = $stmt->fetch(PDO::FETCH_ASSOC); + $is_forced_pw_update = !empty($_SESSION['pending_pw_update']); + if (((!empty($password_old) || $is_forced_pw_update) && !empty($_data['user_new_pass']) && !empty($_data['user_new_pass2']))) { + // Only verify old password if this is NOT a forced password update + if (!$is_forced_pw_update) { + $stmt = $pdo->prepare("SELECT `password` FROM `mailbox` + WHERE `kind` NOT REGEXP 'location|thing|group' + AND `username` = :user AND authsource = 'mailcow'"); + $stmt->execute(array(':user' => $username)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); - if (!verify_hash($row['password'], $password_old)) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'access_denied' - ); - return false; + if (!verify_hash($row['password'], $password_old)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_data_log), + 'msg' => 'access_denied' + ); + return false; + } } $password_new = $_data['user_new_pass']; @@ -1210,50 +1214,52 @@ function set_tfa($_data) { global $iam_settings; $_data_log = $_data; - $access_denied = null; !isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*'; - $username = $_SESSION['mailcow_cc_username']; - // check for empty user and role - if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true; - - // check admin confirm password - if ($access_denied === null) { - $stmt = $pdo->prepare("SELECT `password` FROM `admin` - WHERE `username` = :username"); - $stmt->execute(array(':username' => $username)); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - if ($row) { - if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true; - else $access_denied = false; + // skip password check if this is a forced TFA enrollment after login + if (!empty($_SESSION['pending_tfa_setup'])) { + $username = $_SESSION['mailcow_cc_username']; + if (empty($username) || !isset($_SESSION['mailcow_cc_role'])) { + $_SESSION['return'][] = array('type' => 'danger', 'log' => array(__FUNCTION__, $_data_log), 'msg' => 'access_denied'); + return false; } - } + } else { + $username = $_SESSION['mailcow_cc_username']; + $access_denied = null; - // check mailbox confirm password - if ($access_denied === null) { - $stmt = $pdo->prepare("SELECT `password`, `authsource` FROM `mailbox` - WHERE `username` = :username"); - $stmt->execute(array(':username' => $username)); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - if ($row) { - if ($row['authsource'] == 'ldap'){ - if (!ldap_mbox_login($username, $_data["confirm_password"], $iam_settings)) $access_denied = true; - else $access_denied = false; - } else { + if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true; + + // check admin password + if ($access_denied === null) { + $stmt = $pdo->prepare("SELECT `password` FROM `admin` WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row) { if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true; else $access_denied = false; } } - } - // set access_denied error - if ($access_denied){ - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_data_log), - 'msg' => 'access_denied' - ); - return false; + // check mailbox password + if ($access_denied === null) { + $stmt = $pdo->prepare("SELECT `password`, `authsource` FROM `mailbox` WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row) { + if ($row['authsource'] == 'ldap'){ + if (!ldap_mbox_login($username, $_data["confirm_password"], $iam_settings)) $access_denied = true; + else $access_denied = false; + } else { + if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true; + else $access_denied = false; + } + } + } + + if ($access_denied) { + $_SESSION['return'][] = array('type' => 'danger', 'log' => array(__FUNCTION__, $_data_log), 'msg' => 'access_denied'); + return false; + } } switch ($_data["tfa_method"]) { @@ -1306,6 +1312,7 @@ function set_tfa($_data) { ); return false; } + unset($_SESSION['pending_tfa_setup']); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_data_log), @@ -1319,6 +1326,7 @@ function set_tfa($_data) { //$stmt->execute(array(':username' => $username)); $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `secret`, `active`) VALUES (?, ?, 'totp', ?, '1')"); $stmt->execute(array($username, $key_id, $_POST['totp_secret'])); + unset($_SESSION['pending_tfa_setup']); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_data_log), @@ -1347,6 +1355,7 @@ function set_tfa($_data) { 0 )); + unset($_SESSION['pending_tfa_setup']); $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_data_log), @@ -1354,6 +1363,25 @@ function set_tfa($_data) { ); break; case "none": + // Block TFA removal if force_tfa policy is active + $is_forced_tfa = false; + if ($_SESSION['mailcow_cc_role'] === 'user') { + $stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?"); + $stmt_check->execute(array($username)); + $is_forced_tfa = ($stmt_check->fetchColumn() == '1'); + } else { + $stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?"); + $stmt_check->execute(array($username)); + $is_forced_tfa = ($stmt_check->fetchColumn() == '1'); + } + if ($is_forced_tfa) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_data_log), + 'msg' => 'tfa_removal_blocked' + ); + return false; + } $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username"); $stmt->execute(array(':username' => $username)); $_SESSION['return'][] = array( @@ -1606,6 +1634,26 @@ function unset_tfa_key($_data) { return false; } + // Block key removal if force_tfa policy is active + $is_forced_tfa = false; + if ($_SESSION['mailcow_cc_role'] === 'user') { + $stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `mailbox` WHERE `username` = ?"); + $stmt_check->execute(array($username)); + $is_forced_tfa = ($stmt_check->fetchColumn() == '1'); + } else { + $stmt_check = $pdo->prepare("SELECT JSON_EXTRACT(`attributes`, '$.force_tfa') FROM `admin` WHERE `username` = ?"); + $stmt_check->execute(array($username)); + $is_forced_tfa = ($stmt_check->fetchColumn() == '1'); + } + if ($is_forced_tfa) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_data_log), + 'msg' => 'tfa_removal_blocked' + ); + return false; + } + // check if it's last key $stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa` WHERE `username` = :username AND `active` = '1'"); @@ -1638,6 +1686,15 @@ function unset_tfa_key($_data) { return false; } } +function tfa_exists($username) { + global $pdo; + if (empty($username)) { + return false; + } + $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM `tfa` WHERE `username` = :username"); + $stmt->execute(array(':username' => $username)); + return $stmt->fetch(PDO::FETCH_ASSOC)['count'] > 0; +} function get_tfa($username = null, $id = null) { global $pdo; if (empty($username) && isset($_SESSION['mailcow_cc_username'])) { @@ -3440,6 +3497,49 @@ function set_user_loggedin_session($user) { unset($_SESSION['pending_mailcow_cc_role']); unset($_SESSION['pending_tfa_methods']); } +function protect_route($allowed_roles = ['admin', 'domainadmin', 'user'], $redirects = []) { + // Check if user is authenticated + if (!isset($_SESSION['mailcow_cc_role'])) { + if (isset($redirects['unauthenticated'])) { + header('Location: ' . $redirects['unauthenticated']); + } else { + header('Location: /'); + } + exit(); + } + + // Check for pending actions (2FA setup, password update) + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + $pending_redirect = '/'; + if ($_SESSION['mailcow_cc_role'] === 'admin') { + $pending_redirect = '/admin'; + } elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') { + $pending_redirect = '/domainadmin'; + } + header('Location: ' . $pending_redirect); + exit(); + } + + // Check if user's role is in the allowed roles for the route + if (!in_array($_SESSION['mailcow_cc_role'], $allowed_roles)) { + if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { + header('Location: /admin/dashboard'); + exit(); + } + elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { + header('Location: /domainadmin/mailbox'); + exit(); + } + elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { + header('Location: /user'); + exit(); + } + else { + header('Location: /'); + exit(); + } + } +} function get_logs($application, $lines = false) { if ($lines === false) { $lines = $GLOBALS['LOG_LINES'] - 1; diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 8d2efea3d..a002ab41b 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -9,6 +9,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $_data_log = $_data; !isset($_data_log['password']) ?: $_data_log['password'] = '*'; !isset($_data_log['password2']) ?: $_data_log['password2'] = '*'; + + // Track mailboxes affected by alias operations for incremental SOGo updates + $update_sogo_mailboxes = array(); + switch ($_action) { case 'add': switch ($_type) { @@ -886,6 +890,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'msg' => array('alias_added', $address, $id) ); + + // Track affected mailboxes for SOGo update + if (!empty($goto)) { + $gotos = array_map('trim', explode(',', $goto)); + foreach ($gotos as $g) { + if (filter_var($g, FILTER_VALIDATE_EMAIL) && + !in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) { + $update_sogo_mailboxes[] = $g; + } + } + } } break; case 'alias_domain': @@ -1083,6 +1098,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } $active = (isset($_data['active'])) ? intval($_data['active']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['active']); $force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']); + $force_tfa = (isset($_data['force_tfa'])) ? intval($_data['force_tfa']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_tfa']); $tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in']); $tls_enforce_out = (isset($_data['tls_enforce_out'])) ? intval($_data['tls_enforce_out']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out']); $sogo_access = (isset($_data['sogo_access'])) ? intval($_data['sogo_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['sogo_access']); @@ -1099,10 +1115,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : ''; if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){ $force_pw_update = 0; + $force_tfa = 0; } $mailbox_attrs = json_encode( array( 'force_pw_update' => strval($force_pw_update), + 'force_tfa' => strval($force_tfa), 'tls_enforce_in' => strval($tls_enforce_in), 'tls_enforce_out' => strval($tls_enforce_out), 'sogo_access' => strval($sogo_access), @@ -1365,15 +1383,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ), $_extra); } - try { - update_sogo_static_view($username); - } catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), - 'msg' => $e->getMessage() - ); - } + // Track affected mailboxes for SOGo update + $update_sogo_mailboxes[] = $username; $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), @@ -1604,6 +1615,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'msg' => array('resource_added', htmlspecialchars($name)) ); + + // Track affected mailboxes for SOGo update + $update_sogo_mailboxes[] = $name; break; case 'domain_templates': if ($_SESSION['mailcow_cc_role'] != "admin") { @@ -1720,6 +1734,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s"; $attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : ""; $attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']); + $attr["force_tfa"] = isset($_data['force_tfa']) ? intval($_data['force_tfa']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_tfa']); $attr["sogo_access"] = isset($_data['sogo_access']) ? intval($_data['sogo_access']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['sogo_access']); $attr["active"] = isset($_data['active']) ? intval($_data['active']) : 1; $attr["tls_enforce_in"] = isset($_data['tls_enforce_in']) ? intval($_data['tls_enforce_in']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in']); @@ -2721,6 +2736,28 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'msg' => array('alias_modified', htmlspecialchars($address)) ); + + // Track affected mailboxes for SOGo update (both old and new goto addresses) + // Old goto: to remove alias from their view + if (!empty($is_now['goto'])) { + $old_gotos = array_map('trim', explode(',', $is_now['goto'])); + foreach ($old_gotos as $g) { + if (filter_var($g, FILTER_VALIDATE_EMAIL) && + !in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) { + $update_sogo_mailboxes[] = $g; + } + } + } + // New goto: to add alias to their view + if (!empty($goto)) { + $new_gotos = array_map('trim', explode(',', $goto)); + foreach ($new_gotos as $g) { + if (filter_var($g, FILTER_VALIDATE_EMAIL) && + !in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) { + $update_sogo_mailboxes[] = $g; + } + } + } } break; case 'domain': @@ -3065,6 +3102,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { if (!empty($is_now)) { $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active']; (int)$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($is_now['attributes']['force_pw_update']); + (int)$force_tfa = (isset($_data['force_tfa'])) ? intval($_data['force_tfa']) : intval($is_now['attributes']['force_tfa']); (int)$sogo_access = (isset($_data['sogo_access']) && hasACLAccess("sogo_access")) ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']); (int)$imap_access = (isset($_data['imap_access']) && hasACLAccess("protocol_access")) ? intval($_data['imap_access']) : intval($is_now['attributes']['imap_access']); (int)$pop3_access = (isset($_data['pop3_access']) && hasACLAccess("protocol_access")) ? intval($_data['pop3_access']) : intval($is_now['attributes']['pop3_access']); @@ -3088,6 +3126,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){ $force_pw_update = 0; + $force_tfa = 0; } $pw_recovery_email = (isset($_data['pw_recovery_email']) && $authsource == 'mailcow') ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email']; } @@ -3359,6 +3398,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { `quota` = :quota_b, `authsource` = :authsource, `attributes` = JSON_SET(`attributes`, '$.force_pw_update', :force_pw_update), + `attributes` = JSON_SET(`attributes`, '$.force_tfa', :force_tfa), `attributes` = JSON_SET(`attributes`, '$.sogo_access', :sogo_access), `attributes` = JSON_SET(`attributes`, '$.imap_access', :imap_access), `attributes` = JSON_SET(`attributes`, '$.sieve_access', :sieve_access), @@ -3376,6 +3416,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ':quota_b' => $quota_b, ':attribute_hash' => $attribute_hash, ':force_pw_update' => $force_pw_update, + ':force_tfa' => $force_tfa, ':sogo_access' => $sogo_access, ':imap_access' => $imap_access, ':pop3_access' => $pop3_access, @@ -3431,15 +3472,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'msg' => array('mailbox_modified', $username) ); - try { - update_sogo_static_view($username); - } catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), - 'msg' => $e->getMessage() - ); - } + // Track affected mailboxes for SOGo update + $update_sogo_mailboxes[] = $username; } return true; break; @@ -4068,6 +4102,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'msg' => array('resource_modified', htmlspecialchars($name)) ); + + // Track affected mailboxes for SOGo update + $update_sogo_mailboxes[] = $name; } break; case 'domain_wide_footer': @@ -5772,6 +5809,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); continue; } + + // Track affected mailboxes for SOGo update (capture before deletion) + if (!empty($alias_data['goto'])) { + $gotos = array_map('trim', explode(',', $alias_data['goto'])); + foreach ($gotos as $g) { + if (filter_var($g, FILTER_VALIDATE_EMAIL) && + !in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) { + $update_sogo_mailboxes[] = $g; + } + } + } + $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `id` = :id"); $stmt->execute(array( ':id' => $alias_data['id'] @@ -6030,20 +6079,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { continue; } - try { - update_sogo_static_view($username); - }catch (PDOException $e) { - $_SESSION['return'][] = array( - 'type' => 'success', - 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), - 'msg' => $e->getMessage() - ); - } $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'msg' => array('mailbox_removed', htmlspecialchars($username)) ); + + // Track affected mailboxes for SOGo update + $update_sogo_mailboxes[] = $username; } return true; break; @@ -6145,6 +6188,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'msg' => array('resource_removed', htmlspecialchars($name)) ); + + // Track affected mailboxes for SOGo update + $update_sogo_mailboxes[] = $name; } break; case 'tags_domain': @@ -6251,9 +6297,21 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } break; } - if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource')) && getenv('SKIP_SOGO') != "y") { + if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource', 'mailbox')) && getenv('SKIP_SOGO') != "y") { try { - update_sogo_static_view(); + if (($_type == 'alias' || $_type == 'resource' || $_type == 'mailbox') && !empty($update_sogo_mailboxes)) { + // INCREMENTAL UPDATE: Update only affected mailboxes/resources + $update_sogo_mailboxes = array_unique($update_sogo_mailboxes); + foreach ($update_sogo_mailboxes as $mailbox) { + update_sogo_static_view($mailbox); + } + } + else { + // FULL REBUILD: For domain and alias_domain operations or if no tracked mailboxes + // Domain operations affect all mailboxes + // Alias_domain operations affect entire target domain + update_sogo_static_view(); + } }catch (PDOException $e) { $_SESSION['return'][] = array( 'type' => 'success', diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index c64419800..72018a6bc 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -4,7 +4,7 @@ function init_db_schema() try { global $pdo; - $db_version = "28012026_1000"; + $db_version = "19022026_1220"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -76,7 +76,8 @@ function init_db_schema() "superadmin" => "TINYINT(1) NOT NULL DEFAULT '0'", "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", "modified" => "DATETIME ON UPDATE NOW(0)", - "active" => "TINYINT(1) NOT NULL DEFAULT '1'" + "active" => "TINYINT(1) NOT NULL DEFAULT '1'", + "attributes" => "JSON" ), "keys" => array( "primary" => array( @@ -1390,6 +1391,11 @@ function init_db_schema() $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.passwd_update', \"0\") WHERE JSON_VALUE(`attributes`, '$.passwd_update') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.relayhost', \"0\") WHERE JSON_VALUE(`attributes`, '$.relayhost') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.force_pw_update', \"0\") WHERE JSON_VALUE(`attributes`, '$.force_pw_update') IS NULL;"); + $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.force_tfa', \"0\") WHERE JSON_VALUE(`attributes`, '$.force_tfa') IS NULL;"); + // admin attributes + $pdo->query("UPDATE `admin` SET `attributes` = '{}' WHERE `attributes` = '' OR `attributes` IS NULL;"); + $pdo->query("UPDATE `admin` SET `attributes` = JSON_SET(`attributes`, '$.force_tfa', \"0\") WHERE JSON_VALUE(`attributes`, '$.force_tfa') IS NULL;"); + $pdo->query("UPDATE `admin` SET `attributes` = JSON_SET(`attributes`, '$.force_pw_update', \"0\") WHERE JSON_VALUE(`attributes`, '$.force_pw_update') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.sieve_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.sieve_access') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.sogo_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.sogo_access') IS NULL;"); $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.imap_access', \"1\") WHERE JSON_VALUE(`attributes`, '$.imap_access') IS NULL;"); @@ -1449,6 +1455,7 @@ function init_db_schema() "rl_frame" => "s", "rl_value" => "", "force_pw_update" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['force_pw_update']), + "force_tfa" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['force_tfa']), "sogo_access" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['sogo_access']), "active" => 1, "tls_enforce_in" => intval($GLOBALS['MAILBOX_DEFAULT_ATTRIBUTES']['tls_enforce_in']), diff --git a/data/web/inc/lib/composer.json b/data/web/inc/lib/composer.json index 91a50bcbf..3998065cb 100644 --- a/data/web/inc/lib/composer.json +++ b/data/web/inc/lib/composer.json @@ -11,6 +11,7 @@ "directorytree/ldaprecord": "^3.3", "twig/twig": "^3.0", "stevenmaguire/oauth2-keycloak": "^4.0", - "league/oauth2-client": "^2.7" + "league/oauth2-client": "^2.7", + "bacon/bacon-qr-code": "^2.0" } } diff --git a/data/web/inc/lib/composer.lock b/data/web/inc/lib/composer.lock index fb1c3903b..4254deffb 100644 --- a/data/web/inc/lib/composer.lock +++ b/data/web/inc/lib/composer.lock @@ -4,8 +4,62 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f5a147cdb147b935a158b86f47a4747", + "content-hash": "50fb4a320500820e36f30eabc45222a0", "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.1", + "phpunit/phpunit": "^7 | ^8 | ^9", + "spatie/phpunit-snapshot-assertions": "^4.2.9", + "squizlabs/php_codesniffer": "^3.4" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8" + }, + "time": "2022-12-07T17:46:57+00:00" + }, { "name": "bshaffer/oauth2-server-php", "version": "v1.11.1", @@ -137,6 +191,56 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, { "name": "ddeboer/imap", "version": "1.13.1", @@ -214,30 +318,32 @@ }, { "name": "directorytree/ldaprecord", - "version": "v2.20.5", + "version": "v3.8.5", "source": { "type": "git", "url": "https://github.com/DirectoryTree/LdapRecord.git", - "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16" + "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/5bd0a5a9d257cf1049ae83055dbba4c3479ddf16", - "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16", + "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/00e5f088f8c4028d5f398783cccc2e8119a27a65", + "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65", "shasum": "" }, "require": { + "ext-iconv": "*", "ext-json": "*", "ext-ldap": "*", - "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", - "nesbot/carbon": "^1.0|^2.0", - "php": ">=7.3", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0", - "symfony/polyfill-php80": "^1.25", - "tightenco/collect": "^5.6|^6.0|^7.0|^8.0|^9.0" + "illuminate/collections": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", + "nesbot/carbon": "*", + "php": ">=8.1", + "psr/log": "*", + "psr/simple-cache": "^1.0|^2.0|^3.0" }, "require-dev": { + "fakerphp/faker": "^1.21", + "laravel/pint": "^1.6", "mockery/mockery": "^1.0", "phpunit/phpunit": "^9.0", "spatie/ray": "^1.24" @@ -284,7 +390,7 @@ "type": "github" } ], - "time": "2023-10-11T16:34:34+00:00" + "time": "2025-10-06T02:22:34+00:00" }, { "name": "firebase/php-jwt", @@ -677,6 +783,107 @@ ], "time": "2023-04-17T16:00:45+00:00" }, + { + "name": "illuminate/collections", + "version": "v10.49.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "php": "^8.1" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-09-08T19:05:53+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v10.49.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/47c700320b7a419f0d188d111f3bbed978fcbd3f", + "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-03-24T11:47:24+00:00" + }, { "name": "illuminate/contracts", "version": "v10.44.0", @@ -725,6 +932,52 @@ }, "time": "2024-01-15T18:52:32+00:00" }, + { + "name": "illuminate/macroable", + "version": "v10.49.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-05T12:46:42+00:00" + }, { "name": "league/oauth2-client", "version": "2.7.0", @@ -2452,145 +2705,6 @@ ], "time": "2023-12-26T14:02:43+00:00" }, - { - "name": "symfony/var-dumper", - "version": "v6.4.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "0435a08f69125535336177c29d56af3abc1f69da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0435a08f69125535336177c29d56af3abc1f69da", - "reference": "0435a08f69125535336177c29d56af3abc1f69da", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/console": "<5.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^6.3|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "twig/twig": "^2.13|^3.0.4" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-23T14:53:30+00:00" - }, - { - "name": "tightenco/collect", - "version": "v9.52.7", - "source": { - "type": "git", - "url": "https://github.com/tighten/collect.git", - "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tighten/collect/zipball/b15143cd11fe01a700fcc449df61adc64452fa6d", - "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d", - "shasum": "" - }, - "require": { - "php": "^8.0", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "nesbot/carbon": "^2.23.0", - "phpunit/phpunit": "^8.3" - }, - "type": "library", - "autoload": { - "files": [ - "src/Collect/Support/helpers.php", - "src/Collect/Support/alias.php" - ], - "psr-4": { - "Tightenco\\Collect\\": "src/Collect" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "Collect - Illuminate Collections as a separate package.", - "keywords": [ - "collection", - "laravel" - ], - "support": { - "issues": "https://github.com/tighten/collect/issues", - "source": "https://github.com/tighten/collect/tree/v9.52.7" - }, - "time": "2023-04-14T21:51:36+00:00" - }, { "name": "twig/twig", "version": "v3.14.0", @@ -2674,10 +2788,10 @@ "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/LICENSE b/data/web/inc/lib/vendor/bacon/bacon-qr-code/LICENSE new file mode 100644 index 000000000..d45a35647 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2017, Ben Scholzen 'DASPRiD' +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/README.md b/data/web/inc/lib/vendor/bacon/bacon-qr-code/README.md new file mode 100644 index 000000000..9c099fefd --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/README.md @@ -0,0 +1,39 @@ +# QR Code generator + +[![PHP CI](https://github.com/Bacon/BaconQrCode/actions/workflows/ci.yml/badge.svg)](https://github.com/Bacon/BaconQrCode/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/Bacon/BaconQrCode/branch/master/graph/badge.svg?token=rD0HcAiEEx)](https://codecov.io/gh/Bacon/BaconQrCode) +[![Latest Stable Version](https://poser.pugx.org/bacon/bacon-qr-code/v/stable)](https://packagist.org/packages/bacon/bacon-qr-code) +[![Total Downloads](https://poser.pugx.org/bacon/bacon-qr-code/downloads)](https://packagist.org/packages/bacon/bacon-qr-code) +[![License](https://poser.pugx.org/bacon/bacon-qr-code/license)](https://packagist.org/packages/bacon/bacon-qr-code) + + +## Introduction +BaconQrCode is a port of QR code portion of the ZXing library. It currently +only features the encoder part, but could later receive the decoder part as +well. + +As the Reed Solomon codec implementation of the ZXing library performs quite +slow in PHP, it was exchanged with the implementation by Phil Karn. + + +## Example usage +```php +use BaconQrCode\Renderer\ImageRenderer; +use BaconQrCode\Renderer\Image\ImagickImageBackEnd; +use BaconQrCode\Renderer\RendererStyle\RendererStyle; +use BaconQrCode\Writer; + +$renderer = new ImageRenderer( + new RendererStyle(400), + new ImagickImageBackEnd() +); +$writer = new Writer($renderer); +$writer->writeFile('Hello World!', 'qrcode.png'); +``` + +## Available image renderer back ends +BaconQrCode comes with multiple back ends for rendering images. Currently included are the following: + +- `ImagickImageBackEnd`: renders raster images using the Imagick library +- `SvgImageBackEnd`: renders SVG files using XMLWriter +- `EpsImageBackEnd`: renders EPS files diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/composer.json b/data/web/inc/lib/vendor/bacon/bacon-qr-code/composer.json new file mode 100644 index 000000000..7f193daf0 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/composer.json @@ -0,0 +1,44 @@ +{ + "name": "bacon/bacon-qr-code", + "description": "BaconQrCode is a QR code generator for PHP.", + "license" : "BSD-2-Clause", + "homepage": "https://github.com/Bacon/BaconQrCode", + "require": { + "php": "^7.1 || ^8.0", + "ext-iconv": "*", + "dasprid/enum": "^1.0.3" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "require-dev": { + "phpunit/phpunit": "^7 | ^8 | ^9", + "spatie/phpunit-snapshot-assertions": "^4.2.9", + "squizlabs/php_codesniffer": "^3.4", + "phly/keep-a-changelog": "^2.1" + }, + "config": { + "allow-plugins": { + "ocramius/package-versions": true + } + }, + "archive": { + "exclude": [ + "/test", + "/phpunit.xml.dist" + ] + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/phpunit.xml.dist b/data/web/inc/lib/vendor/bacon/bacon-qr-code/phpunit.xml.dist new file mode 100644 index 000000000..d9e4d57bc --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/phpunit.xml.dist @@ -0,0 +1,13 @@ + + + + + src + + + + + ./test + + + diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitArray.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitArray.php new file mode 100644 index 000000000..158384fe8 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitArray.php @@ -0,0 +1,372 @@ + + */ + private $bits; + + /** + * Size of the bit array in bits. + * + * @var int + */ + private $size; + + /** + * Creates a new bit array with a given size. + */ + public function __construct(int $size = 0) + { + $this->size = $size; + $this->bits = SplFixedArray::fromArray(array_fill(0, ($this->size + 31) >> 3, 0)); + } + + /** + * Gets the size in bits. + */ + public function getSize() : int + { + return $this->size; + } + + /** + * Gets the size in bytes. + */ + public function getSizeInBytes() : int + { + return ($this->size + 7) >> 3; + } + + /** + * Ensures that the array has a minimum capacity. + */ + public function ensureCapacity(int $size) : void + { + if ($size > count($this->bits) << 5) { + $this->bits->setSize(($size + 31) >> 5); + } + } + + /** + * Gets a specific bit. + */ + public function get(int $i) : bool + { + return 0 !== ($this->bits[$i >> 5] & (1 << ($i & 0x1f))); + } + + /** + * Sets a specific bit. + */ + public function set(int $i) : void + { + $this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f); + } + + /** + * Flips a specific bit. + */ + public function flip(int $i) : void + { + $this->bits[$i >> 5] ^= 1 << ($i & 0x1f); + } + + /** + * Gets the next set bit position from a given position. + */ + public function getNextSet(int $from) : int + { + if ($from >= $this->size) { + return $this->size; + } + + $bitsOffset = $from >> 5; + $currentBits = $this->bits[$bitsOffset]; + $bitsLength = count($this->bits); + $currentBits &= ~((1 << ($from & 0x1f)) - 1); + + while (0 === $currentBits) { + if (++$bitsOffset === $bitsLength) { + return $this->size; + } + + $currentBits = $this->bits[$bitsOffset]; + } + + $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits); + return $result > $this->size ? $this->size : $result; + } + + /** + * Gets the next unset bit position from a given position. + */ + public function getNextUnset(int $from) : int + { + if ($from >= $this->size) { + return $this->size; + } + + $bitsOffset = $from >> 5; + $currentBits = ~$this->bits[$bitsOffset]; + $bitsLength = count($this->bits); + $currentBits &= ~((1 << ($from & 0x1f)) - 1); + + while (0 === $currentBits) { + if (++$bitsOffset === $bitsLength) { + return $this->size; + } + + $currentBits = ~$this->bits[$bitsOffset]; + } + + $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits); + return $result > $this->size ? $this->size : $result; + } + + /** + * Sets a bulk of bits. + */ + public function setBulk(int $i, int $newBits) : void + { + $this->bits[$i >> 5] = $newBits; + } + + /** + * Sets a range of bits. + * + * @throws InvalidArgumentException if end is smaller than start + */ + public function setRange(int $start, int $end) : void + { + if ($end < $start) { + throw new InvalidArgumentException('End must be greater or equal to start'); + } + + if ($end === $start) { + return; + } + + --$end; + + $firstInt = $start >> 5; + $lastInt = $end >> 5; + + for ($i = $firstInt; $i <= $lastInt; ++$i) { + $firstBit = $i > $firstInt ? 0 : $start & 0x1f; + $lastBit = $i < $lastInt ? 31 : $end & 0x1f; + + if (0 === $firstBit && 31 === $lastBit) { + $mask = 0x7fffffff; + } else { + $mask = 0; + + for ($j = $firstBit; $j < $lastBit; ++$j) { + $mask |= 1 << $j; + } + } + + $this->bits[$i] = $this->bits[$i] | $mask; + } + } + + /** + * Clears the bit array, unsetting every bit. + */ + public function clear() : void + { + $bitsLength = count($this->bits); + + for ($i = 0; $i < $bitsLength; ++$i) { + $this->bits[$i] = 0; + } + } + + /** + * Checks if a range of bits is set or not set. + + * @throws InvalidArgumentException if end is smaller than start + */ + public function isRange(int $start, int $end, bool $value) : bool + { + if ($end < $start) { + throw new InvalidArgumentException('End must be greater or equal to start'); + } + + if ($end === $start) { + return true; + } + + --$end; + + $firstInt = $start >> 5; + $lastInt = $end >> 5; + + for ($i = $firstInt; $i <= $lastInt; ++$i) { + $firstBit = $i > $firstInt ? 0 : $start & 0x1f; + $lastBit = $i < $lastInt ? 31 : $end & 0x1f; + + if (0 === $firstBit && 31 === $lastBit) { + $mask = 0x7fffffff; + } else { + $mask = 0; + + for ($j = $firstBit; $j <= $lastBit; ++$j) { + $mask |= 1 << $j; + } + } + + if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) { + return false; + } + } + + return true; + } + + /** + * Appends a bit to the array. + */ + public function appendBit(bool $bit) : void + { + $this->ensureCapacity($this->size + 1); + + if ($bit) { + $this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | (1 << ($this->size & 0x1f)); + } + + ++$this->size; + } + + /** + * Appends a number of bits (up to 32) to the array. + + * @throws InvalidArgumentException if num bits is not between 0 and 32 + */ + public function appendBits(int $value, int $numBits) : void + { + if ($numBits < 0 || $numBits > 32) { + throw new InvalidArgumentException('Num bits must be between 0 and 32'); + } + + $this->ensureCapacity($this->size + $numBits); + + for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) { + $this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) === 1); + } + } + + /** + * Appends another bit array to this array. + */ + public function appendBitArray(self $other) : void + { + $otherSize = $other->getSize(); + $this->ensureCapacity($this->size + $other->getSize()); + + for ($i = 0; $i < $otherSize; ++$i) { + $this->appendBit($other->get($i)); + } + } + + /** + * Makes an exclusive-or comparision on the current bit array. + * + * @throws InvalidArgumentException if sizes don't match + */ + public function xorBits(self $other) : void + { + $bitsLength = count($this->bits); + $otherBits = $other->getBitArray(); + + if ($bitsLength !== count($otherBits)) { + throw new InvalidArgumentException('Sizes don\'t match'); + } + + for ($i = 0; $i < $bitsLength; ++$i) { + $this->bits[$i] = $this->bits[$i] ^ $otherBits[$i]; + } + } + + /** + * Converts the bit array to a byte array. + * + * @return SplFixedArray + */ + public function toBytes(int $bitOffset, int $numBytes) : SplFixedArray + { + $bytes = new SplFixedArray($numBytes); + + for ($i = 0; $i < $numBytes; ++$i) { + $byte = 0; + + for ($j = 0; $j < 8; ++$j) { + if ($this->get($bitOffset)) { + $byte |= 1 << (7 - $j); + } + + ++$bitOffset; + } + + $bytes[$i] = $byte; + } + + return $bytes; + } + + /** + * Gets the internal bit array. + * + * @return SplFixedArray + */ + public function getBitArray() : SplFixedArray + { + return $this->bits; + } + + /** + * Reverses the array. + */ + public function reverse() : void + { + $newBits = new SplFixedArray(count($this->bits)); + + for ($i = 0; $i < $this->size; ++$i) { + if ($this->get($this->size - $i - 1)) { + $newBits[$i >> 5] = $newBits[$i >> 5] | (1 << ($i & 0x1f)); + } + } + + $this->bits = $newBits; + } + + /** + * Returns a string representation of the bit array. + */ + public function __toString() : string + { + $result = ''; + + for ($i = 0; $i < $this->size; ++$i) { + if (0 === ($i & 0x07)) { + $result .= ' '; + } + + $result .= $this->get($i) ? 'X' : '.'; + } + + return $result; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitMatrix.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitMatrix.php new file mode 100644 index 000000000..10bf8fe20 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitMatrix.php @@ -0,0 +1,313 @@ + + */ + private $bits; + + /** + * @throws InvalidArgumentException if a dimension is smaller than zero + */ + public function __construct(int $width, int $height = null) + { + if (null === $height) { + $height = $width; + } + + if ($width < 1 || $height < 1) { + throw new InvalidArgumentException('Both dimensions must be greater than zero'); + } + + $this->width = $width; + $this->height = $height; + $this->rowSize = ($width + 31) >> 5; + $this->bits = SplFixedArray::fromArray(array_fill(0, $this->rowSize * $height, 0)); + } + + /** + * Gets the requested bit, where true means black. + */ + public function get(int $x, int $y) : bool + { + $offset = $y * $this->rowSize + ($x >> 5); + return 0 !== (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1); + } + + /** + * Sets the given bit to true. + */ + public function set(int $x, int $y) : void + { + $offset = $y * $this->rowSize + ($x >> 5); + $this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f)); + } + + /** + * Flips the given bit. + */ + public function flip(int $x, int $y) : void + { + $offset = $y * $this->rowSize + ($x >> 5); + $this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f)); + } + + /** + * Clears all bits (set to false). + */ + public function clear() : void + { + $max = count($this->bits); + + for ($i = 0; $i < $max; ++$i) { + $this->bits[$i] = 0; + } + } + + /** + * Sets a square region of the bit matrix to true. + * + * @throws InvalidArgumentException if left or top are negative + * @throws InvalidArgumentException if width or height are smaller than 1 + * @throws InvalidArgumentException if region does not fit into the matix + */ + public function setRegion(int $left, int $top, int $width, int $height) : void + { + if ($top < 0 || $left < 0) { + throw new InvalidArgumentException('Left and top must be non-negative'); + } + + if ($height < 1 || $width < 1) { + throw new InvalidArgumentException('Width and height must be at least 1'); + } + + $right = $left + $width; + $bottom = $top + $height; + + if ($bottom > $this->height || $right > $this->width) { + throw new InvalidArgumentException('The region must fit inside the matrix'); + } + + for ($y = $top; $y < $bottom; ++$y) { + $offset = $y * $this->rowSize; + + for ($x = $left; $x < $right; ++$x) { + $index = $offset + ($x >> 5); + $this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f)); + } + } + } + + /** + * A fast method to retrieve one row of data from the matrix as a BitArray. + */ + public function getRow(int $y, BitArray $row = null) : BitArray + { + if (null === $row || $row->getSize() < $this->width) { + $row = new BitArray($this->width); + } + + $offset = $y * $this->rowSize; + + for ($x = 0; $x < $this->rowSize; ++$x) { + $row->setBulk($x << 5, $this->bits[$offset + $x]); + } + + return $row; + } + + /** + * Sets a row of data from a BitArray. + */ + public function setRow(int $y, BitArray $row) : void + { + $bits = $row->getBitArray(); + + for ($i = 0; $i < $this->rowSize; ++$i) { + $this->bits[$y * $this->rowSize + $i] = $bits[$i]; + } + } + + /** + * This is useful in detecting the enclosing rectangle of a 'pure' barcode. + * + * @return int[]|null + */ + public function getEnclosingRectangle() : ?array + { + $left = $this->width; + $top = $this->height; + $right = -1; + $bottom = -1; + + for ($y = 0; $y < $this->height; ++$y) { + for ($x32 = 0; $x32 < $this->rowSize; ++$x32) { + $bits = $this->bits[$y * $this->rowSize + $x32]; + + if (0 !== $bits) { + if ($y < $top) { + $top = $y; + } + + if ($y > $bottom) { + $bottom = $y; + } + + if ($x32 * 32 < $left) { + $bit = 0; + + while (($bits << (31 - $bit)) === 0) { + $bit++; + } + + if (($x32 * 32 + $bit) < $left) { + $left = $x32 * 32 + $bit; + } + } + } + + if ($x32 * 32 + 31 > $right) { + $bit = 31; + + while (0 === BitUtils::unsignedRightShift($bits, $bit)) { + --$bit; + } + + if (($x32 * 32 + $bit) > $right) { + $right = $x32 * 32 + $bit; + } + } + } + } + + $width = $right - $left; + $height = $bottom - $top; + + if ($width < 0 || $height < 0) { + return null; + } + + return [$left, $top, $width, $height]; + } + + /** + * Gets the most top left set bit. + * + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return int[]|null + */ + public function getTopLeftOnBit() : ?array + { + $bitsOffset = 0; + + while ($bitsOffset < count($this->bits) && 0 === $this->bits[$bitsOffset]) { + ++$bitsOffset; + } + + if (count($this->bits) === $bitsOffset) { + return null; + } + + $x = intdiv($bitsOffset, $this->rowSize); + $y = ($bitsOffset % $this->rowSize) << 5; + + $bits = $this->bits[$bitsOffset]; + $bit = 0; + + while (0 === ($bits << (31 - $bit))) { + ++$bit; + } + + $x += $bit; + + return [$x, $y]; + } + + /** + * Gets the most bottom right set bit. + * + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return int[]|null + */ + public function getBottomRightOnBit() : ?array + { + $bitsOffset = count($this->bits) - 1; + + while ($bitsOffset >= 0 && 0 === $this->bits[$bitsOffset]) { + --$bitsOffset; + } + + if ($bitsOffset < 0) { + return null; + } + + $x = intdiv($bitsOffset, $this->rowSize); + $y = ($bitsOffset % $this->rowSize) << 5; + + $bits = $this->bits[$bitsOffset]; + $bit = 0; + + while (0 === BitUtils::unsignedRightShift($bits, $bit)) { + --$bit; + } + + $x += $bit; + + return [$x, $y]; + } + + /** + * Gets the width of the matrix, + */ + public function getWidth() : int + { + return $this->width; + } + + /** + * Gets the height of the matrix. + */ + public function getHeight() : int + { + return $this->height; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitUtils.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitUtils.php new file mode 100644 index 000000000..0c575b493 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/BitUtils.php @@ -0,0 +1,41 @@ +>>" in other + * languages. + */ + public static function unsignedRightShift(int $a, int $b) : int + { + return ( + $a >= 0 + ? $a >> $b + : (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1)) + ); + } + + /** + * Gets the number of trailing zeros. + */ + public static function numberOfTrailingZeros(int $i) : int + { + $lastPos = strrpos(str_pad(decbin($i), 32, '0', STR_PAD_LEFT), '1'); + return $lastPos === false ? 32 : 31 - $lastPos; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/CharacterSetEci.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/CharacterSetEci.php new file mode 100644 index 000000000..9049ccb30 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/CharacterSetEci.php @@ -0,0 +1,183 @@ +|null + */ + private static $valueToEci; + + /** + * @var array|null + */ + private static $nameToEci; + + /** + * @param int[] $values + */ + public function __construct(array $values, string ...$otherEncodingNames) + { + $this->values = $values; + $this->otherEncodingNames = $otherEncodingNames; + } + + /** + * Returns the primary value. + */ + public function getValue() : int + { + return $this->values[0]; + } + + /** + * Gets character set ECI by value. + * + * Returns the representing ECI of a given value, or null if it is legal but unsupported. + * + * @throws InvalidArgumentException if value is not between 0 and 900 + */ + public static function getCharacterSetEciByValue(int $value) : ?self + { + if ($value < 0 || $value >= 900) { + throw new InvalidArgumentException('Value must be between 0 and 900'); + } + + $valueToEci = self::valueToEci(); + + if (! array_key_exists($value, $valueToEci)) { + return null; + } + + return $valueToEci[$value]; + } + + /** + * Returns character set ECI by name. + * + * Returns the representing ECI of a given name, or null if it is legal but unsupported + */ + public static function getCharacterSetEciByName(string $name) : ?self + { + $nameToEci = self::nameToEci(); + $name = strtolower($name); + + if (! array_key_exists($name, $nameToEci)) { + return null; + } + + return $nameToEci[$name]; + } + + private static function valueToEci() : array + { + if (null !== self::$valueToEci) { + return self::$valueToEci; + } + + self::$valueToEci = []; + + foreach (self::values() as $eci) { + foreach ($eci->values as $value) { + self::$valueToEci[$value] = $eci; + } + } + + return self::$valueToEci; + } + + private static function nameToEci() : array + { + if (null !== self::$nameToEci) { + return self::$nameToEci; + } + + self::$nameToEci = []; + + foreach (self::values() as $eci) { + self::$nameToEci[strtolower($eci->name())] = $eci; + + foreach ($eci->otherEncodingNames as $name) { + self::$nameToEci[strtolower($name)] = $eci; + } + } + + return self::$nameToEci; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/EcBlock.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/EcBlock.php new file mode 100644 index 000000000..a9a1d07d7 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/EcBlock.php @@ -0,0 +1,49 @@ +count = $count; + $this->dataCodewords = $dataCodewords; + } + + /** + * Returns how many times the block is used. + */ + public function getCount() : int + { + return $this->count; + } + + /** + * Returns the number of data codewords. + */ + public function getDataCodewords() : int + { + return $this->dataCodewords; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/EcBlocks.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/EcBlocks.php new file mode 100644 index 000000000..172b5f278 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/EcBlocks.php @@ -0,0 +1,74 @@ +ecCodewordsPerBlock = $ecCodewordsPerBlock; + $this->ecBlocks = $ecBlocks; + } + + /** + * Returns the number of EC codewords per block. + */ + public function getEcCodewordsPerBlock() : int + { + return $this->ecCodewordsPerBlock; + } + + /** + * Returns the total number of EC block appearances. + */ + public function getNumBlocks() : int + { + $total = 0; + + foreach ($this->ecBlocks as $ecBlock) { + $total += $ecBlock->getCount(); + } + + return $total; + } + + /** + * Returns the total count of EC codewords. + */ + public function getTotalEcCodewords() : int + { + return $this->ecCodewordsPerBlock * $this->getNumBlocks(); + } + + /** + * Returns the EC blocks included in this collection. + * + * @return EcBlock[] + */ + public function getEcBlocks() : array + { + return $this->ecBlocks; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.php new file mode 100644 index 000000000..9bbf44068 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/ErrorCorrectionLevel.php @@ -0,0 +1,63 @@ +bits = $bits; + } + + /** + * @throws OutOfBoundsException if number of bits is invalid + */ + public static function forBits(int $bits) : self + { + switch ($bits) { + case 0: + return self::M(); + + case 1: + return self::L(); + + case 2: + return self::H(); + + case 3: + return self::Q(); + } + + throw new OutOfBoundsException('Invalid number of bits'); + } + + /** + * Returns the two bits used to encode this error correction level. + */ + public function getBits() : int + { + return $this->bits; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/FormatInformation.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/FormatInformation.php new file mode 100644 index 000000000..38295fc69 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/FormatInformation.php @@ -0,0 +1,203 @@ +ecLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x3); + $this->dataMask = $formatInfo & 0x7; + } + + /** + * Checks how many bits are different between two integers. + */ + public static function numBitsDiffering(int $a, int $b) : int + { + $a ^= $b; + + return ( + self::BITS_SET_IN_HALF_BYTE[$a & 0xf] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 4) & 0xf)] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 8) & 0xf)] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 12) & 0xf)] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 16) & 0xf)] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 20) & 0xf)] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 24) & 0xf)] + + self::BITS_SET_IN_HALF_BYTE[(BitUtils::unsignedRightShift($a, 28) & 0xf)] + ); + } + + /** + * Decodes format information. + */ + public static function decodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self + { + $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2); + + if (null !== $formatInfo) { + return $formatInfo; + } + + // Should return null, but, some QR codes apparently do not mask this info. Try again by actually masking the + // pattern first. + return self::doDecodeFormatInformation( + $maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR, + $maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR + ); + } + + /** + * Internal method for decoding format information. + */ + private static function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2) : ?self + { + $bestDifference = PHP_INT_MAX; + $bestFormatInfo = 0; + + foreach (self::FORMAT_INFO_DECODE_LOOKUP as $decodeInfo) { + $targetInfo = $decodeInfo[0]; + + if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) { + // Found an exact match + return new self($decodeInfo[1]); + } + + $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo); + + if ($bitsDifference < $bestDifference) { + $bestFormatInfo = $decodeInfo[1]; + $bestDifference = $bitsDifference; + } + + if ($maskedFormatInfo1 !== $maskedFormatInfo2) { + // Also try the other option + $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo); + + if ($bitsDifference < $bestDifference) { + $bestFormatInfo = $decodeInfo[1]; + $bestDifference = $bitsDifference; + } + } + } + + // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match. + if ($bestDifference <= 3) { + return new self($bestFormatInfo); + } + + return null; + } + + /** + * Returns the error correction level. + */ + public function getErrorCorrectionLevel() : ErrorCorrectionLevel + { + return $this->ecLevel; + } + + /** + * Returns the data mask. + */ + public function getDataMask() : int + { + return $this->dataMask; + } + + /** + * Hashes the code of the EC level. + */ + public function hashCode() : int + { + return ($this->ecLevel->getBits() << 3) | $this->dataMask; + } + + /** + * Verifies if this instance equals another one. + */ + public function equals(self $other) : bool + { + return ( + $this->ecLevel === $other->ecLevel + && $this->dataMask === $other->dataMask + ); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/Mode.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/Mode.php new file mode 100644 index 000000000..af5a113f4 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/Mode.php @@ -0,0 +1,79 @@ +characterCountBitsForVersions = $characterCountBitsForVersions; + $this->bits = $bits; + } + + /** + * Returns the number of bits used in a specific QR code version. + */ + public function getCharacterCountBits(Version $version) : int + { + $number = $version->getVersionNumber(); + + if ($number <= 9) { + $offset = 0; + } elseif ($number <= 26) { + $offset = 1; + } else { + $offset = 2; + } + + return $this->characterCountBitsForVersions[$offset]; + } + + /** + * Returns the four bits used to encode this mode. + */ + public function getBits() : int + { + return $this->bits; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.php new file mode 100644 index 000000000..a5aad0b51 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/ReedSolomonCodec.php @@ -0,0 +1,468 @@ + 8) { + throw new InvalidArgumentException('Symbol size must be between 0 and 8'); + } + + if ($firstRoot < 0 || $firstRoot >= (1 << $symbolSize)) { + throw new InvalidArgumentException('First root must be between 0 and ' . (1 << $symbolSize)); + } + + if ($numRoots < 0 || $numRoots >= (1 << $symbolSize)) { + throw new InvalidArgumentException('Num roots must be between 0 and ' . (1 << $symbolSize)); + } + + if ($padding < 0 || $padding >= ((1 << $symbolSize) - 1 - $numRoots)) { + throw new InvalidArgumentException( + 'Padding must be between 0 and ' . ((1 << $symbolSize) - 1 - $numRoots) + ); + } + + $this->symbolSize = $symbolSize; + $this->blockSize = (1 << $symbolSize) - 1; + $this->padding = $padding; + $this->alphaTo = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false); + $this->indexOf = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false); + + // Generate galous field lookup table + $this->indexOf[0] = $this->blockSize; + $this->alphaTo[$this->blockSize] = 0; + + $sr = 1; + + for ($i = 0; $i < $this->blockSize; ++$i) { + $this->indexOf[$sr] = $i; + $this->alphaTo[$i] = $sr; + + $sr <<= 1; + + if ($sr & (1 << $symbolSize)) { + $sr ^= $gfPoly; + } + + $sr &= $this->blockSize; + } + + if (1 !== $sr) { + throw new RuntimeException('Field generator polynomial is not primitive'); + } + + // Form RS code generator polynomial from its roots + $this->generatorPoly = SplFixedArray::fromArray(array_fill(0, $numRoots + 1, 0), false); + $this->firstRoot = $firstRoot; + $this->primitive = $primitive; + $this->numRoots = $numRoots; + + // Find prim-th root of 1, used in decoding + for ($iPrimitive = 1; ($iPrimitive % $primitive) !== 0; $iPrimitive += $this->blockSize) { + } + + $this->iPrimitive = intdiv($iPrimitive, $primitive); + + $this->generatorPoly[0] = 1; + + for ($i = 0, $root = $firstRoot * $primitive; $i < $numRoots; ++$i, $root += $primitive) { + $this->generatorPoly[$i + 1] = 1; + + for ($j = $i; $j > 0; $j--) { + if ($this->generatorPoly[$j] !== 0) { + $this->generatorPoly[$j] = $this->generatorPoly[$j - 1] ^ $this->alphaTo[ + $this->modNn($this->indexOf[$this->generatorPoly[$j]] + $root) + ]; + } else { + $this->generatorPoly[$j] = $this->generatorPoly[$j - 1]; + } + } + + $this->generatorPoly[$j] = $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[0]] + $root)]; + } + + // Convert generator poly to index form for quicker encoding + for ($i = 0; $i <= $numRoots; ++$i) { + $this->generatorPoly[$i] = $this->indexOf[$this->generatorPoly[$i]]; + } + } + + /** + * Encodes data and writes result back into parity array. + */ + public function encode(SplFixedArray $data, SplFixedArray $parity) : void + { + for ($i = 0; $i < $this->numRoots; ++$i) { + $parity[$i] = 0; + } + + $iterations = $this->blockSize - $this->numRoots - $this->padding; + + for ($i = 0; $i < $iterations; ++$i) { + $feedback = $this->indexOf[$data[$i] ^ $parity[0]]; + + if ($feedback !== $this->blockSize) { + // Feedback term is non-zero + $feedback = $this->modNn($this->blockSize - $this->generatorPoly[$this->numRoots] + $feedback); + + for ($j = 1; $j < $this->numRoots; ++$j) { + $parity[$j] = $parity[$j] ^ $this->alphaTo[ + $this->modNn($feedback + $this->generatorPoly[$this->numRoots - $j]) + ]; + } + } + + for ($j = 0; $j < $this->numRoots - 1; ++$j) { + $parity[$j] = $parity[$j + 1]; + } + + if ($feedback !== $this->blockSize) { + $parity[$this->numRoots - 1] = $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[0])]; + } else { + $parity[$this->numRoots - 1] = 0; + } + } + } + + /** + * Decodes received data. + */ + public function decode(SplFixedArray $data, SplFixedArray $erasures = null) : ?int + { + // This speeds up the initialization a bit. + $numRootsPlusOne = SplFixedArray::fromArray(array_fill(0, $this->numRoots + 1, 0), false); + $numRoots = SplFixedArray::fromArray(array_fill(0, $this->numRoots, 0), false); + + $lambda = clone $numRootsPlusOne; + $b = clone $numRootsPlusOne; + $t = clone $numRootsPlusOne; + $omega = clone $numRootsPlusOne; + $root = clone $numRoots; + $loc = clone $numRoots; + + $numErasures = (null !== $erasures ? count($erasures) : 0); + + // Form the Syndromes; i.e., evaluate data(x) at roots of g(x) + $syndromes = SplFixedArray::fromArray(array_fill(0, $this->numRoots, $data[0]), false); + + for ($i = 1; $i < $this->blockSize - $this->padding; ++$i) { + for ($j = 0; $j < $this->numRoots; ++$j) { + if ($syndromes[$j] === 0) { + $syndromes[$j] = $data[$i]; + } else { + $syndromes[$j] = $data[$i] ^ $this->alphaTo[ + $this->modNn($this->indexOf[$syndromes[$j]] + ($this->firstRoot + $j) * $this->primitive) + ]; + } + } + } + + // Convert syndromes to index form, checking for nonzero conditions + $syndromeError = 0; + + for ($i = 0; $i < $this->numRoots; ++$i) { + $syndromeError |= $syndromes[$i]; + $syndromes[$i] = $this->indexOf[$syndromes[$i]]; + } + + if (! $syndromeError) { + // If syndrome is zero, data[] is a codeword and there are no errors to correct, so return data[] + // unmodified. + return 0; + } + + $lambda[0] = 1; + + if ($numErasures > 0) { + // Init lambda to be the erasure locator polynomial + $lambda[1] = $this->alphaTo[$this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[0]))]; + + for ($i = 1; $i < $numErasures; ++$i) { + $u = $this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[$i])); + + for ($j = $i + 1; $j > 0; --$j) { + $tmp = $this->indexOf[$lambda[$j - 1]]; + + if ($tmp !== $this->blockSize) { + $lambda[$j] = $lambda[$j] ^ $this->alphaTo[$this->modNn($u + $tmp)]; + } + } + } + } + + for ($i = 0; $i <= $this->numRoots; ++$i) { + $b[$i] = $this->indexOf[$lambda[$i]]; + } + + // Begin Berlekamp-Massey algorithm to determine error+erasure locator polynomial + $r = $numErasures; + $el = $numErasures; + + while (++$r <= $this->numRoots) { + // Compute discrepancy at the r-th step in poly form + $discrepancyR = 0; + + for ($i = 0; $i < $r; ++$i) { + if ($lambda[$i] !== 0 && $syndromes[$r - $i - 1] !== $this->blockSize) { + $discrepancyR ^= $this->alphaTo[ + $this->modNn($this->indexOf[$lambda[$i]] + $syndromes[$r - $i - 1]) + ]; + } + } + + $discrepancyR = $this->indexOf[$discrepancyR]; + + if ($discrepancyR === $this->blockSize) { + $tmp = $b->toArray(); + array_unshift($tmp, $this->blockSize); + array_pop($tmp); + $b = SplFixedArray::fromArray($tmp, false); + continue; + } + + $t[0] = $lambda[0]; + + for ($i = 0; $i < $this->numRoots; ++$i) { + if ($b[$i] !== $this->blockSize) { + $t[$i + 1] = $lambda[$i + 1] ^ $this->alphaTo[$this->modNn($discrepancyR + $b[$i])]; + } else { + $t[$i + 1] = $lambda[$i + 1]; + } + } + + if (2 * $el <= $r + $numErasures - 1) { + $el = $r + $numErasures - $el; + + for ($i = 0; $i <= $this->numRoots; ++$i) { + $b[$i] = ( + $lambda[$i] === 0 + ? $this->blockSize + : $this->modNn($this->indexOf[$lambda[$i]] - $discrepancyR + $this->blockSize) + ); + } + } else { + $tmp = $b->toArray(); + array_unshift($tmp, $this->blockSize); + array_pop($tmp); + $b = SplFixedArray::fromArray($tmp, false); + } + + $lambda = clone $t; + } + + // Convert lambda to index form and compute deg(lambda(x)) + $degLambda = 0; + + for ($i = 0; $i <= $this->numRoots; ++$i) { + $lambda[$i] = $this->indexOf[$lambda[$i]]; + + if ($lambda[$i] !== $this->blockSize) { + $degLambda = $i; + } + } + + // Find roots of the error+erasure locator polynomial by Chien search. + $reg = clone $lambda; + $reg[0] = 0; + $count = 0; + $i = 1; + + for ($k = $this->iPrimitive - 1; $i <= $this->blockSize; ++$i, $k = $this->modNn($k + $this->iPrimitive)) { + $q = 1; + + for ($j = $degLambda; $j > 0; $j--) { + if ($reg[$j] !== $this->blockSize) { + $reg[$j] = $this->modNn($reg[$j] + $j); + $q ^= $this->alphaTo[$reg[$j]]; + } + } + + if ($q !== 0) { + // Not a root + continue; + } + + // Store root (index-form) and error location number + $root[$count] = $i; + $loc[$count] = $k; + + if (++$count === $degLambda) { + break; + } + } + + if ($degLambda !== $count) { + // deg(lambda) unequal to number of roots: uncorrectable error detected + return null; + } + + // Compute err+eras evaluate poly omega(x) = s(x)*lambda(x) (modulo x**numRoots). In index form. Also find + // deg(omega). + $degOmega = $degLambda - 1; + + for ($i = 0; $i <= $degOmega; ++$i) { + $tmp = 0; + + for ($j = $i; $j >= 0; --$j) { + if ($syndromes[$i - $j] !== $this->blockSize && $lambda[$j] !== $this->blockSize) { + $tmp ^= $this->alphaTo[$this->modNn($syndromes[$i - $j] + $lambda[$j])]; + } + } + + $omega[$i] = $this->indexOf[$tmp]; + } + + // Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = inv(X(l))**(firstRoot-1) and + // den = lambda_pr(inv(X(l))) all in poly form. + for ($j = $count - 1; $j >= 0; --$j) { + $num1 = 0; + + for ($i = $degOmega; $i >= 0; $i--) { + if ($omega[$i] !== $this->blockSize) { + $num1 ^= $this->alphaTo[$this->modNn($omega[$i] + $i * $root[$j])]; + } + } + + $num2 = $this->alphaTo[$this->modNn($root[$j] * ($this->firstRoot - 1) + $this->blockSize)]; + $den = 0; + + // lambda[i+1] for i even is the formal derivativelambda_pr of lambda[i] + for ($i = min($degLambda, $this->numRoots - 1) & ~1; $i >= 0; $i -= 2) { + if ($lambda[$i + 1] !== $this->blockSize) { + $den ^= $this->alphaTo[$this->modNn($lambda[$i + 1] + $i * $root[$j])]; + } + } + + // Apply error to data + if ($num1 !== 0 && $loc[$j] >= $this->padding) { + $data[$loc[$j] - $this->padding] = $data[$loc[$j] - $this->padding] ^ ( + $this->alphaTo[ + $this->modNn( + $this->indexOf[$num1] + $this->indexOf[$num2] + $this->blockSize - $this->indexOf[$den] + ) + ] + ); + } + } + + if (null !== $erasures) { + if (count($erasures) < $count) { + $erasures->setSize($count); + } + + for ($i = 0; $i < $count; $i++) { + $erasures[$i] = $loc[$i]; + } + } + + return $count; + } + + /** + * Computes $x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow divide. + */ + private function modNn(int $x) : int + { + while ($x >= $this->blockSize) { + $x -= $this->blockSize; + $x = ($x >> $this->symbolSize) + ($x & $this->blockSize); + } + + return $x; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/Version.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/Version.php new file mode 100644 index 000000000..917d048df --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Common/Version.php @@ -0,0 +1,596 @@ +|null + */ + private static $versions; + + /** + * @param int[] $alignmentPatternCenters + */ + private function __construct( + int $versionNumber, + array $alignmentPatternCenters, + EcBlocks ...$ecBlocks + ) { + $this->versionNumber = $versionNumber; + $this->alignmentPatternCenters = $alignmentPatternCenters; + $this->ecBlocks = $ecBlocks; + + $totalCodewords = 0; + $ecCodewords = $ecBlocks[0]->getEcCodewordsPerBlock(); + + foreach ($ecBlocks[0]->getEcBlocks() as $ecBlock) { + $totalCodewords += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords); + } + + $this->totalCodewords = $totalCodewords; + } + + /** + * Returns the version number. + */ + public function getVersionNumber() : int + { + return $this->versionNumber; + } + + /** + * Returns the alignment pattern centers. + * + * @return int[] + */ + public function getAlignmentPatternCenters() : array + { + return $this->alignmentPatternCenters; + } + + /** + * Returns the total number of codewords. + */ + public function getTotalCodewords() : int + { + return $this->totalCodewords; + } + + /** + * Calculates the dimension for the current version. + */ + public function getDimensionForVersion() : int + { + return 17 + 4 * $this->versionNumber; + } + + /** + * Returns the number of EC blocks for a specific EC level. + */ + public function getEcBlocksForLevel(ErrorCorrectionLevel $ecLevel) : EcBlocks + { + return $this->ecBlocks[$ecLevel->ordinal()]; + } + + /** + * Gets a provisional version number for a specific dimension. + * + * @throws InvalidArgumentException if dimension is not 1 mod 4 + */ + public static function getProvisionalVersionForDimension(int $dimension) : self + { + if (1 !== $dimension % 4) { + throw new InvalidArgumentException('Dimension is not 1 mod 4'); + } + + return self::getVersionForNumber(intdiv($dimension - 17, 4)); + } + + /** + * Gets a version instance for a specific version number. + * + * @throws InvalidArgumentException if version number is out of range + */ + public static function getVersionForNumber(int $versionNumber) : self + { + if ($versionNumber < 1 || $versionNumber > 40) { + throw new InvalidArgumentException('Version number must be between 1 and 40'); + } + + return self::versions()[$versionNumber - 1]; + } + + /** + * Decodes version information from an integer and returns the version. + */ + public static function decodeVersionInformation(int $versionBits) : ?self + { + $bestDifference = PHP_INT_MAX; + $bestVersion = 0; + + foreach (self::VERSION_DECODE_INFO as $i => $targetVersion) { + if ($targetVersion === $versionBits) { + return self::getVersionForNumber($i + 7); + } + + $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion); + + if ($bitsDifference < $bestDifference) { + $bestVersion = $i + 7; + $bestDifference = $bitsDifference; + } + } + + if ($bestDifference <= 3) { + return self::getVersionForNumber($bestVersion); + } + + return null; + } + + /** + * Builds the function pattern for the current version. + */ + public function buildFunctionPattern() : BitMatrix + { + $dimension = $this->getDimensionForVersion(); + $bitMatrix = new BitMatrix($dimension); + + // Top left finder pattern + separator + format + $bitMatrix->setRegion(0, 0, 9, 9); + // Top right finder pattern + separator + format + $bitMatrix->setRegion($dimension - 8, 0, 8, 9); + // Bottom left finder pattern + separator + format + $bitMatrix->setRegion(0, $dimension - 8, 9, 8); + + // Alignment patterns + $max = count($this->alignmentPatternCenters); + + for ($x = 0; $x < $max; ++$x) { + $i = $this->alignmentPatternCenters[$x] - 2; + + for ($y = 0; $y < $max; ++$y) { + if (($x === 0 && ($y === 0 || $y === $max - 1)) || ($x === $max - 1 && $y === 0)) { + // No alignment patterns near the three finder paterns + continue; + } + + $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5); + } + } + + // Vertical timing pattern + $bitMatrix->setRegion(6, 9, 1, $dimension - 17); + // Horizontal timing pattern + $bitMatrix->setRegion(9, 6, $dimension - 17, 1); + + if ($this->versionNumber > 6) { + // Version info, top right + $bitMatrix->setRegion($dimension - 11, 0, 3, 6); + // Version info, bottom left + $bitMatrix->setRegion(0, $dimension - 11, 6, 3); + } + + return $bitMatrix; + } + + /** + * Returns a string representation for the version. + */ + public function __toString() : string + { + return (string) $this->versionNumber; + } + + /** + * Build and cache a specific version. + * + * See ISO 18004:2006 6.5.1 Table 9. + * + * @return array + */ + private static function versions() : array + { + if (null !== self::$versions) { + return self::$versions; + } + + return self::$versions = [ + new self( + 1, + [], + new EcBlocks(7, new EcBlock(1, 19)), + new EcBlocks(10, new EcBlock(1, 16)), + new EcBlocks(13, new EcBlock(1, 13)), + new EcBlocks(17, new EcBlock(1, 9)) + ), + new self( + 2, + [6, 18], + new EcBlocks(10, new EcBlock(1, 34)), + new EcBlocks(16, new EcBlock(1, 28)), + new EcBlocks(22, new EcBlock(1, 22)), + new EcBlocks(28, new EcBlock(1, 16)) + ), + new self( + 3, + [6, 22], + new EcBlocks(15, new EcBlock(1, 55)), + new EcBlocks(26, new EcBlock(1, 44)), + new EcBlocks(18, new EcBlock(2, 17)), + new EcBlocks(22, new EcBlock(2, 13)) + ), + new self( + 4, + [6, 26], + new EcBlocks(20, new EcBlock(1, 80)), + new EcBlocks(18, new EcBlock(2, 32)), + new EcBlocks(26, new EcBlock(3, 24)), + new EcBlocks(16, new EcBlock(4, 9)) + ), + new self( + 5, + [6, 30], + new EcBlocks(26, new EcBlock(1, 108)), + new EcBlocks(24, new EcBlock(2, 43)), + new EcBlocks(18, new EcBlock(2, 15), new EcBlock(2, 16)), + new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12)) + ), + new self( + 6, + [6, 34], + new EcBlocks(18, new EcBlock(2, 68)), + new EcBlocks(16, new EcBlock(4, 27)), + new EcBlocks(24, new EcBlock(4, 19)), + new EcBlocks(28, new EcBlock(4, 15)) + ), + new self( + 7, + [6, 22, 38], + new EcBlocks(20, new EcBlock(2, 78)), + new EcBlocks(18, new EcBlock(4, 31)), + new EcBlocks(18, new EcBlock(2, 14), new EcBlock(4, 15)), + new EcBlocks(26, new EcBlock(4, 13), new EcBlock(1, 14)) + ), + new self( + 8, + [6, 24, 42], + new EcBlocks(24, new EcBlock(2, 97)), + new EcBlocks(22, new EcBlock(2, 38), new EcBlock(2, 39)), + new EcBlocks(22, new EcBlock(4, 18), new EcBlock(2, 19)), + new EcBlocks(26, new EcBlock(4, 14), new EcBlock(2, 15)) + ), + new self( + 9, + [6, 26, 46], + new EcBlocks(30, new EcBlock(2, 116)), + new EcBlocks(22, new EcBlock(3, 36), new EcBlock(2, 37)), + new EcBlocks(20, new EcBlock(4, 16), new EcBlock(4, 17)), + new EcBlocks(24, new EcBlock(4, 12), new EcBlock(4, 13)) + ), + new self( + 10, + [6, 28, 50], + new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)), + new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)), + new EcBlocks(24, new EcBlock(6, 19), new EcBlock(2, 20)), + new EcBlocks(28, new EcBlock(6, 15), new EcBlock(2, 16)) + ), + new self( + 11, + [6, 30, 54], + new EcBlocks(20, new EcBlock(4, 81)), + new EcBlocks(30, new EcBlock(1, 50), new EcBlock(4, 51)), + new EcBlocks(28, new EcBlock(4, 22), new EcBlock(4, 23)), + new EcBlocks(24, new EcBlock(3, 12), new EcBlock(8, 13)) + ), + new self( + 12, + [6, 32, 58], + new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)), + new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)), + new EcBlocks(26, new EcBlock(4, 20), new EcBlock(6, 21)), + new EcBlocks(28, new EcBlock(7, 14), new EcBlock(4, 15)) + ), + new self( + 13, + [6, 34, 62], + new EcBlocks(26, new EcBlock(4, 107)), + new EcBlocks(22, new EcBlock(8, 37), new EcBlock(1, 38)), + new EcBlocks(24, new EcBlock(8, 20), new EcBlock(4, 21)), + new EcBlocks(22, new EcBlock(12, 11), new EcBlock(4, 12)) + ), + new self( + 14, + [6, 26, 46, 66], + new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 116)), + new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 41)), + new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 17)), + new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13)) + ), + new self( + 15, + [6, 26, 48, 70], + new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)), + new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)), + new EcBlocks(30, new EcBlock(5, 24), new EcBlock(7, 25)), + new EcBlocks(24, new EcBlock(11, 12), new EcBlock(7, 13)) + ), + new self( + 16, + [6, 26, 50, 74], + new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)), + new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)), + new EcBlocks(24, new EcBlock(15, 19), new EcBlock(2, 20)), + new EcBlocks(30, new EcBlock(3, 15), new EcBlock(13, 16)) + ), + new self( + 17, + [6, 30, 54, 78], + new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 108)), + new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 47)), + new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 23)), + new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15)) + ), + new self( + 18, + [6, 30, 56, 82], + new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 121)), + new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 44)), + new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 23)), + new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15)) + ), + new self( + 19, + [6, 30, 58, 86], + new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 114)), + new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 45)), + new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 22)), + new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14)) + ), + new self( + 20, + [6, 34, 62, 90], + new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 108)), + new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 42)), + new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 25)), + new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16)) + ), + new self( + 21, + [6, 28, 50, 72, 94], + new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 117)), + new EcBlocks(26, new EcBlock(17, 42)), + new EcBlocks(28, new EcBlock(17, 22), new EcBlock(6, 23)), + new EcBlocks(30, new EcBlock(19, 16), new EcBlock(6, 17)) + ), + new self( + 22, + [6, 26, 50, 74, 98], + new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 112)), + new EcBlocks(28, new EcBlock(17, 46)), + new EcBlocks(30, new EcBlock(7, 24), new EcBlock(16, 25)), + new EcBlocks(24, new EcBlock(34, 13)) + ), + new self( + 23, + [6, 30, 54, 78, 102], + new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 122)), + new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 48)), + new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 25)), + new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16)) + ), + new self( + 24, + [6, 28, 54, 80, 106], + new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 118)), + new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 46)), + new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 25)), + new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17)) + ), + new self( + 25, + [6, 32, 58, 84, 110], + new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 107)), + new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 48)), + new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 25)), + new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16)) + ), + new self( + 26, + [6, 30, 58, 86, 114], + new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 115)), + new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 47)), + new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 23)), + new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17)) + ), + new self( + 27, + [6, 34, 62, 90, 118], + new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 123)), + new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 46)), + new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 24)), + new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16)) + ), + new self( + 28, + [6, 26, 50, 74, 98, 122], + new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 118)), + new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 46)), + new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 25)), + new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16)) + ), + new self( + 29, + [6, 30, 54, 78, 102, 126], + new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 117)), + new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 46)), + new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 24)), + new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16)) + ), + new self( + 30, + [6, 26, 52, 78, 104, 130], + new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 116)), + new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 48)), + new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 25)), + new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16)) + ), + new self( + 31, + [6, 30, 56, 82, 108, 134], + new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 116)), + new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 47)), + new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 25)), + new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16)) + ), + new self( + 32, + [6, 34, 60, 86, 112, 138], + new EcBlocks(30, new EcBlock(17, 115)), + new EcBlocks(28, new EcBlock(10, 46), new EcBlock(23, 47)), + new EcBlocks(30, new EcBlock(10, 24), new EcBlock(35, 25)), + new EcBlocks(30, new EcBlock(19, 15), new EcBlock(35, 16)) + ), + new self( + 33, + [6, 30, 58, 86, 114, 142], + new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 116)), + new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 47)), + new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 25)), + new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16)) + ), + new self( + 34, + [6, 34, 62, 90, 118, 146], + new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 116)), + new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 47)), + new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 25)), + new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17)) + ), + new self( + 35, + [6, 30, 54, 78, 102, 126, 150], + new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 122)), + new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 48)), + new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 25)), + new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16)) + ), + new self( + 36, + [6, 24, 50, 76, 102, 128, 154], + new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 122)), + new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 48)), + new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 25)), + new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16)) + ), + new self( + 37, + [6, 28, 54, 80, 106, 132, 158], + new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 123)), + new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 47)), + new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 25)), + new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16)) + ), + new self( + 38, + [6, 32, 58, 84, 110, 136, 162], + new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 123)), + new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 47)), + new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 25)), + new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16)) + ), + new self( + 39, + [6, 26, 54, 82, 110, 138, 166], + new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 118)), + new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 48)), + new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 25)), + new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16)) + ), + new self( + 40, + [6, 30, 58, 86, 114, 142, 170], + new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 119)), + new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 48)), + new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 25)), + new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16)) + ), + ]; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/BlockPair.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/BlockPair.php new file mode 100644 index 000000000..be54afaa9 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/BlockPair.php @@ -0,0 +1,58 @@ + + */ + private $dataBytes; + + /** + * Error correction bytes in the block. + * + * @var SplFixedArray + */ + private $errorCorrectionBytes; + + /** + * Creates a new block pair. + * + * @param SplFixedArray $data + * @param SplFixedArray $errorCorrection + */ + public function __construct(SplFixedArray $data, SplFixedArray $errorCorrection) + { + $this->dataBytes = $data; + $this->errorCorrectionBytes = $errorCorrection; + } + + /** + * Gets the data bytes. + * + * @return SplFixedArray + */ + public function getDataBytes() : SplFixedArray + { + return $this->dataBytes; + } + + /** + * Gets the error correction bytes. + * + * @return SplFixedArray + */ + public function getErrorCorrectionBytes() : SplFixedArray + { + return $this->errorCorrectionBytes; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php new file mode 100644 index 000000000..b58cc0ab2 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/ByteMatrix.php @@ -0,0 +1,150 @@ +> + */ + private $bytes; + + /** + * Width of the matrix. + * + * @var int + */ + private $width; + + /** + * Height of the matrix. + * + * @var int + */ + private $height; + + public function __construct(int $width, int $height) + { + $this->height = $height; + $this->width = $width; + $this->bytes = new SplFixedArray($height); + + for ($y = 0; $y < $height; ++$y) { + $this->bytes[$y] = SplFixedArray::fromArray(array_fill(0, $width, 0)); + } + } + + /** + * Gets the width of the matrix. + */ + public function getWidth() : int + { + return $this->width; + } + + /** + * Gets the height of the matrix. + */ + public function getHeight() : int + { + return $this->height; + } + + /** + * Gets the internal representation of the matrix. + * + * @return SplFixedArray> + */ + public function getArray() : SplFixedArray + { + return $this->bytes; + } + + /** + * @return Traversable + */ + public function getBytes() : Traversable + { + foreach ($this->bytes as $row) { + foreach ($row as $byte) { + yield $byte; + } + } + } + + /** + * Gets the byte for a specific position. + */ + public function get(int $x, int $y) : int + { + return $this->bytes[$y][$x]; + } + + /** + * Sets the byte for a specific position. + */ + public function set(int $x, int $y, int $value) : void + { + $this->bytes[$y][$x] = $value; + } + + /** + * Clears the matrix with a specific value. + */ + public function clear(int $value) : void + { + for ($y = 0; $y < $this->height; ++$y) { + for ($x = 0; $x < $this->width; ++$x) { + $this->bytes[$y][$x] = $value; + } + } + } + + public function __clone() + { + $this->bytes = clone $this->bytes; + + foreach ($this->bytes as $index => $row) { + $this->bytes[$index] = clone $row; + } + } + + /** + * Returns a string representation of the matrix. + */ + public function __toString() : string + { + $result = ''; + + for ($y = 0; $y < $this->height; $y++) { + for ($x = 0; $x < $this->width; $x++) { + switch ($this->bytes[$y][$x]) { + case 0: + $result .= ' 0'; + break; + + case 1: + $result .= ' 1'; + break; + + default: + $result .= ' '; + break; + } + } + + $result .= "\n"; + } + + return $result; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/Encoder.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/Encoder.php new file mode 100644 index 000000000..320846003 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/Encoder.php @@ -0,0 +1,668 @@ + + */ + private static $codecs = []; + + /** + * Encodes "content" with the error correction level "ecLevel". + */ + public static function encode( + string $content, + ErrorCorrectionLevel $ecLevel, + string $encoding = self::DEFAULT_BYTE_MODE_ECODING, + ?Version $forcedVersion = null + ) : QrCode { + // Pick an encoding mode appropriate for the content. Note that this + // will not attempt to use multiple modes / segments even if that were + // more efficient. Would be nice. + $mode = self::chooseMode($content, $encoding); + + // This will store the header information, like mode and length, as well + // as "header" segments like an ECI segment. + $headerBits = new BitArray(); + + // Append ECI segment if applicable + if (Mode::BYTE() === $mode && self::DEFAULT_BYTE_MODE_ECODING !== $encoding) { + $eci = CharacterSetEci::getCharacterSetEciByName($encoding); + + if (null !== $eci) { + self::appendEci($eci, $headerBits); + } + } + + // (With ECI in place,) Write the mode marker + self::appendModeInfo($mode, $headerBits); + + // Collect data within the main segment, separately, to count its size + // if needed. Don't add it to main payload yet. + $dataBits = new BitArray(); + self::appendBytes($content, $mode, $dataBits, $encoding); + + // Hard part: need to know version to know how many bits length takes. + // But need to know how many bits it takes to know version. First we + // take a guess at version by assuming version will be the minimum, 1: + $provisionalBitsNeeded = $headerBits->getSize() + + $mode->getCharacterCountBits(Version::getVersionForNumber(1)) + + $dataBits->getSize(); + $provisionalVersion = self::chooseVersion($provisionalBitsNeeded, $ecLevel); + + // Use that guess to calculate the right version. I am still not sure + // this works in 100% of cases. + $bitsNeeded = $headerBits->getSize() + + $mode->getCharacterCountBits($provisionalVersion) + + $dataBits->getSize(); + $version = self::chooseVersion($bitsNeeded, $ecLevel); + + if (null !== $forcedVersion) { + // Forced version check + if ($version->getVersionNumber() <= $forcedVersion->getVersionNumber()) { + // Calculated minimum version is same or equal as forced version + $version = $forcedVersion; + } else { + throw new WriterException( + 'Invalid version! Calculated version: ' + . $version->getVersionNumber() + . ', requested version: ' + . $forcedVersion->getVersionNumber() + ); + } + } + + $headerAndDataBits = new BitArray(); + $headerAndDataBits->appendBitArray($headerBits); + + // Find "length" of main segment and write it. + $numLetters = (Mode::BYTE() === $mode ? $dataBits->getSizeInBytes() : strlen($content)); + self::appendLengthInfo($numLetters, $version, $mode, $headerAndDataBits); + + // Put data together into the overall payload. + $headerAndDataBits->appendBitArray($dataBits); + $ecBlocks = $version->getEcBlocksForLevel($ecLevel); + $numDataBytes = $version->getTotalCodewords() - $ecBlocks->getTotalEcCodewords(); + + // Terminate the bits properly. + self::terminateBits($numDataBytes, $headerAndDataBits); + + // Interleave data bits with error correction code. + $finalBits = self::interleaveWithEcBytes( + $headerAndDataBits, + $version->getTotalCodewords(), + $numDataBytes, + $ecBlocks->getNumBlocks() + ); + + // Choose the mask pattern. + $dimension = $version->getDimensionForVersion(); + $matrix = new ByteMatrix($dimension, $dimension); + $maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix); + + // Build the matrix. + MatrixUtil::buildMatrix($finalBits, $ecLevel, $version, $maskPattern, $matrix); + + return new QrCode($mode, $ecLevel, $version, $maskPattern, $matrix); + } + + /** + * Gets the alphanumeric code for a byte. + */ + private static function getAlphanumericCode(int $code) : int + { + if (isset(self::ALPHANUMERIC_TABLE[$code])) { + return self::ALPHANUMERIC_TABLE[$code]; + } + + return -1; + } + + /** + * Chooses the best mode for a given content. + */ + private static function chooseMode(string $content, string $encoding = null) : Mode + { + if (null !== $encoding && 0 === strcasecmp($encoding, 'SHIFT-JIS')) { + return self::isOnlyDoubleByteKanji($content) ? Mode::KANJI() : Mode::BYTE(); + } + + $hasNumeric = false; + $hasAlphanumeric = false; + $contentLength = strlen($content); + + for ($i = 0; $i < $contentLength; ++$i) { + $char = $content[$i]; + + if (ctype_digit($char)) { + $hasNumeric = true; + } elseif (-1 !== self::getAlphanumericCode(ord($char))) { + $hasAlphanumeric = true; + } else { + return Mode::BYTE(); + } + } + + if ($hasAlphanumeric) { + return Mode::ALPHANUMERIC(); + } elseif ($hasNumeric) { + return Mode::NUMERIC(); + } + + return Mode::BYTE(); + } + + /** + * Calculates the mask penalty for a matrix. + */ + private static function calculateMaskPenalty(ByteMatrix $matrix) : int + { + return ( + MaskUtil::applyMaskPenaltyRule1($matrix) + + MaskUtil::applyMaskPenaltyRule2($matrix) + + MaskUtil::applyMaskPenaltyRule3($matrix) + + MaskUtil::applyMaskPenaltyRule4($matrix) + ); + } + + /** + * Checks if content only consists of double-byte kanji characters. + */ + private static function isOnlyDoubleByteKanji(string $content) : bool + { + $bytes = @iconv('utf-8', 'SHIFT-JIS', $content); + + if (false === $bytes) { + return false; + } + + $length = strlen($bytes); + + if (0 !== $length % 2) { + return false; + } + + for ($i = 0; $i < $length; $i += 2) { + $byte = $bytes[$i] & 0xff; + + if (($byte < 0x81 || $byte > 0x9f) && $byte < 0xe0 || $byte > 0xeb) { + return false; + } + } + + return true; + } + + /** + * Chooses the best mask pattern for a matrix. + */ + private static function chooseMaskPattern( + BitArray $bits, + ErrorCorrectionLevel $ecLevel, + Version $version, + ByteMatrix $matrix + ) : int { + $minPenalty = PHP_INT_MAX; + $bestMaskPattern = -1; + + for ($maskPattern = 0; $maskPattern < QrCode::NUM_MASK_PATTERNS; ++$maskPattern) { + MatrixUtil::buildMatrix($bits, $ecLevel, $version, $maskPattern, $matrix); + $penalty = self::calculateMaskPenalty($matrix); + + if ($penalty < $minPenalty) { + $minPenalty = $penalty; + $bestMaskPattern = $maskPattern; + } + } + + return $bestMaskPattern; + } + + /** + * Chooses the best version for the input. + * + * @throws WriterException if data is too big + */ + private static function chooseVersion(int $numInputBits, ErrorCorrectionLevel $ecLevel) : Version + { + for ($versionNum = 1; $versionNum <= 40; ++$versionNum) { + $version = Version::getVersionForNumber($versionNum); + $numBytes = $version->getTotalCodewords(); + + $ecBlocks = $version->getEcBlocksForLevel($ecLevel); + $numEcBytes = $ecBlocks->getTotalEcCodewords(); + + $numDataBytes = $numBytes - $numEcBytes; + $totalInputBytes = intdiv($numInputBits + 8, 8); + + if ($numDataBytes >= $totalInputBytes) { + return $version; + } + } + + throw new WriterException('Data too big'); + } + + /** + * Terminates the bits in a bit array. + * + * @throws WriterException if data bits cannot fit in the QR code + * @throws WriterException if bits size does not equal the capacity + */ + private static function terminateBits(int $numDataBytes, BitArray $bits) : void + { + $capacity = $numDataBytes << 3; + + if ($bits->getSize() > $capacity) { + throw new WriterException('Data bits cannot fit in the QR code'); + } + + for ($i = 0; $i < 4 && $bits->getSize() < $capacity; ++$i) { + $bits->appendBit(false); + } + + $numBitsInLastByte = $bits->getSize() & 0x7; + + if ($numBitsInLastByte > 0) { + for ($i = $numBitsInLastByte; $i < 8; ++$i) { + $bits->appendBit(false); + } + } + + $numPaddingBytes = $numDataBytes - $bits->getSizeInBytes(); + + for ($i = 0; $i < $numPaddingBytes; ++$i) { + $bits->appendBits(0 === ($i & 0x1) ? 0xec : 0x11, 8); + } + + if ($bits->getSize() !== $capacity) { + throw new WriterException('Bits size does not equal capacity'); + } + } + + /** + * Gets number of data- and EC bytes for a block ID. + * + * @return int[] + * @throws WriterException if block ID is too large + * @throws WriterException if EC bytes mismatch + * @throws WriterException if RS blocks mismatch + * @throws WriterException if total bytes mismatch + */ + private static function getNumDataBytesAndNumEcBytesForBlockId( + int $numTotalBytes, + int $numDataBytes, + int $numRsBlocks, + int $blockId + ) : array { + if ($blockId >= $numRsBlocks) { + throw new WriterException('Block ID too large'); + } + + $numRsBlocksInGroup2 = $numTotalBytes % $numRsBlocks; + $numRsBlocksInGroup1 = $numRsBlocks - $numRsBlocksInGroup2; + $numTotalBytesInGroup1 = intdiv($numTotalBytes, $numRsBlocks); + $numTotalBytesInGroup2 = $numTotalBytesInGroup1 + 1; + $numDataBytesInGroup1 = intdiv($numDataBytes, $numRsBlocks); + $numDataBytesInGroup2 = $numDataBytesInGroup1 + 1; + $numEcBytesInGroup1 = $numTotalBytesInGroup1 - $numDataBytesInGroup1; + $numEcBytesInGroup2 = $numTotalBytesInGroup2 - $numDataBytesInGroup2; + + if ($numEcBytesInGroup1 !== $numEcBytesInGroup2) { + throw new WriterException('EC bytes mismatch'); + } + + if ($numRsBlocks !== $numRsBlocksInGroup1 + $numRsBlocksInGroup2) { + throw new WriterException('RS blocks mismatch'); + } + + if ($numTotalBytes !== + (($numDataBytesInGroup1 + $numEcBytesInGroup1) * $numRsBlocksInGroup1) + + (($numDataBytesInGroup2 + $numEcBytesInGroup2) * $numRsBlocksInGroup2) + ) { + throw new WriterException('Total bytes mismatch'); + } + + if ($blockId < $numRsBlocksInGroup1) { + return [$numDataBytesInGroup1, $numEcBytesInGroup1]; + } else { + return [$numDataBytesInGroup2, $numEcBytesInGroup2]; + } + } + + /** + * Interleaves data with EC bytes. + * + * @throws WriterException if number of bits and data bytes does not match + * @throws WriterException if data bytes does not match offset + * @throws WriterException if an interleaving error occurs + */ + private static function interleaveWithEcBytes( + BitArray $bits, + int $numTotalBytes, + int $numDataBytes, + int $numRsBlocks + ) : BitArray { + if ($bits->getSizeInBytes() !== $numDataBytes) { + throw new WriterException('Number of bits and data bytes does not match'); + } + + $dataBytesOffset = 0; + $maxNumDataBytes = 0; + $maxNumEcBytes = 0; + + $blocks = new SplFixedArray($numRsBlocks); + + for ($i = 0; $i < $numRsBlocks; ++$i) { + list($numDataBytesInBlock, $numEcBytesInBlock) = self::getNumDataBytesAndNumEcBytesForBlockId( + $numTotalBytes, + $numDataBytes, + $numRsBlocks, + $i + ); + + $size = $numDataBytesInBlock; + $dataBytes = $bits->toBytes(8 * $dataBytesOffset, $size); + $ecBytes = self::generateEcBytes($dataBytes, $numEcBytesInBlock); + $blocks[$i] = new BlockPair($dataBytes, $ecBytes); + + $maxNumDataBytes = max($maxNumDataBytes, $size); + $maxNumEcBytes = max($maxNumEcBytes, count($ecBytes)); + $dataBytesOffset += $numDataBytesInBlock; + } + + if ($numDataBytes !== $dataBytesOffset) { + throw new WriterException('Data bytes does not match offset'); + } + + $result = new BitArray(); + + for ($i = 0; $i < $maxNumDataBytes; ++$i) { + foreach ($blocks as $block) { + $dataBytes = $block->getDataBytes(); + + if ($i < count($dataBytes)) { + $result->appendBits($dataBytes[$i], 8); + } + } + } + + for ($i = 0; $i < $maxNumEcBytes; ++$i) { + foreach ($blocks as $block) { + $ecBytes = $block->getErrorCorrectionBytes(); + + if ($i < count($ecBytes)) { + $result->appendBits($ecBytes[$i], 8); + } + } + } + + if ($numTotalBytes !== $result->getSizeInBytes()) { + throw new WriterException( + 'Interleaving error: ' . $numTotalBytes . ' and ' . $result->getSizeInBytes() . ' differ' + ); + } + + return $result; + } + + /** + * Generates EC bytes for given data. + * + * @param SplFixedArray $dataBytes + * @return SplFixedArray + */ + private static function generateEcBytes(SplFixedArray $dataBytes, int $numEcBytesInBlock) : SplFixedArray + { + $numDataBytes = count($dataBytes); + $toEncode = new SplFixedArray($numDataBytes + $numEcBytesInBlock); + + for ($i = 0; $i < $numDataBytes; $i++) { + $toEncode[$i] = $dataBytes[$i] & 0xff; + } + + $ecBytes = new SplFixedArray($numEcBytesInBlock); + $codec = self::getCodec($numDataBytes, $numEcBytesInBlock); + $codec->encode($toEncode, $ecBytes); + + return $ecBytes; + } + + /** + * Gets an RS codec and caches it. + */ + private static function getCodec(int $numDataBytes, int $numEcBytesInBlock) : ReedSolomonCodec + { + $cacheId = $numDataBytes . '-' . $numEcBytesInBlock; + + if (isset(self::$codecs[$cacheId])) { + return self::$codecs[$cacheId]; + } + + return self::$codecs[$cacheId] = new ReedSolomonCodec( + 8, + 0x11d, + 0, + 1, + $numEcBytesInBlock, + 255 - $numDataBytes - $numEcBytesInBlock + ); + } + + /** + * Appends mode information to a bit array. + */ + private static function appendModeInfo(Mode $mode, BitArray $bits) : void + { + $bits->appendBits($mode->getBits(), 4); + } + + /** + * Appends length information to a bit array. + * + * @throws WriterException if num letters is bigger than expected + */ + private static function appendLengthInfo(int $numLetters, Version $version, Mode $mode, BitArray $bits) : void + { + $numBits = $mode->getCharacterCountBits($version); + + if ($numLetters >= (1 << $numBits)) { + throw new WriterException($numLetters . ' is bigger than ' . ((1 << $numBits) - 1)); + } + + $bits->appendBits($numLetters, $numBits); + } + + /** + * Appends bytes to a bit array in a specific mode. + * + * @throws WriterException if an invalid mode was supplied + */ + private static function appendBytes(string $content, Mode $mode, BitArray $bits, string $encoding) : void + { + switch ($mode) { + case Mode::NUMERIC(): + self::appendNumericBytes($content, $bits); + break; + + case Mode::ALPHANUMERIC(): + self::appendAlphanumericBytes($content, $bits); + break; + + case Mode::BYTE(): + self::append8BitBytes($content, $bits, $encoding); + break; + + case Mode::KANJI(): + self::appendKanjiBytes($content, $bits); + break; + + default: + throw new WriterException('Invalid mode: ' . $mode); + } + } + + /** + * Appends numeric bytes to a bit array. + */ + private static function appendNumericBytes(string $content, BitArray $bits) : void + { + $length = strlen($content); + $i = 0; + + while ($i < $length) { + $num1 = (int) $content[$i]; + + if ($i + 2 < $length) { + // Encode three numeric letters in ten bits. + $num2 = (int) $content[$i + 1]; + $num3 = (int) $content[$i + 2]; + $bits->appendBits($num1 * 100 + $num2 * 10 + $num3, 10); + $i += 3; + } elseif ($i + 1 < $length) { + // Encode two numeric letters in seven bits. + $num2 = (int) $content[$i + 1]; + $bits->appendBits($num1 * 10 + $num2, 7); + $i += 2; + } else { + // Encode one numeric letter in four bits. + $bits->appendBits($num1, 4); + ++$i; + } + } + } + + /** + * Appends alpha-numeric bytes to a bit array. + * + * @throws WriterException if an invalid alphanumeric code was found + */ + private static function appendAlphanumericBytes(string $content, BitArray $bits) : void + { + $length = strlen($content); + $i = 0; + + while ($i < $length) { + $code1 = self::getAlphanumericCode(ord($content[$i])); + + if (-1 === $code1) { + throw new WriterException('Invalid alphanumeric code'); + } + + if ($i + 1 < $length) { + $code2 = self::getAlphanumericCode(ord($content[$i + 1])); + + if (-1 === $code2) { + throw new WriterException('Invalid alphanumeric code'); + } + + // Encode two alphanumeric letters in 11 bits. + $bits->appendBits($code1 * 45 + $code2, 11); + $i += 2; + } else { + // Encode one alphanumeric letter in six bits. + $bits->appendBits($code1, 6); + ++$i; + } + } + } + + /** + * Appends regular 8-bit bytes to a bit array. + * + * @throws WriterException if content cannot be encoded to target encoding + */ + private static function append8BitBytes(string $content, BitArray $bits, string $encoding) : void + { + $bytes = @iconv('utf-8', $encoding, $content); + + if (false === $bytes) { + throw new WriterException('Could not encode content to ' . $encoding); + } + + $length = strlen($bytes); + + for ($i = 0; $i < $length; $i++) { + $bits->appendBits(ord($bytes[$i]), 8); + } + } + + /** + * Appends KANJI bytes to a bit array. + * + * @throws WriterException if content does not seem to be encoded in SHIFT-JIS + * @throws WriterException if an invalid byte sequence occurs + */ + private static function appendKanjiBytes(string $content, BitArray $bits) : void + { + if (strlen($content) % 2 > 0) { + // We just do a simple length check here. The for loop will check + // individual characters. + throw new WriterException('Content does not seem to be encoded in SHIFT-JIS'); + } + + $length = strlen($content); + + for ($i = 0; $i < $length; $i += 2) { + $byte1 = ord($content[$i]) & 0xff; + $byte2 = ord($content[$i + 1]) & 0xff; + $code = ($byte1 << 8) | $byte2; + + if ($code >= 0x8140 && $code <= 0x9ffc) { + $subtracted = $code - 0x8140; + } elseif ($code >= 0xe040 && $code <= 0xebbf) { + $subtracted = $code - 0xc140; + } else { + throw new WriterException('Invalid byte sequence'); + } + + $encoded = (($subtracted >> 8) * 0xc0) + ($subtracted & 0xff); + + $bits->appendBits($encoded, 13); + } + } + + /** + * Appends ECI information to a bit array. + */ + private static function appendEci(CharacterSetEci $eci, BitArray $bits) : void + { + $mode = Mode::ECI(); + $bits->appendBits($mode->getBits(), 4); + $bits->appendBits($eci->getValue(), 8); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/MaskUtil.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/MaskUtil.php new file mode 100644 index 000000000..ba97dfb79 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/MaskUtil.php @@ -0,0 +1,271 @@ +getArray(); + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + + for ($y = 0; $y < $height - 1; ++$y) { + for ($x = 0; $x < $width - 1; ++$x) { + $value = $array[$y][$x]; + + if ($value === $array[$y][$x + 1] + && $value === $array[$y + 1][$x] + && $value === $array[$y + 1][$x + 1] + ) { + ++$penalty; + } + } + } + + return self::N2 * $penalty; + } + + /** + * Applies mask penalty rule 3 and returns the penalty. + * + * Finds consecutive cells of 00001011101 or 10111010000, and gives penalty + * to them. If we find patterns like 000010111010000, we give penalties + * twice (i.e. 40 * 2). + */ + public static function applyMaskPenaltyRule3(ByteMatrix $matrix) : int + { + $penalty = 0; + $array = $matrix->getArray(); + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + + for ($y = 0; $y < $height; ++$y) { + for ($x = 0; $x < $width; ++$x) { + if ($x + 6 < $width + && 1 === $array[$y][$x] + && 0 === $array[$y][$x + 1] + && 1 === $array[$y][$x + 2] + && 1 === $array[$y][$x + 3] + && 1 === $array[$y][$x + 4] + && 0 === $array[$y][$x + 5] + && 1 === $array[$y][$x + 6] + && ( + ( + $x + 10 < $width + && 0 === $array[$y][$x + 7] + && 0 === $array[$y][$x + 8] + && 0 === $array[$y][$x + 9] + && 0 === $array[$y][$x + 10] + ) + || ( + $x - 4 >= 0 + && 0 === $array[$y][$x - 1] + && 0 === $array[$y][$x - 2] + && 0 === $array[$y][$x - 3] + && 0 === $array[$y][$x - 4] + ) + ) + ) { + $penalty += self::N3; + } + + if ($y + 6 < $height + && 1 === $array[$y][$x] + && 0 === $array[$y + 1][$x] + && 1 === $array[$y + 2][$x] + && 1 === $array[$y + 3][$x] + && 1 === $array[$y + 4][$x] + && 0 === $array[$y + 5][$x] + && 1 === $array[$y + 6][$x] + && ( + ( + $y + 10 < $height + && 0 === $array[$y + 7][$x] + && 0 === $array[$y + 8][$x] + && 0 === $array[$y + 9][$x] + && 0 === $array[$y + 10][$x] + ) + || ( + $y - 4 >= 0 + && 0 === $array[$y - 1][$x] + && 0 === $array[$y - 2][$x] + && 0 === $array[$y - 3][$x] + && 0 === $array[$y - 4][$x] + ) + ) + ) { + $penalty += self::N3; + } + } + } + + return $penalty; + } + + /** + * Applies mask penalty rule 4 and returns the penalty. + * + * Calculates the ratio of dark cells and gives penalty if the ratio is far + * from 50%. It gives 10 penalty for 5% distance. + */ + public static function applyMaskPenaltyRule4(ByteMatrix $matrix) : int + { + $numDarkCells = 0; + + $array = $matrix->getArray(); + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + + for ($y = 0; $y < $height; ++$y) { + $arrayY = $array[$y]; + + for ($x = 0; $x < $width; ++$x) { + if (1 === $arrayY[$x]) { + ++$numDarkCells; + } + } + } + + $numTotalCells = $height * $width; + $darkRatio = $numDarkCells / $numTotalCells; + $fixedPercentVariances = (int) (abs($darkRatio - 0.5) * 20); + + return $fixedPercentVariances * self::N4; + } + + /** + * Returns the mask bit for "getMaskPattern" at "x" and "y". + * + * See 8.8 of JISX0510:2004 for mask pattern conditions. + * + * @throws InvalidArgumentException if an invalid mask pattern was supplied + */ + public static function getDataMaskBit(int $maskPattern, int $x, int $y) : bool + { + switch ($maskPattern) { + case 0: + $intermediate = ($y + $x) & 0x1; + break; + + case 1: + $intermediate = $y & 0x1; + break; + + case 2: + $intermediate = $x % 3; + break; + + case 3: + $intermediate = ($y + $x) % 3; + break; + + case 4: + $intermediate = (BitUtils::unsignedRightShift($y, 1) + (int) ($x / 3)) & 0x1; + break; + + case 5: + $temp = $y * $x; + $intermediate = ($temp & 0x1) + ($temp % 3); + break; + + case 6: + $temp = $y * $x; + $intermediate = (($temp & 0x1) + ($temp % 3)) & 0x1; + break; + + case 7: + $temp = $y * $x; + $intermediate = (($temp % 3) + (($y + $x) & 0x1)) & 0x1; + break; + + default: + throw new InvalidArgumentException('Invalid mask pattern: ' . $maskPattern); + } + + return 0 == $intermediate; + } + + /** + * Helper function for applyMaskPenaltyRule1. + * + * We need this for doing this calculation in both vertical and horizontal + * orders respectively. + */ + private static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, bool $isHorizontal) : int + { + $penalty = 0; + $iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth(); + $jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight(); + $array = $matrix->getArray(); + + for ($i = 0; $i < $iLimit; ++$i) { + $numSameBitCells = 0; + $prevBit = -1; + + for ($j = 0; $j < $jLimit; $j++) { + $bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i]; + + if ($bit === $prevBit) { + ++$numSameBitCells; + } else { + if ($numSameBitCells >= 5) { + $penalty += self::N1 + ($numSameBitCells - 5); + } + + $numSameBitCells = 1; + $prevBit = $bit; + } + } + + if ($numSameBitCells >= 5) { + $penalty += self::N1 + ($numSameBitCells - 5); + } + } + + return $penalty; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/MatrixUtil.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/MatrixUtil.php new file mode 100644 index 000000000..0967e298a --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/MatrixUtil.php @@ -0,0 +1,513 @@ +clear(-1); + } + + /** + * Builds a complete matrix. + */ + public static function buildMatrix( + BitArray $dataBits, + ErrorCorrectionLevel $level, + Version $version, + int $maskPattern, + ByteMatrix $matrix + ) : void { + self::clearMatrix($matrix); + self::embedBasicPatterns($version, $matrix); + self::embedTypeInfo($level, $maskPattern, $matrix); + self::maybeEmbedVersionInfo($version, $matrix); + self::embedDataBits($dataBits, $maskPattern, $matrix); + } + + /** + * Removes the position detection patterns from a matrix. + * + * This can be useful if you need to render those patterns separately. + */ + public static function removePositionDetectionPatterns(ByteMatrix $matrix) : void + { + $pdpWidth = count(self::POSITION_DETECTION_PATTERN[0]); + + self::removePositionDetectionPattern(0, 0, $matrix); + self::removePositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix); + self::removePositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix); + } + + /** + * Embeds type information into a matrix. + */ + private static function embedTypeInfo(ErrorCorrectionLevel $level, int $maskPattern, ByteMatrix $matrix) : void + { + $typeInfoBits = new BitArray(); + self::makeTypeInfoBits($level, $maskPattern, $typeInfoBits); + + $typeInfoBitsSize = $typeInfoBits->getSize(); + + for ($i = 0; $i < $typeInfoBitsSize; ++$i) { + $bit = $typeInfoBits->get($typeInfoBitsSize - 1 - $i); + + $x1 = self::TYPE_INFO_COORDINATES[$i][0]; + $y1 = self::TYPE_INFO_COORDINATES[$i][1]; + + $matrix->set($x1, $y1, (int) $bit); + + if ($i < 8) { + $x2 = $matrix->getWidth() - $i - 1; + $y2 = 8; + } else { + $x2 = 8; + $y2 = $matrix->getHeight() - 7 + ($i - 8); + } + + $matrix->set($x2, $y2, (int) $bit); + } + } + + /** + * Generates type information bits and appends them to a bit array. + * + * @throws RuntimeException if bit array resulted in invalid size + */ + private static function makeTypeInfoBits(ErrorCorrectionLevel $level, int $maskPattern, BitArray $bits) : void + { + $typeInfo = ($level->getBits() << 3) | $maskPattern; + $bits->appendBits($typeInfo, 5); + + $bchCode = self::calculateBchCode($typeInfo, self::TYPE_INFO_POLY); + $bits->appendBits($bchCode, 10); + + $maskBits = new BitArray(); + $maskBits->appendBits(self::TYPE_INFO_MASK_PATTERN, 15); + $bits->xorBits($maskBits); + + if (15 !== $bits->getSize()) { + throw new RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize()); + } + } + + /** + * Embeds version information if required. + */ + private static function maybeEmbedVersionInfo(Version $version, ByteMatrix $matrix) : void + { + if ($version->getVersionNumber() < 7) { + return; + } + + $versionInfoBits = new BitArray(); + self::makeVersionInfoBits($version, $versionInfoBits); + + $bitIndex = 6 * 3 - 1; + + for ($i = 0; $i < 6; ++$i) { + for ($j = 0; $j < 3; ++$j) { + $bit = $versionInfoBits->get($bitIndex); + --$bitIndex; + + $matrix->set($i, $matrix->getHeight() - 11 + $j, (int) $bit); + $matrix->set($matrix->getHeight() - 11 + $j, $i, (int) $bit); + } + } + } + + /** + * Generates version information bits and appends them to a bit array. + * + * @throws RuntimeException if bit array resulted in invalid size + */ + private static function makeVersionInfoBits(Version $version, BitArray $bits) : void + { + $bits->appendBits($version->getVersionNumber(), 6); + + $bchCode = self::calculateBchCode($version->getVersionNumber(), self::VERSION_INFO_POLY); + $bits->appendBits($bchCode, 12); + + if (18 !== $bits->getSize()) { + throw new RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize()); + } + } + + /** + * Calculates the BCH code for a value and a polynomial. + */ + private static function calculateBchCode(int $value, int $poly) : int + { + $msbSetInPoly = self::findMsbSet($poly); + $value <<= $msbSetInPoly - 1; + + while (self::findMsbSet($value) >= $msbSetInPoly) { + $value ^= $poly << (self::findMsbSet($value) - $msbSetInPoly); + } + + return $value; + } + + /** + * Finds and MSB set. + */ + private static function findMsbSet(int $value) : int + { + $numDigits = 0; + + while (0 !== $value) { + $value >>= 1; + ++$numDigits; + } + + return $numDigits; + } + + /** + * Embeds basic patterns into a matrix. + */ + private static function embedBasicPatterns(Version $version, ByteMatrix $matrix) : void + { + self::embedPositionDetectionPatternsAndSeparators($matrix); + self::embedDarkDotAtLeftBottomCorner($matrix); + self::maybeEmbedPositionAdjustmentPatterns($version, $matrix); + self::embedTimingPatterns($matrix); + } + + /** + * Embeds position detection patterns and separators into a byte matrix. + */ + private static function embedPositionDetectionPatternsAndSeparators(ByteMatrix $matrix) : void + { + $pdpWidth = count(self::POSITION_DETECTION_PATTERN[0]); + + self::embedPositionDetectionPattern(0, 0, $matrix); + self::embedPositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix); + self::embedPositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix); + + $hspWidth = 8; + + self::embedHorizontalSeparationPattern(0, $hspWidth - 1, $matrix); + self::embedHorizontalSeparationPattern($matrix->getWidth() - $hspWidth, $hspWidth - 1, $matrix); + self::embedHorizontalSeparationPattern(0, $matrix->getWidth() - $hspWidth, $matrix); + + $vspSize = 7; + + self::embedVerticalSeparationPattern($vspSize, 0, $matrix); + self::embedVerticalSeparationPattern($matrix->getHeight() - $vspSize - 1, 0, $matrix); + self::embedVerticalSeparationPattern($vspSize, $matrix->getHeight() - $vspSize, $matrix); + } + + /** + * Embeds a single position detection pattern into a byte matrix. + */ + private static function embedPositionDetectionPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void + { + for ($y = 0; $y < 7; ++$y) { + for ($x = 0; $x < 7; ++$x) { + $matrix->set($xStart + $x, $yStart + $y, self::POSITION_DETECTION_PATTERN[$y][$x]); + } + } + } + + private static function removePositionDetectionPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void + { + for ($y = 0; $y < 7; ++$y) { + for ($x = 0; $x < 7; ++$x) { + $matrix->set($xStart + $x, $yStart + $y, 0); + } + } + } + + /** + * Embeds a single horizontal separation pattern. + * + * @throws RuntimeException if a byte was already set + */ + private static function embedHorizontalSeparationPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void + { + for ($x = 0; $x < 8; $x++) { + if (-1 !== $matrix->get($xStart + $x, $yStart)) { + throw new RuntimeException('Byte already set'); + } + + $matrix->set($xStart + $x, $yStart, 0); + } + } + + /** + * Embeds a single vertical separation pattern. + * + * @throws RuntimeException if a byte was already set + */ + private static function embedVerticalSeparationPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void + { + for ($y = 0; $y < 7; $y++) { + if (-1 !== $matrix->get($xStart, $yStart + $y)) { + throw new RuntimeException('Byte already set'); + } + + $matrix->set($xStart, $yStart + $y, 0); + } + } + + /** + * Embeds a dot at the left bottom corner. + * + * @throws RuntimeException if a byte was already set to 0 + */ + private static function embedDarkDotAtLeftBottomCorner(ByteMatrix $matrix) : void + { + if (0 === $matrix->get(8, $matrix->getHeight() - 8)) { + throw new RuntimeException('Byte already set to 0'); + } + + $matrix->set(8, $matrix->getHeight() - 8, 1); + } + + /** + * Embeds position adjustment patterns if required. + */ + private static function maybeEmbedPositionAdjustmentPatterns(Version $version, ByteMatrix $matrix) : void + { + if ($version->getVersionNumber() < 2) { + return; + } + + $index = $version->getVersionNumber() - 1; + + $coordinates = self::POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[$index]; + $numCoordinates = count($coordinates); + + for ($i = 0; $i < $numCoordinates; ++$i) { + for ($j = 0; $j < $numCoordinates; ++$j) { + $y = $coordinates[$i]; + $x = $coordinates[$j]; + + if (null === $x || null === $y) { + continue; + } + + if (-1 === $matrix->get($x, $y)) { + self::embedPositionAdjustmentPattern($x - 2, $y - 2, $matrix); + } + } + } + } + + /** + * Embeds a single position adjustment pattern. + */ + private static function embedPositionAdjustmentPattern(int $xStart, int $yStart, ByteMatrix $matrix) : void + { + for ($y = 0; $y < 5; $y++) { + for ($x = 0; $x < 5; $x++) { + $matrix->set($xStart + $x, $yStart + $y, self::POSITION_ADJUSTMENT_PATTERN[$y][$x]); + } + } + } + + /** + * Embeds timing patterns into a matrix. + */ + private static function embedTimingPatterns(ByteMatrix $matrix) : void + { + $matrixWidth = $matrix->getWidth(); + + for ($i = 8; $i < $matrixWidth - 8; ++$i) { + $bit = ($i + 1) % 2; + + if (-1 === $matrix->get($i, 6)) { + $matrix->set($i, 6, $bit); + } + + if (-1 === $matrix->get(6, $i)) { + $matrix->set(6, $i, $bit); + } + } + } + + /** + * Embeds "dataBits" using "getMaskPattern". + * + * For debugging purposes, it skips masking process if "getMaskPattern" is -1. See 8.7 of JISX0510:2004 (p.38) for + * how to embed data bits. + * + * @throws WriterException if not all bits could be consumed + */ + private static function embedDataBits(BitArray $dataBits, int $maskPattern, ByteMatrix $matrix) : void + { + $bitIndex = 0; + $direction = -1; + + // Start from the right bottom cell. + $x = $matrix->getWidth() - 1; + $y = $matrix->getHeight() - 1; + + while ($x > 0) { + // Skip vertical timing pattern. + if (6 === $x) { + --$x; + } + + while ($y >= 0 && $y < $matrix->getHeight()) { + for ($i = 0; $i < 2; $i++) { + $xx = $x - $i; + + // Skip the cell if it's not empty. + if (-1 !== $matrix->get($xx, $y)) { + continue; + } + + if ($bitIndex < $dataBits->getSize()) { + $bit = $dataBits->get($bitIndex); + ++$bitIndex; + } else { + // Padding bit. If there is no bit left, we'll fill the + // left cells with 0, as described in 8.4.9 of + // JISX0510:2004 (p. 24). + $bit = false; + } + + // Skip masking if maskPattern is -1. + if (-1 !== $maskPattern && MaskUtil::getDataMaskBit($maskPattern, $xx, $y)) { + $bit = ! $bit; + } + + $matrix->set($xx, $y, (int) $bit); + } + + $y += $direction; + } + + $direction = -$direction; + $y += $direction; + $x -= 2; + } + + // All bits should be consumed + if ($dataBits->getSize() !== $bitIndex) { + throw new WriterException('Not all bits consumed (' . $bitIndex . ' out of ' . $dataBits->getSize() .')'); + } + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/QrCode.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/QrCode.php new file mode 100644 index 000000000..f568e88cf --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Encoder/QrCode.php @@ -0,0 +1,141 @@ +mode = $mode; + $this->errorCorrectionLevel = $errorCorrectionLevel; + $this->version = $version; + $this->maskPattern = $maskPattern; + $this->matrix = $matrix; + } + + /** + * Gets the mode. + */ + public function getMode() : Mode + { + return $this->mode; + } + + /** + * Gets the EC level. + */ + public function getErrorCorrectionLevel() : ErrorCorrectionLevel + { + return $this->errorCorrectionLevel; + } + + /** + * Gets the version. + */ + public function getVersion() : Version + { + return $this->version; + } + + /** + * Gets the mask pattern. + */ + public function getMaskPattern() : int + { + return $this->maskPattern; + } + + /** + * Gets the matrix. + * + * @return ByteMatrix + */ + public function getMatrix() + { + return $this->matrix; + } + + /** + * Validates whether a mask pattern is valid. + */ + public static function isValidMaskPattern(int $maskPattern) : bool + { + return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS; + } + + /** + * Returns a string representation of the QR code. + */ + public function __toString() : string + { + $result = "<<\n" + . ' mode: ' . $this->mode . "\n" + . ' ecLevel: ' . $this->errorCorrectionLevel . "\n" + . ' version: ' . $this->version . "\n" + . ' maskPattern: ' . $this->maskPattern . "\n"; + + if ($this->matrix === null) { + $result .= " matrix: null\n"; + } else { + $result .= " matrix:\n"; + $result .= $this->matrix; + } + + $result .= ">>\n"; + + return $result; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php new file mode 100644 index 000000000..6f70c2055 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Exception/ExceptionInterface.php @@ -0,0 +1,10 @@ + 100) { + throw new Exception\InvalidArgumentException('Alpha must be between 0 and 100'); + } + + $this->alpha = $alpha; + $this->baseColor = $baseColor; + } + + public function getAlpha() : int + { + return $this->alpha; + } + + public function getBaseColor() : ColorInterface + { + return $this->baseColor; + } + + public function toRgb() : Rgb + { + return $this->baseColor->toRgb(); + } + + public function toCmyk() : Cmyk + { + return $this->baseColor->toCmyk(); + } + + public function toGray() : Gray + { + return $this->baseColor->toGray(); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.php new file mode 100644 index 000000000..d6de390ad --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/Cmyk.php @@ -0,0 +1,103 @@ + 100) { + throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100'); + } + + if ($magenta < 0 || $magenta > 100) { + throw new Exception\InvalidArgumentException('Magenta must be between 0 and 100'); + } + + if ($yellow < 0 || $yellow > 100) { + throw new Exception\InvalidArgumentException('Yellow must be between 0 and 100'); + } + + if ($black < 0 || $black > 100) { + throw new Exception\InvalidArgumentException('Black must be between 0 and 100'); + } + + $this->cyan = $cyan; + $this->magenta = $magenta; + $this->yellow = $yellow; + $this->black = $black; + } + + public function getCyan() : int + { + return $this->cyan; + } + + public function getMagenta() : int + { + return $this->magenta; + } + + public function getYellow() : int + { + return $this->yellow; + } + + public function getBlack() : int + { + return $this->black; + } + + public function toRgb() : Rgb + { + $k = $this->black / 100; + $c = (-$k * $this->cyan + $k * 100 + $this->cyan) / 100; + $m = (-$k * $this->magenta + $k * 100 + $this->magenta) / 100; + $y = (-$k * $this->yellow + $k * 100 + $this->yellow) / 100; + + return new Rgb( + (int) (-$c * 255 + 255), + (int) (-$m * 255 + 255), + (int) (-$y * 255 + 255) + ); + } + + public function toCmyk() : Cmyk + { + return $this; + } + + public function toGray() : Gray + { + return $this->toRgb()->toGray(); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.php new file mode 100644 index 000000000..b50d1cace --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/ColorInterface.php @@ -0,0 +1,22 @@ + 100) { + throw new Exception\InvalidArgumentException('Gray must be between 0 and 100'); + } + + $this->gray = (int) $gray; + } + + public function getGray() : int + { + return $this->gray; + } + + public function toRgb() : Rgb + { + return new Rgb((int) ($this->gray * 2.55), (int) ($this->gray * 2.55), (int) ($this->gray * 2.55)); + } + + public function toCmyk() : Cmyk + { + return new Cmyk(0, 0, 0, 100 - $this->gray); + } + + public function toGray() : Gray + { + return $this; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php new file mode 100644 index 000000000..793540621 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Color/Rgb.php @@ -0,0 +1,88 @@ + 255) { + throw new Exception\InvalidArgumentException('Red must be between 0 and 255'); + } + + if ($green < 0 || $green > 255) { + throw new Exception\InvalidArgumentException('Green must be between 0 and 255'); + } + + if ($blue < 0 || $blue > 255) { + throw new Exception\InvalidArgumentException('Blue must be between 0 and 255'); + } + + $this->red = $red; + $this->green = $green; + $this->blue = $blue; + } + + public function getRed() : int + { + return $this->red; + } + + public function getGreen() : int + { + return $this->green; + } + + public function getBlue() : int + { + return $this->blue; + } + + public function toRgb() : Rgb + { + return $this; + } + + public function toCmyk() : Cmyk + { + $c = 1 - ($this->red / 255); + $m = 1 - ($this->green / 255); + $y = 1 - ($this->blue / 255); + $k = min($c, $m, $y); + + return new Cmyk( + (int) (100 * ($c - $k) / (1 - $k)), + (int) (100 * ($m - $k) / (1 - $k)), + (int) (100 * ($y - $k) / (1 - $k)), + (int) (100 * $k) + ); + } + + public function toGray() : Gray + { + return new Gray((int) (($this->red * 0.21 + $this->green * 0.71 + $this->blue * 0.07) / 2.55)); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php new file mode 100644 index 000000000..0d0312590 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/CompositeEye.php @@ -0,0 +1,38 @@ +externalEye = $externalEye; + $this->internalEye = $internalEye; + } + + public function getExternalPath() : Path + { + return $this->externalEye->getExternalPath(); + } + + public function getInternalPath() : Path + { + return $this->internalEye->getInternalPath(); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.php new file mode 100644 index 000000000..ab68f3cd1 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/EyeInterface.php @@ -0,0 +1,26 @@ +module = $module; + } + + public function getExternalPath() : Path + { + $matrix = new ByteMatrix(7, 7); + + for ($x = 0; $x < 7; ++$x) { + $matrix->set($x, 0, 1); + $matrix->set($x, 6, 1); + } + + for ($y = 1; $y < 6; ++$y) { + $matrix->set(0, $y, 1); + $matrix->set(6, $y, 1); + } + + return $this->module->createPath($matrix)->translate(-3.5, -3.5); + } + + public function getInternalPath() : Path + { + $matrix = new ByteMatrix(3, 3); + + for ($x = 0; $x < 3; ++$x) { + for ($y = 0; $y < 3; ++$y) { + $matrix->set($x, $y, 1); + } + } + + return $this->module->createPath($matrix)->translate(-1.5, -1.5); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php new file mode 100644 index 000000000..64d54ee28 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/SimpleCircleEye.php @@ -0,0 +1,54 @@ +move(-3.5, -3.5) + ->line(3.5, -3.5) + ->line(3.5, 3.5) + ->line(-3.5, 3.5) + ->close() + ->move(-2.5, -2.5) + ->line(-2.5, 2.5) + ->line(2.5, 2.5) + ->line(2.5, -2.5) + ->close() + ; + } + + public function getInternalPath() : Path + { + return (new Path()) + ->move(1.5, 0) + ->ellipticArc(1.5, 1.5, 0., false, true, 0., 1.5) + ->ellipticArc(1.5, 1.5, 0., false, true, -1.5, 0.) + ->ellipticArc(1.5, 1.5, 0., false, true, 0., -1.5) + ->ellipticArc(1.5, 1.5, 0., false, true, 1.5, 0.) + ->close() + ; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php new file mode 100644 index 000000000..a3892b476 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/SquareEye.php @@ -0,0 +1,53 @@ +move(-3.5, -3.5) + ->line(3.5, -3.5) + ->line(3.5, 3.5) + ->line(-3.5, 3.5) + ->close() + ->move(-2.5, -2.5) + ->line(-2.5, 2.5) + ->line(2.5, 2.5) + ->line(2.5, -2.5) + ->close() + ; + } + + public function getInternalPath() : Path + { + return (new Path()) + ->move(-1.5, -1.5) + ->line(1.5, -1.5) + ->line(1.5, 1.5) + ->line(-1.5, 1.5) + ->close() + ; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php new file mode 100644 index 000000000..b581b540d --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/EpsImageBackEnd.php @@ -0,0 +1,376 @@ +eps = "%!PS-Adobe-3.0 EPSF-3.0\n" + . "%%Creator: BaconQrCode\n" + . sprintf("%%%%BoundingBox: 0 0 %d %d \n", $size, $size) + . "%%BeginProlog\n" + . "save\n" + . "50 dict begin\n" + . "/q { gsave } bind def\n" + . "/Q { grestore } bind def\n" + . "/s { scale } bind def\n" + . "/t { translate } bind def\n" + . "/r { rotate } bind def\n" + . "/n { newpath } bind def\n" + . "/m { moveto } bind def\n" + . "/l { lineto } bind def\n" + . "/c { curveto } bind def\n" + . "/z { closepath } bind def\n" + . "/f { eofill } bind def\n" + . "/rgb { setrgbcolor } bind def\n" + . "/cmyk { setcmykcolor } bind def\n" + . "/gray { setgray } bind def\n" + . "%%EndProlog\n" + . "1 -1 s\n" + . sprintf("0 -%d t\n", $size); + + if ($backgroundColor instanceof Alpha && 0 === $backgroundColor->getAlpha()) { + return; + } + + $this->eps .= wordwrap( + '0 0 m' + . sprintf(' %s 0 l', (string) $size) + . sprintf(' %s %s l', (string) $size, (string) $size) + . sprintf(' 0 %s l', (string) $size) + . ' z' + . ' ' .$this->getColorSetString($backgroundColor) . " f\n", + 75, + "\n " + ); + } + + public function scale(float $size) : void + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $this->eps .= sprintf("%1\$s %1\$s s\n", round($size, self::PRECISION)); + } + + public function translate(float $x, float $y) : void + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $this->eps .= sprintf("%s %s t\n", round($x, self::PRECISION), round($y, self::PRECISION)); + } + + public function rotate(int $degrees) : void + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $this->eps .= sprintf("%d r\n", $degrees); + } + + public function push() : void + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $this->eps .= "q\n"; + } + + public function pop() : void + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $this->eps .= "Q\n"; + } + + public function drawPathWithColor(Path $path, ColorInterface $color) : void + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $fromX = 0; + $fromY = 0; + $this->eps .= wordwrap( + 'n ' + . $this->drawPathOperations($path, $fromX, $fromY) + . ' ' . $this->getColorSetString($color) . " f\n", + 75, + "\n " + ); + } + + public function drawPathWithGradient( + Path $path, + Gradient $gradient, + float $x, + float $y, + float $width, + float $height + ) : void { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $fromX = 0; + $fromY = 0; + $this->eps .= wordwrap( + 'q n ' . $this->drawPathOperations($path, $fromX, $fromY) . "\n", + 75, + "\n " + ); + + $this->createGradientFill($gradient, $x, $y, $width, $height); + } + + public function done() : string + { + if (null === $this->eps) { + throw new RuntimeException('No image has been started'); + } + + $this->eps .= "%%TRAILER\nend restore\n%%EOF"; + $blob = $this->eps; + $this->eps = null; + + return $blob; + } + + private function drawPathOperations(Iterable $ops, &$fromX, &$fromY) : string + { + $pathData = []; + + foreach ($ops as $op) { + switch (true) { + case $op instanceof Move: + $fromX = $toX = round($op->getX(), self::PRECISION); + $fromY = $toY = round($op->getY(), self::PRECISION); + $pathData[] = sprintf('%s %s m', $toX, $toY); + break; + + case $op instanceof Line: + $fromX = $toX = round($op->getX(), self::PRECISION); + $fromY = $toY = round($op->getY(), self::PRECISION); + $pathData[] = sprintf('%s %s l', $toX, $toY); + break; + + case $op instanceof EllipticArc: + $pathData[] = $this->drawPathOperations($op->toCurves($fromX, $fromY), $fromX, $fromY); + break; + + case $op instanceof Curve: + $x1 = round($op->getX1(), self::PRECISION); + $y1 = round($op->getY1(), self::PRECISION); + $x2 = round($op->getX2(), self::PRECISION); + $y2 = round($op->getY2(), self::PRECISION); + $fromX = $x3 = round($op->getX3(), self::PRECISION); + $fromY = $y3 = round($op->getY3(), self::PRECISION); + $pathData[] = sprintf('%s %s %s %s %s %s c', $x1, $y1, $x2, $y2, $x3, $y3); + break; + + case $op instanceof Close: + $pathData[] = 'z'; + break; + + default: + throw new RuntimeException('Unexpected draw operation: ' . get_class($op)); + } + } + + return implode(' ', $pathData); + } + + private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : void + { + $startColor = $gradient->getStartColor(); + $endColor = $gradient->getEndColor(); + + if ($startColor instanceof Alpha) { + $startColor = $startColor->getBaseColor(); + } + + $startColorType = get_class($startColor); + + if (! in_array($startColorType, [Rgb::class, Cmyk::class, Gray::class])) { + $startColorType = Cmyk::class; + $startColor = $startColor->toCmyk(); + } + + if (get_class($endColor) !== $startColorType) { + switch ($startColorType) { + case Cmyk::class: + $endColor = $endColor->toCmyk(); + break; + + case Rgb::class: + $endColor = $endColor->toRgb(); + break; + + case Gray::class: + $endColor = $endColor->toGray(); + break; + } + } + + $this->eps .= "eoclip\n<<\n"; + + if ($gradient->getType() === GradientType::RADIAL()) { + $this->eps .= " /ShadingType 3\n"; + } else { + $this->eps .= " /ShadingType 2\n"; + } + + $this->eps .= " /Extend [ true true ]\n" + . " /AntiAlias true\n"; + + switch ($startColorType) { + case Cmyk::class: + $this->eps .= " /ColorSpace /DeviceCMYK\n"; + break; + + case Rgb::class: + $this->eps .= " /ColorSpace /DeviceRGB\n"; + break; + + case Gray::class: + $this->eps .= " /ColorSpace /DeviceGray\n"; + break; + } + + switch ($gradient->getType()) { + case GradientType::HORIZONTAL(): + $this->eps .= sprintf( + " /Coords [ %s %s %s %s ]\n", + round($x, self::PRECISION), + round($y, self::PRECISION), + round($x + $width, self::PRECISION), + round($y, self::PRECISION) + ); + break; + + case GradientType::VERTICAL(): + $this->eps .= sprintf( + " /Coords [ %s %s %s %s ]\n", + round($x, self::PRECISION), + round($y, self::PRECISION), + round($x, self::PRECISION), + round($y + $height, self::PRECISION) + ); + break; + + case GradientType::DIAGONAL(): + $this->eps .= sprintf( + " /Coords [ %s %s %s %s ]\n", + round($x, self::PRECISION), + round($y, self::PRECISION), + round($x + $width, self::PRECISION), + round($y + $height, self::PRECISION) + ); + break; + + case GradientType::INVERSE_DIAGONAL(): + $this->eps .= sprintf( + " /Coords [ %s %s %s %s ]\n", + round($x, self::PRECISION), + round($y + $height, self::PRECISION), + round($x + $width, self::PRECISION), + round($y, self::PRECISION) + ); + break; + + case GradientType::RADIAL(): + $centerX = ($x + $width) / 2; + $centerY = ($y + $height) / 2; + + $this->eps .= sprintf( + " /Coords [ %s %s 0 %s %s %s ]\n", + round($centerX, self::PRECISION), + round($centerY, self::PRECISION), + round($centerX, self::PRECISION), + round($centerY, self::PRECISION), + round(max($width, $height) / 2, self::PRECISION) + ); + break; + } + + $this->eps .= " /Function\n" + . " <<\n" + . " /FunctionType 2\n" + . " /Domain [ 0 1 ]\n" + . sprintf(" /C0 [ %s ]\n", $this->getColorString($startColor)) + . sprintf(" /C1 [ %s ]\n", $this->getColorString($endColor)) + . " /N 1\n" + . " >>\n>>\nshfill\nQ\n"; + } + + private function getColorSetString(ColorInterface $color) : string + { + if ($color instanceof Rgb) { + return $this->getColorString($color) . ' rgb'; + } + + if ($color instanceof Cmyk) { + return $this->getColorString($color) . ' cmyk'; + } + + if ($color instanceof Gray) { + return $this->getColorString($color) . ' gray'; + } + + return $this->getColorSetString($color->toCmyk()); + } + + private function getColorString(ColorInterface $color) : string + { + if ($color instanceof Rgb) { + return sprintf('%s %s %s', $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255); + } + + if ($color instanceof Cmyk) { + return sprintf( + '%s %s %s %s', + $color->getCyan() / 100, + $color->getMagenta() / 100, + $color->getYellow() / 100, + $color->getBlack() / 100 + ); + } + + if ($color instanceof Gray) { + return sprintf('%s', $color->getGray() / 100); + } + + return $this->getColorString($color->toCmyk()); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.php new file mode 100644 index 000000000..0935819a8 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/ImageBackEndInterface.php @@ -0,0 +1,87 @@ +imageFormat = $imageFormat; + $this->compressionQuality = $compressionQuality; + } + + public function new(int $size, ColorInterface $backgroundColor) : void + { + $this->image = new Imagick(); + $this->image->newImage($size, $size, $this->getColorPixel($backgroundColor)); + $this->image->setImageFormat($this->imageFormat); + $this->image->setCompressionQuality($this->compressionQuality); + $this->draw = new ImagickDraw(); + $this->gradientCount = 0; + $this->matrices = [new TransformationMatrix()]; + $this->matrixIndex = 0; + } + + public function scale(float $size) : void + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->scale($size, $size); + $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex] + ->multiply(TransformationMatrix::scale($size)); + } + + public function translate(float $x, float $y) : void + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->translate($x, $y); + $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex] + ->multiply(TransformationMatrix::translate($x, $y)); + } + + public function rotate(int $degrees) : void + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->rotate($degrees); + $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex] + ->multiply(TransformationMatrix::rotate($degrees)); + } + + public function push() : void + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->push(); + $this->matrices[++$this->matrixIndex] = $this->matrices[$this->matrixIndex - 1]; + } + + public function pop() : void + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->pop(); + unset($this->matrices[$this->matrixIndex--]); + } + + public function drawPathWithColor(Path $path, ColorInterface $color) : void + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->setFillColor($this->getColorPixel($color)); + $this->drawPath($path); + } + + public function drawPathWithGradient( + Path $path, + Gradient $gradient, + float $x, + float $y, + float $width, + float $height + ) : void { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->draw->setFillPatternURL('#' . $this->createGradientFill($gradient, $x, $y, $width, $height)); + $this->drawPath($path); + } + + public function done() : string + { + if (null === $this->draw) { + throw new RuntimeException('No image has been started'); + } + + $this->image->drawImage($this->draw); + $blob = $this->image->getImageBlob(); + $this->draw->clear(); + $this->image->clear(); + $this->draw = null; + $this->image = null; + $this->gradientCount = null; + + return $blob; + } + + private function drawPath(Path $path) : void + { + $this->draw->pathStart(); + + foreach ($path as $op) { + switch (true) { + case $op instanceof Move: + $this->draw->pathMoveToAbsolute($op->getX(), $op->getY()); + break; + + case $op instanceof Line: + $this->draw->pathLineToAbsolute($op->getX(), $op->getY()); + break; + + case $op instanceof EllipticArc: + $this->draw->pathEllipticArcAbsolute( + $op->getXRadius(), + $op->getYRadius(), + $op->getXAxisAngle(), + $op->isLargeArc(), + $op->isSweep(), + $op->getX(), + $op->getY() + ); + break; + + case $op instanceof Curve: + $this->draw->pathCurveToAbsolute( + $op->getX1(), + $op->getY1(), + $op->getX2(), + $op->getY2(), + $op->getX3(), + $op->getY3() + ); + break; + + case $op instanceof Close: + $this->draw->pathClose(); + break; + + default: + throw new RuntimeException('Unexpected draw operation: ' . get_class($op)); + } + } + + $this->draw->pathFinish(); + } + + private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string + { + list($width, $height) = $this->matrices[$this->matrixIndex]->apply($width, $height); + + $startColor = $this->getColorPixel($gradient->getStartColor())->getColorAsString(); + $endColor = $this->getColorPixel($gradient->getEndColor())->getColorAsString(); + $gradientImage = new Imagick(); + + switch ($gradient->getType()) { + case GradientType::HORIZONTAL(): + $gradientImage->newPseudoImage((int) $height, (int) $width, sprintf( + 'gradient:%s-%s', + $startColor, + $endColor + )); + $gradientImage->rotateImage('transparent', -90); + break; + + case GradientType::VERTICAL(): + $gradientImage->newPseudoImage((int) $width, (int) $height, sprintf( + 'gradient:%s-%s', + $startColor, + $endColor + )); + break; + + case GradientType::DIAGONAL(): + case GradientType::INVERSE_DIAGONAL(): + $gradientImage->newPseudoImage((int) ($width * sqrt(2)), (int) ($height * sqrt(2)), sprintf( + 'gradient:%s-%s', + $startColor, + $endColor + )); + + if (GradientType::DIAGONAL() === $gradient->getType()) { + $gradientImage->rotateImage('transparent', -45); + } else { + $gradientImage->rotateImage('transparent', -135); + } + + $rotatedWidth = $gradientImage->getImageWidth(); + $rotatedHeight = $gradientImage->getImageHeight(); + + $gradientImage->setImagePage($rotatedWidth, $rotatedHeight, 0, 0); + $gradientImage->cropImage( + intdiv($rotatedWidth, 2) - 2, + intdiv($rotatedHeight, 2) - 2, + intdiv($rotatedWidth, 4) + 1, + intdiv($rotatedWidth, 4) + 1 + ); + break; + + case GradientType::RADIAL(): + $gradientImage->newPseudoImage((int) $width, (int) $height, sprintf( + 'radial-gradient:%s-%s', + $startColor, + $endColor + )); + break; + } + + $id = sprintf('g%d', ++$this->gradientCount); + $this->draw->pushPattern($id, 0, 0, $width, $height); + $this->draw->composite(Imagick::COMPOSITE_COPY, 0, 0, $width, $height, $gradientImage); + $this->draw->popPattern(); + return $id; + } + + private function getColorPixel(ColorInterface $color) : ImagickPixel + { + $alpha = 100; + + if ($color instanceof Alpha) { + $alpha = $color->getAlpha(); + $color = $color->getBaseColor(); + } + + if ($color instanceof Rgb) { + return new ImagickPixel(sprintf( + 'rgba(%d, %d, %d, %F)', + $color->getRed(), + $color->getGreen(), + $color->getBlue(), + $alpha / 100 + )); + } + + if ($color instanceof Cmyk) { + return new ImagickPixel(sprintf( + 'cmyka(%d, %d, %d, %d, %F)', + $color->getCyan(), + $color->getMagenta(), + $color->getYellow(), + $color->getBlack(), + $alpha / 100 + )); + } + + if ($color instanceof Gray) { + return new ImagickPixel(sprintf( + 'graya(%d%%, %F)', + $color->getGray(), + $alpha / 100 + )); + } + + return $this->getColorPixel(new Alpha($alpha, $color->toRgb())); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php new file mode 100644 index 000000000..cb37a9ff8 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/SvgImageBackEnd.php @@ -0,0 +1,369 @@ +xmlWriter = new XMLWriter(); + $this->xmlWriter->openMemory(); + + $this->xmlWriter->startDocument('1.0', 'UTF-8'); + $this->xmlWriter->startElement('svg'); + $this->xmlWriter->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); + $this->xmlWriter->writeAttribute('version', '1.1'); + $this->xmlWriter->writeAttribute('width', (string) $size); + $this->xmlWriter->writeAttribute('height', (string) $size); + $this->xmlWriter->writeAttribute('viewBox', '0 0 '. $size . ' ' . $size); + + $this->gradientCount = 0; + $this->currentStack = 0; + $this->stack[0] = 0; + + $alpha = 1; + + if ($backgroundColor instanceof Alpha) { + $alpha = $backgroundColor->getAlpha() / 100; + } + + if (0 === $alpha) { + return; + } + + $this->xmlWriter->startElement('rect'); + $this->xmlWriter->writeAttribute('x', '0'); + $this->xmlWriter->writeAttribute('y', '0'); + $this->xmlWriter->writeAttribute('width', (string) $size); + $this->xmlWriter->writeAttribute('height', (string) $size); + $this->xmlWriter->writeAttribute('fill', $this->getColorString($backgroundColor)); + + if ($alpha < 1) { + $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha); + } + + $this->xmlWriter->endElement(); + } + + public function scale(float $size) : void + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + $this->xmlWriter->startElement('g'); + $this->xmlWriter->writeAttribute( + 'transform', + sprintf('scale(%s)', round($size, self::PRECISION)) + ); + ++$this->stack[$this->currentStack]; + } + + public function translate(float $x, float $y) : void + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + $this->xmlWriter->startElement('g'); + $this->xmlWriter->writeAttribute( + 'transform', + sprintf('translate(%s,%s)', round($x, self::PRECISION), round($y, self::PRECISION)) + ); + ++$this->stack[$this->currentStack]; + } + + public function rotate(int $degrees) : void + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + $this->xmlWriter->startElement('g'); + $this->xmlWriter->writeAttribute('transform', sprintf('rotate(%d)', $degrees)); + ++$this->stack[$this->currentStack]; + } + + public function push() : void + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + $this->xmlWriter->startElement('g'); + $this->stack[] = 1; + ++$this->currentStack; + } + + public function pop() : void + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + for ($i = 0; $i < $this->stack[$this->currentStack]; ++$i) { + $this->xmlWriter->endElement(); + } + + array_pop($this->stack); + --$this->currentStack; + } + + public function drawPathWithColor(Path $path, ColorInterface $color) : void + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + $alpha = 1; + + if ($color instanceof Alpha) { + $alpha = $color->getAlpha() / 100; + } + + $this->startPathElement($path); + $this->xmlWriter->writeAttribute('fill', $this->getColorString($color)); + + if ($alpha < 1) { + $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha); + } + + $this->xmlWriter->endElement(); + } + + public function drawPathWithGradient( + Path $path, + Gradient $gradient, + float $x, + float $y, + float $width, + float $height + ) : void { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + $gradientId = $this->createGradientFill($gradient, $x, $y, $width, $height); + $this->startPathElement($path); + $this->xmlWriter->writeAttribute('fill', 'url(#' . $gradientId . ')'); + $this->xmlWriter->endElement(); + } + + public function done() : string + { + if (null === $this->xmlWriter) { + throw new RuntimeException('No image has been started'); + } + + foreach ($this->stack as $openElements) { + for ($i = $openElements; $i > 0; --$i) { + $this->xmlWriter->endElement(); + } + } + + $this->xmlWriter->endDocument(); + $blob = $this->xmlWriter->outputMemory(true); + $this->xmlWriter = null; + $this->stack = null; + $this->currentStack = null; + $this->gradientCount = null; + + return $blob; + } + + private function startPathElement(Path $path) : void + { + $pathData = []; + + foreach ($path as $op) { + switch (true) { + case $op instanceof Move: + $pathData[] = sprintf( + 'M%s %s', + round($op->getX(), self::PRECISION), + round($op->getY(), self::PRECISION) + ); + break; + + case $op instanceof Line: + $pathData[] = sprintf( + 'L%s %s', + round($op->getX(), self::PRECISION), + round($op->getY(), self::PRECISION) + ); + break; + + case $op instanceof EllipticArc: + $pathData[] = sprintf( + 'A%s %s %s %u %u %s %s', + round($op->getXRadius(), self::PRECISION), + round($op->getYRadius(), self::PRECISION), + round($op->getXAxisAngle(), self::PRECISION), + $op->isLargeArc(), + $op->isSweep(), + round($op->getX(), self::PRECISION), + round($op->getY(), self::PRECISION) + ); + break; + + case $op instanceof Curve: + $pathData[] = sprintf( + 'C%s %s %s %s %s %s', + round($op->getX1(), self::PRECISION), + round($op->getY1(), self::PRECISION), + round($op->getX2(), self::PRECISION), + round($op->getY2(), self::PRECISION), + round($op->getX3(), self::PRECISION), + round($op->getY3(), self::PRECISION) + ); + break; + + case $op instanceof Close: + $pathData[] = 'Z'; + break; + + default: + throw new RuntimeException('Unexpected draw operation: ' . get_class($op)); + } + } + + $this->xmlWriter->startElement('path'); + $this->xmlWriter->writeAttribute('fill-rule', 'evenodd'); + $this->xmlWriter->writeAttribute('d', implode('', $pathData)); + } + + private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string + { + $this->xmlWriter->startElement('defs'); + + $startColor = $gradient->getStartColor(); + $endColor = $gradient->getEndColor(); + + if ($gradient->getType() === GradientType::RADIAL()) { + $this->xmlWriter->startElement('radialGradient'); + } else { + $this->xmlWriter->startElement('linearGradient'); + } + + $this->xmlWriter->writeAttribute('gradientUnits', 'userSpaceOnUse'); + + switch ($gradient->getType()) { + case GradientType::HORIZONTAL(): + $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION)); + $this->xmlWriter->writeAttribute('y1', (string) round($y, self::PRECISION)); + $this->xmlWriter->writeAttribute('x2', (string) round($x + $width, self::PRECISION)); + $this->xmlWriter->writeAttribute('y2', (string) round($y, self::PRECISION)); + break; + + case GradientType::VERTICAL(): + $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION)); + $this->xmlWriter->writeAttribute('y1', (string) round($y, self::PRECISION)); + $this->xmlWriter->writeAttribute('x2', (string) round($x, self::PRECISION)); + $this->xmlWriter->writeAttribute('y2', (string) round($y + $height, self::PRECISION)); + break; + + case GradientType::DIAGONAL(): + $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION)); + $this->xmlWriter->writeAttribute('y1', (string) round($y, self::PRECISION)); + $this->xmlWriter->writeAttribute('x2', (string) round($x + $width, self::PRECISION)); + $this->xmlWriter->writeAttribute('y2', (string) round($y + $height, self::PRECISION)); + break; + + case GradientType::INVERSE_DIAGONAL(): + $this->xmlWriter->writeAttribute('x1', (string) round($x, self::PRECISION)); + $this->xmlWriter->writeAttribute('y1', (string) round($y + $height, self::PRECISION)); + $this->xmlWriter->writeAttribute('x2', (string) round($x + $width, self::PRECISION)); + $this->xmlWriter->writeAttribute('y2', (string) round($y, self::PRECISION)); + break; + + case GradientType::RADIAL(): + $this->xmlWriter->writeAttribute('cx', (string) round(($x + $width) / 2, self::PRECISION)); + $this->xmlWriter->writeAttribute('cy', (string) round(($y + $height) / 2, self::PRECISION)); + $this->xmlWriter->writeAttribute('r', (string) round(max($width, $height) / 2, self::PRECISION)); + break; + } + + $id = sprintf('g%d', ++$this->gradientCount); + $this->xmlWriter->writeAttribute('id', $id); + + $this->xmlWriter->startElement('stop'); + $this->xmlWriter->writeAttribute('offset', '0%'); + $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor)); + + if ($startColor instanceof Alpha) { + $this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha()); + } + + $this->xmlWriter->endElement(); + + $this->xmlWriter->startElement('stop'); + $this->xmlWriter->writeAttribute('offset', '100%'); + $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor)); + + if ($endColor instanceof Alpha) { + $this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha()); + } + + $this->xmlWriter->endElement(); + + $this->xmlWriter->endElement(); + $this->xmlWriter->endElement(); + + return $id; + } + + private function getColorString(ColorInterface $color) : string + { + $color = $color->toRgb(); + + return sprintf( + '#%02x%02x%02x', + $color->getRed(), + $color->getGreen(), + $color->getBlue() + ); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.php new file mode 100644 index 000000000..7e88da6df --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Image/TransformationMatrix.php @@ -0,0 +1,68 @@ +values = [1, 0, 0, 1, 0, 0]; + } + + public function multiply(self $other) : self + { + $matrix = new self(); + $matrix->values[0] = $this->values[0] * $other->values[0] + $this->values[2] * $other->values[1]; + $matrix->values[1] = $this->values[1] * $other->values[0] + $this->values[3] * $other->values[1]; + $matrix->values[2] = $this->values[0] * $other->values[2] + $this->values[2] * $other->values[3]; + $matrix->values[3] = $this->values[1] * $other->values[2] + $this->values[3] * $other->values[3]; + $matrix->values[4] = $this->values[0] * $other->values[4] + $this->values[2] * $other->values[5] + + $this->values[4]; + $matrix->values[5] = $this->values[1] * $other->values[4] + $this->values[3] * $other->values[5] + + $this->values[5]; + + return $matrix; + } + + public static function scale(float $size) : self + { + $matrix = new self(); + $matrix->values = [$size, 0, 0, $size, 0, 0]; + return $matrix; + } + + public static function translate(float $x, float $y) : self + { + $matrix = new self(); + $matrix->values = [1, 0, 0, 1, $x, $y]; + return $matrix; + } + + public static function rotate(int $degrees) : self + { + $matrix = new self(); + $rad = deg2rad($degrees); + $matrix->values = [cos($rad), sin($rad), -sin($rad), cos($rad), 0, 0]; + return $matrix; + } + + + /** + * Applies this matrix onto a point and returns the resulting viewport point. + * + * @return float[] + */ + public function apply(float $x, float $y) : array + { + return [ + $x * $this->values[0] + $y * $this->values[2] + $this->values[4], + $x * $this->values[1] + $y * $this->values[3] + $this->values[5], + ]; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php new file mode 100644 index 000000000..ab16276c9 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/ImageRenderer.php @@ -0,0 +1,152 @@ +rendererStyle = $rendererStyle; + $this->imageBackEnd = $imageBackEnd; + } + + /** + * @throws InvalidArgumentException if matrix width doesn't match height + */ + public function render(QrCode $qrCode) : string + { + $size = $this->rendererStyle->getSize(); + $margin = $this->rendererStyle->getMargin(); + $matrix = $qrCode->getMatrix(); + $matrixSize = $matrix->getWidth(); + + if ($matrixSize !== $matrix->getHeight()) { + throw new InvalidArgumentException('Matrix must have the same width and height'); + } + + $totalSize = $matrixSize + ($margin * 2); + $moduleSize = $size / $totalSize; + $fill = $this->rendererStyle->getFill(); + + $this->imageBackEnd->new($size, $fill->getBackgroundColor()); + $this->imageBackEnd->scale((float) $moduleSize); + $this->imageBackEnd->translate((float) $margin, (float) $margin); + + $module = $this->rendererStyle->getModule(); + $moduleMatrix = clone $matrix; + MatrixUtil::removePositionDetectionPatterns($moduleMatrix); + $modulePath = $this->drawEyes($matrixSize, $module->createPath($moduleMatrix)); + + if ($fill->hasGradientFill()) { + $this->imageBackEnd->drawPathWithGradient( + $modulePath, + $fill->getForegroundGradient(), + 0, + 0, + $matrixSize, + $matrixSize + ); + } else { + $this->imageBackEnd->drawPathWithColor($modulePath, $fill->getForegroundColor()); + } + + return $this->imageBackEnd->done(); + } + + private function drawEyes(int $matrixSize, Path $modulePath) : Path + { + $fill = $this->rendererStyle->getFill(); + + $eye = $this->rendererStyle->getEye(); + $externalPath = $eye->getExternalPath(); + $internalPath = $eye->getInternalPath(); + + $modulePath = $this->drawEye( + $externalPath, + $internalPath, + $fill->getTopLeftEyeFill(), + 3.5, + 3.5, + 0, + $modulePath + ); + $modulePath = $this->drawEye( + $externalPath, + $internalPath, + $fill->getTopRightEyeFill(), + $matrixSize - 3.5, + 3.5, + 90, + $modulePath + ); + $modulePath = $this->drawEye( + $externalPath, + $internalPath, + $fill->getBottomLeftEyeFill(), + 3.5, + $matrixSize - 3.5, + -90, + $modulePath + ); + + return $modulePath; + } + + private function drawEye( + Path $externalPath, + Path $internalPath, + EyeFill $fill, + float $xTranslation, + float $yTranslation, + int $rotation, + Path $modulePath + ) : Path { + if ($fill->inheritsBothColors()) { + return $modulePath + ->append($externalPath->translate($xTranslation, $yTranslation)) + ->append($internalPath->translate($xTranslation, $yTranslation)); + } + + $this->imageBackEnd->push(); + $this->imageBackEnd->translate($xTranslation, $yTranslation); + + if (0 !== $rotation) { + $this->imageBackEnd->rotate($rotation); + } + + if ($fill->inheritsExternalColor()) { + $modulePath = $modulePath->append($externalPath->translate($xTranslation, $yTranslation)); + } else { + $this->imageBackEnd->drawPathWithColor($externalPath, $fill->getExternalColor()); + } + + if ($fill->inheritsInternalColor()) { + $modulePath = $modulePath->append($internalPath->translate($xTranslation, $yTranslation)); + } else { + $this->imageBackEnd->drawPathWithColor($internalPath, $fill->getInternalColor()); + } + + $this->imageBackEnd->pop(); + + return $modulePath; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.php new file mode 100644 index 000000000..f536e5ab3 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/DotsModule.php @@ -0,0 +1,63 @@ + 1) { + throw new InvalidArgumentException('Size must between 0 (exclusive) and 1 (inclusive)'); + } + + $this->size = $size; + } + + public function createPath(ByteMatrix $matrix) : Path + { + $width = $matrix->getWidth(); + $height = $matrix->getHeight(); + $path = new Path(); + $halfSize = $this->size / 2; + $margin = (1 - $this->size) / 2; + + for ($y = 0; $y < $height; ++$y) { + for ($x = 0; $x < $width; ++$x) { + if (! $matrix->get($x, $y)) { + continue; + } + + $pathX = $x + $margin; + $pathY = $y + $margin; + + $path = $path + ->move($pathX + $this->size, $pathY + $halfSize) + ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX + $halfSize, $pathY + $this->size) + ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX, $pathY + $halfSize) + ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX + $halfSize, $pathY) + ->ellipticArc($halfSize, $halfSize, 0, false, true, $pathX + $this->size, $pathY + $halfSize) + ->close() + ; + } + } + + return $path; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.php new file mode 100644 index 000000000..90482f21d --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/Edge.php @@ -0,0 +1,100 @@ + + */ + private $points = []; + + /** + * @var array|null + */ + private $simplifiedPoints; + + /** + * @var int + */ + private $minX = PHP_INT_MAX; + + /** + * @var int + */ + private $minY = PHP_INT_MAX; + + /** + * @var int + */ + private $maxX = -1; + + /** + * @var int + */ + private $maxY = -1; + + public function __construct(bool $positive) + { + $this->positive = $positive; + } + + public function addPoint(int $x, int $y) : void + { + $this->points[] = [$x, $y]; + $this->minX = min($this->minX, $x); + $this->minY = min($this->minY, $y); + $this->maxX = max($this->maxX, $x); + $this->maxY = max($this->maxY, $y); + } + + public function isPositive() : bool + { + return $this->positive; + } + + /** + * @return array + */ + public function getPoints() : array + { + return $this->points; + } + + public function getMaxX() : int + { + return $this->maxX; + } + + public function getSimplifiedPoints() : array + { + if (null !== $this->simplifiedPoints) { + return $this->simplifiedPoints; + } + + $points = []; + $length = count($this->points); + + for ($i = 0; $i < $length; ++$i) { + $previousPoint = $this->points[(0 === $i ? $length : $i) - 1]; + $nextPoint = $this->points[($length - 1 === $i ? -1 : $i) + 1]; + $currentPoint = $this->points[$i]; + + if (($previousPoint[0] === $currentPoint[0] && $currentPoint[0] === $nextPoint[0]) + || ($previousPoint[1] === $currentPoint[1] && $currentPoint[1] === $nextPoint[1]) + ) { + continue; + } + + $points[] = $currentPoint; + } + + return $this->simplifiedPoints = $points; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php new file mode 100644 index 000000000..eb29dc60a --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/EdgeIterator/EdgeIterator.php @@ -0,0 +1,169 @@ +bytes = iterator_to_array($matrix->getBytes()); + $this->size = count($this->bytes); + $this->width = $matrix->getWidth(); + $this->height = $matrix->getHeight(); + } + + /** + * @return Traversable + */ + public function getIterator() : Traversable + { + $originalBytes = $this->bytes; + $point = $this->findNext(0, 0); + + while (null !== $point) { + $edge = $this->findEdge($point[0], $point[1]); + $this->xorEdge($edge); + + yield $edge; + + $point = $this->findNext($point[0], $point[1]); + } + + $this->bytes = $originalBytes; + } + + /** + * @return int[]|null + */ + private function findNext(int $x, int $y) : ?array + { + $i = $this->width * $y + $x; + + while ($i < $this->size && 1 !== $this->bytes[$i]) { + ++$i; + } + + if ($i < $this->size) { + return $this->pointOf($i); + } + + return null; + } + + private function findEdge(int $x, int $y) : Edge + { + $edge = new Edge($this->isSet($x, $y)); + $startX = $x; + $startY = $y; + $dirX = 0; + $dirY = 1; + + while (true) { + $edge->addPoint($x, $y); + $x += $dirX; + $y += $dirY; + + if ($x === $startX && $y === $startY) { + break; + } + + $left = $this->isSet($x + ($dirX + $dirY - 1 ) / 2, $y + ($dirY - $dirX - 1) / 2); + $right = $this->isSet($x + ($dirX - $dirY - 1) / 2, $y + ($dirY + $dirX - 1) / 2); + + if ($right && ! $left) { + $tmp = $dirX; + $dirX = -$dirY; + $dirY = $tmp; + } elseif ($right) { + $tmp = $dirX; + $dirX = -$dirY; + $dirY = $tmp; + } elseif (! $left) { + $tmp = $dirX; + $dirX = $dirY; + $dirY = -$tmp; + } + } + + return $edge; + } + + private function xorEdge(Edge $path) : void + { + $points = $path->getPoints(); + $y1 = $points[0][1]; + $length = count($points); + $maxX = $path->getMaxX(); + + for ($i = 1; $i < $length; ++$i) { + $y = $points[$i][1]; + + if ($y === $y1) { + continue; + } + + $x = $points[$i][0]; + $minY = min($y1, $y); + + for ($j = $x; $j < $maxX; ++$j) { + $this->flip($j, $minY); + } + + $y1 = $y; + } + } + + private function isSet(int $x, int $y) : bool + { + return ( + $x >= 0 + && $x < $this->width + && $y >= 0 + && $y < $this->height + ) && 1 === $this->bytes[$this->width * $y + $x]; + } + + /** + * @return int[] + */ + private function pointOf(int $i) : array + { + $y = intdiv($i, $this->width); + return [$i - $y * $this->width, $y]; + } + + private function flip(int $x, int $y) : void + { + $this->bytes[$this->width * $y + $x] = ( + $this->isSet($x, $y) ? 0 : 1 + ); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.php new file mode 100644 index 000000000..0ccb0e0fe --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/ModuleInterface.php @@ -0,0 +1,18 @@ + 1) { + throw new InvalidArgumentException('Intensity must between 0 (exclusive) and 1 (inclusive)'); + } + + $this->intensity = $intensity / 2; + } + + public function createPath(ByteMatrix $matrix) : Path + { + $path = new Path(); + + foreach (new EdgeIterator($matrix) as $edge) { + $points = $edge->getSimplifiedPoints(); + $length = count($points); + + $currentPoint = $points[0]; + $nextPoint = $points[1]; + $horizontal = ($currentPoint[1] === $nextPoint[1]); + + if ($horizontal) { + $right = $nextPoint[0] > $currentPoint[0]; + $path = $path->move( + $currentPoint[0] + ($right ? $this->intensity : -$this->intensity), + $currentPoint[1] + ); + } else { + $up = $nextPoint[0] < $currentPoint[0]; + $path = $path->move( + $currentPoint[0], + $currentPoint[1] + ($up ? -$this->intensity : $this->intensity) + ); + } + + for ($i = 1; $i <= $length; ++$i) { + if ($i === $length) { + $previousPoint = $points[$length - 1]; + $currentPoint = $points[0]; + $nextPoint = $points[1]; + } else { + $previousPoint = $points[(0 === $i ? $length : $i) - 1]; + $currentPoint = $points[$i]; + $nextPoint = $points[($length - 1 === $i ? -1 : $i) + 1]; + } + + $horizontal = ($previousPoint[1] === $currentPoint[1]); + + if ($horizontal) { + $right = $previousPoint[0] < $currentPoint[0]; + $up = $nextPoint[1] < $currentPoint[1]; + $sweep = ($up xor $right); + + if ($this->intensity < 0.5 + || ($right && $previousPoint[0] !== $currentPoint[0] - 1) + || (! $right && $previousPoint[0] - 1 !== $currentPoint[0]) + ) { + $path = $path->line( + $currentPoint[0] + ($right ? -$this->intensity : $this->intensity), + $currentPoint[1] + ); + } + + $path = $path->ellipticArc( + $this->intensity, + $this->intensity, + 0, + false, + $sweep, + $currentPoint[0], + $currentPoint[1] + ($up ? -$this->intensity : $this->intensity) + ); + } else { + $up = $previousPoint[1] > $currentPoint[1]; + $right = $nextPoint[0] > $currentPoint[0]; + $sweep = ! ($up xor $right); + + if ($this->intensity < 0.5 + || ($up && $previousPoint[1] !== $currentPoint[1] + 1) + || (! $up && $previousPoint[0] + 1 !== $currentPoint[0]) + ) { + $path = $path->line( + $currentPoint[0], + $currentPoint[1] + ($up ? $this->intensity : -$this->intensity) + ); + } + + $path = $path->ellipticArc( + $this->intensity, + $this->intensity, + 0, + false, + $sweep, + $currentPoint[0] + ($right ? $this->intensity : -$this->intensity), + $currentPoint[1] + ); + } + } + + $path = $path->close(); + } + + return $path; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php new file mode 100644 index 000000000..9ab460766 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Module/SquareModule.php @@ -0,0 +1,47 @@ +getSimplifiedPoints(); + $length = count($points); + $path = $path->move($points[0][0], $points[0][1]); + + for ($i = 1; $i < $length; ++$i) { + $path = $path->line($points[$i][0], $points[$i][1]); + } + + $path = $path->close(); + } + + return $path; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Close.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Close.php new file mode 100644 index 000000000..b07feb02d --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Close.php @@ -0,0 +1,29 @@ +x1 = $x1; + $this->y1 = $y1; + $this->x2 = $x2; + $this->y2 = $y2; + $this->x3 = $x3; + $this->y3 = $y3; + } + + public function getX1() : float + { + return $this->x1; + } + + public function getY1() : float + { + return $this->y1; + } + + public function getX2() : float + { + return $this->x2; + } + + public function getY2() : float + { + return $this->y2; + } + + public function getX3() : float + { + return $this->x3; + } + + public function getY3() : float + { + return $this->y3; + } + + /** + * @return self + */ + public function translate(float $x, float $y) : OperationInterface + { + return new self( + $this->x1 + $x, + $this->y1 + $y, + $this->x2 + $x, + $this->y2 + $y, + $this->x3 + $x, + $this->y3 + $y + ); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.php new file mode 100644 index 000000000..9f2385abb --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/EllipticArc.php @@ -0,0 +1,278 @@ +xRadius = abs($xRadius); + $this->yRadius = abs($yRadius); + $this->xAxisAngle = $xAxisAngle % 360; + $this->largeArc = $largeArc; + $this->sweep = $sweep; + $this->x = $x; + $this->y = $y; + } + + public function getXRadius() : float + { + return $this->xRadius; + } + + public function getYRadius() : float + { + return $this->yRadius; + } + + public function getXAxisAngle() : float + { + return $this->xAxisAngle; + } + + public function isLargeArc() : bool + { + return $this->largeArc; + } + + public function isSweep() : bool + { + return $this->sweep; + } + + public function getX() : float + { + return $this->x; + } + + public function getY() : float + { + return $this->y; + } + + /** + * @return self + */ + public function translate(float $x, float $y) : OperationInterface + { + return new self( + $this->xRadius, + $this->yRadius, + $this->xAxisAngle, + $this->largeArc, + $this->sweep, + $this->x + $x, + $this->y + $y + ); + } + + /** + * Converts the elliptic arc to multiple curves. + * + * Since not all image back ends support elliptic arcs, this method allows to convert the arc into multiple curves + * resembling the same result. + * + * @see https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ + * @return array + */ + public function toCurves(float $fromX, float $fromY) : array + { + if (sqrt(($fromX - $this->x) ** 2 + ($fromY - $this->y) ** 2) < self::ZERO_TOLERANCE) { + return []; + } + + if ($this->xRadius < self::ZERO_TOLERANCE || $this->yRadius < self::ZERO_TOLERANCE) { + return [new Line($this->x, $this->y)]; + } + + return $this->createCurves($fromX, $fromY); + } + + /** + * @return Curve[] + */ + private function createCurves(float $fromX, float $fromY) : array + { + $xAngle = deg2rad($this->xAxisAngle); + list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) = + $this->calculateCenterPointParameters($fromX, $fromY, $xAngle); + + $s = $startAngle; + $e = $s + $deltaAngle; + $sign = ($e < $s) ? -1 : 1; + $remain = abs($e - $s); + $p1 = self::point($centerX, $centerY, $radiusX, $radiusY, $xAngle, $s); + $curves = []; + + while ($remain > self::ZERO_TOLERANCE) { + $step = min($remain, pi() / 2); + $signStep = $step * $sign; + $p2 = self::point($centerX, $centerY, $radiusX, $radiusY, $xAngle, $s + $signStep); + + $alphaT = tan($signStep / 2); + $alpha = sin($signStep) * (sqrt(4 + 3 * $alphaT ** 2) - 1) / 3; + $d1 = self::derivative($radiusX, $radiusY, $xAngle, $s); + $d2 = self::derivative($radiusX, $radiusY, $xAngle, $s + $signStep); + + $curves[] = new Curve( + $p1[0] + $alpha * $d1[0], + $p1[1] + $alpha * $d1[1], + $p2[0] - $alpha * $d2[0], + $p2[1] - $alpha * $d2[1], + $p2[0], + $p2[1] + ); + + $s += $signStep; + $remain -= $step; + $p1 = $p2; + } + + return $curves; + } + + /** + * @return float[] + */ + private function calculateCenterPointParameters(float $fromX, float $fromY, float $xAngle) + { + $rX = $this->xRadius; + $rY = $this->yRadius; + + // F.6.5.1 + $dx2 = ($fromX - $this->x) / 2; + $dy2 = ($fromY - $this->y) / 2; + $x1p = cos($xAngle) * $dx2 + sin($xAngle) * $dy2; + $y1p = -sin($xAngle) * $dx2 + cos($xAngle) * $dy2; + + // F.6.5.2 + $rxs = $rX ** 2; + $rys = $rY ** 2; + $x1ps = $x1p ** 2; + $y1ps = $y1p ** 2; + $cr = $x1ps / $rxs + $y1ps / $rys; + + if ($cr > 1) { + $s = sqrt($cr); + $rX *= $s; + $rY *= $s; + $rxs = $rX ** 2; + $rys = $rY ** 2; + } + + $dq = ($rxs * $y1ps + $rys * $x1ps); + $pq = ($rxs * $rys - $dq) / $dq; + $q = sqrt(max(0, $pq)); + + if ($this->largeArc === $this->sweep) { + $q = -$q; + } + + $cxp = $q * $rX * $y1p / $rY; + $cyp = -$q * $rY * $x1p / $rX; + + // F.6.5.3 + $cx = cos($xAngle) * $cxp - sin($xAngle) * $cyp + ($fromX + $this->x) / 2; + $cy = sin($xAngle) * $cxp + cos($xAngle) * $cyp + ($fromY + $this->y) / 2; + + // F.6.5.5 + $theta = self::angle(1, 0, ($x1p - $cxp) / $rX, ($y1p - $cyp) / $rY); + + // F.6.5.6 + $delta = self::angle(($x1p - $cxp) / $rX, ($y1p - $cyp) / $rY, (-$x1p - $cxp) / $rX, (-$y1p - $cyp) / $rY); + $delta = fmod($delta, pi() * 2); + + if (! $this->sweep) { + $delta -= 2 * pi(); + } + + return [$cx, $cy, $rX, $rY, $theta, $delta]; + } + + private static function angle(float $ux, float $uy, float $vx, float $vy) : float + { + // F.6.5.4 + $dot = $ux * $vx + $uy * $vy; + $length = sqrt($ux ** 2 + $uy ** 2) * sqrt($vx ** 2 + $vy ** 2); + $angle = acos(min(1, max(-1, $dot / $length))); + + if (($ux * $vy - $uy * $vx) < 0) { + return -$angle; + } + + return $angle; + } + + /** + * @return float[] + */ + private static function point( + float $centerX, + float $centerY, + float $radiusX, + float $radiusY, + float $xAngle, + float $angle + ) : array { + return [ + $centerX + $radiusX * cos($xAngle) * cos($angle) - $radiusY * sin($xAngle) * sin($angle), + $centerY + $radiusX * sin($xAngle) * cos($angle) + $radiusY * cos($xAngle) * sin($angle), + ]; + } + + /** + * @return float[] + */ + private static function derivative(float $radiusX, float $radiusY, float $xAngle, float $angle) : array + { + return [ + -$radiusX * cos($xAngle) * sin($angle) - $radiusY * sin($xAngle) * cos($angle), + -$radiusX * sin($xAngle) * sin($angle) + $radiusY * cos($xAngle) * cos($angle), + ]; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Line.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Line.php new file mode 100644 index 000000000..3149a39bc --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Line.php @@ -0,0 +1,41 @@ +x = $x; + $this->y = $y; + } + + public function getX() : float + { + return $this->x; + } + + public function getY() : float + { + return $this->y; + } + + /** + * @return self + */ + public function translate(float $x, float $y) : OperationInterface + { + return new self($this->x + $x, $this->y + $y); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Move.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Move.php new file mode 100644 index 000000000..481d0dd15 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/Move.php @@ -0,0 +1,41 @@ +x = $x; + $this->y = $y; + } + + public function getX() : float + { + return $this->x; + } + + public function getY() : float + { + return $this->y; + } + + /** + * @return self + */ + public function translate(float $x, float $y) : OperationInterface + { + return new self($this->x + $x, $this->y + $y); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.php new file mode 100644 index 000000000..a5fa0edc4 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/Path/OperationInterface.php @@ -0,0 +1,12 @@ +operations[] = new Move($x, $y); + return $path; + } + + /** + * Draws a line from the current position to another position. + */ + public function line(float $x, float $y) : self + { + $path = clone $this; + $path->operations[] = new Line($x, $y); + return $path; + } + + /** + * Draws an elliptic arc from the current position to another position. + */ + public function ellipticArc( + float $xRadius, + float $yRadius, + float $xAxisRotation, + bool $largeArc, + bool $sweep, + float $x, + float $y + ) : self { + $path = clone $this; + $path->operations[] = new EllipticArc($xRadius, $yRadius, $xAxisRotation, $largeArc, $sweep, $x, $y); + return $path; + } + + /** + * Draws a curve from the current position to another position. + */ + public function curve(float $x1, float $y1, float $x2, float $y2, float $x3, float $y3) : self + { + $path = clone $this; + $path->operations[] = new Curve($x1, $y1, $x2, $y2, $x3, $y3); + return $path; + } + + /** + * Closes a sub-path. + */ + public function close() : self + { + $path = clone $this; + $path->operations[] = Close::instance(); + return $path; + } + + /** + * Appends another path to this one. + */ + public function append(self $other) : self + { + $path = clone $this; + $path->operations = array_merge($this->operations, $other->operations); + return $path; + } + + public function translate(float $x, float $y) : self + { + $path = new self(); + + foreach ($this->operations as $operation) { + $path->operations[] = $operation->translate($x, $y); + } + + return $path; + } + + /** + * @return OperationInterface[]|Traversable + */ + public function getIterator() : Traversable + { + foreach ($this->operations as $operation) { + yield $operation; + } + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.php new file mode 100644 index 000000000..8aa76528b --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/PlainTextRenderer.php @@ -0,0 +1,86 @@ +margin = $margin; + } + + /** + * @throws InvalidArgumentException if matrix width doesn't match height + */ + public function render(QrCode $qrCode) : string + { + $matrix = $qrCode->getMatrix(); + $matrixSize = $matrix->getWidth(); + + if ($matrixSize !== $matrix->getHeight()) { + throw new InvalidArgumentException('Matrix must have the same width and height'); + } + + $rows = $matrix->getArray()->toArray(); + + if (0 !== $matrixSize % 2) { + $rows[] = array_fill(0, $matrixSize, 0); + } + + $horizontalMargin = str_repeat(self::EMPTY_BLOCK, $this->margin); + $result = str_repeat("\n", (int) ceil($this->margin / 2)); + + for ($i = 0; $i < $matrixSize; $i += 2) { + $result .= $horizontalMargin; + + $upperRow = $rows[$i]; + $lowerRow = $rows[$i + 1]; + + for ($j = 0; $j < $matrixSize; ++$j) { + $upperBit = $upperRow[$j]; + $lowerBit = $lowerRow[$j]; + + if ($upperBit) { + $result .= $lowerBit ? self::FULL_BLOCK : self::UPPER_HALF_BLOCK; + } else { + $result .= $lowerBit ? self::LOWER_HALF_BLOCK : self::EMPTY_BLOCK; + } + } + + $result .= $horizontalMargin . "\n"; + } + + $result .= str_repeat("\n", (int) ceil($this->margin / 2)); + + return $result; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererInterface.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererInterface.php new file mode 100644 index 000000000..b0aae390d --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererInterface.php @@ -0,0 +1,11 @@ +externalColor = $externalColor; + $this->internalColor = $internalColor; + } + + public static function uniform(ColorInterface $color) : self + { + return new self($color, $color); + } + + public static function inherit() : self + { + return self::$inherit ?: self::$inherit = new self(null, null); + } + + public function inheritsBothColors() : bool + { + return null === $this->externalColor && null === $this->internalColor; + } + + public function inheritsExternalColor() : bool + { + return null === $this->externalColor; + } + + public function inheritsInternalColor() : bool + { + return null === $this->internalColor; + } + + public function getExternalColor() : ColorInterface + { + if (null === $this->externalColor) { + throw new RuntimeException('External eye color inherits foreground color'); + } + + return $this->externalColor; + } + + public function getInternalColor() : ColorInterface + { + if (null === $this->internalColor) { + throw new RuntimeException('Internal eye color inherits foreground color'); + } + + return $this->internalColor; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php new file mode 100644 index 000000000..d54268e63 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Fill.php @@ -0,0 +1,168 @@ +backgroundColor = $backgroundColor; + $this->foregroundColor = $foregroundColor; + $this->foregroundGradient = $foregroundGradient; + $this->topLeftEyeFill = $topLeftEyeFill; + $this->topRightEyeFill = $topRightEyeFill; + $this->bottomLeftEyeFill = $bottomLeftEyeFill; + } + + public static function default() : self + { + return self::$default ?: self::$default = self::uniformColor(new Gray(100), new Gray(0)); + } + + public static function withForegroundColor( + ColorInterface $backgroundColor, + ColorInterface $foregroundColor, + EyeFill $topLeftEyeFill, + EyeFill $topRightEyeFill, + EyeFill $bottomLeftEyeFill + ) : self { + return new self( + $backgroundColor, + $foregroundColor, + null, + $topLeftEyeFill, + $topRightEyeFill, + $bottomLeftEyeFill + ); + } + + public static function withForegroundGradient( + ColorInterface $backgroundColor, + Gradient $foregroundGradient, + EyeFill $topLeftEyeFill, + EyeFill $topRightEyeFill, + EyeFill $bottomLeftEyeFill + ) : self { + return new self( + $backgroundColor, + null, + $foregroundGradient, + $topLeftEyeFill, + $topRightEyeFill, + $bottomLeftEyeFill + ); + } + + public static function uniformColor(ColorInterface $backgroundColor, ColorInterface $foregroundColor) : self + { + return new self( + $backgroundColor, + $foregroundColor, + null, + EyeFill::inherit(), + EyeFill::inherit(), + EyeFill::inherit() + ); + } + + public static function uniformGradient(ColorInterface $backgroundColor, Gradient $foregroundGradient) : self + { + return new self( + $backgroundColor, + null, + $foregroundGradient, + EyeFill::inherit(), + EyeFill::inherit(), + EyeFill::inherit() + ); + } + + public function hasGradientFill() : bool + { + return null !== $this->foregroundGradient; + } + + public function getBackgroundColor() : ColorInterface + { + return $this->backgroundColor; + } + + public function getForegroundColor() : ColorInterface + { + if (null === $this->foregroundColor) { + throw new RuntimeException('Fill uses a gradient, thus no foreground color is available'); + } + + return $this->foregroundColor; + } + + public function getForegroundGradient() : Gradient + { + if (null === $this->foregroundGradient) { + throw new RuntimeException('Fill uses a single color, thus no foreground gradient is available'); + } + + return $this->foregroundGradient; + } + + public function getTopLeftEyeFill() : EyeFill + { + return $this->topLeftEyeFill; + } + + public function getTopRightEyeFill() : EyeFill + { + return $this->topRightEyeFill; + } + + public function getBottomLeftEyeFill() : EyeFill + { + return $this->bottomLeftEyeFill; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.php new file mode 100644 index 000000000..3813dfd17 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/Gradient.php @@ -0,0 +1,46 @@ +startColor = $startColor; + $this->endColor = $endColor; + $this->type = $type; + } + + public function getStartColor() : ColorInterface + { + return $this->startColor; + } + + public function getEndColor() : ColorInterface + { + return $this->endColor; + } + + public function getType() : GradientType + { + return $this->type; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php new file mode 100644 index 000000000..c1ca75471 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Renderer/RendererStyle/GradientType.php @@ -0,0 +1,22 @@ +margin = $margin; + $this->size = $size; + $this->module = $module ?: SquareModule::instance(); + $this->eye = $eye ?: new ModuleEye($this->module); + $this->fill = $fill ?: Fill::default(); + } + + public function withSize(int $size) : self + { + $style = clone $this; + $style->size = $size; + return $style; + } + + public function withMargin(int $margin) : self + { + $style = clone $this; + $style->margin = $margin; + return $style; + } + + public function getSize() : int + { + return $this->size; + } + + public function getMargin() : int + { + return $this->margin; + } + + public function getModule() : ModuleInterface + { + return $this->module; + } + + public function getEye() : EyeInterface + { + return $this->eye; + } + + public function getFill() : Fill + { + return $this->fill; + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Writer.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Writer.php new file mode 100644 index 000000000..d5bdc5c35 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/src/Writer.php @@ -0,0 +1,71 @@ +renderer = $renderer; + } + + /** + * Writes QR code and returns it as string. + * + * Content is a string which *should* be encoded in UTF-8, in case there are + * non ASCII-characters present. + * + * @throws InvalidArgumentException if the content is empty + */ + public function writeString( + string $content, + string $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, + ?ErrorCorrectionLevel $ecLevel = null, + ?Version $forcedVersion = null + ) : string { + if (strlen($content) === 0) { + throw new InvalidArgumentException('Found empty contents'); + } + + if (null === $ecLevel) { + $ecLevel = ErrorCorrectionLevel::L(); + } + + return $this->renderer->render(Encoder::encode($content, $ecLevel, $encoding, $forcedVersion)); + } + + /** + * Writes QR code to a file. + * + * @see Writer::writeString() + */ + public function writeFile( + string $content, + string $filename, + string $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, + ?ErrorCorrectionLevel $ecLevel = null, + ?Version $forcedVersion = null + ) : void { + file_put_contents($filename, $this->writeString($content, $encoding, $ecLevel, $forcedVersion)); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitArrayTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitArrayTest.php new file mode 100644 index 000000000..add798b1b --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitArrayTest.php @@ -0,0 +1,222 @@ +assertFalse($array->get($i)); + $array->set($i); + $this->assertTrue($array->get($i)); + } + } + + public function testGetNextSet1() : void + { + $array = new BitArray(32); + + for ($i = 0; $i < $array->getSize(); ++$i) { + if ($this->getPhpUnitMajorVersion() === 7) { + $this->assertEquals($i, 32, '', $array->getNextSet($i)); + } else { + $this->assertEqualsWithDelta($i, 32, $array->getNextSet($i)); + } + } + + $array = new BitArray(33); + + for ($i = 0; $i < $array->getSize(); ++$i) { + if ($this->getPhpUnitMajorVersion() === 7) { + $this->assertEquals($i, 33, '', $array->getNextSet($i)); + } else { + $this->assertEqualsWithDelta($i, 33, $array->getNextSet($i)); + } + } + } + + public function testGetNextSet2() : void + { + $array = new BitArray(33); + + for ($i = 0; $i < $array->getSize(); ++$i) { + if ($this->getPhpUnitMajorVersion() === 7) { + $this->assertEquals($i, $i <= 31 ? 31 : 33, '', $array->getNextSet($i)); + } else { + $this->assertEqualsWithDelta($i, $i <= 31 ? 31 : 33, $array->getNextSet($i)); + } + } + + $array = new BitArray(33); + + for ($i = 0; $i < $array->getSize(); ++$i) { + if ($this->getPhpUnitMajorVersion() === 7) { + $this->assertEquals($i, 32, '', $array->getNextSet($i)); + } else { + $this->assertEqualsWithDelta($i, 32, $array->getNextSet($i)); + } + } + } + + public function testGetNextSet3() : void + { + $array = new BitArray(63); + $array->set(31); + $array->set(32); + + for ($i = 0; $i < $array->getSize(); ++$i) { + if ($i <= 31) { + $expected = 31; + } elseif ($i <= 32) { + $expected = 32; + } else { + $expected = 63; + } + + if ($this->getPhpUnitMajorVersion() === 7) { + $this->assertEquals($i, $expected, '', $array->getNextSet($i)); + } else { + $this->assertEqualsWithDelta($i, $expected, $array->getNextSet($i)); + } + } + } + + public function testGetNextSet4() : void + { + $array = new BitArray(63); + $array->set(33); + $array->set(40); + + for ($i = 0; $i < $array->getSize(); ++$i) { + if ($i <= 33) { + $expected = 33; + } elseif ($i <= 40) { + $expected = 40; + } else { + $expected = 63; + } + + if ($this->getPhpUnitMajorVersion() === 7) { + $this->assertEquals($i, $expected, '', $array->getNextSet($i)); + } else { + $this->assertEqualsWithDelta($i, $expected, $array->getNextSet($i)); + } + } + } + + public function testGetNextSet5() : void + { + mt_srand(0xdeadbeef, MT_RAND_PHP); + + for ($i = 0; $i < 10; ++$i) { + $array = new BitArray(mt_rand(1, 100)); + $numSet = mt_rand(0, 19); + + for ($j = 0; $j < $numSet; ++$j) { + $array->set(mt_rand(0, $array->getSize() - 1)); + } + + $numQueries = mt_rand(0, 19); + + for ($j = 0; $j < $numQueries; ++$j) { + $query = mt_rand(0, $array->getSize() - 1); + $expected = $query; + + while ($expected < $array->getSize() && ! $array->get($expected)) { + ++$expected; + } + + $actual = $array->getNextSet($query); + + if ($actual !== $expected) { + $array->getNextSet($query); + } + + $this->assertEquals($expected, $actual); + } + } + } + + public function testSetBulk() : void + { + $array = new BitArray(64); + $array->setBulk(32, 0xFFFF0000); + + for ($i = 0; $i < 48; ++$i) { + $this->assertFalse($array->get($i)); + } + + for ($i = 48; $i < 64; ++$i) { + $this->assertTrue($array->get($i)); + } + } + + public function testClear() : void + { + $array = new BitArray(32); + + for ($i = 0; $i < 32; ++$i) { + $array->set($i); + } + + $array->clear(); + + for ($i = 0; $i < 32; ++$i) { + $this->assertFalse($array->get($i)); + } + } + + public function testGetArray() : void + { + $array = new BitArray(64); + $array->set(0); + $array->set(63); + + $ints = $array->getBitArray(); + + $this->assertSame(1, $ints[0]); + $this->assertSame(0x80000000, $ints[1]); + } + + public function testIsRange() : void + { + $array = new BitArray(64); + $this->assertTrue($array->isRange(0, 64, false)); + $this->assertFalse($array->isRange(0, 64, true)); + + $array->set(32); + $this->assertTrue($array->isRange(32, 33, true)); + + $array->set(31); + $this->assertTrue($array->isRange(31, 33, true)); + + $array->set(34); + $this->assertFalse($array->isRange(31, 35, true)); + + for ($i = 0; $i < 31; ++$i) { + $array->set($i); + } + + $this->assertTrue($array->isRange(0, 33, true)); + + for ($i = 33; $i < 64; ++$i) { + $array->set($i); + } + + $this->assertTrue($array->isRange(0, 64, true)); + $this->assertFalse($array->isRange(0, 64, false)); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitMatrixTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitMatrixTest.php new file mode 100644 index 000000000..8ad86d4c2 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitMatrixTest.php @@ -0,0 +1,115 @@ +assertEquals(33, $matrix->getHeight()); + + for ($y = 0; $y < 33; ++$y) { + for ($x = 0; $x < 33; ++$x) { + if ($y * $x % 3 === 0) { + $matrix->set($x, $y); + } + } + } + + for ($y = 0; $y < 33; $y++) { + for ($x = 0; $x < 33; ++$x) { + $this->assertSame(0 === $x * $y % 3, $matrix->get($x, $y)); + } + } + } + + public function testSetRegion() : void + { + $matrix = new BitMatrix(5); + $matrix->setRegion(1, 1, 3, 3); + + for ($y = 0; $y < 5; ++$y) { + for ($x = 0; $x < 5; ++$x) { + $this->assertSame($y >= 1 && $y <= 3 && $x >= 1 && $x <= 3, $matrix->get($x, $y)); + } + } + } + + public function testRectangularMatrix() : void + { + $matrix = new BitMatrix(75, 20); + $this->assertSame(75, $matrix->getWidth()); + $this->assertSame(20, $matrix->getHeight()); + + $matrix->set(10, 0); + $matrix->set(11, 1); + $matrix->set(50, 2); + $matrix->set(51, 3); + $matrix->flip(74, 4); + $matrix->flip(0, 5); + + $this->assertTrue($matrix->get(10, 0)); + $this->assertTrue($matrix->get(11, 1)); + $this->assertTrue($matrix->get(50, 2)); + $this->assertTrue($matrix->get(51, 3)); + $this->assertTrue($matrix->get(74, 4)); + $this->assertTrue($matrix->get(0, 5)); + + $matrix->flip(50, 2); + $matrix->flip(51, 3); + + $this->assertFalse($matrix->get(50, 2)); + $this->assertFalse($matrix->get(51, 3)); + } + + public function testRectangularSetRegion() : void + { + $matrix = new BitMatrix(320, 240); + $this->assertSame(320, $matrix->getWidth()); + $this->assertSame(240, $matrix->getHeight()); + + $matrix->setRegion(105, 22, 80, 12); + + for ($y = 0; $y < 240; ++$y) { + for ($x = 0; $x < 320; ++$x) { + $this->assertEquals($y >= 22 && $y < 34 && $x >= 105 && $x < 185, $matrix->get($x, $y)); + } + } + } + + public function testGetRow() : void + { + $matrix = new BitMatrix(102, 5); + + for ($x = 0; $x < 102; ++$x) { + if (0 === ($x & 3)) { + $matrix->set($x, 2); + } + } + + $array1 = $matrix->getRow(2, null); + $this->assertSame(102, $array1->getSize()); + + $array2 = new BitArray(60); + $array2 = $matrix->getRow(2, $array2); + $this->assertSame(102, $array2->getSize()); + + $array3 = new BitArray(200); + $array3 = $matrix->getRow(2, $array3); + $this->assertSame(200, $array3->getSize()); + + for ($x = 0; $x < 102; ++$x) { + $on = (0 === ($x & 3)); + + $this->assertSame($on, $array1->get($x)); + $this->assertSame($on, $array2->get($x)); + $this->assertSame($on, $array3->get($x)); + } + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitUtilsTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitUtilsTest.php new file mode 100644 index 000000000..2904d312f --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/BitUtilsTest.php @@ -0,0 +1,25 @@ +assertSame(1, BitUtils::unsignedRightShift(1, 0)); + $this->assertSame(1, BitUtils::unsignedRightShift(10, 3)); + $this->assertSame(536870910, BitUtils::unsignedRightShift(-10, 3)); + } + + public function testNumberOfTrailingZeros() : void + { + $this->assertSame(32, BitUtils::numberOfTrailingZeros(0)); + $this->assertSame(1, BitUtils::numberOfTrailingZeros(10)); + $this->assertSame(0, BitUtils::numberOfTrailingZeros(15)); + $this->assertSame(2, BitUtils::numberOfTrailingZeros(20)); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ErrorCorrectionLevelTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ErrorCorrectionLevelTest.php new file mode 100644 index 000000000..369b5d917 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ErrorCorrectionLevelTest.php @@ -0,0 +1,25 @@ +assertSame(0x0, ErrorCorrectionLevel::M()->getBits()); + $this->assertSame(0x1, ErrorCorrectionLevel::L()->getBits()); + $this->assertSame(0x2, ErrorCorrectionLevel::H()->getBits()); + $this->assertSame(0x3, ErrorCorrectionLevel::Q()->getBits()); + } + + public function testInvalidErrorCorrectionLevelThrowsException() : void + { + $this->expectException(OutOfBoundsException::class); + ErrorCorrectionLevel::forBits(4); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/FormatInformationTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/FormatInformationTest.php new file mode 100644 index 000000000..39534a24a --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/FormatInformationTest.php @@ -0,0 +1,94 @@ +assertSame(0, FormatInformation::numBitsDiffering(1, 1)); + $this->assertSame(1, FormatInformation::numBitsDiffering(0, 2)); + $this->assertSame(2, FormatInformation::numBitsDiffering(1, 2)); + $this->assertEquals(32, FormatInformation::numBitsDiffering(-1, 0)); + } + + public function testDecode() : void + { + $expected = FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO, + self::MASKED_TEST_FORMAT_INFO + ); + + $this->assertNotNull($expected); + $this->assertSame(7, $expected->getDataMask()); + $this->assertSame(ErrorCorrectionLevel::Q(), $expected->getErrorCorrectionLevel()); + + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + self::UNMAKSED_TEST_FORMAT_INFO, + self::MASKED_TEST_FORMAT_INFO + ) + ); + } + + public function testDecodeWithBitDifference() : void + { + $expected = FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO, + self::MASKED_TEST_FORMAT_INFO + ); + + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO ^ 0x1, + self::MASKED_TEST_FORMAT_INFO ^ 0x1 + ) + ); + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO ^ 0x3, + self::MASKED_TEST_FORMAT_INFO ^ 0x3 + ) + ); + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO ^ 0x7, + self::MASKED_TEST_FORMAT_INFO ^ 0x7 + ) + ); + $this->assertNull( + FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO ^ 0xf, + self::MASKED_TEST_FORMAT_INFO ^ 0xf + ) + ); + } + + public function testDecodeWithMisRead() : void + { + $expected = FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO, + self::MASKED_TEST_FORMAT_INFO + ); + + $this->assertEquals( + $expected, + FormatInformation::decodeFormatInformation( + self::MASKED_TEST_FORMAT_INFO ^ 0x3, + self::MASKED_TEST_FORMAT_INFO ^ 0xf + ) + ); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ModeTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ModeTest.php new file mode 100644 index 000000000..51fcb3eb4 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ModeTest.php @@ -0,0 +1,19 @@ +assertSame(0x0, Mode::TERMINATOR()->getBits()); + $this->assertSame(0x1, Mode::NUMERIC()->getBits()); + $this->assertSame(0x2, Mode::ALPHANUMERIC()->getBits()); + $this->assertSame(0x4, Mode::BYTE()->getBits()); + $this->assertSame(0x8, Mode::KANJI()->getBits()); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ReedSolomonCodecTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ReedSolomonCodecTest.php new file mode 100644 index 000000000..47975b523 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/ReedSolomonCodecTest.php @@ -0,0 +1,96 @@ +encode($block, $parity); + + // Copy parity into test blocks + for ($i = 0; $i < $numRoots; ++$i) { + $block[$i + $dataSize] = $parity[$i]; + $tBlock[$i + $dataSize] = $parity[$i]; + } + + // Seed with errors + for ($i = 0; $i < $errors; ++$i) { + $errorValue = mt_rand(1, $blockSize); + + do { + $errorLocation = mt_rand(0, $blockSize); + } while (0 !== $errorLocations[$errorLocation]); + + $errorLocations[$errorLocation] = 1; + + if (mt_rand(0, 1)) { + $erasures[] = $errorLocation; + } + + $tBlock[$errorLocation] ^= $errorValue; + } + + $erasures = SplFixedArray::fromArray($erasures, false); + + // Decode the errored block + $foundErrors = $codec->decode($tBlock, $erasures); + + if ($errors > 0 && null === $foundErrors) { + $this->assertSame($block, $tBlock, 'Decoder failed to correct errors'); + } + + $this->assertSame($errors, $foundErrors, 'Found errors do not equal expected errors'); + + for ($i = 0; $i < $foundErrors; ++$i) { + if (0 === $errorLocations[$erasures[$i]]) { + $this->fail(sprintf('Decoder indicates error in location %d without error', $erasures[$i])); + } + } + + $this->assertEquals($block, $tBlock, 'Decoder did not correct errors'); + } + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/VersionTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/VersionTest.php new file mode 100644 index 000000000..f6f038ba0 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Common/VersionTest.php @@ -0,0 +1,78 @@ +assertNotNull($version); + $this->assertEquals($versionNumber, $version->getVersionNumber()); + $this->assertNotNull($version->getAlignmentPatternCenters()); + + if ($versionNumber > 1) { + $this->assertTrue(count($version->getAlignmentPatternCenters()) > 0); + } + + $this->assertEquals($dimension, $version->getDimensionForVersion()); + $this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::H())); + $this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::L())); + $this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::M())); + $this->assertNotNull($version->getEcBlocksForLevel(ErrorCorrectionLevel::Q())); + $this->assertNotNull($version->buildFunctionPattern()); + } + + /** + * @dataProvider versions + */ + public function testGetProvisionalVersionForDimension(int $versionNumber, int $dimension) : void + { + $this->assertSame( + $versionNumber, + Version::getProvisionalVersionForDimension($dimension)->getVersionNumber() + ); + } + + /** + * @dataProvider decodeInformation + */ + public function testDecodeVersionInformation(int $expectedVersion, int $mask) : void + { + $version = Version::decodeVersionInformation($mask); + $this->assertNotNull($version); + $this->assertSame($expectedVersion, $version->getVersionNumber()); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/EncoderTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/EncoderTest.php new file mode 100644 index 000000000..9baa66b16 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/EncoderTest.php @@ -0,0 +1,487 @@ +getMethods(ReflectionMethod::IS_STATIC) as $method) { + $method->setAccessible(true); + $this->methods[$method->getName()] = $method; + } + } + + public function testGetAlphanumericCode() : void + { + // The first ten code points are numbers. + for ($i = 0; $i < 10; ++$i) { + $this->assertSame($i, $this->methods['getAlphanumericCode']->invoke(null, ord('0') + $i)); + } + + // The next 26 code points are capital alphabet letters. + for ($i = 10; $i < 36; ++$i) { + // The first ten code points are numbers + $this->assertSame($i, $this->methods['getAlphanumericCode']->invoke(null, ord('A') + $i - 10)); + } + + // Others are symbol letters. + $this->assertSame(36, $this->methods['getAlphanumericCode']->invoke(null, ord(' '))); + $this->assertSame(37, $this->methods['getAlphanumericCode']->invoke(null, ord('$'))); + $this->assertSame(38, $this->methods['getAlphanumericCode']->invoke(null, ord('%'))); + $this->assertSame(39, $this->methods['getAlphanumericCode']->invoke(null, ord('*'))); + $this->assertSame(40, $this->methods['getAlphanumericCode']->invoke(null, ord('+'))); + $this->assertSame(41, $this->methods['getAlphanumericCode']->invoke(null, ord('-'))); + $this->assertSame(42, $this->methods['getAlphanumericCode']->invoke(null, ord('.'))); + $this->assertSame(43, $this->methods['getAlphanumericCode']->invoke(null, ord('/'))); + $this->assertSame(44, $this->methods['getAlphanumericCode']->invoke(null, ord(':'))); + + // Should return -1 for other letters. + $this->assertSame(-1, $this->methods['getAlphanumericCode']->invoke(null, ord('a'))); + $this->assertSame(-1, $this->methods['getAlphanumericCode']->invoke(null, ord('#'))); + $this->assertSame(-1, $this->methods['getAlphanumericCode']->invoke(null, ord("\0"))); + } + + public function testChooseMode() : void + { + // Numeric mode + $this->assertSame(Mode::NUMERIC(), $this->methods['chooseMode']->invoke(null, '0')); + $this->assertSame(Mode::NUMERIC(), $this->methods['chooseMode']->invoke(null, '0123456789')); + + // Alphanumeric mode + $this->assertSame(Mode::ALPHANUMERIC(), $this->methods['chooseMode']->invoke(null, 'A')); + $this->assertSame( + Mode::ALPHANUMERIC(), + $this->methods['chooseMode']->invoke(null, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:') + ); + + // 8-bit byte mode + $this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, 'a')); + $this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, '#')); + $this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, '')); + + // AIUE in Hiragana in SHIFT-JIS + $this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, "\x8\xa\x8\xa\x8\xa\x8\xa6")); + + // Nihon in Kanji in SHIFT-JIS + $this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, "\x9\xf\x9\x7b")); + + // Sou-Utso-Byou in Kanji in SHIFT-JIS + $this->assertSame(Mode::BYTE(), $this->methods['chooseMode']->invoke(null, "\xe\x4\x9\x5\x9\x61")); + } + + public function testEncode() : void + { + $qrCode = Encoder::encode('ABCDEF', ErrorCorrectionLevel::H()); + $expected = "<<\n" + . " mode: ALPHANUMERIC\n" + . " ecLevel: H\n" + . " version: 1\n" + . " maskPattern: 0\n" + . " matrix:\n" + . " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n" + . " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n" + . " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n" + . " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n" + . " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n" + . " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n" + . " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n" + . " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n" + . " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n" + . ">>\n"; + + $this->assertSame($expected, (string) $qrCode); + } + + public function testSimpleUtf8Eci() : void + { + $qrCode = Encoder::encode('hello', ErrorCorrectionLevel::H(), 'utf-8'); + $expected = "<<\n" + . " mode: BYTE\n" + . " ecLevel: H\n" + . " version: 1\n" + . " maskPattern: 3\n" + . " matrix:\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n" + . " 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n" + . " 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n" + . " 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n" + . " 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n" + . " 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n" + . " 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n" + . " 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n" + . ">>\n"; + + $this->assertSame($expected, (string) $qrCode); + } + + public function testAppendModeInfo() : void + { + $bits = new BitArray(); + $this->methods['appendModeInfo']->invoke(null, Mode::NUMERIC(), $bits); + $this->assertSame(' ...X', (string) $bits); + } + + public function testAppendLengthInfo() : void + { + // 1 letter (1/1), 10 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 1, + Version::getVersionForNumber(1), + Mode::NUMERIC(), + $bits + ); + $this->assertSame(' ........ .X', (string) $bits); + + // 2 letters (2/1), 11 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 2, + Version::getVersionForNumber(10), + Mode::ALPHANUMERIC(), + $bits + ); + $this->assertSame(' ........ .X.', (string) $bits); + + // 255 letters (255/1), 16 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 255, + Version::getVersionForNumber(27), + Mode::BYTE(), + $bits + ); + $this->assertSame(' ........ XXXXXXXX', (string) $bits); + + // 512 letters (1024/2), 12 bits. + $bits = new BitArray(); + $this->methods['appendLengthInfo']->invoke( + null, + 512, + Version::getVersionForNumber(40), + Mode::KANJI(), + $bits + ); + $this->assertSame(' ..X..... ....', (string) $bits); + } + + public function testAppendBytes() : void + { + // Should use appendNumericBytes. + // 1 = 01 = 0001 in 4 bits. + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + '1', + Mode::NUMERIC(), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertSame(' ...X', (string) $bits); + + // Should use appendAlphaNumericBytes. + // A = 10 = 0xa = 001010 in 6 bits. + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + 'A', + Mode::ALPHANUMERIC(), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertSame(' ..X.X.', (string) $bits); + + // Should use append8BitBytes. + // 0x61, 0x62, 0x63 + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + 'abc', + Mode::BYTE(), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertSame(' .XX....X .XX...X. .XX...XX', (string) $bits); + + // Should use appendKanjiBytes. + // 0x93, 0x5f + $bits = new BitArray(); + $this->methods['appendBytes']->invoke( + null, + "\x93\x5f", + Mode::KANJI(), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + $this->assertSame(' .XX.XX.. XXXXX', (string) $bits); + + // Lower letters such as 'a' cannot be encoded in alphanumeric mode. + $this->expectException(WriterException::class); + $this->methods['appendBytes']->invoke( + null, + 'a', + Mode::ALPHANUMERIC(), + $bits, + Encoder::DEFAULT_BYTE_MODE_ECODING + ); + } + + public function testTerminateBits() : void + { + $bits = new BitArray(); + $this->methods['terminateBits']->invoke(null, 0, $bits); + $this->assertSame('', (string) $bits); + + $bits = new BitArray(); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertSame(' ........', (string) $bits); + + $bits = new BitArray(); + $bits->appendBits(0, 3); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertSame(' ........', (string) $bits); + + $bits = new BitArray(); + $bits->appendBits(0, 5); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertSame(' ........', (string) $bits); + + $bits = new BitArray(); + $bits->appendBits(0, 8); + $this->methods['terminateBits']->invoke(null, 1, $bits); + $this->assertSame(' ........', (string) $bits); + + $bits = new BitArray(); + $this->methods['terminateBits']->invoke(null, 2, $bits); + $this->assertSame(' ........ XXX.XX..', (string) $bits); + + $bits = new BitArray(); + $bits->appendBits(0, 1); + $this->methods['terminateBits']->invoke(null, 3, $bits); + $this->assertSame(' ........ XXX.XX.. ...X...X', (string) $bits); + } + + public function testGetNumDataBytesAndNumEcBytesForBlockId() : void + { + // Version 1-H. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 26, 9, 1, 0); + $this->assertSame(9, $numDataBytes); + $this->assertSame(17, $numEcBytes); + + // Version 3-H. 2 blocks. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 70, 26, 2, 0); + $this->assertSame(13, $numDataBytes); + $this->assertSame(22, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 70, 26, 2, 1); + $this->assertSame(13, $numDataBytes); + $this->assertSame(22, $numEcBytes); + + // Version 7-H. (4 + 1) blocks. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 196, 66, 5, 0); + $this->assertSame(13, $numDataBytes); + $this->assertSame(26, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 196, 66, 5, 4); + $this->assertSame(14, $numDataBytes); + $this->assertSame(26, $numEcBytes); + + // Version 40-H. (20 + 61) blocks. + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 3706, 1276, 81, 0); + $this->assertSame(15, $numDataBytes); + $this->assertSame(30, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 3706, 1276, 81, 20); + $this->assertSame(16, $numDataBytes); + $this->assertSame(30, $numEcBytes); + list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId'] + ->invoke(null, 3706, 1276, 81, 80); + $this->assertSame(16, $numDataBytes); + $this->assertSame(30, $numEcBytes); + } + + public function testInterleaveWithEcBytes() : void + { + $dataBytes = SplFixedArray::fromArray([32, 65, 205, 69, 41, 220, 46, 128, 236], false); + $in = new BitArray(); + + foreach ($dataBytes as $dataByte) { + $in->appendBits($dataByte, 8); + } + + $outBits = $this->methods['interleaveWithEcBytes']->invoke(null, $in, 26, 9, 1); + $expected = SplFixedArray::fromArray([ + // Data bytes. + 32, 65, 205, 69, 41, 220, 46, 128, 236, + // Error correction bytes. + 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61, + ], false); + + $out = $outBits->toBytes(0, count($expected)); + + $this->assertEquals($expected, $out); + } + + public function testAppendNumericBytes() : void + { + // 1 = 01 = 0001 in 4 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '1', $bits); + $this->assertSame(' ...X', (string) $bits); + + // 12 = 0xc = 0001100 in 7 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '12', $bits); + $this->assertSame(' ...XX..', (string) $bits); + + // 123 = 0x7b = 0001111011 in 10 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '123', $bits); + $this->assertSame(' ...XXXX. XX', (string) $bits); + + // 1234 = "123" + "4" = 0001111011 + 0100 in 14 bits. + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '1234', $bits); + $this->assertSame(' ...XXXX. XX.X..', (string) $bits); + + // Empty + $bits = new BitArray(); + $this->methods['appendNumericBytes']->invoke(null, '', $bits); + $this->assertSame('', (string) $bits); + } + + public function testAppendAlphanumericBytes() : void + { + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'A', $bits); + $this->assertSame(' ..X.X.', (string) $bits); + + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'AB', $bits); + $this->assertSame(' ..XXX..X X.X', (string) $bits); + + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'ABC', $bits); + $this->assertSame(' ..XXX..X X.X..XX. .', (string) $bits); + + // Empty + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, '', $bits); + $this->assertSame('', (string) $bits); + + // Invalid data + $this->expectException(WriterException::class); + $bits = new BitArray(); + $this->methods['appendAlphanumericBytes']->invoke(null, 'abc', $bits); + } + + public function testAppend8BitBytes() : void + { + // 0x61, 0x62, 0x63 + $bits = new BitArray(); + $this->methods['append8BitBytes']->invoke(null, 'abc', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING); + $this->assertSame(' .XX....X .XX...X. .XX...XX', (string) $bits); + + // Empty + $bits = new BitArray(); + $this->methods['append8BitBytes']->invoke(null, '', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING); + $this->assertSame('', (string) $bits); + } + + public function testAppendKanjiBytes() : void + { + // Numbers are from page 21 of JISX0510:2004 + $bits = new BitArray(); + $this->methods['appendKanjiBytes']->invoke(null, "\x93\x5f", $bits); + $this->assertSame(' .XX.XX.. XXXXX', (string) $bits); + + $this->methods['appendKanjiBytes']->invoke(null, "\xe4\xaa", $bits); + $this->assertSame(' .XX.XX.. XXXXXXX. X.X.X.X. X.', (string) $bits); + } + + public function testGenerateEcBytes() : void + { + // Numbers are from http://www.swetake.com/qr/qr3.html and + // http://www.swetake.com/qr/qr9.html + $dataBytes = SplFixedArray::fromArray([32, 65, 205, 69, 41, 220, 46, 128, 236], false); + $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17); + $expected = SplFixedArray::fromArray( + [42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61], + false + ); + $this->assertEquals($expected, $ecBytes); + + $dataBytes = SplFixedArray::fromArray( + [67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214], + false + ); + $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 18); + $expected = SplFixedArray::fromArray( + [175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187], + false + ); + $this->assertEquals($expected, $ecBytes); + + // High-order zero coefficient case. + $dataBytes = SplFixedArray::fromArray([32, 49, 205, 69, 42, 20, 0, 236, 17], false); + $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17); + $expected = SplFixedArray::fromArray( + [0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213], + false + ); + $this->assertEquals($expected, $ecBytes); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/MaskUtilTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/MaskUtilTest.php new file mode 100644 index 000000000..46670fce7 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/MaskUtilTest.php @@ -0,0 +1,251 @@ +assertSame( + 1 === $expected[$y][$x], + MaskUtil::getDataMaskBit($maskPattern, $x, $y) + ); + } + } + } + + public function testApplyMaskPenaltyRule1() : void + { + $matrix = new ByteMatrix(4, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(3, 0, 0); + + $this->assertSame(0, MaskUtil::applyMaskPenaltyRule1($matrix)); + + // Horizontal + $matrix = new ByteMatrix(6, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(3, 0, 0); + $matrix->set(4, 0, 0); + $matrix->set(5, 0, 1); + $this->assertSame(3, MaskUtil::applyMaskPenaltyRule1($matrix)); + $matrix->set(5, 0, 0); + $this->assertSame(4, MaskUtil::applyMaskPenaltyRule1($matrix)); + + // Vertical + $matrix = new ByteMatrix(1, 6); + $matrix->set(0, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(0, 2, 0); + $matrix->set(0, 3, 0); + $matrix->set(0, 4, 0); + $matrix->set(0, 5, 1); + $this->assertSame(3, MaskUtil::applyMaskPenaltyRule1($matrix)); + $matrix->set(0, 5, 0); + $this->assertSame(4, MaskUtil::applyMaskPenaltyRule1($matrix)); + } + + public function testApplyMaskPenaltyRule2() : void + { + $matrix = new ByteMatrix(1, 1); + $matrix->set(0, 0, 0); + $this->assertSame(0, MaskUtil::applyMaskPenaltyRule2($matrix)); + + $matrix = new ByteMatrix(2, 2); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(1, 1, 1); + $this->assertSame(0, MaskUtil::applyMaskPenaltyRule2($matrix)); + + $matrix = new ByteMatrix(2, 2); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(1, 1, 0); + $this->assertSame(3, MaskUtil::applyMaskPenaltyRule2($matrix)); + + $matrix = new ByteMatrix(3, 3); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(1, 1, 0); + $matrix->set(2, 1, 0); + $matrix->set(0, 2, 0); + $matrix->set(1, 2, 0); + $matrix->set(2, 2, 0); + $this->assertSame(3 * 4, MaskUtil::applyMaskPenaltyRule2($matrix)); + } + + public function testApplyMaskPenalty3() : void + { + // Horizontal 00001011101 + $matrix = new ByteMatrix(11, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 0); + $matrix->set(3, 0, 0); + $matrix->set(4, 0, 1); + $matrix->set(5, 0, 0); + $matrix->set(6, 0, 1); + $matrix->set(7, 0, 1); + $matrix->set(8, 0, 1); + $matrix->set(9, 0, 0); + $matrix->set(10, 0, 1); + $this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + + // Horizontal 10111010000 + $matrix = new ByteMatrix(11, 1); + $matrix->set(0, 0, 1); + $matrix->set(1, 0, 0); + $matrix->set(2, 0, 1); + $matrix->set(3, 0, 1); + $matrix->set(4, 0, 1); + $matrix->set(5, 0, 0); + $matrix->set(6, 0, 1); + $matrix->set(7, 0, 0); + $matrix->set(8, 0, 0); + $matrix->set(9, 0, 0); + $matrix->set(10, 0, 0); + $this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + + // Vertical 00001011101 + $matrix = new ByteMatrix(1, 11); + $matrix->set(0, 0, 0); + $matrix->set(0, 1, 0); + $matrix->set(0, 2, 0); + $matrix->set(0, 3, 0); + $matrix->set(0, 4, 1); + $matrix->set(0, 5, 0); + $matrix->set(0, 6, 1); + $matrix->set(0, 7, 1); + $matrix->set(0, 8, 1); + $matrix->set(0, 9, 0); + $matrix->set(0, 10, 1); + $this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + + // Vertical 10111010000 + $matrix = new ByteMatrix(1, 11); + $matrix->set(0, 0, 1); + $matrix->set(0, 1, 0); + $matrix->set(0, 2, 1); + $matrix->set(0, 3, 1); + $matrix->set(0, 4, 1); + $matrix->set(0, 5, 0); + $matrix->set(0, 6, 1); + $matrix->set(0, 7, 0); + $matrix->set(0, 8, 0); + $matrix->set(0, 9, 0); + $matrix->set(0, 10, 0); + $this->assertSame(40, MaskUtil::applyMaskPenaltyRule3($matrix)); + } + + public function testApplyMaskPenaltyRule4() : void + { + // Dark cell ratio = 0% + $matrix = new ByteMatrix(1, 1); + $matrix->set(0, 0, 0); + $this->assertSame(100, MaskUtil::applyMaskPenaltyRule4($matrix)); + + // Dark cell ratio = 5% + $matrix = new ByteMatrix(2, 1); + $matrix->set(0, 0, 0); + $matrix->set(0, 0, 1); + $this->assertSame(0, MaskUtil::applyMaskPenaltyRule4($matrix)); + + // Dark cell ratio = 66.67% + $matrix = new ByteMatrix(6, 1); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 1); + $matrix->set(2, 0, 1); + $matrix->set(3, 0, 1); + $matrix->set(4, 0, 1); + $matrix->set(5, 0, 0); + $this->assertSame(30, MaskUtil::applyMaskPenaltyRule4($matrix)); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/MatrixUtilTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/MatrixUtilTest.php new file mode 100644 index 000000000..106ceaaf5 --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Encoder/MatrixUtilTest.php @@ -0,0 +1,335 @@ +getMethods(ReflectionMethod::IS_STATIC) as $method) { + $method->setAccessible(true); + $this->methods[$method->getName()] = $method; + } + } + + public function testToString() : void + { + $matrix = new ByteMatrix(3, 3); + $matrix->set(0, 0, 0); + $matrix->set(1, 0, 1); + $matrix->set(2, 0, 0); + $matrix->set(0, 1, 1); + $matrix->set(1, 1, 0); + $matrix->set(2, 1, 1); + $matrix->set(0, 2, -1); + $matrix->set(1, 2, -1); + $matrix->set(2, 2, -1); + + $expected = " 0 1 0\n 1 0 1\n \n"; + $this->assertSame($expected, (string) $matrix); + } + + public function testClearMatrix() : void + { + $matrix = new ByteMatrix(2, 2); + MatrixUtil::clearMatrix($matrix); + + $this->assertSame(-1, $matrix->get(0, 0)); + $this->assertSame(-1, $matrix->get(1, 0)); + $this->assertSame(-1, $matrix->get(0, 1)); + $this->assertSame(-1, $matrix->get(1, 1)); + } + + public function testEmbedBasicPatterns1() : void + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedBasicPatterns']->invoke( + null, + Version::getVersionForNumber(1), + $matrix + ); + $expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 0 0 0 0 0 0 0 1 \n" + . " 1 1 1 1 1 1 1 0 \n" + . " 1 0 0 0 0 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 0 0 0 0 1 0 \n" + . " 1 1 1 1 1 1 1 0 \n"; + + $this->assertSame($expected, (string) $matrix); + } + + public function testEmbedBasicPatterns2() : void + { + $matrix = new ByteMatrix(25, 25); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedBasicPatterns']->invoke( + null, + Version::getVersionForNumber(2), + $matrix + ); + $expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 \n" + . " 0 \n" + . " 1 1 1 1 1 1 \n" + . " 0 0 0 0 0 0 0 0 1 1 0 0 0 1 \n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 \n" + . " 1 0 0 0 0 0 1 0 1 0 0 0 1 \n" + . " 1 0 1 1 1 0 1 0 1 1 1 1 1 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 1 1 1 0 1 0 \n" + . " 1 0 0 0 0 0 1 0 \n" + . " 1 1 1 1 1 1 1 0 \n"; + + $this->assertSame($expected, (string) $matrix); + } + + public function testEmbedTypeInfo() : void + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedTypeInfo']->invoke( + null, + ErrorCorrectionLevel::M(), + 5, + $matrix + ); + $expected = " 0 \n" + . " 1 \n" + . " 1 \n" + . " 1 \n" + . " 0 \n" + . " 0 \n" + . " \n" + . " 1 \n" + . " 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0\n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 0 \n" + . " 1 \n"; + + $this->assertSame($expected, (string) $matrix); + } + + public function testEmbedVersionInfo() : void + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['maybeEmbedVersionInfo']->invoke( + null, + Version::getVersionForNumber(7), + $matrix + ); + $expected = " 0 0 1 \n" + . " 0 1 0 \n" + . " 0 1 0 \n" + . " 0 1 1 \n" + . " 1 1 1 \n" + . " 0 0 0 \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " 0 0 0 0 1 0 \n" + . " 0 1 1 1 1 0 \n" + . " 1 0 0 1 1 0 \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n" + . " \n"; + + $this->assertSame($expected, (string) $matrix); + } + + public function testEmbedDataBits() : void + { + $matrix = new ByteMatrix(21, 21); + MatrixUtil::clearMatrix($matrix); + $this->methods['embedBasicPatterns']->invoke( + null, + Version::getVersionForNumber(1), + $matrix + ); + + $bits = new BitArray(); + $this->methods['embedDataBits']->invoke( + null, + $bits, + -1, + $matrix + ); + + $expected = " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; + + $this->assertSame($expected, (string) $matrix); + } + + public function testBuildMatrix() : void + { + $bytes = [ + 32, 65, 205, 69, 41, 220, 46, 128, 236, 42, 159, 74, 221, 244, 169, + 239, 150, 138, 70, 237, 85, 224, 96, 74, 219 , 61 + ]; + $bits = new BitArray(); + + foreach ($bytes as $byte) { + $bits->appendBits($byte, 8); + } + + $matrix = new ByteMatrix(21, 21); + MatrixUtil::buildMatrix( + $bits, + ErrorCorrectionLevel::H(), + Version::getVersionForNumber(1), + 3, + $matrix + ); + + $expected = " 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" + . " 1 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 1\n" + . " 1 0 1 1 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 1\n" + . " 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 1\n" + . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0\n" + . " 0 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 0 0 0 0\n" + . " 1 0 1 0 1 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0\n" + . " 1 1 1 1 0 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0\n" + . " 1 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0\n" + . " 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 1 1 1 1 1\n" + . " 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 1 1\n" + . " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0\n" + . " 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1\n" + . " 1 0 1 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 1 0\n" + . " 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0\n" + . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0\n" + . " 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 0 1 0 0 1 0\n"; + + $this->assertSame($expected, (string) $matrix); + } + + public function testFindMsbSet() : void + { + $this->assertSame(0, $this->methods['findMsbSet']->invoke(null, 0)); + $this->assertSame(1, $this->methods['findMsbSet']->invoke(null, 1)); + $this->assertSame(8, $this->methods['findMsbSet']->invoke(null, 0x80)); + $this->assertSame(32, $this->methods['findMsbSet']->invoke(null, 0x80000000)); + } + + public function testCalculateBchCode() : void + { + // Encoding of type information. + // From Appendix C in JISX0510:2004 (p 65) + $this->assertSame(0xdc, $this->methods['calculateBchCode']->invoke(null, 5, 0x537)); + // From http://www.swetake.com/qr/qr6.html + $this->assertSame(0x1c2, $this->methods['calculateBchCode']->invoke(null, 0x13, 0x537)); + // From http://www.swetake.com/qr/qr11.html + $this->assertSame(0x214, $this->methods['calculateBchCode']->invoke(null, 0x1b, 0x537)); + + // Encoding of version information. + // From Appendix D in JISX0510:2004 (p 68) + $this->assertSame(0xc94, $this->methods['calculateBchCode']->invoke(null, 7, 0x1f25)); + $this->assertSame(0x5bc, $this->methods['calculateBchCode']->invoke(null, 8, 0x1f25)); + $this->assertSame(0xa99, $this->methods['calculateBchCode']->invoke(null, 9, 0x1f25)); + $this->assertSame(0x4d3, $this->methods['calculateBchCode']->invoke(null, 10, 0x1f25)); + $this->assertSame(0x9a6, $this->methods['calculateBchCode']->invoke(null, 20, 0x1f25)); + $this->assertSame(0xd75, $this->methods['calculateBchCode']->invoke(null, 30, 0x1f25)); + $this->assertSame(0xc69, $this->methods['calculateBchCode']->invoke(null, 40, 0x1f25)); + } + + public function testMakeVersionInfoBits() : void + { + // From Appendix D in JISX0510:2004 (p 68) + $bits = new BitArray(); + $this->methods['makeVersionInfoBits']->invoke(null, Version::getVersionForNumber(7), $bits); + $this->assertSame(' ...XXXXX ..X..X.X ..', (string) $bits); + } + + public function testMakeTypeInfoBits() : void + { + // From Appendix D in JISX0510:2004 (p 68) + $bits = new BitArray(); + $this->methods['makeTypeInfoBits']->invoke(null, ErrorCorrectionLevel::M(), 5, $bits); + $this->assertSame(' X......X X..XXX.', (string) $bits); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/ImagickRenderingTest.php b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/ImagickRenderingTest.php new file mode 100644 index 000000000..3df8687ec --- /dev/null +++ b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/ImagickRenderingTest.php @@ -0,0 +1,72 @@ +writeFile('Hello World!', $tempName); + + $this->assertMatchesFileSnapshot($tempName); + unlink($tempName); + } + + /** + * @requires extension imagick + */ + public function testIssue79() : void + { + $eye = SquareEye::instance(); + $squareModule = SquareModule::instance(); + + $eyeFill = new EyeFill(new Rgb(100, 100, 55), new Rgb(100, 100, 255)); + $gradient = new Gradient(new Rgb(100, 100, 55), new Rgb(100, 100, 255), GradientType::HORIZONTAL()); + + $renderer = new ImageRenderer( + new RendererStyle( + 400, + 2, + $squareModule, + $eye, + Fill::withForegroundGradient(new Rgb(255, 255, 255), $gradient, $eyeFill, $eyeFill, $eyeFill) + ), + new ImagickImageBackEnd() + ); + $writer = new Writer($renderer); + $tempName = tempnam(sys_get_temp_dir(), 'test') . '.png'; + $writer->writeFile('https://apiroad.net/very-long-url', $tempName); + + $this->assertMatchesFileSnapshot($tempName); + unlink($tempName); + } +} diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/__snapshots__/files/ImagickRenderingTest__testGenericQrCode__1.png b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/__snapshots__/files/ImagickRenderingTest__testGenericQrCode__1.png new file mode 100644 index 000000000..9a429edc8 Binary files /dev/null and b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/__snapshots__/files/ImagickRenderingTest__testGenericQrCode__1.png differ diff --git a/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/__snapshots__/files/ImagickRenderingTest__testIssue79__1.png b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/__snapshots__/files/ImagickRenderingTest__testIssue79__1.png new file mode 100644 index 000000000..47e3b1a29 Binary files /dev/null and b/data/web/inc/lib/vendor/bacon/bacon-qr-code/test/Integration/__snapshots__/files/ImagickRenderingTest__testIssue79__1.png differ diff --git a/data/web/inc/lib/vendor/bin/var-dump-server b/data/web/inc/lib/vendor/bin/var-dump-server deleted file mode 100755 index 18db1c1eb..000000000 --- a/data/web/inc/lib/vendor/bin/var-dump-server +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env php -realpath = realpath($opened_path) ?: $opened_path; - $opened_path = $this->realpath; - $this->handle = fopen($this->realpath, $mode); - $this->position = 0; - - return (bool) $this->handle; - } - - public function stream_read($count) - { - $data = fread($this->handle, $count); - - if ($this->position === 0) { - $data = preg_replace('{^#!.*\r?\n}', '', $data); - } - - $this->position += strlen($data); - - return $data; - } - - public function stream_cast($castAs) - { - return $this->handle; - } - - public function stream_close() - { - fclose($this->handle); - } - - public function stream_lock($operation) - { - return $operation ? flock($this->handle, $operation) : true; - } - - public function stream_seek($offset, $whence) - { - if (0 === fseek($this->handle, $offset, $whence)) { - $this->position = ftell($this->handle); - return true; - } - - return false; - } - - public function stream_tell() - { - return $this->position; - } - - public function stream_eof() - { - return feof($this->handle); - } - - public function stream_stat() - { - return array(); - } - - public function stream_set_option($option, $arg1, $arg2) - { - return true; - } - - public function url_stat($path, $flags) - { - $path = substr($path, 17); - if (file_exists($path)) { - return stat($path); - } - - return false; - } - } - } - - if ( - (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) - || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) - ) { - return include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server'); - } -} - -return include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server'; diff --git a/data/web/inc/lib/vendor/composer/InstalledVersions.php b/data/web/inc/lib/vendor/composer/InstalledVersions.php index 51e734a77..6d29bff66 100644 --- a/data/web/inc/lib/vendor/composer/InstalledVersions.php +++ b/data/web/inc/lib/vendor/composer/InstalledVersions.php @@ -32,6 +32,11 @@ class InstalledVersions */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +314,12 @@ class InstalledVersions { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; } /** @@ -322,19 +333,27 @@ class InstalledVersions } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = strtr(__DIR__, '\\', '/'); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @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} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -350,7 +369,7 @@ class InstalledVersions } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; } diff --git a/data/web/inc/lib/vendor/composer/autoload_classmap.php b/data/web/inc/lib/vendor/composer/autoload_classmap.php index 8e4b7d313..3885b22a6 100644 --- a/data/web/inc/lib/vendor/composer/autoload_classmap.php +++ b/data/web/inc/lib/vendor/composer/autoload_classmap.php @@ -9,6 +9,7 @@ return array( 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', diff --git a/data/web/inc/lib/vendor/composer/autoload_files.php b/data/web/inc/lib/vendor/composer/autoload_files.php index c426e71df..e2cabb90d 100644 --- a/data/web/inc/lib/vendor/composer/autoload_files.php +++ b/data/web/inc/lib/vendor/composer/autoload_files.php @@ -10,15 +10,12 @@ return array( '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php', - '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + '60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', - 'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php', - 'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php', '04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php', '89efb1254ef2d1c5d80096acd12c4098' => $vendorDir . '/twig/twig/src/Resources/core.php', 'ffecb95d45175fd40f75be8a23b34f90' => $vendorDir . '/twig/twig/src/Resources/debug.php', diff --git a/data/web/inc/lib/vendor/composer/autoload_psr4.php b/data/web/inc/lib/vendor/composer/autoload_psr4.php index eca85b846..236034cf0 100644 --- a/data/web/inc/lib/vendor/composer/autoload_psr4.php +++ b/data/web/inc/lib/vendor/composer/autoload_psr4.php @@ -7,13 +7,11 @@ $baseDir = dirname($vendorDir); return array( 'Twig\\' => array($vendorDir . '/twig/twig/src'), - 'Tightenco\\Collect\\' => array($vendorDir . '/tightenco/collect/src/Collect'), 'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'), 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'), - 'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), 'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'), 'Stevenmaguire\\OAuth2\\Client\\' => array($vendorDir . '/stevenmaguire/oauth2-keycloak/src'), 'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'), @@ -29,6 +27,7 @@ return array( 'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'), 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-client/src'), 'LdapRecord\\' => array($vendorDir . '/directorytree/ldaprecord/src'), + 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/conditionable'), 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'), 'Html2Text\\' => array($vendorDir . '/soundasleep/html2text/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), @@ -36,6 +35,8 @@ return array( 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'), 'Ddeboer\\Imap\\' => array($vendorDir . '/ddeboer/imap/src'), + 'DASPRiD\\Enum\\' => array($vendorDir . '/dasprid/enum/src'), 'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'), 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'), + 'BaconQrCode\\' => array($vendorDir . '/bacon/bacon-qr-code/src'), ); diff --git a/data/web/inc/lib/vendor/composer/autoload_static.php b/data/web/inc/lib/vendor/composer/autoload_static.php index 5375b0bea..f66ad0c71 100644 --- a/data/web/inc/lib/vendor/composer/autoload_static.php +++ b/data/web/inc/lib/vendor/composer/autoload_static.php @@ -11,15 +11,12 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', 'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php', - '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + '60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', - 'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php', - 'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php', '04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php', '89efb1254ef2d1c5d80096acd12c4098' => __DIR__ . '/..' . '/twig/twig/src/Resources/core.php', 'ffecb95d45175fd40f75be8a23b34f90' => __DIR__ . '/..' . '/twig/twig/src/Resources/debug.php', @@ -28,27 +25,25 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b ); public static $prefixLengthsPsr4 = array ( - 'T' => + 'T' => array ( 'Twig\\' => 5, - 'Tightenco\\Collect\\' => 18, ), - 'S' => + 'S' => array ( 'Symfony\\Polyfill\\Php81\\' => 23, 'Symfony\\Polyfill\\Php80\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Ctype\\' => 23, 'Symfony\\Contracts\\Translation\\' => 30, - 'Symfony\\Component\\VarDumper\\' => 28, 'Symfony\\Component\\Translation\\' => 30, 'Stevenmaguire\\OAuth2\\Client\\' => 28, ), - 'R' => + 'R' => array ( 'RobThree\\Auth\\' => 14, ), - 'P' => + 'P' => array ( 'Psr\\SimpleCache\\' => 16, 'Psr\\Log\\' => 8, @@ -59,181 +54,193 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b 'PhpMimeMailParser\\' => 18, 'PHPMailer\\PHPMailer\\' => 20, ), - 'M' => + 'M' => array ( 'MatthiasMullie\\PathConverter\\' => 29, 'MatthiasMullie\\Minify\\' => 22, ), - 'L' => + 'L' => array ( 'League\\OAuth2\\Client\\' => 21, 'LdapRecord\\' => 11, ), - 'I' => + 'I' => array ( + 'Illuminate\\Support\\' => 19, 'Illuminate\\Contracts\\' => 21, ), - 'H' => + 'H' => array ( 'Html2Text\\' => 10, ), - 'G' => + 'G' => array ( 'GuzzleHttp\\Psr7\\' => 16, 'GuzzleHttp\\Promise\\' => 19, 'GuzzleHttp\\' => 11, ), - 'F' => + 'F' => array ( 'Firebase\\JWT\\' => 13, ), - 'D' => + 'D' => array ( 'Ddeboer\\Imap\\' => 13, + 'DASPRiD\\Enum\\' => 13, ), - 'C' => + 'C' => array ( 'Carbon\\Doctrine\\' => 16, 'Carbon\\' => 7, ), + 'B' => + array ( + 'BaconQrCode\\' => 12, + ), ); public static $prefixDirsPsr4 = array ( - 'Twig\\' => + 'Twig\\' => array ( 0 => __DIR__ . '/..' . '/twig/twig/src', ), - 'Tightenco\\Collect\\' => - array ( - 0 => __DIR__ . '/..' . '/tightenco/collect/src/Collect', - ), - 'Symfony\\Polyfill\\Php81\\' => + 'Symfony\\Polyfill\\Php81\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php81', ), - 'Symfony\\Polyfill\\Php80\\' => + 'Symfony\\Polyfill\\Php80\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', ), - 'Symfony\\Polyfill\\Mbstring\\' => + 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), - 'Symfony\\Polyfill\\Ctype\\' => + 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), - 'Symfony\\Contracts\\Translation\\' => + 'Symfony\\Contracts\\Translation\\' => array ( 0 => __DIR__ . '/..' . '/symfony/translation-contracts', ), - 'Symfony\\Component\\VarDumper\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/var-dumper', - ), - 'Symfony\\Component\\Translation\\' => + 'Symfony\\Component\\Translation\\' => array ( 0 => __DIR__ . '/..' . '/symfony/translation', ), - 'Stevenmaguire\\OAuth2\\Client\\' => + 'Stevenmaguire\\OAuth2\\Client\\' => array ( 0 => __DIR__ . '/..' . '/stevenmaguire/oauth2-keycloak/src', ), - 'RobThree\\Auth\\' => + 'RobThree\\Auth\\' => array ( 0 => __DIR__ . '/..' . '/robthree/twofactorauth/lib', ), - 'Psr\\SimpleCache\\' => + 'Psr\\SimpleCache\\' => array ( 0 => __DIR__ . '/..' . '/psr/simple-cache/src', ), - 'Psr\\Log\\' => + 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/src', ), - 'Psr\\Http\\Message\\' => + 'Psr\\Http\\Message\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-factory/src', 1 => __DIR__ . '/..' . '/psr/http-message/src', ), - 'Psr\\Http\\Client\\' => + 'Psr\\Http\\Client\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-client/src', ), - 'Psr\\Container\\' => + 'Psr\\Container\\' => array ( 0 => __DIR__ . '/..' . '/psr/container/src', ), - 'Psr\\Clock\\' => + 'Psr\\Clock\\' => array ( 0 => __DIR__ . '/..' . '/psr/clock/src', ), - 'PhpMimeMailParser\\' => + 'PhpMimeMailParser\\' => array ( 0 => __DIR__ . '/..' . '/php-mime-mail-parser/php-mime-mail-parser/src', ), - 'PHPMailer\\PHPMailer\\' => + 'PHPMailer\\PHPMailer\\' => array ( 0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src', ), - 'MatthiasMullie\\PathConverter\\' => + 'MatthiasMullie\\PathConverter\\' => array ( 0 => __DIR__ . '/..' . '/matthiasmullie/path-converter/src', ), - 'MatthiasMullie\\Minify\\' => + 'MatthiasMullie\\Minify\\' => array ( 0 => __DIR__ . '/..' . '/matthiasmullie/minify/src', ), - 'League\\OAuth2\\Client\\' => + 'League\\OAuth2\\Client\\' => array ( 0 => __DIR__ . '/..' . '/league/oauth2-client/src', ), - 'LdapRecord\\' => + 'LdapRecord\\' => array ( 0 => __DIR__ . '/..' . '/directorytree/ldaprecord/src', ), - 'Illuminate\\Contracts\\' => + 'Illuminate\\Support\\' => + array ( + 0 => __DIR__ . '/..' . '/illuminate/collections', + 1 => __DIR__ . '/..' . '/illuminate/macroable', + 2 => __DIR__ . '/..' . '/illuminate/conditionable', + ), + 'Illuminate\\Contracts\\' => array ( 0 => __DIR__ . '/..' . '/illuminate/contracts', ), - 'Html2Text\\' => + 'Html2Text\\' => array ( 0 => __DIR__ . '/..' . '/soundasleep/html2text/src', ), - 'GuzzleHttp\\Psr7\\' => + 'GuzzleHttp\\Psr7\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', ), - 'GuzzleHttp\\Promise\\' => + 'GuzzleHttp\\Promise\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src', ), - 'GuzzleHttp\\' => + 'GuzzleHttp\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', ), - 'Firebase\\JWT\\' => + 'Firebase\\JWT\\' => array ( 0 => __DIR__ . '/..' . '/firebase/php-jwt/src', ), - 'Ddeboer\\Imap\\' => + 'Ddeboer\\Imap\\' => array ( 0 => __DIR__ . '/..' . '/ddeboer/imap/src', ), - 'Carbon\\Doctrine\\' => + 'DASPRiD\\Enum\\' => + array ( + 0 => __DIR__ . '/..' . '/dasprid/enum/src', + ), + 'Carbon\\Doctrine\\' => array ( 0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine', ), - 'Carbon\\' => + 'Carbon\\' => array ( 0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon', ), + 'BaconQrCode\\' => + array ( + 0 => __DIR__ . '/..' . '/bacon/bacon-qr-code/src', + ), ); public static $prefixesPsr0 = array ( - 'O' => + 'O' => array ( - 'OAuth2' => + 'OAuth2' => array ( 0 => __DIR__ . '/..' . '/bshaffer/oauth2-server-php/src', ), @@ -244,6 +251,7 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', 'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', diff --git a/data/web/inc/lib/vendor/composer/installed.json b/data/web/inc/lib/vendor/composer/installed.json index 26e8bc42b..e2d7113bc 100644 --- a/data/web/inc/lib/vendor/composer/installed.json +++ b/data/web/inc/lib/vendor/composer/installed.json @@ -1,5 +1,62 @@ { "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "2.0.8", + "version_normalized": "2.0.8.0", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22", + "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.1", + "phpunit/phpunit": "^7 | ^8 | ^9", + "spatie/phpunit-snapshot-assertions": "^4.2.9", + "squizlabs/php_codesniffer": "^3.4" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "time": "2022-12-07T17:46:57+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8" + }, + "install-path": "../bacon/bacon-qr-code" + }, { "name": "bshaffer/oauth2-server-php", "version": "v1.11.1", @@ -133,6 +190,59 @@ ], "install-path": "../carbonphp/carbon-doctrine-types" }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "version_normalized": "1.0.7.0", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "time": "2025-09-16T12:23:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "install-path": "../dasprid/enum" + }, { "name": "ddeboer/imap", "version": "1.13.1", @@ -213,36 +323,38 @@ }, { "name": "directorytree/ldaprecord", - "version": "v2.20.5", - "version_normalized": "2.20.5.0", + "version": "v3.8.5", + "version_normalized": "3.8.5.0", "source": { "type": "git", "url": "https://github.com/DirectoryTree/LdapRecord.git", - "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16" + "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/5bd0a5a9d257cf1049ae83055dbba4c3479ddf16", - "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16", + "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/00e5f088f8c4028d5f398783cccc2e8119a27a65", + "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65", "shasum": "" }, "require": { + "ext-iconv": "*", "ext-json": "*", "ext-ldap": "*", - "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", - "nesbot/carbon": "^1.0|^2.0", - "php": ">=7.3", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0", - "symfony/polyfill-php80": "^1.25", - "tightenco/collect": "^5.6|^6.0|^7.0|^8.0|^9.0" + "illuminate/collections": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", + "nesbot/carbon": "*", + "php": ">=8.1", + "psr/log": "*", + "psr/simple-cache": "^1.0|^2.0|^3.0" }, "require-dev": { + "fakerphp/faker": "^1.21", + "laravel/pint": "^1.6", "mockery/mockery": "^1.0", "phpunit/phpunit": "^9.0", "spatie/ray": "^1.24" }, - "time": "2023-10-11T16:34:34+00:00", + "time": "2025-10-06T02:22:34+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -691,6 +803,113 @@ ], "install-path": "../guzzlehttp/psr7" }, + { + "name": "illuminate/collections", + "version": "v10.49.0", + "version_normalized": "10.49.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "php": "^8.1" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.2)." + }, + "time": "2025-09-08T19:05:53+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/collections" + }, + { + "name": "illuminate/conditionable", + "version": "v10.49.0", + "version_normalized": "10.49.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/47c700320b7a419f0d188d111f3bbed978fcbd3f", + "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "time": "2025-03-24T11:47:24+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/conditionable" + }, { "name": "illuminate/contracts", "version": "v10.44.0", @@ -742,6 +961,55 @@ }, "install-path": "../illuminate/contracts" }, + { + "name": "illuminate/macroable", + "version": "v10.49.0", + "version_normalized": "10.49.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "time": "2023-06-05T12:46:42+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/macroable" + }, { "name": "league/oauth2-client", "version": "2.7.0", @@ -2538,151 +2806,6 @@ ], "install-path": "../symfony/translation-contracts" }, - { - "name": "symfony/var-dumper", - "version": "v6.4.3", - "version_normalized": "6.4.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "0435a08f69125535336177c29d56af3abc1f69da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0435a08f69125535336177c29d56af3abc1f69da", - "reference": "0435a08f69125535336177c29d56af3abc1f69da", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/console": "<5.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^6.3|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "twig/twig": "^2.13|^3.0.4" - }, - "time": "2024-01-23T14:53:30+00:00", - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "install-path": "../symfony/var-dumper" - }, - { - "name": "tightenco/collect", - "version": "v9.52.7", - "version_normalized": "9.52.7.0", - "source": { - "type": "git", - "url": "https://github.com/tighten/collect.git", - "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tighten/collect/zipball/b15143cd11fe01a700fcc449df61adc64452fa6d", - "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d", - "shasum": "" - }, - "require": { - "php": "^8.0", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "nesbot/carbon": "^2.23.0", - "phpunit/phpunit": "^8.3" - }, - "time": "2023-04-14T21:51:36+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "src/Collect/Support/helpers.php", - "src/Collect/Support/alias.php" - ], - "psr-4": { - "Tightenco\\Collect\\": "src/Collect" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "Collect - Illuminate Collections as a separate package.", - "keywords": [ - "collection", - "laravel" - ], - "support": { - "issues": "https://github.com/tighten/collect/issues", - "source": "https://github.com/tighten/collect/tree/v9.52.7" - }, - "install-path": "../tightenco/collect" - }, { "name": "twig/twig", "version": "v3.14.0", diff --git a/data/web/inc/lib/vendor/composer/installed.php b/data/web/inc/lib/vendor/composer/installed.php index 074ec9f80..15a1aa89f 100644 --- a/data/web/inc/lib/vendor/composer/installed.php +++ b/data/web/inc/lib/vendor/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => '__root__', - 'pretty_version' => 'dev-master', - 'version' => 'dev-master', - 'reference' => '220fdbb168792c07493db330d898b345cc902055', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -11,14 +11,23 @@ ), 'versions' => array( '__root__' => array( - 'pretty_version' => 'dev-master', - 'version' => 'dev-master', - 'reference' => '220fdbb168792c07493db330d898b345cc902055', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => null, 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), + 'bacon/bacon-qr-code' => array( + 'pretty_version' => '2.0.8', + 'version' => '2.0.8.0', + 'reference' => '8674e51bb65af933a5ffaf1c308a660387c35c22', + 'type' => 'library', + 'install_path' => __DIR__ . '/../bacon/bacon-qr-code', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'bshaffer/oauth2-server-php' => array( 'pretty_version' => 'v1.11.1', 'version' => '1.11.1.0', @@ -37,6 +46,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'dasprid/enum' => array( + 'pretty_version' => '1.0.7', + 'version' => '1.0.7.0', + 'reference' => 'b5874fa9ed0043116c72162ec7f4fb50e02e7cce', + 'type' => 'library', + 'install_path' => __DIR__ . '/../dasprid/enum', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'ddeboer/imap' => array( 'pretty_version' => '1.13.1', 'version' => '1.13.1.0', @@ -47,9 +65,9 @@ 'dev_requirement' => false, ), 'directorytree/ldaprecord' => array( - 'pretty_version' => 'v2.20.5', - 'version' => '2.20.5.0', - 'reference' => '5bd0a5a9d257cf1049ae83055dbba4c3479ddf16', + 'pretty_version' => 'v3.8.5', + 'version' => '3.8.5.0', + 'reference' => '00e5f088f8c4028d5f398783cccc2e8119a27a65', 'type' => 'library', 'install_path' => __DIR__ . '/../directorytree/ldaprecord', 'aliases' => array(), @@ -97,6 +115,24 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'illuminate/collections' => array( + 'pretty_version' => 'v10.49.0', + 'version' => '10.49.0.0', + 'reference' => '6ae9c74fa92d4e1824d1b346cd435e8eacdc3232', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/collections', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'illuminate/conditionable' => array( + 'pretty_version' => 'v10.49.0', + 'version' => '10.49.0.0', + 'reference' => '47c700320b7a419f0d188d111f3bbed978fcbd3f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/conditionable', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'illuminate/contracts' => array( 'pretty_version' => 'v10.44.0', 'version' => '10.44.0.0', @@ -106,6 +142,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'illuminate/macroable' => array( + 'pretty_version' => 'v10.49.0', + 'version' => '10.49.0.0', + 'reference' => 'dff667a46ac37b634dcf68909d9d41e94dc97c27', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/macroable', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'league/oauth2-client' => array( 'pretty_version' => '2.7.0', 'version' => '2.7.0.0', @@ -376,24 +421,6 @@ 0 => '2.3|3.0', ), ), - 'symfony/var-dumper' => array( - 'pretty_version' => 'v6.4.3', - 'version' => '6.4.3.0', - 'reference' => '0435a08f69125535336177c29d56af3abc1f69da', - 'type' => 'library', - 'install_path' => __DIR__ . '/../symfony/var-dumper', - 'aliases' => array(), - 'dev_requirement' => false, - ), - 'tightenco/collect' => array( - 'pretty_version' => 'v9.52.7', - 'version' => '9.52.7.0', - 'reference' => 'b15143cd11fe01a700fcc449df61adc64452fa6d', - 'type' => 'library', - 'install_path' => __DIR__ . '/../tightenco/collect', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'twig/twig' => array( 'pretty_version' => 'v3.14.0', 'version' => '3.14.0.0', diff --git a/data/web/inc/lib/vendor/dasprid/enum/LICENSE b/data/web/inc/lib/vendor/dasprid/enum/LICENSE new file mode 100644 index 000000000..d45a35647 --- /dev/null +++ b/data/web/inc/lib/vendor/dasprid/enum/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2017, Ben Scholzen 'DASPRiD' +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/data/web/inc/lib/vendor/dasprid/enum/README.md b/data/web/inc/lib/vendor/dasprid/enum/README.md new file mode 100644 index 000000000..da37045e3 --- /dev/null +++ b/data/web/inc/lib/vendor/dasprid/enum/README.md @@ -0,0 +1,164 @@ +# PHP 7.1 enums + +[![Build Status](https://github.com/DASPRiD/Enum/actions/workflows/tests.yml/badge.svg)](https://github.com/DASPRiD/Enum/actions?query=workflow%3Atests) +[![Coverage Status](https://coveralls.io/repos/github/DASPRiD/Enum/badge.svg?branch=master)](https://coveralls.io/github/DASPRiD/Enum?branch=master) +[![Latest Stable Version](https://poser.pugx.org/dasprid/enum/v/stable)](https://packagist.org/packages/dasprid/enum) +[![Total Downloads](https://poser.pugx.org/dasprid/enum/downloads)](https://packagist.org/packages/dasprid/enum) +[![License](https://poser.pugx.org/dasprid/enum/license)](https://packagist.org/packages/dasprid/enum) + +It is a well known fact that PHP is missing a basic enum type, ignoring the rather incomplete `SplEnum` implementation +which is only available as a PECL extension. There are also quite a few other userland enum implementations around, +but all of them have one or another compromise. This library tries to close that gap as far as PHP allows it to. + +## Usage + +### Basics + +At its core, there is the `DASPRiD\Enum\AbstractEnum` class, which by default will work with constants like any other +enum implementation you might know. The first clear difference is that you should define all the constants as protected +(so nobody outside your class can read them but the `AbstractEnum` can still do so). The other even mightier difference +is that, for simple enums, the value of the constant doesn't matter at all. Let's have a look at a simple example: + +```php +use DASPRiD\Enum\AbstractEnum; + +/** + * @method static self MONDAY() + * @method static self TUESDAY() + * @method static self WEDNESDAY() + * @method static self THURSDAY() + * @method static self FRIDAY() + * @method static self SATURDAY() + * @method static self SUNDAY() + */ +final class WeekDay extends AbstractEnum +{ + protected const MONDAY = null; + protected const TUESDAY = null; + protected const WEDNESDAY = null; + protected const THURSDAY = null; + protected const FRIDAY = null; + protected const SATURDAY = null; + protected const SUNDAY = null; +} +``` + +If you need to provide constants for either internal use or public use, you can mark them as either private or public, +in which case they will be ignored by the enum, which only considers protected constants as valid values. As you can +see, we specifically defined the generated magic methods in a class level doc block, so anyone using this class will +automatically have proper auto-completion in their IDE. Now since you have defined the enum, you can simply use it like +that: + +```php +function tellItLikeItIs(WeekDay $weekDay) +{ + switch ($weekDay) { + case WeekDay::MONDAY(): + echo 'Mondays are bad.'; + break; + + case WeekDay::FRIDAY(): + echo 'Fridays are better.'; + break; + + case WeekDay::SATURDAY(): + case WeekDay::SUNDAY(): + echo 'Weekends are best.'; + break; + + default: + echo 'Midweek days are so-so.'; + } +} + +tellItLikeItIs(WeekDay::MONDAY()); +tellItLikeItIs(WeekDay::WEDNESDAY()); +tellItLikeItIs(WeekDay::FRIDAY()); +tellItLikeItIs(WeekDay::SATURDAY()); +tellItLikeItIs(WeekDay::SUNDAY()); +``` + +### More complex example + +Of course, all enums are singletons, which are not cloneable or serializable. Thus you can be sure that there is always +just one instance of the same type. Of course, the values of constants are not completely useless, let's have a look at +a more complex example: + +```php +use DASPRiD\Enum\AbstractEnum; + +/** + * @method static self MERCURY() + * @method static self VENUS() + * @method static self EARTH() + * @method static self MARS() + * @method static self JUPITER() + * @method static self SATURN() + * @method static self URANUS() + * @method static self NEPTUNE() + */ +final class Planet extends AbstractEnum +{ + protected const MERCURY = [3.303e+23, 2.4397e6]; + protected const VENUS = [4.869e+24, 6.0518e6]; + protected const EARTH = [5.976e+24, 6.37814e6]; + protected const MARS = [6.421e+23, 3.3972e6]; + protected const JUPITER = [1.9e+27, 7.1492e7]; + protected const SATURN = [5.688e+26, 6.0268e7]; + protected const URANUS = [8.686e+25, 2.5559e7]; + protected const NEPTUNE = [1.024e+26, 2.4746e7]; + + /** + * Universal gravitational constant. + * + * @var float + */ + private const G = 6.67300E-11; + + /** + * Mass in kilograms. + * + * @var float + */ + private $mass; + + /** + * Radius in meters. + * + * @var float + */ + private $radius; + + protected function __construct(float $mass, float $radius) + { + $this->mass = $mass; + $this->radius = $radius; + } + + public function mass() : float + { + return $this->mass; + } + + public function radius() : float + { + return $this->radius; + } + + public function surfaceGravity() : float + { + return self::G * $this->mass / ($this->radius * $this->radius); + } + + public function surfaceWeight(float $otherMass) : float + { + return $otherMass * $this->surfaceGravity(); + } +} + +$myMass = 80; + +foreach (Planet::values() as $planet) { + printf("Your weight on %s is %f\n", $planet, $planet->surfaceWeight($myMass)); +} +``` diff --git a/data/web/inc/lib/vendor/dasprid/enum/composer.json b/data/web/inc/lib/vendor/dasprid/enum/composer.json new file mode 100644 index 000000000..a099aba54 --- /dev/null +++ b/data/web/inc/lib/vendor/dasprid/enum/composer.json @@ -0,0 +1,34 @@ +{ + "name": "dasprid/enum", + "description": "PHP 7.1 enum implementation", + "license": "BSD-2-Clause", + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "keywords": [ + "enum", + "map" + ], + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "DASPRiD\\EnumTest\\": "test/" + } + } +} diff --git a/data/web/inc/lib/vendor/dasprid/enum/src/AbstractEnum.php b/data/web/inc/lib/vendor/dasprid/enum/src/AbstractEnum.php new file mode 100644 index 000000000..43006dce6 --- /dev/null +++ b/data/web/inc/lib/vendor/dasprid/enum/src/AbstractEnum.php @@ -0,0 +1,261 @@ +> + */ + private static $values = []; + + /** + * @var array + */ + private static $allValuesLoaded = []; + + /** + * @var array + */ + private static $constants = []; + + /** + * The constructor is private by default to avoid arbitrary enum creation. + * + * When creating your own constructor for a parameterized enum, make sure to declare it as protected, so that + * the static methods are able to construct it. Avoid making it public, as that would allow creation of + * non-singleton enum instances. + */ + private function __construct() + { + } + + /** + * Magic getter which forwards all calls to {@see self::valueOf()}. + * + * @return static + */ + final public static function __callStatic(string $name, array $arguments) : self + { + return static::valueOf($name); + } + + /** + * Returns an enum with the specified name. + * + * The name must match exactly an identifier used to declare an enum in this type (extraneous whitespace characters + * are not permitted). + * + * @return static + * @throws IllegalArgumentException if the enum has no constant with the specified name + */ + final public static function valueOf(string $name) : self + { + if (isset(self::$values[static::class][$name])) { + return self::$values[static::class][$name]; + } + + $constants = self::constants(); + + if (array_key_exists($name, $constants)) { + return self::createValue($name, $constants[$name][0], $constants[$name][1]); + } + + throw new IllegalArgumentException(sprintf('No enum constant %s::%s', static::class, $name)); + } + + /** + * @return static + */ + private static function createValue(string $name, int $ordinal, array $arguments) : self + { + $instance = new static(...$arguments); + $instance->name = $name; + $instance->ordinal = $ordinal; + self::$values[static::class][$name] = $instance; + return $instance; + } + + /** + * Obtains all possible types defined by this enum. + * + * @return static[] + */ + final public static function values() : array + { + if (isset(self::$allValuesLoaded[static::class])) { + return self::$values[static::class]; + } + + if (! isset(self::$values[static::class])) { + self::$values[static::class] = []; + } + + foreach (self::constants() as $name => $constant) { + if (array_key_exists($name, self::$values[static::class])) { + continue; + } + + static::createValue($name, $constant[0], $constant[1]); + } + + uasort(self::$values[static::class], function (self $a, self $b) { + return $a->ordinal() <=> $b->ordinal(); + }); + + self::$allValuesLoaded[static::class] = true; + return self::$values[static::class]; + } + + private static function constants() : array + { + if (isset(self::$constants[static::class])) { + return self::$constants[static::class]; + } + + self::$constants[static::class] = []; + $reflectionClass = new ReflectionClass(static::class); + $ordinal = -1; + + foreach ($reflectionClass->getReflectionConstants() as $reflectionConstant) { + if (! $reflectionConstant->isProtected()) { + continue; + } + + $value = $reflectionConstant->getValue(); + + self::$constants[static::class][$reflectionConstant->name] = [ + ++$ordinal, + is_array($value) ? $value : [] + ]; + } + + return self::$constants[static::class]; + } + + /** + * Returns the name of this enum constant, exactly as declared in its enum declaration. + * + * Most programmers should use the {@see self::__toString()} method in preference to this one, as the toString + * method may return a more user-friendly name. This method is designed primarily for use in specialized situations + * where correctness depends on getting the exact name, which will not vary from release to release. + */ + final public function name() : string + { + return $this->name; + } + + /** + * Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial + * constant is assigned an ordinal of zero). + * + * Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data + * structures. + */ + final public function ordinal() : int + { + return $this->ordinal; + } + + /** + * Compares this enum with the specified object for order. + * + * Returns negative integer, zero or positive integer as this object is less than, equal to or greater than the + * specified object. + * + * Enums are only comparable to other enums of the same type. The natural order implemented by this method is the + * order in which the constants are declared. + * + * @throws MismatchException if the passed enum is not of the same type + */ + final public function compareTo(self $other) : int + { + if (! $other instanceof static) { + throw new MismatchException(sprintf( + 'The passed enum %s is not of the same type as %s', + get_class($other), + static::class + )); + } + + return $this->ordinal - $other->ordinal; + } + + /** + * Forbid cloning enums. + * + * @throws CloneNotSupportedException + */ + final public function __clone() + { + throw new CloneNotSupportedException(); + } + + /** + * Forbid serializing enums. + * + * @throws SerializeNotSupportedException + */ + final public function __sleep() : array + { + throw new SerializeNotSupportedException(); + } + + /** + * Forbid serializing enums. + * + * @throws SerializeNotSupportedException + */ + final public function __serialize() : array + { + throw new SerializeNotSupportedException(); + } + + /** + * Forbid unserializing enums. + * + * @throws UnserializeNotSupportedException + */ + final public function __wakeup() : void + { + throw new UnserializeNotSupportedException(); + } + + /** + * Forbid unserializing enums. + * + * @throws UnserializeNotSupportedException + */ + final public function __unserialize($arg) : void + { + throw new UnserializeNotSupportedException(); + } + + /** + * Turns the enum into a string representation. + * + * You may override this method to give a more user-friendly version. + */ + public function __toString() : string + { + return $this->name; + } +} diff --git a/data/web/inc/lib/vendor/dasprid/enum/src/EnumMap.php b/data/web/inc/lib/vendor/dasprid/enum/src/EnumMap.php new file mode 100644 index 000000000..95b88568d --- /dev/null +++ b/data/web/inc/lib/vendor/dasprid/enum/src/EnumMap.php @@ -0,0 +1,385 @@ + + */ + private $keyUniverse; + + /** + * Array representation of this map. The ith element is the value to which universe[i] is currently mapped, or null + * if it isn't mapped to anything, or NullValue if it's mapped to null. + * + * @var array + */ + private $values; + + /** + * @var int + */ + private $size = 0; + + /** + * Creates a new enum map. + * + * @param string $keyType the type of the keys, must extend AbstractEnum + * @param string $valueType the type of the values + * @param bool $allowNullValues whether to allow null values + * @throws IllegalArgumentException when key type does not extend AbstractEnum + */ + public function __construct(string $keyType, string $valueType, bool $allowNullValues) + { + if (! is_subclass_of($keyType, AbstractEnum::class)) { + throw new IllegalArgumentException(sprintf( + 'Class %s does not extend %s', + $keyType, + AbstractEnum::class + )); + } + + $this->keyType = $keyType; + $this->valueType = $valueType; + $this->allowNullValues = $allowNullValues; + $this->keyUniverse = $keyType::values(); + $this->values = array_fill(0, count($this->keyUniverse), null); + } + + public function __serialize(): array + { + $values = []; + + foreach ($this->values as $ordinal => $value) { + if (null === $value) { + continue; + } + + $values[$ordinal] = $this->unmaskNull($value); + } + + return [ + 'keyType' => $this->keyType, + 'valueType' => $this->valueType, + 'allowNullValues' => $this->allowNullValues, + 'values' => $values, + ]; + } + + public function __unserialize(array $data): void + { + $this->unserialize(serialize($data)); + } + + /** + * Checks whether the map types match the supplied ones. + * + * You should call this method when an EnumMap is passed to you and you want to ensure that it's made up of the + * correct types. + * + * @throws ExpectationException when supplied key type mismatches local key type + * @throws ExpectationException when supplied value type mismatches local value type + * @throws ExpectationException when the supplied map allows null values, abut should not + */ + public function expect(string $keyType, string $valueType, bool $allowNullValues) : void + { + if ($keyType !== $this->keyType) { + throw new ExpectationException(sprintf( + 'Callee expected an EnumMap with key type %s, but got %s', + $keyType, + $this->keyType + )); + } + + if ($valueType !== $this->valueType) { + throw new ExpectationException(sprintf( + 'Callee expected an EnumMap with value type %s, but got %s', + $keyType, + $this->keyType + )); + } + + if ($allowNullValues !== $this->allowNullValues) { + throw new ExpectationException(sprintf( + 'Callee expected an EnumMap with nullable flag %s, but got %s', + ($allowNullValues ? 'true' : 'false'), + ($this->allowNullValues ? 'true' : 'false') + )); + } + } + + /** + * Returns the number of key-value mappings in this map. + */ + public function size() : int + { + return $this->size; + } + + /** + * Returns true if this map maps one or more keys to the specified value. + */ + public function containsValue($value) : bool + { + return in_array($this->maskNull($value), $this->values, true); + } + + /** + * Returns true if this map contains a mapping for the specified key. + */ + public function containsKey(AbstractEnum $key) : bool + { + $this->checkKeyType($key); + return null !== $this->values[$key->ordinal()]; + } + + /** + * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. + * + * More formally, if this map contains a mapping from a key to a value, then this method returns the value; + * otherwise it returns null (there can be at most one such mapping). + * + * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also + * possible that hte map explicitly maps the key to null. The {@see self::containsKey()} operation may be used to + * distinguish these two cases. + * + * @return mixed + */ + public function get(AbstractEnum $key) + { + $this->checkKeyType($key); + return $this->unmaskNull($this->values[$key->ordinal()]); + } + + /** + * Associates the specified value with the specified key in this map. + * + * If the map previously contained a mapping for this key, the old value is replaced. + * + * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key. + * (a null return can also indicate that the map previously associated null with the specified key.) + * @throws IllegalArgumentException when the passed values does not match the internal value type + */ + public function put(AbstractEnum $key, $value) + { + $this->checkKeyType($key); + + if (! $this->isValidValue($value)) { + throw new IllegalArgumentException(sprintf('Value is not of type %s', $this->valueType)); + } + + $index = $key->ordinal(); + $oldValue = $this->values[$index]; + $this->values[$index] = $this->maskNull($value); + + if (null === $oldValue) { + ++$this->size; + } + + return $this->unmaskNull($oldValue); + } + + /** + * Removes the mapping for this key frm this map if present. + * + * @return mixed the previous value associated with the specified key, or null if there was no mapping for the key. + * (a null return can also indicate that the map previously associated null with the specified key.) + */ + public function remove(AbstractEnum $key) + { + $this->checkKeyType($key); + + $index = $key->ordinal(); + $oldValue = $this->values[$index]; + $this->values[$index] = null; + + if (null !== $oldValue) { + --$this->size; + } + + return $this->unmaskNull($oldValue); + } + + /** + * Removes all mappings from this map. + */ + public function clear() : void + { + $this->values = array_fill(0, count($this->keyUniverse), null); + $this->size = 0; + } + + /** + * Compares the specified map with this map for quality. + * + * Returns true if the two maps represent the same mappings. + */ + public function equals(self $other) : bool + { + if ($this === $other) { + return true; + } + + if ($this->size !== $other->size) { + return false; + } + + return $this->values === $other->values; + } + + /** + * Returns the values contained in this map. + * + * The array will contain the values in the order their corresponding keys appear in the map, which is their natural + * order (the order in which the num constants are declared). + */ + public function values() : array + { + return array_values(array_map(function ($value) { + return $this->unmaskNull($value); + }, array_filter($this->values, function ($value) : bool { + return null !== $value; + }))); + } + + public function serialize() : string + { + return serialize($this->__serialize()); + } + + public function unserialize($serialized) : void + { + $data = unserialize($serialized); + $this->__construct($data['keyType'], $data['valueType'], $data['allowNullValues']); + + foreach ($this->keyUniverse as $key) { + if (array_key_exists($key->ordinal(), $data['values'])) { + $this->put($key, $data['values'][$key->ordinal()]); + } + } + } + + public function getIterator() : Traversable + { + foreach ($this->keyUniverse as $key) { + if (null === $this->values[$key->ordinal()]) { + continue; + } + + yield $key => $this->unmaskNull($this->values[$key->ordinal()]); + } + } + + private function maskNull($value) + { + if (null === $value) { + return NullValue::instance(); + } + + return $value; + } + + private function unmaskNull($value) + { + if ($value instanceof NullValue) { + return null; + } + + return $value; + } + + /** + * @throws IllegalArgumentException when the passed key does not match the internal key type + */ + private function checkKeyType(AbstractEnum $key) : void + { + if (get_class($key) !== $this->keyType) { + throw new IllegalArgumentException(sprintf( + 'Object of type %s is not the same type as %s', + get_class($key), + $this->keyType + )); + } + } + + private function isValidValue($value) : bool + { + if (null === $value) { + if ($this->allowNullValues) { + return true; + } + + return false; + } + + switch ($this->valueType) { + case 'mixed': + return true; + + case 'bool': + case 'boolean': + return is_bool($value); + + case 'int': + case 'integer': + return is_int($value); + + case 'float': + case 'double': + return is_float($value); + + case 'string': + return is_string($value); + + case 'object': + return is_object($value); + + case 'array': + return is_array($value); + } + + return $value instanceof $this->valueType; + } +} diff --git a/data/web/inc/lib/vendor/dasprid/enum/src/Exception/CloneNotSupportedException.php b/data/web/inc/lib/vendor/dasprid/enum/src/Exception/CloneNotSupportedException.php new file mode 100644 index 000000000..4b37dbebe --- /dev/null +++ b/data/web/inc/lib/vendor/dasprid/enum/src/Exception/CloneNotSupportedException.php @@ -0,0 +1,10 @@ + **Environment:** - LDAP Server Type: [e.g. ActiveDirectory / OpenLDAP / FreeIPA] - - PHP Version: [e.g. 7.3 / 7.4 / 8.0] + - PHP Version: [e.g. 8.1 / 8.2] **Describe the bug:** diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/support---help-request.md b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/support---help-request.md index 731d5cea0..b97a977cb 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/support---help-request.md +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/support---help-request.md @@ -17,4 +17,4 @@ assignees: '' --> **Environment:** - LDAP Server Type: [e.g. ActiveDirectory / OpenLDAP / FreeIPA] - - PHP Version: [e.g. 7.3 / 7.4 / 8.0] + - PHP Version: [e.g. 8.1 / 8.2] diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-cs-fix.yml b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-cs-fix.yml new file mode 100644 index 000000000..80cd34d61 --- /dev/null +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-cs-fix.yml @@ -0,0 +1,34 @@ +name: run-cs-fix + +on: + push: + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: [8.3] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, dom, curl, libxml, mbstring + coverage: none + + - name: Install Pint + run: composer global require laravel/pint + + - name: Run Pint + run: pint + + - name: Commit linted files + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Fix code style" diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-integration-tests.yml b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-integration-tests.yml index 6a6b2f9e5..de67e91c9 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-integration-tests.yml +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-integration-tests.yml @@ -1,62 +1,62 @@ name: run-integration-tests on: - push: - pull_request: - schedule: - - cron: "0 0 * * *" + push: + pull_request: + schedule: + - cron: "0 0 * * *" jobs: - run-tests: - runs-on: ${{ matrix.os }} + run-tests: + runs-on: ${{ matrix.os }} - services: - ldap: - image: osixia/openldap:1.4.0 - env: - LDAP_TLS_VERIFY_CLIENT: try - LDAP_OPENLDAP_UID: 1000 - LDAP_OPENLDAP_GID: 1000 - LDAP_ORGANISATION: Local - LDAP_DOMAIN: local.com - LDAP_ADMIN_PASSWORD: secret - ports: - - 389:389 - - 636:636 + services: + ldap: + image: osixia/openldap:1.4.0 + env: + LDAP_TLS_VERIFY_CLIENT: try + LDAP_OPENLDAP_UID: 1000 + LDAP_OPENLDAP_GID: 1000 + LDAP_ORGANISATION: Local + LDAP_DOMAIN: local.com + LDAP_ADMIN_PASSWORD: secret + ports: + - 389:389 + - 636:636 - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - php: [8.1, 8.0, 7.4] + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + php: [8.1, 8.2, 8.3] - name: ${{ matrix.os }} - P${{ matrix.php }} + name: ${{ matrix.os }} - P${{ matrix.php }} - steps: - - name: Checkout code - uses: actions/checkout@v2 + steps: + - name: Checkout code + uses: actions/checkout@v2 - - name: Cache dependencies - uses: actions/cache@v2 - with: - path: ~/.composer/cache/files - key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.composer/cache/files + key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} - - name: Set ldap.conf file permissions - run: sudo chown -R $USER:$USER /etc/ldap/ldap.conf + - name: Set ldap.conf file permissions + run: sudo chown -R $USER:$USER /etc/ldap/ldap.conf - - name: Create ldap.conf file disabling TLS verification - run: sudo echo "TLS_REQCERT never" > "/etc/ldap/ldap.conf" + - name: Create ldap.conf file disabling TLS verification + run: sudo echo "TLS_REQCERT never" > "/etc/ldap/ldap.conf" - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: ldap, json - coverage: none + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ldap, json + coverage: none - - name: Install dependencies - run: composer update --prefer-dist --no-interaction + - name: Install dependencies + run: composer update --prefer-dist --no-interaction - - name: Execute tests - run: vendor/bin/phpunit --testsuite Integration + - name: Execute tests + run: vendor/bin/phpunit --testsuite Integration diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-tests.yml b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-tests.yml index 6aaa3fd4a..8781e918a 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-tests.yml +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-tests.yml @@ -1,41 +1,41 @@ name: run-tests on: - push: - pull_request: - schedule: - - cron: "0 0 * * *" + push: + pull_request: + schedule: + - cron: "0 0 * * *" jobs: - run-tests: - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} - P${{ matrix.php }} + run-tests: + runs-on: ${{ matrix.os }} + name: ${{ matrix.os }} - P${{ matrix.php }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest] - php: [8.1, 8.0, 7.4, 7.3] + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + php: [8.1, 8.2, 8.3, 8.4] - steps: - - name: Checkout code - uses: actions/checkout@v2 + steps: + - name: Checkout code + uses: actions/checkout@v2 - - name: Cache dependencies - uses: actions/cache@v2 - with: - path: ~/.composer/cache/files - key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.composer/cache/files + key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: ldap, json - coverage: none + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ldap, json + coverage: none - - name: Install dependencies - run: composer update --prefer-dist --no-interaction + - name: Install dependencies + run: composer update --prefer-dist --no-interaction - - name: Execute tests - run: vendor/bin/phpunit --testsuite Unit + - name: Execute tests + run: vendor/bin/phpunit --testsuite Unit diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/.scrutinizer.yml b/data/web/inc/lib/vendor/directorytree/ldaprecord/.scrutinizer.yml deleted file mode 100644 index b3f8f88b2..000000000 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/.scrutinizer.yml +++ /dev/null @@ -1,9 +0,0 @@ -filter: - excluded_paths: - - tests/* -build: - nodes: - analysis: - tests: - override: - - command: php-scrutinizer-run diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/.styleci.yml b/data/web/inc/lib/vendor/directorytree/ldaprecord/.styleci.yml deleted file mode 100644 index 0285f1790..000000000 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/.styleci.yml +++ /dev/null @@ -1 +0,0 @@ -preset: laravel diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/composer.json b/data/web/inc/lib/vendor/directorytree/ldaprecord/composer.json index 65d7dd8f6..97e84cd5e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/composer.json +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/composer.json @@ -29,20 +29,22 @@ } ], "require": { - "php": ">=7.3", + "php": ">=8.1", "ext-ldap": "*", "ext-json": "*", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0", - "nesbot/carbon": "^1.0|^2.0", - "tightenco/collect": "^5.6|^6.0|^7.0|^8.0|^9.0", - "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", - "symfony/polyfill-php80": "^1.25" + "ext-iconv": "*", + "psr/log": "*", + "nesbot/carbon": "*", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/collections": "^8.0|^9.0|^10.0|^11.0|^12.0" }, "require-dev": { "phpunit/phpunit": "^9.0", "mockery/mockery": "^1.0", - "spatie/ray": "^1.24" + "spatie/ray": "^1.24", + "laravel/pint": "^1.6", + "fakerphp/faker": "^1.21" }, "archive": { "exclude": [ diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/docker-compose.yml b/data/web/inc/lib/vendor/directorytree/ldaprecord/docker-compose.yml index de96b1398..1abf0979d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/docker-compose.yml +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: ldap: image: osixia/openldap:1.4.0 diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/readme.md b/data/web/inc/lib/vendor/directorytree/ldaprecord/readme.md index e00f8ef69..4e34bd727 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/readme.md +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/readme.md @@ -1,25 +1,12 @@ - -

- +

- - - - - - - - - - - - - - - + + + +

@@ -32,9 +19,9 @@

- Quickstart + Quickstart · - Documentation + Documentation · Laravel Integration · @@ -67,15 +54,15 @@ Active Directory Features 🚪 **Enable / Disable Accounts** -Detect and assign User Account Control values on accounts with the fluent [Account Control builder](https://ldaprecord.com/docs/core/v2/active-directory/users/#uac). +Detect and assign User Account Control values on accounts with the fluent [Account Control builder](https://ldaprecord.com/docs/core/v3/active-directory/users/#uac). 🔑 **Reset / Change Passwords** -Built-in support for [changing](https://ldaprecord.com/docs/core/v2/active-directory/users/#changing-passwords) and [resetting](https://ldaprecord.com/docs/core/v2/active-directory/users/#resetting-passwords) passwords on Active Directory accounts. +Built-in support for [changing](https://ldaprecord.com/docs/core/v3/active-directory/users/#changing-passwords) and [resetting](https://ldaprecord.com/docs/core/v3/active-directory/users/#resetting-passwords) passwords on Active Directory accounts. 🗑 **Restore Deleted Objects** -We've all been there -- accidentally deleting a user or group in Active Directory. [Restore them](https://ldaprecord.com/docs/core/v2/models/#restoring-deleted-models) by seamlessly accessing your directory's recycle bin. +Seamlessly access your Active Directory recycle bin and [restore deleted objects](https://ldaprecord.com/docs/core/v3/models/#restoring-deleted-models). --- diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Event.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Event.php index 7c1bb3c87..29687dbf2 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Event.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Event.php @@ -8,33 +8,23 @@ abstract class Event { /** * The connection that the username and password is being bound on. - * - * @var LdapInterface */ - protected $connection; + protected LdapInterface $connection; /** * The username that is being used for binding. - * - * @var string */ - protected $username; + protected ?string $username; /** * The password that is being used for binding. - * - * @var string */ - protected $password; + protected ?string $password; /** * Constructor. - * - * @param LdapInterface $connection - * @param string $username - * @param string $password */ - public function __construct(LdapInterface $connection, $username, $password) + public function __construct(LdapInterface $connection, ?string $username = null, ?string $password = null) { $this->connection = $connection; $this->username = $username; @@ -42,31 +32,25 @@ abstract class Event } /** - * Returns the events connection. - * - * @return LdapInterface + * Get the event's connection. */ - public function getConnection() + public function getConnection(): LdapInterface { return $this->connection; } /** - * Returns the authentication events username. - * - * @return string + * Get the authentication event's username. */ - public function getUsername() + public function getUsername(): ?string { return $this->username; } /** - * Returns the authentication events password. - * - * @return string + * Get the authentication event's password. */ - public function getPassword() + public function getPassword(): ?string { return $this->password; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Failed.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Failed.php index 7133e4390..b26049169 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Failed.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Failed.php @@ -2,7 +2,32 @@ namespace LdapRecord\Auth\Events; +use Exception; +use LdapRecord\Auth\BindException; +use LdapRecord\LdapInterface; + class Failed extends Event { - // + /** + * The exception that was thrown during the bind attempt. + */ + protected BindException $exception; + + /** + * Constructor. + */ + public function __construct(LdapInterface $connection, ?string $username, ?string $password, BindException $exception) + { + parent::__construct($connection, $username, $password); + + $this->exception = $exception; + } + + /** + * Get the exception that was thrown during the bind attempt. + */ + public function getException(): BindException + { + return $this->exception; + } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Guard.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Guard.php index c00989ae6..d560098b7 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Guard.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Guard.php @@ -3,11 +3,6 @@ namespace LdapRecord\Auth; use Exception; -use LdapRecord\Auth\Events\Attempting; -use LdapRecord\Auth\Events\Binding; -use LdapRecord\Auth\Events\Bound; -use LdapRecord\Auth\Events\Failed; -use LdapRecord\Auth\Events\Passed; use LdapRecord\Configuration\DomainConfiguration; use LdapRecord\Events\DispatcherInterface; use LdapRecord\LdapInterface; @@ -16,30 +11,21 @@ class Guard { /** * The connection to bind to. - * - * @var LdapInterface */ - protected $connection; + protected LdapInterface $connection; /** * The domain configuration to utilize. - * - * @var DomainConfiguration */ - protected $configuration; + protected DomainConfiguration $configuration; /** * The event dispatcher. - * - * @var DispatcherInterface */ - protected $events; + protected ?DispatcherInterface $events = null; /** * Constructor. - * - * @param LdapInterface $connection - * @param DomainConfiguration $configuration */ public function __construct(LdapInterface $connection, DomainConfiguration $configuration) { @@ -50,15 +36,10 @@ class Guard /** * Attempt binding a user to the LDAP server. * - * @param string $username - * @param string $password - * @param bool $stayBound - * @return bool - * * @throws UsernameRequiredException * @throws PasswordRequiredException */ - public function attempt($username, $password, $stayBound = false) + public function attempt(string $username, string $password, bool $stayBound = false): bool { switch (true) { case empty($username): @@ -67,58 +48,73 @@ class Guard throw new PasswordRequiredException('A password must be specified.'); } - $this->fireAttemptingEvent($username, $password); + $this->fireAuthEvent('attempting', $username, $password); try { $this->bind($username, $password); - $authenticated = true; + $bound = true; - $this->firePassedEvent($username, $password); - } catch (BindException $e) { - $authenticated = false; + $this->fireAuthEvent('passed', $username, $password); + } catch (BindException) { + $bound = false; } if (! $stayBound) { $this->bindAsConfiguredUser(); } - return $authenticated; + return $bound; } /** - * Attempt binding a user to the LDAP server. Supports anonymous binding. - * - * @param string|null $username - * @param string|null $password + * Attempt binding a user to the LDAP server. Supports sasl and anonymous binding. * * @throws BindException * @throws \LdapRecord\ConnectionException */ - public function bind($username = null, $password = null) + public function bind(?string $username = null, ?string $password = null): void { - $this->fireBindingEvent($username, $password); + $this->fireAuthEvent('binding', $username, $password); // Prior to binding, we will upgrade our connectivity to TLS on our current // connection and ensure we are not already bound before upgrading. // This is to prevent subsequent upgrading on several binds. - if ($this->connection->isUsingTLS() && ! $this->connection->isBound()) { + if ($this->connection->isUsingTLS() && ! $this->connection->isSecure()) { $this->connection->startTLS(); } try { - if (! $this->connection->bind($username, $password)) { + if (! $this->authenticate($username, $password)) { throw new Exception($this->connection->getLastError(), $this->connection->errNo()); } - $this->fireBoundEvent($username, $password); + $this->fireAuthEvent('bound', $username, $password); } catch (Exception $e) { - $this->fireFailedEvent($username, $password); + $exception = BindException::withDetailedError($e, $this->connection->getDetailedError()); - throw BindException::withDetailedError($e, $this->connection->getDetailedError()); + $this->fireAuthEvent('failed', $username, $password, $exception); + + throw $exception; } } + /** + * Authenticate by binding to the LDAP server. + * + * @throws \LdapRecord\ConnectionException + */ + protected function authenticate(?string $username = null, ?string $password = null): bool + { + if ($this->configuration->get('use_sasl') ?? false) { + return $this->connection->saslBind( + $username, $password, $this->configuration->get('sasl_options') + ); + } + + return $this->connection->bind($username, $password)->successful(); + } + /** * Bind to the LDAP server using the configured username and password. * @@ -126,7 +122,7 @@ class Guard * @throws \LdapRecord\ConnectionException * @throws \LdapRecord\Configuration\ConfigurationException */ - public function bindAsConfiguredUser() + public function bindAsConfiguredUser(): void { $this->bind( $this->configuration->get('username'), @@ -136,92 +132,29 @@ class Guard /** * Get the event dispatcher instance. - * - * @return DispatcherInterface */ - public function getDispatcher() + public function getDispatcher(): ?DispatcherInterface { return $this->events; } /** * Set the event dispatcher instance. - * - * @param DispatcherInterface $dispatcher - * @return void */ - public function setDispatcher(DispatcherInterface $dispatcher) + public function setDispatcher(?DispatcherInterface $dispatcher = null): void { $this->events = $dispatcher; } /** - * Fire the attempting event. - * - * @param string $username - * @param string $password - * @return void + * Fire an authentication event. */ - protected function fireAttemptingEvent($username, $password) + protected function fireAuthEvent(string $name, ?string $username = null, ?string $password = null, ...$args): void { if (isset($this->events)) { - $this->events->fire(new Attempting($this->connection, $username, $password)); - } - } + $event = implode('\\', [Events::class, ucfirst($name)]); - /** - * Fire the passed event. - * - * @param string $username - * @param string $password - * @return void - */ - protected function firePassedEvent($username, $password) - { - if (isset($this->events)) { - $this->events->fire(new Passed($this->connection, $username, $password)); - } - } - - /** - * Fire the failed event. - * - * @param string $username - * @param string $password - * @return void - */ - protected function fireFailedEvent($username, $password) - { - if (isset($this->events)) { - $this->events->fire(new Failed($this->connection, $username, $password)); - } - } - - /** - * Fire the binding event. - * - * @param string $username - * @param string $password - * @return void - */ - protected function fireBindingEvent($username, $password) - { - if (isset($this->events)) { - $this->events->fire(new Binding($this->connection, $username, $password)); - } - } - - /** - * Fire the bound event. - * - * @param string $username - * @param string $password - * @return void - */ - protected function fireBoundEvent($username, $password) - { - if (isset($this->events)) { - $this->events->fire(new Bound($this->connection, $username, $password)); + $this->events->fire(new $event($this->connection, $username, $password, ...$args)); } } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/DomainConfiguration.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/DomainConfiguration.php index f71f63d87..425fbd9ed 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/DomainConfiguration.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/DomainConfiguration.php @@ -8,19 +8,15 @@ class DomainConfiguration { /** * The extended configuration options. - * - * @var array */ - protected static $extended = []; + protected static array $extended = []; /** * The configuration options array. * * The default values for each key indicate the type of value it requires. - * - * @var array */ - protected $options = [ + protected array $options = [ // An array of LDAP hosts. 'hosts' => [], @@ -33,6 +29,9 @@ class DomainConfiguration // The port to use for connecting to your hosts. 'port' => LdapInterface::PORT, + // The protocol to use for connecting to your hosts (ldap:// or ldaps://). + 'protocol' => null, + // The base distinguished name of your domain. 'base_dn' => '', @@ -42,13 +41,28 @@ class DomainConfiguration // The password to use for binding. 'password' => '', - // Whether or not to use SSL when connecting. + // Whether to use SSL when connecting. 'use_ssl' => false, - // Whether or not to use TLS when connecting. + // Whether to use TLS when connecting. 'use_tls' => false, - // Whether or not follow referrals is enabled when performing LDAP operations. + // Whether to use SASL when connecting. + 'use_sasl' => false, + + // Whether to allow password changes over plaintext. + 'allow_insecure_password_changes' => false, + + // SASL options + 'sasl_options' => [ + 'mech' => null, + 'realm' => null, + 'authc_id' => null, + 'authz_id' => null, + 'props' => null, + ], + + // Whether follow referrals is enabled when performing LDAP operations. 'follow_referrals' => false, // Custom LDAP options. @@ -58,8 +72,6 @@ class DomainConfiguration /** * Constructor. * - * @param array $options - * * @throws ConfigurationException When an option value given is an invalid type. */ public function __construct(array $options = []) @@ -73,32 +85,24 @@ class DomainConfiguration /** * Extend the configuration with a custom option, or override an existing. - * - * @param string $option - * @param mixed $default - * @return void */ - public static function extend($option, $default = null) + public static function extend(string $option, mixed $default = null): void { static::$extended[$option] = $default; } /** * Flush the extended configuration options. - * - * @return void */ - public static function flushExtended() + public static function flushExtended(): void { static::$extended = []; } /** * Get all configuration options. - * - * @return array */ - public function all() + public function all(): array { return $this->options; } @@ -106,12 +110,9 @@ class DomainConfiguration /** * Set a configuration option. * - * @param string $key - * @param mixed $value - * * @throws ConfigurationException When an option value given is an invalid type. */ - public function set($key, $value) + public function set(string $key, mixed $value): void { if ($this->validate($key, $value)) { $this->options[$key] = $value; @@ -119,17 +120,14 @@ class DomainConfiguration } /** - * Returns the value for the specified configuration options. - * - * @param string $key - * @return mixed + * Get the value for the specified configuration options. * * @throws ConfigurationException When the option specified does not exist. */ - public function get($key) + public function get(string $key): mixed { if (! $this->has($key)) { - throw new ConfigurationException("Option {$key} does not exist."); + throw new ConfigurationException("Option $key does not exist."); } return $this->options[$key]; @@ -137,11 +135,8 @@ class DomainConfiguration /** * Checks if a configuration option exists. - * - * @param string $key - * @return bool */ - public function has($key) + public function has(string $key): bool { return array_key_exists($key, $this->options); } @@ -149,13 +144,9 @@ class DomainConfiguration /** * Validate the configuration option. * - * @param string $key - * @param mixed $value - * @return bool - * * @throws ConfigurationException When an option value given is an invalid type. */ - protected function validate($key, $value) + protected function validate(string $key, mixed $value): bool { $default = $this->get($key); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/ArrayValidator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/ArrayValidator.php index 4aa43ed46..1324d1681 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/ArrayValidator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/ArrayValidator.php @@ -6,15 +6,13 @@ class ArrayValidator extends Validator { /** * The validation exception message. - * - * @var string */ - protected $message = 'Option [:option] must be an array.'; + protected string $message = 'Option [:option] must be an array.'; /** - * @inheritdoc + * {@inheritdoc} */ - public function passes() + public function passes(): bool { return is_array($this->value); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/BooleanValidator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/BooleanValidator.php index 1d25a4b25..1156859d3 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/BooleanValidator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/BooleanValidator.php @@ -6,15 +6,13 @@ class BooleanValidator extends Validator { /** * The validation exception message. - * - * @var string */ - protected $message = 'Option [:option] must be a boolean.'; + protected string $message = 'Option [:option] must be a boolean.'; /** - * @inheritdoc + * {@inheritdoc} */ - public function passes() + public function passes(): bool { return is_bool($this->value); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/IntegerValidator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/IntegerValidator.php index 5c4f0f95e..d7015888e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/IntegerValidator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/IntegerValidator.php @@ -6,15 +6,13 @@ class IntegerValidator extends Validator { /** * The validation exception message. - * - * @var string */ - protected $message = 'Option [:option] must be an integer.'; + protected string $message = 'Option [:option] must be an integer.'; /** - * @inheritdoc + * {@inheritdoc} */ - public function passes() + public function passes(): bool { return is_numeric($this->value); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/StringOrNullValidator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/StringOrNullValidator.php index bc2337245..5ca453c74 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/StringOrNullValidator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/StringOrNullValidator.php @@ -6,15 +6,13 @@ class StringOrNullValidator extends Validator { /** * The validation exception message. - * - * @var string */ - protected $message = 'Option [:option] must be a string or null.'; + protected string $message = 'Option [:option] must be a string or null.'; /** - * @inheritdoc + * {@inheritdoc} */ - public function passes() + public function passes(): bool { return is_string($this->value) || is_null($this->value); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/Validator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/Validator.php index d107314d5..25aedcbfa 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/Validator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/Validator.php @@ -8,32 +8,23 @@ abstract class Validator { /** * The configuration key under validation. - * - * @var string */ - protected $key; + protected string $key; /** * The configuration value under validation. - * - * @var mixed */ - protected $value; + protected mixed $value; /** * The validation exception message. - * - * @var string */ - protected $message; + protected string $message; /** * Constructor. - * - * @param string $key - * @param mixed $value */ - public function __construct($key, $value) + public function __construct(string $key, mixed $value) { $this->key = $key; $this->value = $value; @@ -41,19 +32,15 @@ abstract class Validator /** * Determine if the validation rule passes. - * - * @return bool */ - abstract public function passes(); + abstract public function passes(): bool; /** * Validate the configuration value. * - * @return bool - * * @throws ConfigurationException */ - public function validate() + public function validate(): bool { if (! $this->passes()) { $this->fail(); @@ -65,11 +52,9 @@ abstract class Validator /** * Throw a configuration exception. * - * @return void - * * @throws ConfigurationException */ - protected function fail() + protected function fail(): void { throw new ConfigurationException( str_replace(':option', $this->key, $this->message) diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php index 6e55cffc9..e04393a27 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php @@ -5,6 +5,7 @@ namespace LdapRecord; use Carbon\Carbon; use Closure; use LdapRecord\Auth\Guard; +use LdapRecord\Configuration\ConfigurationException; use LdapRecord\Configuration\DomainConfiguration; use LdapRecord\Events\DispatcherInterface; use LdapRecord\Query\Builder; @@ -17,85 +18,64 @@ class Connection /** * The underlying LDAP connection. - * - * @var Ldap */ - protected $ldap; + protected LdapInterface $ldap; /** * The cache driver. - * - * @var Cache|null */ - protected $cache; + protected ?Cache $cache = null; /** * The domain configuration. - * - * @var DomainConfiguration */ - protected $configuration; + protected DomainConfiguration $configuration; /** - * The event dispatcher;. - * - * @var DispatcherInterface|null + * The event dispatcher. */ - protected $dispatcher; + protected ?DispatcherInterface $dispatcher = null; /** - * The current host connected to. - * - * @var string + * The currently connected host. */ - protected $host; + protected string $host; /** * The configured domain hosts. - * - * @var array */ - protected $hosts = []; + protected array $hosts = []; /** * The attempted hosts that failed connecting to. - * - * @var array */ - protected $attempted = []; + protected array $attempted = []; /** * The callback to execute upon total connection failure. - * - * @var Closure */ - protected $failed; + protected Closure $failed; /** * The authentication guard resolver. - * - * @var Closure */ - protected $authGuardResolver; + protected Closure $authGuardResolver; /** * Whether the connection is retrying the initial connection attempt. - * - * @var bool */ - protected $retryingInitialConnection = false; + protected bool $retryingInitialConnection = false; /** * Constructor. * - * @param array|DomainConfiguration $config - * @param LdapInterface|null $ldap + * @throws ConfigurationException */ - public function __construct($config = [], LdapInterface $ldap = null) + public function __construct(DomainConfiguration|array $config = [], ?LdapInterface $ldap = null) { $this->setConfiguration($config); - $this->setLdapConnection($ldap ?? new Ldap()); + $this->setLdapConnection($ldap ?? new Ldap); $this->failed = function () { $this->dispatch(new Events\ConnectionFailed($this)); @@ -109,12 +89,9 @@ class Connection /** * Set the connection configuration. * - * @param array|DomainConfiguration $config - * @return $this - * * @throws Configuration\ConfigurationException */ - public function setConfiguration($config = []) + public function setConfiguration(DomainConfiguration|array $config = []): void { if (! $config instanceof DomainConfiguration) { $config = new DomainConfiguration($config); @@ -125,54 +102,50 @@ class Connection $this->hosts = $this->configuration->get('hosts'); $this->host = reset($this->hosts); - - return $this; } /** * Set the LDAP connection. - * - * @param LdapInterface $ldap - * @return $this */ - public function setLdapConnection(LdapInterface $ldap) + public function setLdapConnection(LdapInterface $ldap): void { $this->ldap = $ldap; - - return $this; } /** * Set the event dispatcher. - * - * @param DispatcherInterface $dispatcher - * @return $this */ - public function setDispatcher(DispatcherInterface $dispatcher) + public function setDispatcher(DispatcherInterface $dispatcher): void { $this->dispatcher = $dispatcher; - - return $this; } /** - * Initializes the LDAP connection. - * - * @return void + * Get the event dispatcher. */ - public function initialize() + public function getDispatcher(): ?DispatcherInterface + { + return $this->dispatcher; + } + + /** + * Initialize the LDAP connection. + */ + public function initialize(): void { $this->configure(); - $this->ldap->connect($this->host, $this->configuration->get('port')); + $this->ldap->connect( + $this->host, + $this->configuration->get('port'), + $this->configuration->get('protocol') + ); } /** * Configure the LDAP connection. - * - * @return void */ - protected function configure() + protected function configure(): void { if ($this->configuration->get('use_ssl')) { $this->ldap->ssl(); @@ -192,60 +165,53 @@ class Connection /** * Set the cache store. - * - * @param CacheInterface $store - * @return $this */ - public function setCache(CacheInterface $store) + public function setCache(CacheInterface $store): void { $this->cache = new Cache($store); - - return $this; } /** * Get the cache store. - * - * @return Cache|null */ - public function getCache() + public function getCache(): ?Cache { return $this->cache; } /** * Get the LDAP configuration instance. - * - * @return DomainConfiguration */ - public function getConfiguration() + public function getConfiguration(): DomainConfiguration { return $this->configuration; } /** * Get the LDAP connection instance. - * - * @return Ldap */ - public function getLdapConnection() + public function getLdapConnection(): LdapInterface { return $this->ldap; } + /** + * Set the auth guard resolver callback. + */ + public function setGuardResolver(Closure $callback): void + { + $this->authGuardResolver = $callback; + } + /** * Bind to the LDAP server. * * If no username or password is specified, then the configured credentials are used. * - * @param string|null $username - * @param string|null $password - * @return Connection - * * @throws Auth\BindException * @throws LdapRecordException */ - public function connect($username = null, $password = null) + public function connect(?string $username = null, ?string $password = null): void { $attempt = function () use ($username, $password) { $this->dispatch(new Events\Connecting($this)); @@ -266,19 +232,16 @@ class Connection $this->retryOnNextHost($e, $attempt); } - - return $this; } /** * Reconnect to the LDAP server. * - * @return void - * * @throws Auth\BindException * @throws ConnectionException + * @throws LdapRecordException */ - public function reconnect() + public function reconnect(): void { $this->reinitialize(); @@ -287,10 +250,8 @@ class Connection /** * Reinitialize the connection. - * - * @return void */ - protected function reinitialize() + protected function reinitialize(): void { $this->disconnect(); @@ -299,31 +260,24 @@ class Connection /** * Clone the connection. - * - * @return static */ - public function replicate() + public function replicate(): static { return new static($this->configuration, new $this->ldap); } /** * Disconnect from the LDAP server. - * - * @return void */ - public function disconnect() + public function disconnect(): void { $this->ldap->close(); } /** * Dispatch an event. - * - * @param object $event - * @return void */ - public function dispatch($event) + public function dispatch(object $event): void { if (isset($this->dispatcher)) { $this->dispatcher->dispatch($event); @@ -332,25 +286,20 @@ class Connection /** * Get the attempted hosts that failed connecting to. - * - * @return array */ - public function attempted() + public function attempted(): array { return $this->attempted; } /** * Perform the operation on the LDAP connection. - * - * @param Closure $operation - * @return mixed */ - public function run(Closure $operation) + public function run(Closure $operation): mixed { try { // Before running the operation, we will check if the current - // connection is bound and connect if necessary. Otherwise + // connection is bound and connect if necessary. Otherwise, // some LDAP operations will not be executed properly. if (! $this->isConnected()) { $this->connect(); @@ -368,11 +317,8 @@ class Connection /** * Perform the operation on an isolated LDAP connection. - * - * @param Closure $operation - * @return mixed */ - public function isolate(Closure $operation) + public function isolate(Closure $operation): mixed { $connection = $this->replicate(); @@ -385,11 +331,8 @@ class Connection /** * Attempt to get an exception for the cause of failure. - * - * @param LdapRecordException $e - * @return mixed */ - protected function getExceptionForCauseOfFailure(LdapRecordException $e) + protected function getExceptionForCauseOfFailure(LdapRecordException $e): ?LdapRecordException { switch (true) { case $this->errorContainsMessage($e->getMessage(), 'Already exists'): @@ -399,29 +342,22 @@ class Connection case $this->errorContainsMessage($e->getMessage(), 'Constraint violation'): return Exceptions\ConstraintViolationException::withDetailedError($e, $e->getDetailedError()); default: - return; + return null; } } /** * Run the operation callback on the current LDAP connection. - * - * @param Closure $operation - * @return mixed - * - * @throws LdapRecordException */ - protected function runOperationCallback(Closure $operation) + protected function runOperationCallback(Closure $operation): mixed { return $operation($this->ldap); } /** * Get a new auth guard instance. - * - * @return Auth\Guard */ - public function auth() + public function auth(): Guard { if (! $this->ldap->isConnected()) { $this->initialize(); @@ -430,7 +366,7 @@ class Connection $guard = call_user_func($this->authGuardResolver); $guard->setDispatcher( - Container::getInstance()->getEventDispatcher() + Container::getInstance()->getDispatcher() ); return $guard; @@ -438,10 +374,8 @@ class Connection /** * Get a new query builder for the connection. - * - * @return Query\Builder */ - public function query() + public function query(): Builder { return (new Builder($this)) ->setCache($this->cache) @@ -450,10 +384,8 @@ class Connection /** * Determine if the LDAP connection is bound. - * - * @return bool */ - public function isConnected() + public function isConnected(): bool { return $this->ldap->isBound(); } @@ -461,13 +393,9 @@ class Connection /** * Attempt to retry an LDAP operation if due to a lost connection. * - * @param LdapRecordException $e - * @param Closure $operation - * @return mixed - * * @throws LdapRecordException */ - protected function tryAgainIfCausedByLostConnection(LdapRecordException $e, Closure $operation) + protected function tryAgainIfCausedByLostConnection(LdapRecordException $e, Closure $operation): mixed { // If the operation failed due to a lost or failed connection, // we'll attempt reconnecting and running the operation again @@ -482,12 +410,9 @@ class Connection /** * Retry the operation on the current host. * - * @param Closure $operation - * @return mixed - * * @throws LdapRecordException */ - protected function retry(Closure $operation) + protected function retry(Closure $operation): mixed { try { $this->retryingInitialConnection @@ -503,13 +428,9 @@ class Connection /** * Attempt the operation again on the next host. * - * @param LdapRecordException $e - * @param Closure $operation - * @return mixed - * * @throws LdapRecordException */ - protected function retryOnNextHost(LdapRecordException $e, Closure $operation) + protected function retryOnNextHost(LdapRecordException $e, Closure $operation): mixed { $this->attempted[$this->host] = Carbon::now(); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/ConnectionManager.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/ConnectionManager.php index 0597f1f68..8bef8d150 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/ConnectionManager.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/ConnectionManager.php @@ -2,7 +2,6 @@ namespace LdapRecord; -use BadMethodCallException; use LdapRecord\Events\Dispatcher; use LdapRecord\Events\DispatcherInterface; use LdapRecord\Events\Logger; @@ -10,148 +9,83 @@ use Psr\Log\LoggerInterface; class ConnectionManager { - /** - * The logger instance. - * - * @var LoggerInterface|null - */ - protected $logger; - - /** - * The event dispatcher instance. - * - * @var DispatcherInterface|null - */ - protected $dispatcher; - /** * The added LDAP connections. * * @var Connection[] */ - protected $connections = []; + protected array $connections = []; /** * The name of the default connection. - * - * @var string */ - protected $default = 'default'; + protected string $default = 'default'; /** * The events to register listeners for during initialization. - * - * @var array */ - protected $listen = [ + protected array $listen = [ 'LdapRecord\Auth\Events\*', 'LdapRecord\Query\Events\*', 'LdapRecord\Models\Events\*', ]; /** - * The method calls to proxy for compatibility. - * - * To be removed in the next major version. - * - * @var array + * The logger instance. */ - protected $proxy = [ - 'reset' => 'flush', - 'addConnection' => 'add', - 'getConnection' => 'get', - 'allConnections' => 'all', - 'removeConnection' => 'remove', - 'getDefaultConnection' => 'getDefault', - 'setDefaultConnection' => 'setDefault', - 'getEventDispatcher' => 'dispatcher', - 'setEventDispatcher' => 'setDispatcher', - ]; + protected ?LoggerInterface $logger = null; + + /** + * The event dispatcher instance. + */ + protected ?DispatcherInterface $dispatcher = null; /** * Constructor. - * - * @return void */ - public function __construct() + public function __construct($dispatcher = new Dispatcher) { - $this->dispatcher = new Dispatcher(); - } - - /** - * Forward missing method calls onto the instance. - * - * @param string $method - * @param mixed $args - * @return mixed - */ - public function __call($method, $args) - { - $method = $this->proxy[$method] ?? $method; - - if (! method_exists($this, $method)) { - throw new BadMethodCallException(sprintf( - 'Call to undefined method %s::%s()', - static::class, - $method - )); - } - - return $this->{$method}(...$args); + $this->dispatcher = $dispatcher; } /** * Add a new connection. - * - * @param Connection $connection - * @param string|null $name - * @return $this */ - public function add(Connection $connection, $name = null) + public function addConnection(Connection $connection, ?string $name = null): void { $this->connections[$name ?? $this->default] = $connection; if ($this->dispatcher) { $connection->setDispatcher($this->dispatcher); } - - return $this; } /** - * Remove a connection. - * - * @param $name - * @return $this + * Remove a connection by its name. */ - public function remove($name) + public function removeConnection(string $name): void { unset($this->connections[$name]); - - return $this; } /** - * Get all of the connections. + * Get all the registered connections. * * @return Connection[] */ - public function all() + public function getConnections(): array { return $this->connections; } /** - * Get a connection by name or return the default. - * - * @param string|null $name - * @return Connection + * Get a connection by its name or return the default. * * @throws ContainerException If the given connection does not exist. */ - public function get($name = null) + public function getConnection(?string $name = null): Connection { - if ($this->exists($name = $name ?? $this->default)) { + if ($this->hasConnection($name = $name ?? $this->default)) { return $this->connections[$name]; } @@ -159,82 +93,61 @@ class ConnectionManager } /** - * Return the default connection. - * - * @return Connection + * Get the default connection. */ - public function getDefault() + public function getDefaultConnection(): Connection { - return $this->get($this->default); + return $this->getConnection($this->default); + } + + /** + * Set the default connection by its name. + */ + public function setDefaultConnection(string $name): void + { + $this->default = $name; } /** * Get the default connection name. - * - * @return string */ - public function getDefaultConnectionName() + public function getDefaultConnectionName(): string { return $this->default; } /** - * Checks if the connection exists. - * - * @param string $name - * @return bool + * Determine if a connection exists. */ - public function exists($name) + public function hasConnection(string $name): bool { return array_key_exists($name, $this->connections); } - /** - * Set the default connection name. - * - * @param string $name - * @return $this - */ - public function setDefault($name = null) - { - $this->default = $name; - - return $this; - } - /** * Flush the manager of all instances and connections. - * - * @return $this */ - public function flush() + public function flush(): void { $this->logger = null; $this->connections = []; - $this->dispatcher = new Dispatcher(); - - return $this; + $this->dispatcher->forgetAll(); } /** * Get the logger instance. - * - * @return LoggerInterface|null */ - public function getLogger() + public function getLogger(): ?LoggerInterface { return $this->logger; } /** * Set the event logger to use. - * - * @param LoggerInterface $logger - * @return void */ - public function setLogger(LoggerInterface $logger) + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; @@ -243,10 +156,8 @@ class ConnectionManager /** * Initialize the event logger. - * - * @return void */ - public function initEventLogger() + protected function initEventLogger(): void { $logger = $this->newEventLogger(); @@ -261,51 +172,40 @@ class ConnectionManager /** * Make a new event logger instance. - * - * @return Logger */ - protected function newEventLogger() + protected function newEventLogger(): Logger { return new Logger($this->logger); } /** * Unset the logger instance. - * - * @return void */ - public function unsetLogger() + public function unsetLogger(): void { $this->logger = null; } /** * Get the event dispatcher. - * - * @return DispatcherInterface|null */ - public function dispatcher() + public function getDispatcher(): ?DispatcherInterface { return $this->dispatcher; } /** * Set the event dispatcher. - * - * @param DispatcherInterface $dispatcher - * @return void */ - public function setDispatcher(DispatcherInterface $dispatcher) + public function setDispatcher(DispatcherInterface $dispatcher): void { $this->dispatcher = $dispatcher; } /** * Unset the event dispatcher. - * - * @return void */ - public function unsetEventDispatcher() + public function unsetDispatcher(): void { $this->dispatcher = null; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Container.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Container.php index 0c125593d..5a3bd7f4c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Container.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Container.php @@ -2,122 +2,79 @@ namespace LdapRecord; -/** - * @method static $this reset() - * @method static Connection[] all() - * @method static Connection[] allConnections() - * @method static Connection getDefaultConnection() - * @method static Connection get(string|null $name = null) - * @method static Connection getConnection(string|null $name = null) - * @method static bool exists(string $name) - * @method static $this remove(string|null $name = null) - * @method static $this removeConnection(string|null $name = null) - * @method static $this setDefault(string|null $name = null) - * @method static $this setDefaultConnection(string|null $name = null) - * @method static $this add(Connection $connection, string|null $name = null) - * @method static $this addConnection(Connection $connection, string|null $name = null) - */ +/** @mixin ConnectionManager */ class Container { /** * The current container instance. - * - * @var Container */ - protected static $instance; + protected static Container $instance; /** * The connection manager instance. - * - * @var ConnectionManager */ - protected $manager; - - /** - * The methods to passthru, for compatibility. - * - * @var array - */ - protected $passthru = [ - 'reset', 'flush', - 'add', 'addConnection', - 'remove', 'removeConnection', - 'setDefault', 'setDefaultConnection', - ]; - - /** - * Forward missing static calls onto the current instance. - * - * @param string $method - * @param mixed $args - * @return mixed - */ - public static function __callStatic($method, $args) - { - return static::getInstance()->{$method}(...$args); - } + protected ConnectionManager $manager; /** * Get or set the current instance of the container. - * - * @return Container */ - public static function getInstance() + public static function getInstance(): static { return static::$instance ?? static::getNewInstance(); } /** * Set the container instance. - * - * @param Container|null $container - * @return Container|null */ - public static function setInstance(self $container = null) + public static function setInstance(?self $container = null): ?static { return static::$instance = $container; } /** * Set and get a new instance of the container. - * - * @return Container */ - public static function getNewInstance() + public static function getNewInstance(): static { - return static::setInstance(new static()); + return static::setInstance(new static); + } + + /** + * Forward missing static calls onto the current instance. + */ + public static function __callStatic(string $method, array $parameters): mixed + { + return static::getInstance()->{$method}(...$parameters); } /** * Constructor. - * - * @return void */ - public function __construct() + public function __construct(ConnectionManager $manager = new ConnectionManager) { - $this->manager = new ConnectionManager(); + $this->manager = $manager; } /** * Forward missing method calls onto the connection manager. - * - * @param string $method - * @param mixed $args - * @return mixed */ - public function __call($method, $args) + public function __call(string $method, array $parameters): mixed { - $value = $this->manager->{$method}(...$args); + return $this->manager->{$method}(...$parameters); + } - return in_array($method, $this->passthru) ? $this : $value; + /** + * Set the current container instance available globally. + */ + public function setAsGlobal(): void + { + static::setInstance($this); } /** * Get the connection manager. - * - * @return ConnectionManager */ - public function manager() + public function getConnectionManager(): ConnectionManager { return $this->manager; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetailedError.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetailedError.php index fff528c98..e9d9609dc 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetailedError.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetailedError.php @@ -4,67 +4,35 @@ namespace LdapRecord; class DetailedError { - /** - * The error code from ldap_errno. - * - * @var int|null - */ - protected $errorCode; - - /** - * The error message from ldap_error. - * - * @var string|null - */ - protected $errorMessage; - - /** - * The diagnostic message when retrieved after an ldap_error. - * - * @var string|null - */ - protected $diagnosticMessage; - /** * Constructor. - * - * @param int $errorCode - * @param string $errorMessage - * @param string $diagnosticMessage */ - public function __construct($errorCode, $errorMessage, $diagnosticMessage) - { - $this->errorCode = $errorCode; - $this->errorMessage = $errorMessage; - $this->diagnosticMessage = $diagnosticMessage; - } + public function __construct( + protected int $errorCode, + protected string $errorMessage, + protected ?string $diagnosticMessage + ) {} /** - * Returns the LDAP error code. - * - * @return int + * Get the LDAP error code. */ - public function getErrorCode() + public function getErrorCode(): int { return $this->errorCode; } /** - * Returns the LDAP error message. - * - * @return string + * Get the LDAP error message. */ - public function getErrorMessage() + public function getErrorMessage(): string { return $this->errorMessage; } /** - * Returns the LDAP diagnostic message. - * - * @return string + * Get the LDAP diagnostic message. */ - public function getDiagnosticMessage() + public function getDiagnosticMessage(): ?string { return $this->diagnosticMessage; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetectsErrors.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetectsErrors.php index 61fc4a02e..bb6f675f0 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetectsErrors.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetectsErrors.php @@ -6,59 +6,43 @@ trait DetectsErrors { /** * Determine if the error was caused by a lost connection. - * - * @param string $error - * @return bool */ - protected function causedByLostConnection($error) + protected function causedByLostConnection(string $error): bool { return $this->errorContainsMessage($error, ["Can't contact LDAP server", 'Operations error']); } /** * Determine if the error was caused by lack of pagination support. - * - * @param string $error - * @return bool */ - protected function causedByPaginationSupport($error) + protected function causedByPaginationSupport(string $error): bool { return $this->errorContainsMessage($error, 'No server controls in result'); } /** * Determine if the error was caused by a size limit warning. - * - * @param $error - * @return bool */ - protected function causedBySizeLimit($error) + protected function causedBySizeLimit(string $error): bool { return $this->errorContainsMessage($error, ['Partial search results returned', 'Size limit exceeded']); } /** * Determine if the error was caused by a "No such object" warning. - * - * @param string $error - * @return bool */ - protected function causedByNoSuchObject($error) + protected function causedByNoSuchObject(string $error): bool { return $this->errorContainsMessage($error, ['No such object']); } /** - * Determine if the error contains the any of the messages. - * - * @param string $error - * @param string|array $messages - * @return bool + * Determine if the error contains any of the messages. */ - protected function errorContainsMessage($error, $messages = []) + protected function errorContainsMessage(string $error, string|array $messages = []): bool { foreach ((array) $messages as $message) { - if (str_contains((string) $error, $message)) { + if (str_contains($error, $message)) { return true; } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/EscapesValues.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/EscapesValues.php index ea53a2f93..088b50013 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/EscapesValues.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/EscapesValues.php @@ -8,13 +8,8 @@ trait EscapesValues { /** * Prepare a value to be escaped. - * - * @param string $value - * @param string $ignore - * @param int $flags - * @return EscapedValue */ - public function escape($value, $ignore = '', $flags = 0) + public function escape(mixed $value = null, string $ignore = '', int $flags = 0): EscapedValue { return new EscapedValue($value, $ignore, $flags); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/ConnectionEvent.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/ConnectionEvent.php index d2d77e2fc..99a6e6f1c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/ConnectionEvent.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/ConnectionEvent.php @@ -8,15 +8,11 @@ abstract class ConnectionEvent { /** * The LDAP connection. - * - * @var Connection */ - protected $connection; + protected Connection $connection; /** * Constructor. - * - * @param Connection $connection */ public function __construct(Connection $connection) { @@ -25,10 +21,8 @@ abstract class ConnectionEvent /** * Get the connection pertaining to the event. - * - * @return Connection */ - public function getConnection() + public function getConnection(): Connection { return $this->connection; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Dispatcher.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Dispatcher.php index 4fedbc2ef..304e15ccd 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Dispatcher.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Dispatcher.php @@ -2,7 +2,9 @@ namespace LdapRecord\Events; +use Closure; use LdapRecord\Support\Arr; +use LdapRecord\Support\Str; /** * Class Dispatcher. @@ -21,29 +23,23 @@ class Dispatcher implements DispatcherInterface { /** * The registered event listeners. - * - * @var array */ - protected $listeners = []; + protected array $listeners = []; /** * The wildcard listeners. - * - * @var array */ - protected $wildcards = []; + protected array $wildcards = []; /** * The cached wildcard listeners. - * - * @var array */ - protected $wildcardsCache = []; + protected array $wildcardsCache = []; /** - * @inheritdoc + * {@inheritdoc} */ - public function listen($events, $listener) + public function listen(string|array $events, mixed $listener): void { foreach ((array) $events as $event) { if (str_contains((string) $event, '*')) { @@ -56,12 +52,8 @@ class Dispatcher implements DispatcherInterface /** * Setup a wildcard listener callback. - * - * @param string $event - * @param mixed $listener - * @return void */ - protected function setupWildcardListen($event, $listener) + protected function setupWildcardListen(string $event, mixed $listener): void { $this->wildcards[$event][] = $this->makeListener($listener, true); @@ -69,33 +61,33 @@ class Dispatcher implements DispatcherInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function hasListeners($eventName) + public function hasListeners(string $event): bool { - return isset($this->listeners[$eventName]) || isset($this->wildcards[$eventName]); + return isset($this->listeners[$event]) || isset($this->wildcards[$event]); } /** - * @inheritdoc + * {@inheritdoc} */ - public function until($event, $payload = []) + public function until(string|object $event, mixed $payload = []): mixed { return $this->dispatch($event, $payload, true); } /** - * @inheritdoc + * {@inheritdoc} */ - public function fire($event, $payload = [], $halt = false) + public function fire(string|object $event, mixed $payload = [], bool $halt = false): void { - return $this->dispatch($event, $payload, $halt); + $this->dispatch($event, $payload, $halt); } /** - * @inheritdoc + * {@inheritdoc} */ - public function dispatch($event, $payload = [], $halt = false) + public function dispatch(string|object $event, mixed $payload = [], $halt = false): mixed { // When the given "event" is actually an object we will assume it is an event // object and use the class as the event name and this event itself as the @@ -132,12 +124,8 @@ class Dispatcher implements DispatcherInterface /** * Parse the given event and payload and prepare them for dispatching. - * - * @param mixed $event - * @param mixed $payload - * @return array */ - protected function parseEventAndPayload($event, $payload) + protected function parseEventAndPayload(string|object $event, mixed $payload): array { if (is_object($event)) { [$payload, $event] = [[$event], get_class($event)]; @@ -147,89 +135,42 @@ class Dispatcher implements DispatcherInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function getListeners($eventName) + public function getListeners(string $event): array { - $listeners = $this->listeners[$eventName] ?? []; + $listeners = $this->listeners[$event] ?? []; $listeners = array_merge( $listeners, - $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName) + $this->wildcardsCache[$event] ?? $this->getWildcardListeners($event) ); - return class_exists($eventName, false) - ? $this->addInterfaceListeners($eventName, $listeners) + return class_exists($event, false) + ? $this->addInterfaceListeners($event, $listeners) : $listeners; } /** * Get the wildcard listeners for the event. - * - * @param string $eventName - * @return array */ - protected function getWildcardListeners($eventName) + protected function getWildcardListeners(string $event): array { $wildcards = []; foreach ($this->wildcards as $key => $listeners) { - if ($this->wildcardContainsEvent($key, $eventName)) { + if (Str::is($key, $event)) { $wildcards = array_merge($wildcards, $listeners); } } - return $this->wildcardsCache[$eventName] = $wildcards; - } - - /** - * Determine if the wildcard matches or contains the given event. - * - * This function is a direct excerpt from Laravel's Str::is(). - * - * @param string $wildcard - * @param string $eventName - * @return bool - */ - protected function wildcardContainsEvent($wildcard, $eventName) - { - $patterns = Arr::wrap($wildcard); - - if (empty($patterns)) { - return false; - } - - foreach ($patterns as $pattern) { - // If the given event is an exact match we can of course return true right - // from the beginning. Otherwise, we will translate asterisks and do an - // actual pattern match against the two strings to see if they match. - if ($pattern == $eventName) { - return true; - } - - $pattern = preg_quote($pattern, '#'); - - // Asterisks are translated into zero-or-more regular expression wildcards - // to make it convenient to check if the strings starts with the given - // pattern such as "library/*", making any string check convenient. - $pattern = str_replace('\*', '.*', $pattern); - - if (preg_match('#^'.$pattern.'\z#u', $eventName) === 1) { - return true; - } - } - - return false; + return $this->wildcardsCache[$event] = $wildcards; } /** * Add the listeners for the event's interfaces to the given array. - * - * @param string $eventName - * @param array $listeners - * @return array */ - protected function addInterfaceListeners($eventName, array $listeners = []) + protected function addInterfaceListeners(string $eventName, array $listeners = []): array { foreach (class_implements($eventName) as $interface) { if (isset($this->listeners[$interface])) { @@ -244,12 +185,8 @@ class Dispatcher implements DispatcherInterface /** * Register an event listener with the dispatcher. - * - * @param \Closure|string $listener - * @param bool $wildcard - * @return \Closure */ - public function makeListener($listener, $wildcard = false) + public function makeListener(Closure|string $listener, bool $wildcard = false): Closure { if (is_string($listener)) { return $this->createClassListener($listener, $wildcard); @@ -266,12 +203,8 @@ class Dispatcher implements DispatcherInterface /** * Create a class based listener. - * - * @param string $listener - * @param bool $wildcard - * @return \Closure */ - protected function createClassListener($listener, $wildcard = false) + protected function createClassListener(string $listener, bool $wildcard = false): Closure { return function ($event, $payload) use ($listener, $wildcard) { if ($wildcard) { @@ -287,39 +220,53 @@ class Dispatcher implements DispatcherInterface /** * Create the class based event callable. - * - * @param string $listener - * @return callable */ - protected function createClassCallable($listener) + protected function createClassCallable(string $listener): callable { [$class, $method] = $this->parseListenerCallback($listener); - return [new $class(), $method]; + return [new $class, $method]; } /** * Parse the class listener into class and method. - * - * @param string $listener - * @return array */ - protected function parseListenerCallback($listener) + protected function parseListenerCallback(string $listener): array { - return str_contains((string) $listener, '@') + return str_contains($listener, '@') ? explode('@', $listener, 2) : [$listener, 'handle']; } /** - * @inheritdoc + * {@inheritdoc} */ - public function forget($event) + public function forget(string $event): void { - if (str_contains((string) $event, '*')) { + if (str_contains($event, '*')) { unset($this->wildcards[$event]); } else { unset($this->listeners[$event]); } + + foreach ($this->wildcardsCache as $key => $listeners) { + if (Str::is($event, $key)) { + unset($this->wildcardsCache[$key]); + } + } + } + + /** + * Remove all the listeners from the dispatcher. + */ + public function forgetAll(): void + { + $listeners = array_merge( + $this->listeners, $this->wildcards + ); + + foreach (array_keys($listeners) as $listener) { + $this->forget($listener); + } } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/DispatcherInterface.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/DispatcherInterface.php index 590328f09..71de892c0 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/DispatcherInterface.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/DispatcherInterface.php @@ -6,63 +6,41 @@ interface DispatcherInterface { /** * Register an event listener with the dispatcher. - * - * @param string|array $events - * @param mixed $listener - * @return void */ - public function listen($events, $listener); + public function listen(string|array $events, mixed $listener): void; /** * Determine if a given event has listeners. - * - * @param string $eventName - * @return bool */ - public function hasListeners($eventName); + public function hasListeners(string $event): bool; /** * Fire an event until the first non-null response is returned. - * - * @param string|object $event - * @param mixed $payload - * @return array|null */ - public function until($event, $payload = []); + public function until(string|object $event, mixed $payload = []): mixed; /** * Fire an event and call the listeners. - * - * @param string|object $event - * @param mixed $payload - * @param bool $halt - * @return mixed */ - public function fire($event, $payload = [], $halt = false); + public function fire(string|object $event, mixed $payload = [], bool $halt = false): void; /** * Fire an event and call the listeners. - * - * @param string|object $event - * @param mixed $payload - * @param bool $halt - * @return array|null */ - public function dispatch($event, $payload = [], $halt = false); + public function dispatch(string|object $event, mixed $payload = [], $halt = false): mixed; /** - * Get all of the listeners for a given event name. - * - * @param string $eventName - * @return array + * Get all the listeners for a given event name. */ - public function getListeners($eventName); + public function getListeners(string $event): array; /** * Remove a set of listeners from the dispatcher. - * - * @param string $event - * @return void */ - public function forget($event); + public function forget(string $event): void; + + /** + * Remove all the listeners from the dispatcher. + */ + public function forgetAll(): void; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Logger.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Logger.php index b8a849169..5ac66f278 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Logger.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Logger.php @@ -13,28 +13,21 @@ class Logger { /** * The logger instance. - * - * @var LoggerInterface|null */ - protected $logger; + protected ?LoggerInterface $logger; /** * Constructor. - * - * @param LoggerInterface|null $logger */ - public function __construct(LoggerInterface $logger = null) + public function __construct(?LoggerInterface $logger = null) { $this->logger = $logger; } /** * Logs the given event. - * - * @param mixed $event - * @return void */ - public function log($event) + public function log($event): void { switch (true) { case $event instanceof AuthEvent: @@ -51,11 +44,8 @@ class Logger /** * Logs an authentication event. - * - * @param AuthEvent $event - * @return void */ - public function auth(AuthEvent $event) + public function auth(AuthEvent $event): void { if (isset($this->logger)) { $connection = $event->getConnection(); @@ -78,11 +68,8 @@ class Logger /** * Logs a model event. - * - * @param ModelEvent $event - * @return void */ - public function model(ModelEvent $event) + public function model(ModelEvent $event): void { if (isset($this->logger)) { $model = $event->getModel(); @@ -102,11 +89,8 @@ class Logger /** * Logs a query event. - * - * @param QueryEvent $event - * @return void */ - public function query(QueryEvent $event) + public function query(QueryEvent $event): void { if (isset($this->logger)) { $query = $event->getQuery(); @@ -127,12 +111,9 @@ class Logger } /** - * Returns the operational name of the given event. - * - * @param mixed $event - * @return string + * Get the operational name of the given event. */ - protected function getOperationName($event) + protected function getOperationName($event): string { return (new ReflectionClass($event))->getShortName(); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/NullDispatcher.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/NullDispatcher.php index 7683d1f80..31f95cb77 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/NullDispatcher.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/NullDispatcher.php @@ -6,15 +6,11 @@ class NullDispatcher implements DispatcherInterface { /** * The underlying dispatcher instance. - * - * @var DispatcherInterface */ - protected $dispatcher; + protected DispatcherInterface $dispatcher; /** * Constructor. - * - * @param DispatcherInterface $dispatcher */ public function __construct(DispatcherInterface $dispatcher) { @@ -23,84 +19,62 @@ class NullDispatcher implements DispatcherInterface /** * Register an event listener with the dispatcher. - * - * @param string|array $events - * @param mixed $listener - * @return void */ - public function listen($events, $listener) + public function listen(string|array $events, mixed $listener): void { $this->dispatcher->listen($events, $listener); } /** * Determine if a given event has listeners. - * - * @param string $eventName - * @return bool */ - public function hasListeners($eventName) + public function hasListeners(string $event): bool { - return $this->dispatcher->hasListeners($eventName); + return $this->dispatcher->hasListeners($event); } /** * Fire an event until the first non-null response is returned. - * - * @param string|object $event - * @param mixed $payload - * @return null */ - public function until($event, $payload = []) + public function until(string|object $event, mixed $payload = []): ?array { return null; } /** * Fire an event and call the listeners. - * - * @param string|object $event - * @param mixed $payload - * @param bool $halt - * @return null */ - public function fire($event, $payload = [], $halt = false) - { - return null; - } + public function fire(string|object $event, mixed $payload = [], bool $halt = false): void {} /** * Fire an event and call the listeners. - * - * @param string|object $event - * @param mixed $payload - * @param bool $halt - * @return null */ - public function dispatch($event, $payload = [], $halt = false) + public function dispatch(string|object $event, mixed $payload = [], $halt = false): mixed { return null; } /** - * Get all of the listeners for a given event name. - * - * @param string $eventName - * @return array + * Get all the listeners for a given event name. */ - public function getListeners($eventName) + public function getListeners(string $event): array { - return $this->dispatcher->getListeners($eventName); + return $this->dispatcher->getListeners($event); } /** * Remove a set of listeners from the dispatcher. - * - * @param string $event - * @return void */ - public function forget($event) + public function forget(string $event): void { $this->dispatcher->forget($event); } + + /** + * Remove all the listeners from the dispatcher. + */ + public function forgetAll(): void + { + $this->dispatcher->forgetAll(); + } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Exceptions/ConstraintViolationException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Exceptions/ConstraintViolationException.php index 641843a9b..b5d30a739 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Exceptions/ConstraintViolationException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Exceptions/ConstraintViolationException.php @@ -11,25 +11,17 @@ class ConstraintViolationException extends LdapRecordException /** * Determine if the exception was generated due to the password policy. - * - * @return bool */ - public function causedByPasswordPolicy() + public function causedByPasswordPolicy(): bool { - return isset($this->detailedError) - ? $this->errorContainsMessage($this->detailedError->getDiagnosticMessage(), '0000052D') - : false; + return isset($this->detailedError) && $this->errorContainsMessage($this->detailedError->getDiagnosticMessage(), '0000052D'); } /** * Determine if the exception was generated due to an incorrect password. - * - * @return bool */ - public function causedByIncorrectPassword() + public function causedByIncorrectPassword(): bool { - return isset($this->detailedError) - ? $this->errorContainsMessage($this->detailedError->getDiagnosticMessage(), '00000056') - : false; + return isset($this->detailedError) && $this->errorContainsMessage($this->detailedError->getDiagnosticMessage(), '00000056'); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/HandlesConnection.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/HandlesConnection.php index 4a2e27598..e47904d8b 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/HandlesConnection.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/HandlesConnection.php @@ -5,88 +5,100 @@ namespace LdapRecord; use Closure; use ErrorException; use Exception; +use LDAP\Connection; +/** @mixin Ldap */ trait HandlesConnection { /** * The LDAP host that is currently connected. - * - * @var string|null */ - protected $host; + protected ?string $host = null; + + /** + * The LDAP protocol to use (ldap:// or ldaps://). + */ + protected ?string $protocol = null; /** * The LDAP connection resource. * - * @var resource|null + * @var Connection */ - protected $connection; + protected mixed $connection = null; /** - * The bound status of the connection. - * - * @var bool + * Whether the connection is bound. */ - protected $bound = false; + protected bool $bound = false; + + /** + * Whether the connection is secured over TLS or SSL. + */ + protected bool $secure = false; /** * Whether the connection must be bound over SSL. - * - * @var bool */ - protected $useSSL = false; + protected bool $useSSL = false; /** * Whether the connection must be bound over TLS. - * - * @var bool */ - protected $useTLS = false; + protected bool $useTLS = false; /** - * @inheritdoc + * {@inheritdoc} */ - public function isUsingSSL() + public function isUsingSSL(): bool { return $this->useSSL; } /** - * @inheritdoc + * {@inheritdoc} */ - public function isUsingTLS() + public function isUsingTLS(): bool { return $this->useTLS; } /** - * @inheritdoc + * {@inheritdoc} */ - public function isBound() + public function isBound(): bool { return $this->bound; } /** - * @inheritdoc + * {@inheritdoc} */ - public function isConnected() + public function isSecure(): bool + { + return $this->secure; + } + + /** + * {@inheritdoc} + */ + public function isConnected(): bool { return ! is_null($this->connection); } /** - * @inheritdoc + * {@inheritdoc} */ - public function canChangePasswords() + public function canChangePasswords(): bool { return $this->isUsingSSL() || $this->isUsingTLS(); } /** - * @inheritdoc + * {@inheritdoc} */ - public function ssl($enabled = true) + public function ssl(bool $enabled = true): static { $this->useSSL = $enabled; @@ -94,9 +106,9 @@ trait HandlesConnection } /** - * @inheritdoc + * {@inheritdoc} */ - public function tls($enabled = true) + public function tls(bool $enabled = true): static { $this->useTLS = $enabled; @@ -104,9 +116,9 @@ trait HandlesConnection } /** - * @inheritdoc + * {@inheritdoc} */ - public function setOptions(array $options = []) + public function setOptions(array $options = []): void { foreach ($options as $option => $value) { $this->setOption($option, $value); @@ -114,46 +126,57 @@ trait HandlesConnection } /** - * @inheritdoc + * {@inheritdoc} */ - public function getHost() + public function getHost(): ?string { return $this->host; } /** - * @inheritdoc + * {@inheritdoc} */ - public function getConnection() + public function getConnection(): ?Connection { return $this->connection; } /** - * @inheritdoc + * {@inheritdoc} */ - public function getProtocol() + public function getProtocol(): string { - return $this->isUsingSSL() ? LdapInterface::PROTOCOL_SSL : LdapInterface::PROTOCOL; + return $this->protocol ?: ( + $this->isUsingSSL() + ? LdapInterface::PROTOCOL_SSL + : LdapInterface::PROTOCOL + ); } /** - * @inheritdoc + * {@inheritdoc} */ - public function getExtendedError() + public function getExtendedError(): ?string { return $this->getDiagnosticMessage(); } + /** + * Handle the bind response. + */ + protected function handleBindResponse(LdapResultResponse $response): void + { + $this->bound = $response->successful(); + + $this->secure = $this->secure ?: $this->bound && $this->isUsingSSL(); + } + /** * Convert warnings to exceptions for the given operation. * - * @param Closure $operation - * @return mixed - * * @throws LdapRecordException */ - protected function executeFailableOperation(Closure $operation) + protected function executeFailableOperation(Closure $operation): mixed { // If some older versions of PHP, errors are reported instead of throwing // exceptions, which could be a significant detriment to our application. @@ -172,8 +195,8 @@ trait HandlesConnection } // If the failed query operation was a based on a query being executed - // -- such as a search, read, or listing, then we can safely return - // the failed response here and prevent throwning an exception. + // -- such as a search, read, or list, then we can safely return + // the failed response here and prevent throwing an exception. if ($this->shouldBypassFailure($method = debug_backtrace()[1]['function'])) { return $result; } @@ -188,46 +211,24 @@ trait HandlesConnection /** * Determine if the failed operation should be bypassed. - * - * @param string $method - * @return bool */ - protected function shouldBypassFailure($method) + protected function shouldBypassFailure(string $method): bool { - return in_array($method, ['search', 'read', 'listing']); + return in_array($method, ['search', 'read', 'list']); } /** * Determine if the error should be bypassed. - * - * @param string $error - * @return bool */ - protected function shouldBypassError($error) + protected function shouldBypassError(string $error): bool { return $this->causedByPaginationSupport($error) || $this->causedBySizeLimit($error) || $this->causedByNoSuchObject($error); } - /** - * Determine if the current PHP version supports server controls. - * - * @deprecated since v2.5.0 - * - * @return bool - */ - public function supportsServerControlsInMethods() - { - return version_compare(PHP_VERSION, '7.3.0') >= 0; - } - /** * Generates an LDAP connection string for each host given. - * - * @param string|array $hosts - * @param string $port - * @return string */ - protected function makeConnectionUris($hosts, $port) + protected function makeConnectionUris(array|string $hosts, string|int $port): string { // If an attempt to connect via SSL protocol is being performed, // and we are still using the default port, we will swap it @@ -244,15 +245,9 @@ trait HandlesConnection /** * Assemble the host URI strings. - * - * @param array|string $hosts - * @param string $port - * @return array */ - protected function assembleHostUris($hosts, $port) + protected function assembleHostUris(array|string $hosts, string|int $port): array { - return array_map(function ($host) use ($port) { - return "{$this->getProtocol()}{$host}:{$port}"; - }, (array) $hosts); + return array_map(fn ($host) => "{$this->getProtocol()}{$host}:{$port}", (array) $hosts); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Ldap.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Ldap.php index 76c372040..7f2be60b6 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Ldap.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Ldap.php @@ -6,15 +6,16 @@ use LDAP\Connection as RawLdapConnection; class Ldap implements LdapInterface { - use HandlesConnection, DetectsErrors; + use DetectsErrors; + use HandlesConnection; /** - * @inheritdoc + * {@inheritdoc} */ - public function getEntries($searchResults) + public function getEntries(mixed $result): array { - return $this->executeFailableOperation(function () use ($searchResults) { - return ldap_get_entries($this->connection, $searchResults); + return $this->executeFailableOperation(function () use ($result) { + return ldap_get_entries($this->connection, $result); }); } @@ -23,13 +24,12 @@ class Ldap implements LdapInterface * * @see http://php.net/manual/en/function.ldap-first-entry.php * - * @param resource $searchResults - * @return resource + * @param \Ldap\Result $result */ - public function getFirstEntry($searchResults) + public function getFirstEntry(mixed $result): mixed { - return $this->executeFailableOperation(function () use ($searchResults) { - return ldap_first_entry($this->connection, $searchResults); + return $this->executeFailableOperation(function () use ($result) { + return ldap_first_entry($this->connection, $result); }); } @@ -38,10 +38,9 @@ class Ldap implements LdapInterface * * @see http://php.net/manual/en/function.ldap-next-entry.php * - * @param resource $entry - * @return resource + * @param \Ldap\ResultEntry $entry */ - public function getNextEntry($entry) + public function getNextEntry(mixed $entry): mixed { return $this->executeFailableOperation(function () use ($entry) { return ldap_next_entry($this->connection, $entry); @@ -53,10 +52,9 @@ class Ldap implements LdapInterface * * @see http://php.net/manual/en/function.ldap-get-attributes.php * - * @param resource $entry - * @return array|false + * @param \Ldap\ResultEntry $entry */ - public function getAttributes($entry) + public function getAttributes(mixed $entry): array|false { return $this->executeFailableOperation(function () use ($entry) { return ldap_get_attributes($this->connection, $entry); @@ -64,41 +62,29 @@ class Ldap implements LdapInterface } /** - * Returns the number of entries from a search result. - * - * @see http://php.net/manual/en/function.ldap-count-entries.php - * - * @param resource $searchResults - * @return int + * {@inheritDoc} */ - public function countEntries($searchResults) + public function countEntries(mixed $result): int { - return $this->executeFailableOperation(function () use ($searchResults) { - return ldap_count_entries($this->connection, $searchResults); + return $this->executeFailableOperation(function () use ($result) { + return ldap_count_entries($this->connection, $result); }); } /** - * Compare value of attribute found in entry specified with DN. - * - * @see http://php.net/manual/en/function.ldap-compare.php - * - * @param string $dn - * @param string $attribute - * @param string $value - * @return mixed + * {@inheritDoc} */ - public function compare($dn, $attribute, $value) + public function compare(string $dn, string $attribute, string $value, ?array $controls = null): bool|int { - return $this->executeFailableOperation(function () use ($dn, $attribute, $value) { - return ldap_compare($this->connection, $dn, $attribute, $value); + return $this->executeFailableOperation(function () use ($dn, $attribute, $value, $controls) { + return ldap_compare($this->connection, $dn, $attribute, $value, $controls); }); } /** - * @inheritdoc + * {@inheritdoc} */ - public function getLastError() + public function getLastError(): ?string { if (! $this->connection) { return null; @@ -108,9 +94,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function getDetailedError() + public function getDetailedError(): ?DetailedError { if (! $number = $this->errNo()) { return null; @@ -122,15 +108,9 @@ class Ldap implements LdapInterface } /** - * Get all binary values from the specified result entry. - * - * @see http://php.net/manual/en/function.ldap-get-values-len.php - * - * @param $entry - * @param $attribute - * @return array + * {@inheritDoc} */ - public function getValuesLen($entry, $attribute) + public function getValuesLen(mixed $entry, string $attribute): array|false { return $this->executeFailableOperation(function () use ($entry, $attribute) { return ldap_get_values_len($this->connection, $entry, $attribute); @@ -138,17 +118,17 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function setOption($option, $value) + public function setOption(int $option, mixed $value): bool { return ldap_set_option($this->connection, $option, $value); } /** - * @inheritdoc + * {@inheritdoc} */ - public function getOption($option, &$value = null) + public function getOption(int $option, mixed &$value = null): mixed { ldap_get_option($this->connection, $option, $value); @@ -159,59 +139,63 @@ class Ldap implements LdapInterface * Set a callback function to do re-binds on referral chasing. * * @see http://php.net/manual/en/function.ldap-set-rebind-proc.php - * - * @param callable $callback - * @return bool */ - public function setRebindCallback(callable $callback) + public function setRebindCallback(callable $callback): bool { return ldap_set_rebind_proc($this->connection, $callback); } /** - * @inheritdoc + * {@inheritdoc} */ - public function startTLS() + public function startTLS(): bool { - return $this->executeFailableOperation(function () { + return $this->secure = $this->executeFailableOperation(function () { return ldap_start_tls($this->connection); }); } /** - * @inheritdoc + * {@inheritdoc} */ - public function connect($hosts = [], $port = 389) + public function connect(string|array $hosts = [], int $port = 389, ?string $protocol = null): bool { $this->bound = false; - + $this->protocol = $protocol; $this->host = $this->makeConnectionUris($hosts, $port); - return $this->connection = $this->executeFailableOperation(function () { + $this->connection = $this->executeFailableOperation(function () { return ldap_connect($this->host); }); + + return $this->connection instanceof RawLdapConnection; } /** - * @inheritdoc + * {@inheritdoc} */ - public function close() + public function close(): bool { - $result = (is_resource($this->connection) || $this->connection instanceof RawLdapConnection) - ? @ldap_close($this->connection) - : false; + $result = false; + + if ($this->connection instanceof RawLdapConnection) { + $result = @ldap_close($this->connection); + } - $this->connection = null; $this->bound = false; + $this->secure = false; + $this->host = null; + $this->protocol = null; + $this->connection = null; return $result; } /** - * @inheritdoc + * {@inheritdoc} */ - public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []) + public function search(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed { return $this->executeFailableOperation(function () use ( $dn, @@ -221,18 +205,16 @@ class Ldap implements LdapInterface $size, $time, $deref, - $serverControls + $controls ) { - return empty($serverControls) - ? ldap_search($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref) - : ldap_search($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $serverControls); + return ldap_search($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $controls); }); } /** - * @inheritdoc + * {@inheritdoc} */ - public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []) + public function list(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed { return $this->executeFailableOperation(function () use ( $dn, @@ -242,18 +224,16 @@ class Ldap implements LdapInterface $size, $time, $deref, - $serverControls + $controls ) { - return empty($serverControls) - ? ldap_list($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref) - : ldap_list($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $serverControls); + return ldap_list($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $controls); }); } /** - * @inheritdoc + * {@inheritdoc} */ - public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []) + public function read(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed { return $this->executeFailableOperation(function () use ( $dn, @@ -263,47 +243,78 @@ class Ldap implements LdapInterface $size, $time, $deref, - $serverControls + $controls ) { - return empty($serverControls) - ? ldap_read($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref) - : ldap_read($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $serverControls); + return ldap_read($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $controls); }); } /** - * @inheritdoc + * {@inheritdoc} */ - public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = []) + public function parseResult(mixed $result, int &$errorCode = 0, ?string &$dn = null, ?string &$errorMessage = null, ?array &$referrals = null, ?array &$controls = null): LdapResultResponse|false { - return $this->executeFailableOperation(function () use ( - $result, - &$errorCode, - &$dn, - &$errorMessage, - &$referrals, - &$serverControls - ) { - return empty($serverControls) - ? ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals) - : ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals, $serverControls); - }); + if (ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals, $controls)) { + return new LdapResultResponse( + $errorCode, + $dn, + $errorMessage, + $referrals, + $controls + ); + } + + return false; } /** - * @inheritdoc + * {@inheritdoc} */ - public function bind($username, $password) + public function bind(?string $dn = null, ?string $password = null, ?array $controls = null): LdapResultResponse { - return $this->bound = $this->executeFailableOperation(function () use ($username, $password) { - return ldap_bind($this->connection, $username, $password ? html_entity_decode($password) : null); + /** @var \LDAP\Result $result */ + $result = $this->executeFailableOperation(function () use ($dn, $password, $controls) { + return ldap_bind_ext($this->connection, $dn, $password ? html_entity_decode($password) : null, $controls); + }); + + $response = $this->parseResult($result); + + $this->handleBindResponse($response); + + return $response; + } + + /** + * {@inheritDoc} + */ + public function saslBind(?string $dn = null, ?string $password = null, array $options = []): bool + { + return $this->executeFailableOperation(function () use ($dn, $password, $options) { + $options = array_merge([ + 'mech' => null, + 'realm' => null, + 'authc_id' => null, + 'authz_id' => null, + 'props' => null, + ], $options); + + return $this->bound = ldap_sasl_bind( + $this->connection, + $dn, + $password ? html_entity_decode($password) : null, + $options['mech'], + $options['realm'], + $options['authc_id'], + $options['authz_id'], + $options['props'], + ); }); } /** - * @inheritdoc + * {@inheritdoc} */ - public function add($dn, array $entry) + public function add(string $dn, array $entry): bool { return $this->executeFailableOperation(function () use ($dn, $entry) { return ldap_add($this->connection, $dn, $entry); @@ -311,9 +322,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function delete($dn) + public function delete(string $dn): bool { return $this->executeFailableOperation(function () use ($dn) { return ldap_delete($this->connection, $dn); @@ -321,9 +332,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false) + public function rename(string $dn, string $newRdn, string $newParent, bool $deleteOldRdn = false): bool { return $this->executeFailableOperation(function () use ( $dn, @@ -336,9 +347,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function modify($dn, array $entry) + public function modify(string $dn, array $entry): bool { return $this->executeFailableOperation(function () use ($dn, $entry) { return ldap_modify($this->connection, $dn, $entry); @@ -346,9 +357,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function modifyBatch($dn, array $values) + public function modifyBatch(string $dn, array $values): bool { return $this->executeFailableOperation(function () use ($dn, $values) { return ldap_modify_batch($this->connection, $dn, $values); @@ -356,9 +367,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function modAdd($dn, array $entry) + public function modAdd(string $dn, array $entry): bool { return $this->executeFailableOperation(function () use ($dn, $entry) { return ldap_mod_add($this->connection, $dn, $entry); @@ -366,9 +377,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function modReplace($dn, array $entry) + public function modReplace(string $dn, array $entry): bool { return $this->executeFailableOperation(function () use ($dn, $entry) { return ldap_mod_replace($this->connection, $dn, $entry); @@ -376,9 +387,9 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function modDelete($dn, array $entry) + public function modDelete(string $dn, array $entry): bool { return $this->executeFailableOperation(function () use ($dn, $entry) { return ldap_mod_del($this->connection, $dn, $entry); @@ -386,88 +397,63 @@ class Ldap implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '') - { - return $this->executeFailableOperation(function () use ($pageSize, $isCritical, $cookie) { - return ldap_control_paged_result($this->connection, $pageSize, $isCritical, $cookie); - }); - } - - /** - * @inheritdoc - */ - public function controlPagedResultResponse($result, &$cookie, &$estimated = null) - { - return $this->executeFailableOperation(function () use ($result, &$cookie, &$estimated) { - return ldap_control_paged_result_response($this->connection, $result, $cookie, $estimated); - }); - } - - /** - * @inheritdoc - */ - public function freeResult($result) + public function freeResult(mixed $result): bool { return ldap_free_result($result); } /** - * @inheritdoc + * {@inheritdoc} */ - public function errNo() + public function errNo(): ?int { return $this->connection ? ldap_errno($this->connection) : null; } /** - * @inheritdoc + * {@inheritdoc} */ - public function err2Str($number) + public function err2Str(int $number): string { return ldap_err2str($number); } /** - * Returns the extended error hex code of the last command. - * - * @return string|null + * Get the extended error hex code of the last command. */ - public function getExtendedErrorHex() + public function getExtendedErrorHex(): ?string { if (preg_match("/(?<=data\s).*?(?=,)/", $this->getExtendedError(), $code)) { return $code[0]; } + + return null; } /** - * Returns the extended error code of the last command. - * - * @return bool|string + * Get the extended error code of the last command. */ - public function getExtendedErrorCode() + public function getExtendedErrorCode(): string|false { return $this->extractDiagnosticCode($this->getExtendedError()); } /** * Extract the diagnostic code from the message. - * - * @param string $message - * @return string|bool */ - public function extractDiagnosticCode($message) + public function extractDiagnosticCode(string $message): string|false { preg_match('/^([\da-fA-F]+):/', $message, $matches); - return isset($matches[1]) ? $matches[1] : false; + return $matches[1] ?? false; } /** - * @inheritdoc + * {@inheritdoc} */ - public function getDiagnosticMessage() + public function getDiagnosticMessage(): ?string { $this->getOption(LDAP_OPT_ERROR_STRING, $message); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapInterface.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapInterface.php index fcff57f48..735f858ca 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapInterface.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapInterface.php @@ -2,15 +2,15 @@ namespace LdapRecord; +use LDAP\Connection; + +/** + * @see https://ldap.com/ldap-oid-reference-guide + * @see http://msdn.microsoft.com/en-us/library/cc223359.aspx + * @see https://help.univention.com/t/openldap-debug-level/19301 + */ interface LdapInterface { - /** - * The SSL LDAP protocol string. - * - * @var string - */ - public const PROTOCOL_SSL = 'ldaps://'; - /** * The standard LDAP protocol string. * @@ -19,136 +19,370 @@ interface LdapInterface public const PROTOCOL = 'ldap://'; /** - * The LDAP SSL port number. + * The SSL LDAP protocol string. * * @var string */ - public const PORT_SSL = 636; + public const PROTOCOL_SSL = 'ldaps://'; /** * The standard LDAP port number. * - * @var string + * @var int */ public const PORT = 389; /** - * Various useful server control OID's. + * The LDAP SSL port number. * - * @see https://ldap.com/ldap-oid-reference-guide/ - * @see http://msdn.microsoft.com/en-us/library/cc223359.aspx + * @var int + */ + public const PORT_SSL = 636; + + /** + * Print entry and exit from routines. + * + * @var int + */ + public const DEBUG_TRACE = 1; + + /** + * Print packet activity. + * + * @var int + */ + public const DEBUG_PACKETS = 2; + + /** + * Print data arguments from requests. + * + * @var int + */ + public const DEBUG_ARGS = 4; + + /** + * Print connection activity. + * + * @var int + */ + public const DEBUG_CONNS = 8; + + /** + * Print encoding and decoding of data. + * + * @var int + */ + public const DEBUG_BER = 16; + + /** + * Print search filters. + * + * @var int + */ + public const DEBUG_FILTER = 32; + + /** + * Print configuration file processing. + * + * @var int + */ + public const DEBUG_CONFIG = 64; + + /** + * Print Access Control List activities. + * + * @var int + */ + public const DEBUG_ACL = 128; + + /** + * Print operational statistics. + * + * @var int + */ + public const DEBUG_STATS = 256; + + /** + * Print more detailed statistics. + * + * @var int + */ + public const DEBUG_STATS2 = 512; + + /** + * Print communication with shell backends. + * + * @var int + */ + public const DEBUG_SHELL = 1024; + + /** + * Print entry parsing. + * + * @var int + */ + public const DEBUG_PARSE = 2048; + + /** + * Print LDAPSync replication. + * + * @var int + */ + public const DEBUG_SYNC = 16384; + + /** + * Print referral activities. + * + * @var int + */ + public const DEBUG_REFERRAL = 32768; + + /** + * Print error conditions. + * + * @var int + */ + public const DEBUG_ERROR = 32768; + + /** + * Print all levels of debug. + * + * @var int + */ + public const DEBUG_ANY = 65535; + + /** + * OID for StartTLS extended operation. Signals the server to initiate a TLS connection. + * + * @var string */ public const OID_SERVER_START_TLS = '1.3.6.1.4.1.1466.20037'; + + /** + * OID for Paged Results Control. Used to retrieve search results in pages. + * + * @var string + */ public const OID_SERVER_PAGED_RESULTS = '1.2.840.113556.1.4.319'; + + /** + * OID for Show Deleted Control. Includes deleted entries in the search results. + * + * @var string + */ public const OID_SERVER_SHOW_DELETED = '1.2.840.113556.1.4.417'; + + /** + * OID for Server Side Sort Control. Requests the server to sort the search results. + * + * @var string + */ public const OID_SERVER_SORT = '1.2.840.113556.1.4.473'; + + /** + * OID for Cross-Domain Move Target Control. Used in cross-domain move operations. + * + * @var string + */ public const OID_SERVER_CROSSDOM_MOVE_TARGET = '1.2.840.113556.1.4.521'; + + /** + * OID for LDAP Notification Control. Used to register for change notifications. + * + * @var string + */ public const OID_SERVER_NOTIFICATION = '1.2.840.113556.1.4.528'; + + /** + * OID for Extended DN Control. Requests extended DN information in search results. + * + * @var string + */ public const OID_SERVER_EXTENDED_DN = '1.2.840.113556.1.4.529'; + + /** + * OID for Lazy Commit Control. Delays the actual commit of changes until requested. + * + * @var string + */ public const OID_SERVER_LAZY_COMMIT = '1.2.840.113556.1.4.619'; + + /** + * OID for Security Descriptor Flags Control. Used to manipulate security descriptor flags. + * + * @var string + */ public const OID_SERVER_SD_FLAGS = '1.2.840.113556.1.4.801'; + + /** + * OID for Tree Delete Control. Enables the deletion of an entire subtree. + * + * @var string + */ public const OID_SERVER_TREE_DELETE = '1.2.840.113556.1.4.805'; + + /** + * OID for DirSync Control. Used for directory synchronization operations. + * + * @var string + */ public const OID_SERVER_DIRSYNC = '1.2.840.113556.1.4.841'; + + /** + * OID for Verify Name Control. Allows verification of an entry without retrieving attributes. + * + * @var string + */ public const OID_SERVER_VERIFY_NAME = '1.2.840.113556.1.4.1338'; + + /** + * OID for Domain Scope Control. Limits a search to the current domain. + * + * @var string + */ public const OID_SERVER_DOMAIN_SCOPE = '1.2.840.113556.1.4.1339'; + + /** + * OID for Search Options Control. Used to set various search options. + * + * @var string + */ public const OID_SERVER_SEARCH_OPTIONS = '1.2.840.113556.1.4.1340'; + + /** + * OID for Permissive Modify Control. Allows modifications even if some attributes are missing. + * + * @var string + */ public const OID_SERVER_PERMISSIVE_MODIFY = '1.2.840.113556.1.4.1413'; + + /** + * OID for Authentication Service Queries (ASQ) Control. Used to perform ASQ operations. + * + * @var string + */ public const OID_SERVER_ASQ = '1.2.840.113556.1.4.1504'; + + /** + * OID for Fast Bind Control. Optimizes the bind process for faster authentication. + * + * @var string + */ public const OID_SERVER_FAST_BIND = '1.2.840.113556.1.4.1781'; + + /** + * OID for Virtual List View (VLV) Request Control. Used to request a specific range of entries. + * + * @var string + */ public const OID_SERVER_CONTROL_VLVREQUEST = '2.16.840.1.113730.3.4.9'; /** - * Query OID's. - * - * @see https://ldapwiki.com/wiki/LDAP_MATCHING_RULE_IN_CHAIN + * OID for the 'matchingRuleInChain' matching rule. Used for substring searches in multi-valued attributes. */ public const OID_MATCHING_RULE_IN_CHAIN = '1.2.840.113556.1.4.1941'; /** * Set the current connection to use SSL. - * - * @param bool $enabled - * @return $this */ - public function ssl(); + public function ssl(): static; /** * Determine if the current connection instance is using SSL. - * - * @return bool */ - public function isUsingSSL(); + public function isUsingSSL(): bool; /** * Set the current connection to use TLS. - * - * @param bool $enabled - * @return $this */ - public function tls(); + public function tls(): static; /** * Determine if the current connection instance is using TLS. - * - * @return bool */ - public function isUsingTLS(); + public function isUsingTLS(): bool; /** * Determine if the connection is bound. - * - * @return bool */ - public function isBound(); + public function isBound(): bool; + + /** + * Determine if the connection is secure over TLS or SSL. + */ + public function isSecure(): bool; /** * Determine if the connection has been created. - * - * @return bool */ - public function isConnected(); + public function isConnected(): bool; /** * Determine the connection is able to modify passwords. - * - * @return bool */ - public function canChangePasswords(); + public function canChangePasswords(): bool; /** - * Returns the full LDAP host URL. + * Get the full LDAP host URL. * * Ex: ldap://192.168.1.1:386 - * - * @return string|null */ - public function getHost(); + public function getHost(): ?string; /** - * Get the underlying connection resource. - * - * @return resource|null + * Get the underlying raw LDAP connection. */ - public function getConnection(); + public function getConnection(): ?Connection; /** * Retrieve the entries from a search result. * * @see http://php.net/manual/en/function.ldap-get-entries.php * - * @param resource $searchResults - * @return array + * @param \LDAP\Result $result */ - public function getEntries($searchResults); + public function getEntries(mixed $result): array; + + /** + * Get the entry identifier for first entry in the result. + * + * @see https://www.php.net/manual/en/function.ldap-first-entry.php + * + * @param \LDAP\Result $result + */ + public function getFirstEntry(mixed $result): mixed; + + /** + * Retrieve the next result entry. + * + * @see https://www.php.net/manual/en/function.ldap-next-entry.php + * + * @param \LDAP\Result $entry + */ + public function getNextEntry(mixed $entry): mixed; + + /** + * Reads attributes and values from an entry in the search result. + * + * @see https://www.php.net/manual/en/function.ldap-get-attributes.php + * + * @param \LDAP\Result $entry + */ + public function getAttributes(mixed $entry): array|false; + + /** + * Reads all the values of the attribute in the entry in the result. + * + * @param \LDAP\Result $entry + */ + public function getValuesLen(mixed $entry, string $attribute): array|false; /** * Retrieve the last error on the current connection. * * @see http://php.net/manual/en/function.ldap-error.php - * - * @return string|null */ - public function getLastError(); + public function getLastError(): ?string; /** * Return detailed information about an error. @@ -156,62 +390,64 @@ interface LdapInterface * Returns null when there was a successful last request. * * Returns DetailedError when there was an error. - * - * @return DetailedError|null */ - public function getDetailedError(); + public function getDetailedError(): ?DetailedError; + + /** + * Count the number of entries in a search. + * + * @see https://www.php.net/manual/en/function.ldap-count-entries.php + * + * @param \LDAP\Result $result + */ + public function countEntries(mixed $result): int; + + /** + * Compare value of attribute found in entry specified with DN. + */ + public function compare(string $dn, string $attribute, string $value, ?array $controls = null): bool|int; /** * Set an option on the current connection. * * @see http://php.net/manual/en/function.ldap-set-option.php - * - * @param int $option - * @param mixed $value - * @return bool */ - public function setOption($option, $value); + public function setOption(int $option, mixed $value): bool; /** - * Set options on the current connection. - * - * @param array $options - * @return void + * Set multiple options on the current connection. */ - public function setOptions(array $options = []); + public function setOptions(array $options = []): void; + + /** + * Set a callback function to do re-binds on referral chasing. + * + * @see https://www.php.net/manual/en/function.ldap-set-rebind-proc.php + */ + public function setRebindCallback(callable $callback): bool; /** * Get the value for the LDAP option. * * @see https://www.php.net/manual/en/function.ldap-get-option.php - * - * @param int $option - * @param mixed $value - * @return mixed */ - public function getOption($option, &$value = null); + public function getOption(int $option, mixed &$value = null): mixed; /** * Starts a connection using TLS. * * @see http://php.net/manual/en/function.ldap-start-tls.php * - * @return bool - * * @throws LdapRecordException */ - public function startTLS(); + public function startTLS(): bool; /** * Connects to the specified hostname using the specified port. * * @see http://php.net/manual/en/function.ldap-start-tls.php - * - * @param string|array $hosts - * @param int $port - * @return resource|false */ - public function connect($hosts = [], $port = 389); + public function connect(string|array $hosts = [], int $port = 389, ?string $protocol = null): bool; /** * Closes the current connection. @@ -219,275 +455,176 @@ interface LdapInterface * Returns false if no connection is present. * * @see http://php.net/manual/en/function.ldap-close.php - * - * @return bool */ - public function close(); + public function close(): bool; /** * Performs a search on the current connection. * * @see http://php.net/manual/en/function.ldap-search.php * - * @param string $dn - * @param string $filter - * @param array $fields - * @param bool $onlyAttributes - * @param int $size - * @param int $time - * @param int $deref - * @param array $serverControls - * @return resource + * @return \LDAP\Result */ - public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []); + public function search(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed; /** * Performs a single level search on the current connection. * * @see http://php.net/manual/en/function.ldap-list.php * - * @param string $dn - * @param string $filter - * @param array $fields - * @param bool $onlyAttributes - * @param int $size - * @param int $time - * @param int $deref - * @param array $serverControls - * @return resource + * @return \LDAP\Result */ - public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []); + public function list(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed; /** * Reads an entry on the current connection. * * @see http://php.net/manual/en/function.ldap-read.php * - * @param string $dn - * @param string $filter - * @param array $fields - * @param bool $onlyAttributes - * @param int $size - * @param int $time - * @param int $deref - * @param array $serverControls - * @return resource + * @return \LDAP\Result */ - public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []); + public function read(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed; /** * Extract information from an LDAP result. * * @see https://www.php.net/manual/en/function.ldap-parse-result.php * - * @param resource $result - * @param int $errorCode - * @param ?string $dn - * @param ?string $errorMessage - * @param ?array $referrals - * @param ?array $serverControls - * @return bool + * @param \LDAP\Result $result */ - public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = []); + public function parseResult(mixed $result, int &$errorCode = 0, ?string &$dn = null, ?string &$errorMessage = null, ?array &$referrals = null, ?array &$controls = null): LdapResultResponse|false; /** - * Binds to the current connection using the specified username and password. - * If sasl is true, the current connection is bound using SASL. + * Bind to the LDAP directory. * * @see http://php.net/manual/en/function.ldap-bind.php * - * @param string $username - * @param string $password - * @return bool - * * @throws LdapRecordException */ - public function bind($username, $password); + public function bind(?string $dn = null, ?string $password = null, ?array $controls = null): LdapResultResponse; + + /** + * Bind to the LDAP directory using SASL. + * + * SASL options: + * - mech: Mechanism (Defaults: null) + * - realm: Realm (Defaults: null) + * - authc_id: Verification Identity (Defaults: null) + * - authz_id: Authorization Identity (Defaults: null) + * - props: Options for Authorization Identity (Defaults: null) + * + * @see https://php.net/manual/en/function.ldap-sasl-bind.php + * @see https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml + */ + public function saslBind(?string $dn = null, ?string $password = null, array $options = []): bool; /** * Adds an entry to the current connection. * * @see http://php.net/manual/en/function.ldap-add.php * - * @param string $dn - * @param array $entry - * @return bool - * * @throws LdapRecordException */ - public function add($dn, array $entry); + public function add(string $dn, array $entry): bool; /** * Deletes an entry on the current connection. * * @see http://php.net/manual/en/function.ldap-delete.php * - * @param string $dn - * @return bool - * * @throws LdapRecordException */ - public function delete($dn); + public function delete(string $dn): bool; /** * Modify the name of an entry on the current connection. * * @see http://php.net/manual/en/function.ldap-rename.php * - * @param string $dn - * @param string $newRdn - * @param string $newParent - * @param bool $deleteOldRdn - * @return bool - * * @throws LdapRecordException */ - public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false); + public function rename(string $dn, string $newRdn, string $newParent, bool $deleteOldRdn = false): bool; /** * Modifies an existing entry on the current connection. * * @see http://php.net/manual/en/function.ldap-modify.php * - * @param string $dn - * @param array $entry - * @return bool - * * @throws LdapRecordException */ - public function modify($dn, array $entry); + public function modify(string $dn, array $entry): bool; /** * Batch modifies an existing entry on the current connection. * * @see http://php.net/manual/en/function.ldap-modify-batch.php * - * @param string $dn - * @param array $values - * @return bool - * * @throws LdapRecordException */ - public function modifyBatch($dn, array $values); + public function modifyBatch(string $dn, array $values): bool; /** * Add attribute values to current attributes. * * @see http://php.net/manual/en/function.ldap-mod-add.php * - * @param string $dn - * @param array $entry - * @return bool - * * @throws LdapRecordException */ - public function modAdd($dn, array $entry); + public function modAdd(string $dn, array $entry): bool; /** * Replaces attribute values with new ones. * * @see http://php.net/manual/en/function.ldap-mod-replace.php * - * @param string $dn - * @param array $entry - * @return bool - * * @throws LdapRecordException */ - public function modReplace($dn, array $entry); + public function modReplace(string $dn, array $entry): bool; /** * Delete attribute values from current attributes. * * @see http://php.net/manual/en/function.ldap-mod-del.php * - * @param string $dn - * @param array $entry - * @return bool - * * @throws LdapRecordException */ - public function modDelete($dn, array $entry); - - /** - * Send LDAP pagination control. - * - * @see http://php.net/manual/en/function.ldap-control-paged-result.php - * - * @param int $pageSize - * @param bool $isCritical - * @param string $cookie - * @return bool - */ - public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = ''); - - /** - * Retrieve the LDAP pagination cookie. - * - * @see http://php.net/manual/en/function.ldap-control-paged-result-response.php - * - * @param resource $result - * @param string $cookie - * @return bool - */ - public function controlPagedResultResponse($result, &$cookie); + public function modDelete(string $dn, array $entry): bool; /** * Frees up the memory allocated internally to store the result. * * @see https://www.php.net/manual/en/function.ldap-free-result.php * - * @param resource $result - * @return bool + * @param \LDAP\Result $result */ - public function freeResult($result); + public function freeResult(mixed $result): bool; /** - * Returns the error number of the last command executed. + * Get the error number of the last command executed. * * @see http://php.net/manual/en/function.ldap-errno.php - * - * @return int|null */ - public function errNo(); + public function errNo(): ?int; /** - * Returns the error string of the specified error number. + * Get the error string of the specified error number. * * @see http://php.net/manual/en/function.ldap-err2str.php - * - * @param int $number - * @return string */ - public function err2Str($number); + public function err2Str(int $number): string; /** - * Returns the LDAP protocol to utilize for the current connection. - * - * @return string + * Get the LDAP protocol to utilize for the current connection. */ - public function getProtocol(); + public function getProtocol(): string; /** - * Returns the extended error code of the last command. - * - * @return string + * Get the extended error code of the last command. */ - public function getExtendedError(); + public function getExtendedError(): ?string; /** - * Return the diagnostic Message. - * - * @return string + * Get the diagnostic message. */ - public function getDiagnosticMessage(); - - /** - * Determine if the current PHP version supports server controls. - * - * @deprecated since v2.5.0 - * - * @return bool - */ - public function supportsServerControlsInMethods(); + public function getDiagnosticMessage(): ?string; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapRecordException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapRecordException.php index 0669b4c65..f574b9bc4 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapRecordException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapRecordException.php @@ -8,30 +8,21 @@ class LdapRecordException extends Exception { /** * The detailed LDAP error (if available). - * - * @var DetailedError|null */ - protected $detailedError; + protected ?DetailedError $detailedError = null; /** * Create a new Bind Exception with a detailed connection error. - * - * @param Exception $e - * @param DetailedError|null $error - * @return $this */ - public static function withDetailedError(Exception $e, DetailedError $error = null) + public static function withDetailedError(Exception $e, ?DetailedError $error = null): static { return (new static($e->getMessage(), $e->getCode(), $e))->setDetailedError($error); } /** * Set the detailed error. - * - * @param DetailedError|null $error - * @return $this */ - public function setDetailedError(DetailedError $error = null) + public function setDetailedError(?DetailedError $error = null): static { $this->detailedError = $error; @@ -39,11 +30,9 @@ class LdapRecordException extends Exception } /** - * Returns the detailed error. - * - * @return DetailedError|null + * Get the detailed error. */ - public function getDetailedError() + public function getDetailedError(): ?DetailedError { return $this->detailedError; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapResultResponse.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapResultResponse.php new file mode 100644 index 000000000..c0c1e8306 --- /dev/null +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapResultResponse.php @@ -0,0 +1,33 @@ +errorCode === 0; + } + + /** + * Determine if the LDAP response indicates a failed status. + */ + public function failed(): bool + { + return ! $this->successful(); + } +} diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Computer.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Computer.php index 72db0a0d8..3900c61a9 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Computer.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Computer.php @@ -2,18 +2,21 @@ namespace LdapRecord\Models\ActiveDirectory; +use LdapRecord\Models\ActiveDirectory\Concerns\HasAccountControl; use LdapRecord\Models\ActiveDirectory\Concerns\HasPrimaryGroup; +use LdapRecord\Models\ActiveDirectory\Relations\HasOnePrimaryGroup; +use LdapRecord\Models\Relations\HasMany; +use LdapRecord\Models\Relations\HasOne; class Computer extends Entry { + use HasAccountControl; use HasPrimaryGroup; /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'person', 'organizationalperson', @@ -23,32 +26,24 @@ class Computer extends Entry /** * The groups relationship. - * - * Retrieves groups that the current computer is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany */ - public function groups() + public function groups(): HasMany { return $this->hasMany(Group::class, 'member')->with($this->primaryGroup()); } /** * The primary group relationship. - * - * @return Relations\HasOnePrimaryGroup */ - public function primaryGroup() + public function primaryGroup(): HasOnePrimaryGroup { return $this->hasOnePrimaryGroup(Group::class, 'primarygroupid'); } /** * The managed by relationship. - * - * @return \LdapRecord\Models\Relations\HasOne */ - public function managedBy() + public function managedBy(): HasOne { return $this->hasOne([Contact::class, Group::class, User::class], 'managedby'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasAccountControl.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasAccountControl.php new file mode 100644 index 000000000..425cc548f --- /dev/null +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasAccountControl.php @@ -0,0 +1,46 @@ +isDisabled(); + } + + /** + * Determine if the user's account is disabled. + */ + public function isDisabled(): bool + { + return $this->accountControl()->hasFlag(AccountControl::ACCOUNTDISABLE); + } + + /** + * Get the user's account control. + */ + public function accountControl(): AccountControl + { + return new AccountControl( + $this->getFirstAttribute('userAccountControl') + ); + } + + /** + * Set the user's account control attribute. + */ + public function setUserAccountControlAttribute(mixed $value): void + { + if ($value instanceof AccountControl) { + $value = $value->getValue(); + } + + $this->attributes['useraccountcontrol'] = [(int) $value]; + } +} diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasPrimaryGroup.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasPrimaryGroup.php index b7138d30c..c8e69915a 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasPrimaryGroup.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasPrimaryGroup.php @@ -8,13 +8,8 @@ trait HasPrimaryGroup { /** * Returns a new has one primary group relationship. - * - * @param mixed $related - * @param string $relationKey - * @param string $foreignKey - * @return HasOnePrimaryGroup */ - public function hasOnePrimaryGroup($related, $relationKey, $foreignKey = 'primarygroupid') + public function hasOnePrimaryGroup(string $related, string $relationKey, string $foreignKey = 'primarygroupid'): HasOnePrimaryGroup { return new HasOnePrimaryGroup($this->newQuery(), $this, $related, $relationKey, $foreignKey); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Contact.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Contact.php index 52c451fe4..9087f058e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Contact.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Contact.php @@ -2,14 +2,14 @@ namespace LdapRecord\Models\ActiveDirectory; +use LdapRecord\Models\Relations\HasMany; + class Contact extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'person', 'organizationalperson', @@ -18,12 +18,8 @@ class Contact extends Entry /** * The groups relationship. - * - * Retrieves groups that the current contact is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany */ - public function groups() + public function groups(): HasMany { return $this->hasMany(Group::class, 'member'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Container.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Container.php index 1636cf393..e17fb1f70 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Container.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Container.php @@ -6,10 +6,8 @@ class Container extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'container', ]; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Entry.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Entry.php index e1a0233cc..e9924f433 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Entry.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Entry.php @@ -16,10 +16,8 @@ class Entry extends BaseEntry implements ActiveDirectory { /** * The default attributes that should be mutated to dates. - * - * @var array */ - protected $defaultDates = [ + protected array $defaultDates = [ 'whenchanged' => 'windows', 'whencreated' => 'windows', 'dscorepropagationdata' => 'windows', @@ -27,83 +25,73 @@ class Entry extends BaseEntry implements ActiveDirectory /** * The attribute key that contains the Object SID. - * - * @var string */ - protected $sidKey = 'objectsid'; + protected string $sidKey = 'objectsid'; /** - * @inheritdoc + * {@inheritdoc} */ - public function getObjectSidKey() + public function getObjectSidKey(): string { return $this->sidKey; } /** - * @inheritdoc + * {@inheritdoc} */ - public function getObjectSid() + public function getObjectSid(): ?string { return $this->getFirstAttribute($this->sidKey); } /** - * @inheritdoc + * {@inheritdoc} */ - public function getConvertedSid($sid = null) + public function getConvertedSid($sid = null): ?string { try { - return (string) $this->newObjectSid( - $sid ?? $this->getObjectSid() + return $this->newObjectSid( + (string) ($sid ?? $this->getObjectSid()) ); - } catch (InvalidArgumentException $e) { - return; + } catch (InvalidArgumentException) { + return null; } } /** - * @inheritdoc + * {@inheritdoc} */ - public function getBinarySid($sid = null) + public function getBinarySid($sid = null): ?string { try { return $this->newObjectSid( $sid ?? $this->getObjectSid() )->getBinary(); - } catch (InvalidArgumentException $e) { - return; + } catch (InvalidArgumentException) { + return null; } } /** * Make a new object Sid instance. - * - * @param string $value - * @return Sid */ - protected function newObjectSid($value) + protected function newObjectSid(string $value): Sid { return new Sid($value); } /** * Create a new query builder. - * - * @param Connection $connection - * @return ActiveDirectoryBuilder */ - public function newQueryBuilder(Connection $connection) + public function newQueryBuilder(Connection $connection): ActiveDirectoryBuilder { return new ActiveDirectoryBuilder($connection); } /** * Determine if the object is deleted. - * - * @return bool */ - public function isDeleted() + public function isDeleted(): bool { return strtoupper((string) $this->getFirstAttribute('isDeleted')) === 'TRUE'; } @@ -111,12 +99,9 @@ class Entry extends BaseEntry implements ActiveDirectory /** * Restore a deleted object. * - * @param string|null $newParentDn - * @return bool - * * @throws \LdapRecord\LdapRecordException */ - public function restore($newParentDn = null) + public function restore(?string $newParentDn = null): bool { if (! $this->isDeleted()) { return false; @@ -138,30 +123,27 @@ class Entry extends BaseEntry implements ActiveDirectory $this->setRawAttribute('distinguishedname', $newDn); $this->save(['isDeleted' => null]); + + return true; } /** * Get the objects restore location. - * - * @return string */ - protected function getDefaultRestoreLocation() + protected function getDefaultRestoreLocation(): ?string { return $this->getFirstAttribute('lastKnownParent') ?? $this->getParentDn($this->getParentDn($this->getDn())); } /** * Convert the attributes for JSON serialization. - * - * @param array $attributes - * @return array */ - protected function convertAttributesForJson(array $attributes = []) + protected function convertAttributesForJson(array $attributes = []): array { $attributes = parent::convertAttributesForJson($attributes); // If the model has a SID set, we need to convert it to its - // string format, due to it being in binary. Otherwise + // string format, due to it being in binary. Otherwise, // we will receive a JSON serialization exception. if (isset($attributes[$this->sidKey])) { $attributes[$this->sidKey] = [$this->getConvertedSid( @@ -174,23 +156,11 @@ class Entry extends BaseEntry implements ActiveDirectory /** * Convert the attributes from JSON serialization. - * - * @param array $attributes - * @return array */ - protected function convertAttributesFromJson(array $attributes = []) + protected function convertAttributesFromJson(array $attributes = []): array { $attributes = parent::convertAttributesFromJson($attributes); - // Here we are converting the model's GUID and SID attributes - // back to their original values from serialization, so that - // their original value may be used and compared against. - if (isset($attributes[$this->guidKey])) { - $attributes[$this->guidKey] = [$this->getBinaryGuid( - Arr::first($attributes[$this->guidKey]) - )]; - } - if (isset($attributes[$this->sidKey])) { $attributes[$this->sidKey] = [$this->getBinarySid( Arr::first($attributes[$this->sidKey]) diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeDatabase.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeDatabase.php index 77abbbcf5..e32f82009 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeDatabase.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeDatabase.php @@ -5,17 +5,17 @@ namespace LdapRecord\Models\ActiveDirectory; class ExchangeDatabase extends Entry { /** - * @inheritdoc + * {@inheritdoc} */ - public static $objectClasses = ['msExchMDB']; + public static array $objectClasses = ['msExchMDB']; /** - * @inheritdoc + * {@inheritdoc} */ - public static function boot() + public static function boot(): void { parent::boot(); - static::addGlobalScope(new Scopes\InConfigurationContext()); + static::addGlobalScope(new Scopes\InConfigurationContext); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeServer.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeServer.php index d304876ab..5e13d405d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeServer.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ExchangeServer.php @@ -5,18 +5,18 @@ namespace LdapRecord\Models\ActiveDirectory; class ExchangeServer extends Entry { /** - * @inheritdoc + * {@inheritdoc} */ - public static $objectClasses = ['msExchExchangeServer']; + public static array $objectClasses = ['msExchExchangeServer']; /** - * @inheritdoc + * {@inheritdoc} */ - public static function boot() + public static function boot(): void { parent::boot(); - static::addGlobalScope(new Scopes\HasServerRoleAttribute()); - static::addGlobalScope(new Scopes\InConfigurationContext()); + static::addGlobalScope(new Scopes\HasServerRoleAttribute); + static::addGlobalScope(new Scopes\InConfigurationContext); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ForeignSecurityPrincipal.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ForeignSecurityPrincipal.php index 25287ae4a..34e6c3358 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ForeignSecurityPrincipal.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/ForeignSecurityPrincipal.php @@ -2,23 +2,23 @@ namespace LdapRecord\Models\ActiveDirectory; +use LdapRecord\Models\Relations\HasMany; + class ForeignSecurityPrincipal extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = ['foreignsecurityprincipal']; + public static array $objectClasses = [ + 'foreignsecurityprincipal', + ]; /** * The groups relationship. * - * Retrieves groups that the current security principal is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany + * Retrieves groups that the current security principal is a part of. */ - public function groups() + public function groups(): HasMany { return $this->hasMany(Group::class, 'member'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Group.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Group.php index 784588771..165bd90f8 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Group.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Group.php @@ -2,14 +2,14 @@ namespace LdapRecord\Models\ActiveDirectory; +use LdapRecord\Models\Relations\HasMany; + class Group extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'group', ]; @@ -17,23 +17,17 @@ class Group extends Entry /** * The groups relationship. * - * Retrieves groups that the current group is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany + * Retrieves groups that the current group is a part of. */ - public function groups() + public function groups(): HasMany { return $this->hasMany(static::class, 'member'); } /** * The members relationship. - * - * Retrieves members that are apart of the group. - * - * @return \LdapRecord\Models\Relations\HasMany */ - public function members() + public function members(): HasMany { return $this->hasMany([ static::class, User::class, Contact::class, Computer::class, @@ -44,12 +38,8 @@ class Group extends Entry /** * The primary group members relationship. - * - * Retrieves members that are apart the primary group. - * - * @return \LdapRecord\Models\Relations\HasMany */ - public function primaryGroupMembers() + public function primaryGroupMembers(): HasMany { return $this->hasMany([ static::class, User::class, Contact::class, Computer::class, @@ -58,13 +48,11 @@ class Group extends Entry /** * Get the RID of the group. - * - * @return array */ - public function getRidAttribute() + public function getRidAttribute(): array { - $objectSidComponents = explode('-', (string) $this->getConvertedSid()); - - return [end($objectSidComponents)]; + return array_filter([ + last(explode('-', (string) $this->getConvertedSid())), + ]); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/OrganizationalUnit.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/OrganizationalUnit.php index 80aae9f47..0801d1080 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/OrganizationalUnit.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/OrganizationalUnit.php @@ -6,20 +6,16 @@ class OrganizationalUnit extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'organizationalunit', ]; /** * Get the creatable RDN attribute name. - * - * @return string */ - public function getCreatableRdnAttribute() + public function getCreatableRdnAttribute(): string { return 'ou'; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Printer.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Printer.php index df7421610..71534981d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Printer.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Printer.php @@ -6,8 +6,6 @@ class Printer extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = ['printqueue']; + public static array $objectClasses = ['printqueue']; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Relations/HasOnePrimaryGroup.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Relations/HasOnePrimaryGroup.php index 40350c97a..971f79066 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Relations/HasOnePrimaryGroup.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Relations/HasOnePrimaryGroup.php @@ -9,11 +9,8 @@ class HasOnePrimaryGroup extends HasOne { /** * Get the foreign model by the given value. - * - * @param string $value - * @return Model|null */ - protected function getForeignModelByValue($value) + protected function getForeignModelByValue(string $value): ?Model { return $this->query->findBySid( $this->getParentModelObjectSid() @@ -24,11 +21,8 @@ class HasOnePrimaryGroup extends HasOne * Get the foreign value from the given model. * * Retrieves the last RID from the models Object SID. - * - * @param Model $model - * @return string */ - protected function getForeignValueFromModel(Model $model) + protected function getForeignValueFromModel(Model $model): ?string { $objectSidComponents = explode('-', $model->getConvertedSid()); @@ -37,10 +31,8 @@ class HasOnePrimaryGroup extends HasOne /** * Get the parent relationship models converted object sid. - * - * @return string */ - protected function getParentModelObjectSid() + protected function getParentModelObjectSid(): string { return preg_replace( '/\d+$/', diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/HasServerRoleAttribute.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/HasServerRoleAttribute.php index 76df79ca3..24a2387d3 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/HasServerRoleAttribute.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/HasServerRoleAttribute.php @@ -10,12 +10,8 @@ class HasServerRoleAttribute implements Scope { /** * Includes condition of having a serverRole attribute. - * - * @param Builder $query - * @param Model $model - * @return void */ - public function apply(Builder $query, Model $model) + public function apply(Builder $query, Model $model): void { $query->whereHas('serverRole'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/InConfigurationContext.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/InConfigurationContext.php index dc8a4d21b..b7942cf4d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/InConfigurationContext.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/InConfigurationContext.php @@ -12,13 +12,9 @@ class InConfigurationContext implements Scope /** * Refines the base dn to be inside the configuration context. * - * @param Builder $query - * @param Model $model - * @return void - * * @throws \LdapRecord\Models\ModelNotFoundException */ - public function apply(Builder $query, Model $model) + public function apply(Builder $query, Model $model): void { $query->in($this->getConfigurationNamingContext($model)); } @@ -26,9 +22,6 @@ class InConfigurationContext implements Scope /** * Get the LDAP server configuration naming context distinguished name. * - * @param Model $model - * @return mixed - * * @throws \LdapRecord\Models\ModelNotFoundException */ protected function getConfigurationNamingContext(Model $model) diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/RejectComputerObjectClass.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/RejectComputerObjectClass.php index 9d00cf8fa..30545b802 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/RejectComputerObjectClass.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/RejectComputerObjectClass.php @@ -10,12 +10,8 @@ class RejectComputerObjectClass implements Scope { /** * Prevent computer objects from being included in results. - * - * @param Builder $query - * @param Model $model - * @return void */ - public function apply(Builder $query, Model $model) + public function apply(Builder $query, Model $model): void { $query->where('objectclass', '!=', 'computer'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/User.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/User.php index c2ea0325d..3faec3882 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/User.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/User.php @@ -4,39 +4,36 @@ namespace LdapRecord\Models\ActiveDirectory; use Carbon\Carbon; use Illuminate\Contracts\Auth\Authenticatable; +use LdapRecord\Models\ActiveDirectory\Concerns\HasAccountControl; use LdapRecord\Models\ActiveDirectory\Concerns\HasPrimaryGroup; use LdapRecord\Models\ActiveDirectory\Scopes\RejectComputerObjectClass; -use LdapRecord\Models\Attributes\AccountControl; use LdapRecord\Models\Concerns\CanAuthenticate; use LdapRecord\Models\Concerns\HasPassword; +use LdapRecord\Models\Relations\HasMany; +use LdapRecord\Models\Relations\HasOne; use LdapRecord\Query\Model\Builder; class User extends Entry implements Authenticatable { + use CanAuthenticate; + use HasAccountControl; use HasPassword; use HasPrimaryGroup; - use CanAuthenticate; /** * The password's attribute name. - * - * @var string */ - protected $passwordAttribute = 'unicodepwd'; + protected string $passwordAttribute = 'unicodepwd'; /** * The password's hash method. - * - * @var string */ - protected $passwordHashMethod = 'encode'; + protected string $passwordHashMethod = 'encode'; /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'person', 'organizationalperson', @@ -45,10 +42,8 @@ class User extends Entry implements Authenticatable /** * The attributes that should be mutated to dates. - * - * @var array */ - protected $dates = [ + protected array $dates = [ 'lastlogon' => 'windows-int', 'lastlogoff' => 'windows-int', 'pwdlastset' => 'windows-int', @@ -59,9 +54,9 @@ class User extends Entry implements Authenticatable ]; /** - * @inheritdoc + * {@inheritdoc} */ - protected static function boot() + protected static function boot(): void { parent::boot(); @@ -69,49 +64,23 @@ class User extends Entry implements Authenticatable // class. This is needed due to computer objects containing all // of the ActiveDirectory 'user' object classes. Without // this scope, they would be included in results. - static::addGlobalScope(new RejectComputerObjectClass()); + static::addGlobalScope(new RejectComputerObjectClass); } /** - * Determine if the user's account is enabled. - * - * @return bool + * Get the unique identifier for the user. */ - public function isEnabled() + public function getAuthIdentifier(): ?string { - return ! $this->isDisabled(); - } - - /** - * Determine if the user's account is disabled. - * - * @return bool - */ - public function isDisabled() - { - return $this->accountControl()->has(AccountControl::ACCOUNTDISABLE); - } - - /** - * Get the user's account control. - * - * @return AccountControl - */ - public function accountControl() - { - return new AccountControl( - $this->getFirstAttribute('userAccountControl') - ); + return $this->getConvertedGuid(); } /** * The groups relationship. * - * Retrieves groups that the user is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany + * Retrieves groups that the user is a part of. */ - public function groups() + public function groups(): HasMany { return $this->hasMany(Group::class, 'member')->with($this->primaryGroup()); } @@ -120,10 +89,8 @@ class User extends Entry implements Authenticatable * The manager relationship. * * Retrieves the manager of the user. - * - * @return \LdapRecord\Models\Relations\HasOne */ - public function manager() + public function manager(): HasOne { return $this->hasOne(static::class, 'manager'); } @@ -131,33 +98,25 @@ class User extends Entry implements Authenticatable /** * The primary group relationship of the current user. * - * Retrieves the primary group the user is apart of. - * - * @return \LdapRecord\Models\Relations\HasOne + * Retrieves the primary group the user is a part of. */ - public function primaryGroup() + public function primaryGroup(): HasOne { return $this->hasOnePrimaryGroup(Group::class, 'primarygroupid'); } /** * Scopes the query to exchange mailbox users. - * - * @param Builder $query - * @return Builder */ - public function scopeWhereHasMailbox(Builder $query) + public function scopeWhereHasMailbox(Builder $query): Builder { return $query->whereHas('msExchMailboxGuid'); } /** * Scopes the query to users having a lockout value set. - * - * @param Builder $query - * @return Builder */ - public function scopeWhereHasLockout(Builder $query) + public function scopeWhereHasLockout(Builder $query): Builder { return $query->where('lockoutTime', '>=', 1); } @@ -167,12 +126,8 @@ class User extends Entry implements Authenticatable * * @see https://ldapwiki.com/wiki/Active%20Directory%20Account%20Lockout * @see https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/account-lockout-duration - * - * @param string|int $localTimezone - * @param int|null $durationInMinutes - * @return bool */ - public function isLockedOut($localTimezone, $durationInMinutes = null) + public function isLockedOut(string|int $localTimezone, ?int $durationInMinutes = null): bool { $time = $this->getFirstAttribute('lockouttime'); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/AccountControl.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/AccountControl.php index 45fae214d..9024b934e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/AccountControl.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/AccountControl.php @@ -3,8 +3,9 @@ namespace LdapRecord\Models\Attributes; use ReflectionClass; +use Stringable; -class AccountControl +class AccountControl implements Stringable { public const SCRIPT = 1; @@ -51,151 +52,122 @@ class AccountControl public const PARTIAL_SECRETS_ACCOUNT = 67108864; /** - * The account control flag values. + * The account control flags. * * @var array */ - protected $values = []; + protected array $flags = []; /** * Constructor. - * - * @param ?int $flag */ - public function __construct($flag = null) + public function __construct(?int $flag = null) { if (! is_null($flag)) { - $this->apply($flag); + $this->applyFlags($flag); } } /** - * Get the value when casted to string. - * - * @return string + * Get the value when cast to string. */ - public function __toString() + public function __toString(): string { return (string) $this->getValue(); } /** - * Get the value when casted to int. - * - * @return int + * Get the value when cast to int. */ - public function __toInt() + public function __toInt(): int { return $this->getValue(); } /** - * Add the flag to the account control values. - * - * @param int $flag - * @return $this + * Set a flag on the account control. */ - public function add($flag) + public function setFlag(int $flag): static { // Use the value as a key so if the same value // is used, it will always be overwritten - $this->values[$flag] = $flag; + $this->flags[$flag] = $flag; return $this; } /** - * Remove the flag from the account control. - * - * @param int $flag - * @return $this + * Unset a flag from the account control. */ - public function remove($flag) + public function unsetFlag(int $flag): static { - unset($this->values[$flag]); + unset($this->flags[$flag]); return $this; } /** - * Extract and apply the flag. - * - * @param int $flag - * @return void + * Extract and apply several flags. */ - public function apply($flag) + public function applyFlags(int $flags): void { - $this->setValues($this->extractFlags($flag)); + $this->setFlags($this->extractFlags($flags)); } /** - * Determine if the account control contains the given UAC flag(s). - * - * @param int $flag - * @return bool + * Determine if the account control contains the given flag(s). */ - public function has($flag) + public function hasFlag(int $flag): bool { // Here we will extract the given flag into an array // of possible flags. This will allow us to see if // our AccountControl object contains any of them. $flagsUsed = array_intersect( $this->extractFlags($flag), - $this->values + $this->flags ); return in_array($flag, $flagsUsed); } /** - * Determine if the account control does not contain the given UAC flag(s). - * - * @param int $flag - * @return bool + * Determine if the account control does not contain the given flag(s). */ - public function doesntHave($flag) + public function doesntHaveFlag(int $flag): bool { - return ! $this->has($flag); + return ! $this->hasFlag($flag); } /** * Generate an LDAP filter based on the current value. - * - * @return string */ - public function filter() + public function filter(): string { - return sprintf('(UserAccountControl:1.2.840.113556.1.4.803:=%s)', $this->getValue()); + return sprintf('(UserAccountControl:1.2.840.113556.1.4.803:=%s)', $this); } /** * The logon script will be run. - * - * @return $this */ - public function runLoginScript() + public function setRunLoginScript(): static { - return $this->add(static::SCRIPT); + return $this->setFlag(static::SCRIPT); } /** * The user account is locked. - * - * @return $this */ - public function accountIsLocked() + public function setAccountIsLocked(): static { - return $this->add(static::LOCKOUT); + return $this->setFlag(static::LOCKOUT); } /** * The user account is disabled. - * - * @return $this */ - public function accountIsDisabled() + public function setAccountIsDisabled(): static { - return $this->add(static::ACCOUNTDISABLE); + return $this->setFlag(static::ACCOUNTDISABLE); } /** @@ -203,117 +175,95 @@ class AccountControl * * This account provides user access to this domain, but not to any domain that * trusts this domain. This is sometimes referred to as a local user account. - * - * @return $this */ - public function accountIsTemporary() + public function setAccountIsTemporary(): static { - return $this->add(static::TEMP_DUPLICATE_ACCOUNT); + return $this->setFlag(static::TEMP_DUPLICATE_ACCOUNT); } /** * This is a default account type that represents a typical user. - * - * @return $this */ - public function accountIsNormal() + public function setAccountIsNormal(): static { - return $this->add(static::NORMAL_ACCOUNT); + return $this->setFlag(static::NORMAL_ACCOUNT); } /** * This is a permit to trust an account for a system domain that trusts other domains. - * - * @return $this */ - public function accountIsForInterdomain() + public function setAccountIsForInterdomain(): static { - return $this->add(static::INTERDOMAIN_TRUST_ACCOUNT); + return $this->setFlag(static::INTERDOMAIN_TRUST_ACCOUNT); } /** * This is a computer account for a computer that is running Microsoft * Windows NT 4.0 Workstation, Microsoft Windows NT 4.0 Server, Microsoft * Windows 2000 Professional, or Windows 2000 Server and is a member of this domain. - * - * @return $this */ - public function accountIsForWorkstation() + public function setAccountIsForWorkstation(): static { - return $this->add(static::WORKSTATION_TRUST_ACCOUNT); + return $this->setFlag(static::WORKSTATION_TRUST_ACCOUNT); } /** * This is a computer account for a domain controller that is a member of this domain. - * - * @return $this */ - public function accountIsForServer() + public function setAccountIsForServer(): static { - return $this->add(static::SERVER_TRUST_ACCOUNT); + return $this->setFlag(static::SERVER_TRUST_ACCOUNT); } /** * This is an MNS logon account. - * - * @return $this */ - public function accountIsMnsLogon() + public function setAccountIsMnsLogon(): static { - return $this->add(static::MNS_LOGON_ACCOUNT); + return $this->setFlag(static::MNS_LOGON_ACCOUNT); } /** * (Windows 2000/Windows Server 2003) This account does * not require Kerberos pre-authentication for logging on. - * - * @return $this */ - public function accountDoesNotRequirePreAuth() + public function setAccountDoesNotRequirePreAuth(): static { - return $this->add(static::DONT_REQ_PREAUTH); + return $this->setFlag(static::DONT_REQ_PREAUTH); } /** * When this flag is set, it forces the user to log on by using a smart card. - * - * @return $this */ - public function accountRequiresSmartCard() + public function setAccountRequiresSmartCard(): static { - return $this->add(static::SMARTCARD_REQUIRED); + return $this->setFlag(static::SMARTCARD_REQUIRED); } /** * (Windows Server 2008/Windows Server 2008 R2) The account is a read-only domain controller (RODC). * * This is a security-sensitive setting. Removing this setting from an RODC compromises security on that server. - * - * @return $this */ - public function accountIsReadOnly() + public function setAccountIsReadOnly(): static { - return $this->add(static::PARTIAL_SECRETS_ACCOUNT); + return $this->setFlag(static::PARTIAL_SECRETS_ACCOUNT); } /** * The home folder is required. - * - * @return $this */ - public function homeFolderIsRequired() + public function setHomeFolderIsRequired(): static { - return $this->add(static::HOMEDIR_REQUIRED); + return $this->setFlag(static::HOMEDIR_REQUIRED); } /** * No password is required. - * - * @return $this */ - public function passwordIsNotRequired() + public function setPasswordIsNotRequired(): static { - return $this->add(static::PASSWD_NOTREQD); + return $this->setFlag(static::PASSWD_NOTREQD); } /** @@ -322,42 +272,34 @@ class AccountControl * For information about how to programmatically set this permission, visit the following link: * * @see http://msdn2.microsoft.com/en-us/library/aa746398.aspx - * - * @return $this */ - public function passwordCannotBeChanged() + public function setPasswordCannotBeChanged(): static { - return $this->add(static::PASSWD_CANT_CHANGE); + return $this->setFlag(static::PASSWD_CANT_CHANGE); } /** * Represents the password, which should never expire on the account. - * - * @return $this */ - public function passwordDoesNotExpire() + public function setPasswordDoesNotExpire(): static { - return $this->add(static::DONT_EXPIRE_PASSWORD); + return $this->setFlag(static::DONT_EXPIRE_PASSWORD); } /** * (Windows 2000/Windows Server 2003) The user's password has expired. - * - * @return $this */ - public function passwordIsExpired() + public function setPasswordIsExpired(): static { - return $this->add(static::PASSWORD_EXPIRED); + return $this->setFlag(static::PASSWORD_EXPIRED); } /** * The user can send an encrypted password. - * - * @return $this */ - public function allowEncryptedTextPassword() + public function setAllowEncryptedTextPassword(): static { - return $this->add(static::ENCRYPTED_TEXT_PWD_ALLOWED); + return $this->setFlag(static::ENCRYPTED_TEXT_PWD_ALLOWED); } /** @@ -368,12 +310,10 @@ class AccountControl * * To enable a service for Kerberos delegation, you must set this * flag on the userAccountControl property of the service account. - * - * @return $this */ - public function trustForDelegation() + public function setTrustForDelegation(): static { - return $this->add(static::TRUSTED_FOR_DELEGATION); + return $this->setFlag(static::TRUSTED_FOR_DELEGATION); } /** @@ -383,44 +323,36 @@ class AccountControl * should be tightly controlled. This setting lets a service that runs under the * account assume a client's identity and authenticate as that user to other remote * servers on the network. - * - * @return $this */ - public function trustToAuthForDelegation() + public function setTrustToAuthForDelegation(): static { - return $this->add(static::TRUSTED_TO_AUTH_FOR_DELEGATION); + return $this->setFlag(static::TRUSTED_TO_AUTH_FOR_DELEGATION); } /** * When this flag is set, the security context of the user is not delegated to a * service even if the service account is set as trusted for Kerberos delegation. - * - * @return $this */ - public function doNotTrustForDelegation() + public function setDoNotTrustForDelegation(): static { - return $this->add(static::NOT_DELEGATED); + return $this->setFlag(static::NOT_DELEGATED); } /** * (Windows 2000/Windows Server 2003) Restrict this principal to * use only Data Encryption Standard (DES) encryption types for keys. - * - * @return $this */ - public function useDesKeyOnly() + public function setUseDesKeyOnly(): static { - return $this->add(static::USE_DES_KEY_ONLY); + return $this->setFlag(static::USE_DES_KEY_ONLY); } /** * Get the account control value. - * - * @return int */ - public function getValue() + public function getValue(): int { - return array_sum($this->values); + return array_sum($this->flags); } /** @@ -428,64 +360,56 @@ class AccountControl * * @return array */ - public function getValues() + public function getFlags(): array { - return $this->values; + return $this->flags; } /** * Set the account control values. * * @param array $flags - * @return void */ - public function setValues(array $flags) + public function setFlags(array $flags): void { - $this->values = $flags; + $this->flags = $flags; } /** * Get all flags that are currently applied to the value. - * - * @return array */ - public function getAppliedFlags() + public function getAppliedFlags(): array { $flags = $this->getAllFlags(); - $exists = []; + $applied = []; foreach ($flags as $name => $flag) { - if ($this->has($flag)) { - $exists[$name] = $flag; + if ($this->hasFlag($flag)) { + $applied[$name] = $flag; } } - return $exists; + return $applied; } /** * Get all possible account control flags. - * - * @return array */ - public function getAllFlags() + public function getAllFlags(): array { return (new ReflectionClass(__CLASS__))->getConstants(); } /** * Extracts the given flag into an array of flags used. - * - * @param int $flag - * @return array */ - public function extractFlags($flag) + protected function extractFlags(int $flag): array { $flags = []; for ($i = 0; $i <= 26; $i++) { - if ((int) $flag & (1 << $i)) { + if ($flag & (1 << $i)) { $flags[1 << $i] = 1 << $i; } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedName.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedName.php index fc981299a..09391091b 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedName.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedName.php @@ -4,89 +4,69 @@ namespace LdapRecord\Models\Attributes; use LdapRecord\EscapesValues; use LdapRecord\Support\Arr; +use Stringable; -class DistinguishedName +class DistinguishedName implements Stringable { use EscapesValues; /** - * The underlying raw value. - * - * @var string + * The underlying raw distinguished name value. */ - protected $value; + protected string $value; /** * Constructor. - * - * @param string|null $value */ - public function __construct($value = null) + public function __construct(?string $value = null) { $this->value = trim((string) $value); } /** * Get the distinguished name value. - * - * @return string */ - public function __toString() + public function __toString(): string { - return (string) $this->value; + return $this->value; } /** * Alias of the "build" method. - * - * @param string|null $value - * @return DistinguishedNameBuilder */ - public static function of($value = null) + public static function of(?string $value = null): DistinguishedNameBuilder { return static::build($value); } /** * Get a new DN builder object from the given DN. - * - * @param string|null $value - * @return DistinguishedNameBuilder */ - public static function build($value = null) + public static function build(?string $value = null): DistinguishedNameBuilder { return new DistinguishedNameBuilder($value); } /** * Make a new distinguished name instance. - * - * @param string|null $value - * @return static */ - public static function make($value = null) + public static function make(?string $value = null): static { return new static($value); } /** * Determine if the given value is a valid distinguished name. - * - * @param string $value - * @return bool */ - public static function isValid($value) + public static function isValid(?string $value = null): bool { return ! static::make($value)->isEmpty(); } /** * Explode a distinguished name into relative distinguished names. - * - * @param string $dn - * @return array */ - public static function explode($dn) + public static function explode(string $dn): array { $components = ldap_explode_dn($dn, (int) $withoutAttributes = false); @@ -103,58 +83,34 @@ class DistinguishedName return $components; } - /** - * Un-escapes a hexadecimal string into its original string representation. - * - * @param string $value - * @return string - */ - public static function unescape($value) - { - return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/', function ($matches) { - return chr(hexdec($matches[1])); - }, $value); - } - /** * Explode the RDN into an attribute and value. - * - * @param string $rdn - * @return array */ - public static function explodeRdn($rdn) + public static function explodeRdn(string $rdn): array { return explode('=', $rdn, $limit = 2); } /** * Implode the component attribute and value into an RDN. - * - * @param string $rdn - * @return string */ - public static function makeRdn(array $component) + public static function makeRdn(array $component): string { return implode('=', $component); } /** * Get the underlying value. - * - * @return string|null */ - public function get() + public function get(): ?string { return $this->value; } /** * Set the underlying value. - * - * @param string|null $value - * @return $this */ - public function set($value) + public function set(?string $value = null): static { $this->value = $value; @@ -163,15 +119,13 @@ class DistinguishedName /** * Get the distinguished name values without attributes. - * - * @return array */ - public function values() + public function values(): array { $values = []; foreach ($this->multi() as [, $value]) { - $values[] = static::unescape($value); + $values[] = EscapedValue::unescape($value); } return $values; @@ -179,10 +133,8 @@ class DistinguishedName /** * Get the distinguished name attributes without values. - * - * @return array */ - public function attributes() + public function attributes(): array { $attributes = []; @@ -195,10 +147,8 @@ class DistinguishedName /** * Get the distinguished name components with attributes. - * - * @return array */ - public function components() + public function components(): array { $components = []; @@ -207,7 +157,7 @@ class DistinguishedName // escaped. This cannot be opted out of. Here we will unescape // the attribute value, then re-escape it to its original // representation from the server using the "dn" flag. - $value = $this->escape(static::unescape($value))->dn(); + $value = $this->escape(EscapedValue::unescape($value))->forDn(); $components[] = static::makeRdn([$attribute, $value]); } @@ -217,10 +167,8 @@ class DistinguishedName /** * Convert the distinguished name into an associative array. - * - * @return array */ - public function assoc() + public function assoc(): array { $map = []; @@ -237,52 +185,40 @@ class DistinguishedName /** * Split the RDNs into a multi-dimensional array. - * - * @return array */ - public function multi() + public function multi(): array { - return array_map(function ($rdn) { - return static::explodeRdn($rdn); - }, $this->rdns()); + return array_map(fn ($rdn) => static::explodeRdn($rdn), $this->rdns()); } /** * Split the distinguished name into an array of unescaped RDN's. - * - * @return array */ - public function rdns() + public function rdns(): array { return static::explode($this->value); } /** * Get the first RDNs value. - * - * @return string|null */ - public function name() + public function name(): ?string { return Arr::first($this->values()); } /** * Get the first RDNs attribute. - * - * @return string|null */ - public function head() + public function head(): ?string { return Arr::first($this->attributes()); } /** * Get the relative distinguished name. - * - * @return string|null */ - public function relative() + public function relative(): ?string { return Arr::first($this->components()); } @@ -291,20 +227,16 @@ class DistinguishedName * Alias of relative(). * * Get the first RDN from the distinguished name. - * - * @return string|null */ - public function first() + public function first(): ?string { return $this->relative(); } /** * Get the parent distinguished name. - * - * @return string|null */ - public function parent() + public function parent(): ?string { $components = $this->components(); @@ -315,112 +247,91 @@ class DistinguishedName /** * Determine if the distinguished name is empty. - * - * @return bool */ - public function isEmpty() + public function isEmpty(): bool { return empty( - array_filter($this->values()) + array_filter( + array_map('trim', $this->values()) + ) ); } /** - * Determine if the current distinguished name is a parent of the given child. - * - * @param DistinguishedName $child - * @return bool + * Determine if the distinguished name is not empty. */ - public function isParentOf(self $child) + public function isNotEmpty(): bool + { + return ! $this->isEmpty(); + } + + /** + * Determine if the current distinguished name is a parent of the given child. + */ + public function isParentOf(self $child): bool { return $child->isChildOf($this); } /** * Determine if the current distinguished name is a child of the given parent. - * - * @param DistinguishedName $parent - * @return bool */ - public function isChildOf(self $parent) + public function isChildOf(self $parent): bool { - if ( - empty($components = $this->components()) || - empty($parentComponents = $parent->components()) - ) { + if (! $this->isComparable($this->parent(), $parent->get())) { return false; } - array_shift($components); - - return $this->compare($components, $parentComponents); + return $this->normalize($this->parent()) === $this->normalize($parent->get()); } /** * Determine if the current distinguished name is an ancestor of the descendant. - * - * @param DistinguishedName $descendant - * @return bool */ - public function isAncestorOf(self $descendant) + public function isAncestorOf(self $descendant): bool { return $descendant->isDescendantOf($this); } /** * Determine if the current distinguished name is a descendant of the ancestor. - * - * @param DistinguishedName $ancestor - * @return bool */ - public function isDescendantOf(self $ancestor) + public function isDescendantOf(self $ancestor): bool { - if ( - empty($components = $this->components()) || - empty($ancestorComponents = $ancestor->components()) - ) { + if (! $this->isComparable($this->parent(), $ancestor->get())) { return false; } - if (! $length = count($components) - count($ancestorComponents)) { + return str_ends_with( + $this->normalize($this->parent()), + $this->normalize($ancestor->get()) + ); + } + + /** + * Determine if the current distinguished name is a sibling of the given distinguished name. + */ + public function isSiblingOf(self $sibling): bool + { + if (! $this->isComparable($this->parent(), $sibling->parent())) { return false; } - array_splice($components, $offset = 0, $length); - - return $this->compare($components, $ancestorComponents); + return $this->normalize($this->parent()) === $this->normalize($sibling->parent()); } /** - * Compare whether the two distinguished name values are equal. - * - * @param array $values - * @param array $other - * @return bool + * Determine if the distinguished names are comparable. */ - protected function compare(array $values, array $other) + protected function isComparable(?string $first, ?string $second): bool { - return $this->recase($values) == $this->recase($other); - } - - /** - * Recase the array values. - * - * @param array $values - * @return array - */ - protected function recase(array $values) - { - return array_map([$this, 'normalize'], $values); + return static::make($first)->isNotEmpty() && static::make($second)->isNotEmpty(); } /** * Normalize the string value. - * - * @param string $value - * @return string */ - protected function normalize($value) + protected function normalize(string $value): string { return strtolower($value); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedNameBuilder.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedNameBuilder.php index a0d84d828..7345cb92d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedNameBuilder.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedNameBuilder.php @@ -4,67 +4,53 @@ namespace LdapRecord\Models\Attributes; use LdapRecord\EscapesValues; use LdapRecord\Support\Arr; +use Stringable; -class DistinguishedNameBuilder +class DistinguishedNameBuilder implements Stringable { use EscapesValues; /** * The components of the DN. - * - * @var array */ - protected $components = []; + protected array $components = []; /** * Whether to output the DN in reverse. - * - * @var bool */ - protected $reverse = false; + protected bool $reverse = false; /** * Constructor. - * - * @param string|null $value */ public function __construct($dn = null) { - $this->components = array_map(function ($rdn) { - return DistinguishedName::explodeRdn($rdn); - }, DistinguishedName::make($dn)->components()); + $this->components = array_map( + fn ($rdn) => DistinguishedName::explodeRdn($rdn), + DistinguishedName::make($dn)->components() + ); } /** * Forward missing method calls onto the Distinguished Name object. - * - * @param string $method - * @param array $args - * @return mixed */ - public function __call($method, $args) + public function __call(string $method, array $args): mixed { return $this->get()->{$method}(...$args); } /** * Get the distinguished name value. - * - * @return string */ - public function __toString() + public function __toString(): string { return (string) $this->get(); } /** * Prepend an RDN onto the DN. - * - * @param string|array $attribute - * @param string|null $value - * @return $this */ - public function prepend($attribute, $value = null) + public function prepend(array|string $attribute, ?string $value = null): static { array_unshift( $this->components, @@ -76,12 +62,8 @@ class DistinguishedNameBuilder /** * Append an RDN onto the DN. - * - * @param string|array $attribute - * @param string|null $value - * @return $this */ - public function append($attribute, $value = null) + public function append(array|string $attribute, ?string $value = null): static { array_push( $this->components, @@ -93,12 +75,8 @@ class DistinguishedNameBuilder /** * Componentize the attribute and value. - * - * @param string|array $attribute - * @param string|null $value - * @return array */ - protected function componentize($attribute, $value = null) + protected function componentize(array|string $attribute, ?string $value = null): array { // Here we will make the assumption that an array of // RDN's have been given if the value is null, and @@ -120,65 +98,50 @@ class DistinguishedNameBuilder /** * Make a componentized array by exploding the value if it's a string. - * - * @param string $value - * @return array */ - protected function makeComponentizedArray($value) + protected function makeComponentizedArray(array|string $value): array { return is_array($value) ? $value : DistinguishedName::explodeRdn($value); } /** * Make an appendable component array from the attribute and value. - * - * @param string|array $attribute - * @param string|null $value - * @return array */ - protected function makeAppendableComponent($attribute, $value = null) + protected function makeAppendableComponent(string|array $attribute, ?string $value = null): array { - return [trim($attribute), $this->escape(trim($value))->dn()]; + return [trim($attribute), $this->escape(trim($value))->forDn()]; } /** * Pop an RDN off of the end of the DN. - * - * @param int $amount - * @param array $removed - * @return $this */ - public function pop($amount = 1, &$removed = []) + public function pop(int $amount = 1, ?array &$removed = null): static { - $removed = array_map(function ($component) { - return DistinguishedName::makeRdn($component); - }, array_splice($this->components, -$amount, $amount)); + $removed = array_map( + fn ($component) => DistinguishedName::makeRdn($component), + array_splice($this->components, -$amount, $amount) + ); return $this; } /** * Shift an RDN off of the beginning of the DN. - * - * @param int $amount - * @param array $removed - * @return $this */ - public function shift($amount = 1, &$removed = []) + public function shift(int $amount = 1, ?array &$removed = null): static { - $removed = array_map(function ($component) { - return DistinguishedName::makeRdn($component); - }, array_splice($this->components, 0, $amount)); + $removed = array_map( + fn ($component) => DistinguishedName::makeRdn($component), + array_splice($this->components, 0, $amount) + ); return $this; } /** * Whether to output the DN in reverse. - * - * @return $this */ - public function reverse() + public function reverse(): static { $this->reverse = true; @@ -187,11 +150,8 @@ class DistinguishedNameBuilder /** * Get the components of the DN. - * - * @param null|string $type - * @return array */ - public function components($type = null) + public function components(?string $type = null): array { return is_null($type) ? $this->components @@ -200,42 +160,36 @@ class DistinguishedNameBuilder /** * Get the components of a particular type. - * - * @param string $type - * @return array */ - protected function componentsOfType($type) + protected function componentsOfType(string $type): array { - $components = array_filter($this->components, function ($component) use ($type) { - return ([$name] = $component) && strtolower($name) === strtolower($type); - }); + $components = array_filter($this->components, fn ($component) => ( + ([$name] = $component) && strtolower($name) === strtolower($type) + )); return array_values($components); } /** * Get the fully qualified DN. - * - * @return DistinguishedName */ - public function get() + public function get(): DistinguishedName { return new DistinguishedName($this->build()); } /** * Build the distinguished name from the components. - * - * @return string */ - protected function build() + protected function build(): string { $components = $this->reverse ? array_reverse($this->components) : $this->components; - return implode(',', array_map(function ($component) { - return DistinguishedName::makeRdn($component); - }, $components)); + return implode(',', array_map( + fn ($component) => DistinguishedName::makeRdn($component), + $components + )); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/EscapedValue.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/EscapedValue.php index 3c9d4db0a..c28d95c20 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/EscapedValue.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/EscapedValue.php @@ -2,80 +2,75 @@ namespace LdapRecord\Models\Attributes; -class EscapedValue +use Stringable; + +class EscapedValue implements Stringable { /** * The value to be escaped. - * - * @var string */ - protected $value; + protected mixed $value; /** * The characters to ignore when escaping. - * - * @var string */ - protected $ignore; + protected string $ignore; /** * The escape flags. - * - * @var int */ - protected $flags; + protected int $flags; /** * Constructor. - * - * @param string $value - * @param string $ignore - * @param int $flags */ - public function __construct($value, $ignore = '', $flags = 0) + public function __construct(mixed $value, string $ignore = '', int $flags = 0) { - $this->value = (string) $value; + $this->value = $value; $this->ignore = $ignore; $this->flags = $flags; } /** - * Get the escaped value. - * - * @return string + * Un-escapes a hexadecimal string into its original string representation. */ - public function __toString() + public static function unescape(string $value): string { - return (string) $this->get(); + return preg_replace_callback( + '/\\\([0-9A-Fa-f]{2})/', + fn ($matches) => chr(hexdec($matches[1])), + $value + ); } /** * Get the escaped value. - * - * @return mixed */ - public function get() + public function __toString(): string { - return ldap_escape($this->value, $this->ignore, $this->flags); + return $this->get(); + } + + /** + * Get the escaped value. + */ + public function get(): string + { + return ldap_escape((string) $this->value, $this->ignore, $this->flags); } /** * Get the raw (unescaped) value. - * - * @return mixed */ - public function raw() + public function getRaw(): mixed { return $this->value; } /** * Set the characters to exclude from being escaped. - * - * @param string $characters - * @return $this */ - public function ignore($characters) + public function ignore(string $characters): static { $this->ignore = $characters; @@ -84,10 +79,8 @@ class EscapedValue /** * Prepare the value to be escaped for use in a distinguished name. - * - * @return $this */ - public function dn() + public function forDn(): static { $this->flags = LDAP_ESCAPE_DN; @@ -96,10 +89,8 @@ class EscapedValue /** * Prepare the value to be escaped for use in a filter. - * - * @return $this */ - public function filter() + public function forFilter(): static { $this->flags = LDAP_ESCAPE_FILTER; @@ -108,10 +99,8 @@ class EscapedValue /** * Prepare the value to be escaped for use in a distinguished name and filter. - * - * @return $this */ - public function both() + public function forDnAndFilter(): static { $this->flags = LDAP_ESCAPE_FILTER + LDAP_ESCAPE_DN; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Guid.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Guid.php index e04b5cd3f..dfde16429 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Guid.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Guid.php @@ -3,33 +3,14 @@ namespace LdapRecord\Models\Attributes; use InvalidArgumentException; -use LdapRecord\Utilities; +use Stringable; -class Guid +class Guid implements Stringable { /** * The string GUID value. - * - * @var string */ - protected $value; - - /** - * The guid structure in order by section to parse using substr(). - * - * @author Chad Sikorra - * - * @see https://github.com/ldaptools/ldaptools - * - * @var array - */ - protected $guidSections = [ - [[-26, 2], [-28, 2], [-30, 2], [-32, 2]], - [[-22, 2], [-24, 2]], - [[-18, 2], [-20, 2]], - [[-16, 4]], - [[-12, 12]], - ]; + protected ?string $value = null; /** * The hexadecimal octet order based on string position. @@ -37,10 +18,8 @@ class Guid * @author Chad Sikorra * * @see https://github.com/ldaptools/ldaptools - * - * @var array */ - protected $octetSections = [ + protected array $octetSections = [ [6, 4, 2, 0], [10, 8], [14, 12], @@ -48,24 +27,19 @@ class Guid ]; /** - * Determines if the specified GUID is valid. - * - * @param string $guid - * @return bool + * Determine if the specified GUID is valid. */ - public static function isValid($guid) + public static function isValid(string $guid): bool { - return Utilities::isValidGuid($guid); + return (bool) preg_match('/^([0-9a-fA-F]){8}(-([0-9a-fA-F]){4}){3}-([0-9a-fA-F]){12}$/', $guid); } /** * Constructor. * - * @param mixed $value - * * @throws InvalidArgumentException */ - public function __construct($value) + public function __construct(string $value) { if (static::isValid($value)) { $this->value = $value; @@ -77,66 +51,83 @@ class Guid } /** - * Returns the string value of the GUID. - * - * @return string + * Get the string value of the GUID. */ - public function __toString() + public function __toString(): string { return $this->getValue(); } /** - * Returns the string value of the SID. - * - * @return string + * Get the string value of the GUID. */ - public function getValue() + public function getValue(): string { return $this->value; } /** * Get the binary representation of the GUID string. - * - * @return string */ - public function getBinary() + public function getBinary(): string { return hex2bin($this->getHex()); } /** - * Get the hexadecimal representation of the GUID string. - * - * @return string + * Get the encoded hexadecimal representation of the GUID string. */ - public function getHex() + public function getEncodedHex(): string { - $data = ''; + return '\\'.implode('\\', str_split($this->getHex(), 2)); + } + + /** + * Get the hexadecimal representation of the GUID string. + */ + public function getHex(): string + { + return implode($this->getOctetSections()); + } + + /** + * Get the octect sections of the GUID. + */ + protected function getOctetSections(): array + { + $sections = []; $guid = str_replace('-', '', $this->value); foreach ($this->octetSections as $section) { - $data .= $this->parseSection($guid, $section, $octet = true); + $sections[] = $this->parseSection($guid, $section, true); } - return $data; + return $sections; } /** - * Returns the string variant of a binary GUID. - * - * @param string $binary - * @return string|null + * Get the string variant of a binary GUID. */ - protected function binaryGuidToString($binary) + protected function binaryGuidToString(string $binary): ?string { - return Utilities::binaryGuidToString($binary); + if (trim($binary) === '') { + return null; + } + + $hex = unpack('H*hex', $binary)['hex']; + + $hex1 = substr($hex, -26, 2).substr($hex, -28, 2).substr($hex, -30, 2).substr($hex, -32, 2); + $hex2 = substr($hex, -22, 2).substr($hex, -24, 2); + $hex3 = substr($hex, -18, 2).substr($hex, -20, 2); + $hex4 = substr($hex, -16, 4); + $hex5 = substr($hex, -12, 12); + + return sprintf('%s-%s-%s-%s-%s', $hex1, $hex2, $hex3, $hex4, $hex5); } /** - * Return the specified section of the hexadecimal string. + * Get the specified section of the hexadecimal string. * * @author Chad Sikorra * @@ -147,7 +138,7 @@ class Guid * @param bool $octet Whether this is for octet string form. * @return string The concatenated sections in upper-case. */ - protected function parseSection($hex, array $sections, $octet = false) + protected function parseSection(string $hex, array $sections, bool $octet = false): string { $parsedString = ''; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/MbString.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/MbString.php index 72d4c6f48..c6eb4985e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/MbString.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/MbString.php @@ -6,11 +6,8 @@ class MbString { /** * Get the integer value of a specific character. - * - * @param $string - * @return int */ - public static function ord($string) + public static function ord(string $string): int { if (static::isLoaded()) { $result = unpack('N', mb_convert_encoding($string, 'UCS-4BE', 'UTF-8')); @@ -25,11 +22,8 @@ class MbString /** * Get the character for a specific integer value. - * - * @param $int - * @return string */ - public static function chr($int) + public static function chr(int $int): string { if (static::isLoaded()) { return mb_convert_encoding(pack('n', $int), 'UTF-8', 'UTF-16BE'); @@ -40,36 +34,28 @@ class MbString /** * Split a string into its individual characters and return it as an array. - * - * @param string $value - * @return string[] */ - public static function split($value) + public static function split(string $value): array { return preg_split('/(? ['$1$', 12], + static::CRYPT_SALT_TYPE_SHA256 => ['$5$', 16], + static::CRYPT_SALT_TYPE_SHA512 => ['$6$', 16], + default => throw new InvalidArgumentException("Invalid crypt type [$type]."), + }; } /** * Attempt to retrieve the hash method used for the password. - * - * @param string $password - * @return string|void */ - public static function getHashMethod($password) + public static function getHashMethod(string $password): ?string { if (! preg_match('/^\{(\w+)\}/', $password, $matches)) { - return; + return null; } return $matches[1]; @@ -272,14 +198,11 @@ class Password /** * Attempt to retrieve the hash method and algorithm used for the password. - * - * @param string $password - * @return array|void */ - public static function getHashMethodAndAlgo($password) + public static function getHashMethodAndAlgo(string $password): ?array { if (! preg_match('/^\{(\w+)\}\$([0-9a-z]{1})\$/', $password, $matches)) { - return; + return null; } return [$matches[1], $matches[2]]; @@ -288,11 +211,9 @@ class Password /** * Attempt to retrieve a salt from the encrypted password. * - * @return string - * * @throws LdapRecordException */ - public static function getSalt($encryptedPassword) + public static function getSalt(string $encryptedPassword): string { // crypt() methods. if (preg_match('/^\{(\w+)\}(\$.*\$).*$/', $encryptedPassword, $matches)) { @@ -310,12 +231,9 @@ class Password /** * Determine if the hash method requires a salt to be given. * - * @param string $method - * @return bool - * * @throws \ReflectionException */ - public static function hashMethodRequiresSalt($method): bool + public static function hashMethodRequiresSalt(string $method): bool { $parameters = (new ReflectionMethod(static::class, $method))->getParameters(); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Sid.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Sid.php index 7760453ce..08c36267c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Sid.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Sid.php @@ -3,36 +3,29 @@ namespace LdapRecord\Models\Attributes; use InvalidArgumentException; -use LdapRecord\Utilities; +use Stringable; -class Sid +class Sid implements Stringable { /** * The string SID value. - * - * @var string */ - protected $value; + protected string $value; /** - * Determines if the specified SID is valid. - * - * @param string $sid - * @return bool + * Determine if the specified SID is valid. */ - public static function isValid($sid) + public static function isValid(string $sid): bool { - return Utilities::isValidSid($sid); + return (bool) preg_match("/^S-\d(-\d{1,10}){1,16}$/i", $sid); } /** * Constructor. * - * @param mixed $value - * * @throws InvalidArgumentException */ - public function __construct($value) + public function __construct(string $value) { if (static::isValid($value)) { $this->value = $value; @@ -44,31 +37,25 @@ class Sid } /** - * Returns the string value of the SID. - * - * @return string + * Get the string value of the SID. */ - public function __toString() + public function __toString(): string { return $this->getValue(); } /** - * Returns the string value of the SID. - * - * @return string + * Get the string value of the SID. */ - public function getValue() + public function getValue(): string { return $this->value; } /** - * Returns the binary variant of the SID. - * - * @return string + * Get the binary variant of the SID. */ - public function getBinary() + public function getBinary(): string { $sid = explode('-', ltrim($this->value, 'S-')); @@ -87,13 +74,50 @@ class Sid } /** - * Returns the string variant of a binary SID. - * - * @param string $binary - * @return string|null + * Get the string variant of a binary SID. */ - protected function binarySidToString($binary) + protected function binarySidToString(string $binary): ?string { - return Utilities::binarySidToString($binary); + if (trim($binary) === '') { + return null; + } + + // Revision - 8bit unsigned int (C1) + // Count - 8bit unsigned int (C1) + // 2 null bytes + // ID - 32bit unsigned long, big-endian order + $sid = @unpack('C1rev/C1count/x2/N1id', $binary); + + if (! isset($sid['id']) || ! isset($sid['rev'])) { + return null; + } + + $revisionLevel = $sid['rev']; + + $identifierAuthority = $sid['id']; + + $subs = $sid['count'] ?? 0; + + $sidHex = $subs ? bin2hex($binary) : ''; + + $subAuthorities = []; + + // The sub-authorities depend on the count, so only get as + // many as the count, regardless of data beyond it. + for ($i = 0; $i < $subs; $i++) { + $data = implode(array_reverse( + str_split( + substr($sidHex, 16 + ($i * 8), 8), + 2 + ) + )); + + $subAuthorities[] = hexdec($data); + } + + // Tack on the 'S-' and glue it all together... + return 'S-'.$revisionLevel.'-'.$identifierAuthority.implode( + preg_filter('/^/', '-', $subAuthorities) + ); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSProperty.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSProperty.php index 499579f82..dd2d0ebda 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSProperty.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSProperty.php @@ -29,10 +29,8 @@ class TSProperty * cannot find any information on them in Microsoft documentation. However, their values appear to stay in sync with * their non 'W' counterparts. But not doing so when manipulating the data manually does not seem to affect anything. * This probably needs more investigation. - * - * @var array */ - protected $propTypes = [ + protected array $propTypes = [ 'string' => [ 'CtxWFHomeDir', 'CtxWFHomeDirW', @@ -63,31 +61,23 @@ class TSProperty /** * The property name. - * - * @var string */ - protected $name; + protected ?string $name = null; /** * The property value. - * - * @var string|int */ - protected $value; + protected string|int|null $value = null; /** * The property value type. - * - * @var int */ - protected $valueType = 1; + protected int $valueType = 1; /** * Pass binary TSProperty data to construct its object representation. - * - * @param string|null $value */ - public function __construct($value = null) + public function __construct(string|int|null $value = null) { if ($value) { $this->decode(bin2hex($value)); @@ -96,11 +86,8 @@ class TSProperty /** * Set the name for the TSProperty. - * - * @param string $name - * @return TSProperty */ - public function setName($name) + public function setName(string $name): static { $this->name = $name; @@ -109,21 +96,16 @@ class TSProperty /** * Get the name for the TSProperty. - * - * @return string */ - public function getName() + public function getName(): ?string { return $this->name; } /** * Set the value for the TSProperty. - * - * @param string|int $value - * @return TSProperty */ - public function setValue($value) + public function setValue(string|int $value): static { $this->value = $value; @@ -132,10 +114,8 @@ class TSProperty /** * Get the value for the TSProperty. - * - * @return string|int */ - public function getValue() + public function getValue(): string|int|null { return $this->value; } @@ -143,10 +123,8 @@ class TSProperty /** * Convert the TSProperty name/value back to its binary * representation for the userParameters blob. - * - * @return string */ - public function toBinary() + public function toBinary(): string { $name = bin2hex($this->name); @@ -166,10 +144,8 @@ class TSProperty /** * Given a TSProperty blob, decode the name/value/type/etc. - * - * @param string $tsProperty */ - protected function decode($tsProperty) + protected function decode(string $tsProperty): void { $nameLength = hexdec(substr($tsProperty, 0, 2)); @@ -183,12 +159,8 @@ class TSProperty /** * Based on the property name/value in question, get its encoded form. - * - * @param string $propName - * @param string|int $propValue - * @return string */ - protected function getEncodedValueForProp($propName, $propValue) + protected function getEncodedValueForProp(string $propName, string|int $propValue): string { if (in_array($propName, $this->propTypes['string'])) { // Simple strings are null terminated. Unsure if this is @@ -206,12 +178,8 @@ class TSProperty /** * Based on the property name in question, get its actual value from the binary blob value. - * - * @param string $propName - * @param string $propValue - * @return string|int */ - protected function getDecodedValueForProp($propName, $propValue) + protected function getDecodedValueForProp(string $propName, string $propValue): string|int { if (in_array($propName, $this->propTypes['string'])) { // Strip away null terminators. I think this should @@ -234,11 +202,9 @@ class TSProperty * Decode the property by inspecting the nibbles of each blob, checking * the control, and adding up the results into a final value. * - * @param string $hex - * @param bool $string Whether or not this is simple string data. - * @return string + * @param bool $string Whether this is simple string data. */ - protected function decodePropValue($hex, $string = false) + protected function decodePropValue(string $hex, bool $string = false): string { $decodePropValue = ''; @@ -266,12 +232,8 @@ class TSProperty /** * Get the encoded property value as a binary blob. - * - * @param string $value - * @param bool $string - * @return string */ - protected function encodePropValue($value, $string = false) + protected function encodePropValue(string $value, bool $string = false): string { // An int must be properly padded. (then split and reversed). // For a string, we just split the chars. This seems @@ -307,12 +269,8 @@ class TSProperty * PHP's pack() function has no 'b' or 'B' template. This is * a workaround that turns a literal bit-string into a * packed byte-string with 8 bits per byte. - * - * @param string $bits - * @param bool $len - * @return string */ - protected function packBitString($bits, $len) + protected function packBitString(string $bits, int $len): string { $bits = substr($bits, 0, $len); // Pad input with zeros to next multiple of 4 above $len @@ -329,12 +287,8 @@ class TSProperty /** * Based on the control, adjust the nibble accordingly. - * - * @param string $nibble - * @param string $control - * @return string */ - protected function nibbleControl($nibble, $control) + protected function nibbleControl(string $nibble, string $control): string { // This control stays constant for the low/high nibbles, // so it doesn't matter which we compare to @@ -355,10 +309,8 @@ class TSProperty * must be subtracted by 9 before the final value is constructed. * * @param string $nibbleType Either X or Y - * @param string $nibble - * @return string */ - protected function getNibbleWithControl($nibbleType, $nibble) + protected function getNibbleWithControl(string $nibbleType, string $nibble): string { $dec = bindec($nibble); @@ -375,11 +327,9 @@ class TSProperty /** * Need to make sure hex values are always an even length, so pad as needed. * - * @param int $int * @param int $padLength The hex string must be padded to this length (with zeros). - * @return string */ - protected function dec2hex($int, $padLength = 2) + protected function dec2hex(int $int, int $padLength = 2): string { return str_pad(dechex($int), $padLength, 0, STR_PAD_LEFT); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSPropertyArray.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSPropertyArray.php index 8320dd8ed..a81018143 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSPropertyArray.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSPropertyArray.php @@ -36,33 +36,31 @@ class TSPropertyArray ]; /** - * @var string The default data that occurs before the TSPropertyArray (CtxCfgPresent with a bunch of spaces...?) + * The default data that occurs before the TSPropertyArray (CtxCfgPresent with a bunch of spaces...?). */ - protected $defaultPreBinary = '43747843666750726573656e742020202020202020202020202020202020202020202020202020202020202020202020'; + protected string $defaultPreBinary = '43747843666750726573656e742020202020202020202020202020202020202020202020202020202020202020202020'; /** + * The TSProperty array. + * * @var TSProperty[] */ - protected $tsProperty = []; + protected array $tsProperty = []; /** - * @var string + * The TSProperty signature. */ - protected $signature = self::VALID_SIGNATURE; + protected string $signature = self::VALID_SIGNATURE; /** * Binary data that occurs before the TSPropertyArray data in userParameters. - * - * @var string */ - protected $preBinary = ''; + protected string $preBinary = ''; /** * Binary data that occurs after the TSPropertyArray data in userParameters. - * - * @var string */ - protected $postBinary = ''; + protected string $postBinary = ''; /** * Construct in one of the following ways:. @@ -70,10 +68,8 @@ class TSPropertyArray * - Pass an array of TSProperty key => value pairs (See DEFAULTS constant). * - Pass the userParameters binary value. The object representation of that will be decoded and constructed. * - Pass nothing and a default set of TSProperty key => value pairs will be used (See DEFAULTS constant). - * - * @param mixed $tsPropertyArray */ - public function __construct($tsPropertyArray = null) + public function __construct(mixed $tsPropertyArray = null) { $this->preBinary = hex2bin($this->defaultPreBinary); @@ -81,7 +77,7 @@ class TSPropertyArray $tsPropertyArray = $tsPropertyArray ?: self::DEFAULTS; foreach ($tsPropertyArray as $key => $value) { - $tsProperty = new TSProperty(); + $tsProperty = new TSProperty; $this->tsProperty[$key] = $tsProperty->setName($key)->setValue($value); } @@ -92,22 +88,16 @@ class TSPropertyArray /** * Check if a specific TSProperty exists by its property name. - * - * @param string $propName - * @return bool */ - public function has($propName) + public function has(string $propName): bool { return array_key_exists(strtolower($propName), array_change_key_case($this->tsProperty)); } /** * Get a TSProperty object by its property name (ie. CtxWFProfilePath). - * - * @param string $propName - * @return TSProperty */ - public function get($propName) + public function get(string $propName): TSProperty { $this->validateProp($propName); @@ -116,11 +106,8 @@ class TSPropertyArray /** * Add a TSProperty object. If it already exists, it will be overwritten. - * - * @param TSProperty $tsProperty - * @return $this */ - public function add(TSProperty $tsProperty) + public function add(TSProperty $tsProperty): static { $this->tsProperty[$tsProperty->getName()] = $tsProperty; @@ -128,12 +115,9 @@ class TSPropertyArray } /** - * Remove a TSProperty by its property name (ie. CtxMinEncryptionLevel). - * - * @param string $propName - * @return $this + * Remove a TSProperty by its property name (i.e. CtxMinEncryptionLevel). */ - public function remove($propName) + public function remove(string $propName): static { foreach (array_keys($this->tsProperty) as $property) { if (strtolower($propName) == strtolower($property)) { @@ -146,12 +130,8 @@ class TSPropertyArray /** * Set the value for a specific TSProperty by its name. - * - * @param string $propName - * @param mixed $propValue - * @return $this */ - public function set($propName, $propValue) + public function set(string $propName, string|int $propValue): static { $this->validateProp($propName); @@ -162,10 +142,8 @@ class TSPropertyArray /** * Get the full binary representation of the userParameters containing the TSPropertyArray data. - * - * @return string */ - public function toBinary() + public function toBinary(): string { $binary = $this->preBinary; @@ -182,10 +160,8 @@ class TSPropertyArray /** * Get a simple associative array containing of all TSProperty names and values. - * - * @return array */ - public function toArray() + public function toArray(): array { $userParameters = []; @@ -201,17 +177,15 @@ class TSPropertyArray * * @return TSProperty[] */ - public function getTSProperties() + public function getTSProperties(): array { return $this->tsProperty; } /** * Validates that the given property name exists. - * - * @param string $propName */ - protected function validateProp($propName) + protected function validateProp(string $propName): void { if (! $this->has($propName)) { throw new InvalidArgumentException(sprintf('TSProperty for "%s" does not exist.', $propName)); @@ -219,21 +193,17 @@ class TSPropertyArray } /** - * @param string $propName - * @return TSProperty + * Get the TS property object for the given property. */ - protected function getTsPropObj($propName) + protected function getTsPropObj(string $propName): TSProperty { return array_change_key_case($this->tsProperty)[strtolower($propName)]; } /** - * Get an associative array with all of the userParameters property names and values. - * - * @param string $userParameters - * @return void + * Get an associative array with all the userParameters property names and values. */ - protected function decodeUserParameters($userParameters) + protected function decodeUserParameters(string $userParameters): void { $userParameters = bin2hex($userParameters); @@ -263,11 +233,9 @@ class TSPropertyArray * individual TSProperty structures. Return the full length * of the TSPropertyArray data. * - * @param string $tsPropertyArray - * @param int $tsPropCount * @return int The length of the data in the TSPropertyArray */ - protected function addTSPropData($tsPropertyArray, $tsPropCount) + protected function addTSPropData(string $tsPropertyArray, int $tsPropCount): int { $length = 0; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Timestamp.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Timestamp.php index 0f07bc5a0..4046eb715 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Timestamp.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Timestamp.php @@ -7,38 +7,37 @@ use Carbon\CarbonInterface; use DateTime; use DateTimeZone; use LdapRecord\LdapRecordException; -use LdapRecord\Utilities; class Timestamp { + public const TYPE_LDAP = 'ldap'; + + public const TYPE_WINDOWS = 'windows'; + + public const TYPE_WINDOWS_INT = 'windows-int'; + public const WINDOWS_INT_MAX = 9223372036854775807; /** * The current timestamp type. - * - * @var string */ - protected $type; + protected string $type; /** * The available timestamp types. - * - * @var array */ - protected $types = [ - 'ldap', - 'windows', - 'windows-int', + protected array $types = [ + Timestamp::TYPE_LDAP, + Timestamp::TYPE_WINDOWS, + Timestamp::TYPE_WINDOWS_INT, ]; /** * Constructor. * - * @param string $type - * * @throws LdapRecordException */ - public function __construct($type) + public function __construct(string $type) { $this->setType($type); } @@ -46,11 +45,9 @@ class Timestamp /** * Set the type of timestamp to convert from / to. * - * @param string $type - * * @throws LdapRecordException */ - public function setType($type) + public function setType(string $type): void { if (! in_array($type, $this->types)) { throw new LdapRecordException("Unrecognized LDAP date type [$type]"); @@ -62,18 +59,15 @@ class Timestamp /** * Converts the value to an LDAP date string. * - * @param mixed $value - * @return float|string - * * @throws LdapRecordException */ - public function fromDateTime($value) + public function fromDateTime(mixed $value): int|string { $value = is_array($value) ? reset($value) : $value; - // If the value is being converted to a windows integer format but it + // If the value is being converted to a windows integer format, but it // is already in that format, we will simply return the value back. - if ($this->type == 'windows-int' && $this->valueIsWindowsIntegerType($value)) { + if ($this->type === Timestamp::TYPE_WINDOWS_INT && $this->valueIsWindowsIntegerType($value)) { return $value; } // If the value is numeric, we will assume it's a UNIX timestamp. @@ -89,30 +83,18 @@ class Timestamp $value = Carbon::instance($value); } - switch ($this->type) { - case 'ldap': - $value = $this->convertDateTimeToLdapTime($value); - break; - case 'windows': - $value = $this->convertDateTimeToWindows($value); - break; - case 'windows-int': - $value = $this->convertDateTimeToWindowsInteger($value); - break; - default: - throw new LdapRecordException("Unrecognized date type [{$this->type}]"); - } - - return $value; + return match ($this->type) { + Timestamp::TYPE_LDAP => $this->convertDateTimeToLdapTime($value), + Timestamp::TYPE_WINDOWS => $this->convertDateTimeToWindows($value), + Timestamp::TYPE_WINDOWS_INT => $this->convertDateTimeToWindowsInteger($value), + default => throw new LdapRecordException("Unrecognized date type [{$this->type}]"), + }; } /** * Determine if the value given is in Windows Integer (NTFS Filetime) format. - * - * @param int|string $value - * @return bool */ - protected function valueIsWindowsIntegerType($value) + protected function valueIsWindowsIntegerType(mixed $value): bool { return is_numeric($value) && in_array(strlen((string) $value), [18, 19]); } @@ -120,12 +102,9 @@ class Timestamp /** * Converts the LDAP timestamp value to a Carbon instance. * - * @param mixed $value - * @return Carbon|int|false - * * @throws LdapRecordException */ - public function toDateTime($value) + public function toDateTime(mixed $value): Carbon|int|false { $value = is_array($value) ? reset($value) : $value; @@ -133,87 +112,70 @@ class Timestamp return Carbon::instance($value); } - switch ($this->type) { - case 'ldap': - $value = $this->convertLdapTimeToDateTime($value); - break; - case 'windows': - $value = $this->convertWindowsTimeToDateTime($value); - break; - case 'windows-int': - $value = $this->convertWindowsIntegerTimeToDateTime($value); - break; - default: - throw new LdapRecordException("Unrecognized date type [{$this->type}]"); - } + $value = match ($this->type) { + Timestamp::TYPE_LDAP => $this->convertLdapTimeToDateTime($value), + Timestamp::TYPE_WINDOWS => $this->convertWindowsTimeToDateTime($value), + Timestamp::TYPE_WINDOWS_INT => $this->convertWindowsIntegerTimeToDateTime($value), + default => throw new LdapRecordException("Unrecognized date type [{$this->type}]"), + }; return $value instanceof DateTime ? Carbon::instance($value) : $value; } /** * Converts standard LDAP timestamps to a date time object. - * - * @param string $value - * @return DateTime|false */ - protected function convertLdapTimeToDateTime($value) + protected function convertLdapTimeToDateTime(string $value): DateTime|false { - return DateTime::createFromFormat( - str_contains((string) $value, 'Z') ? 'YmdHis\Z' : 'YmdHisT', - $value - ); + return DateTime::createFromFormat(match (true) { + str_ends_with($value, '.000Z') => 'YmdHis.000\Z', + str_ends_with($value, '.0Z') => 'YmdHis.0\Z', + str_ends_with($value, 'Z') => 'YmdHis\Z', + default => 'YmdHisT', + }, $value); } /** * Converts date objects to a standard LDAP timestamp. - * - * @param DateTime $date - * @return string */ - protected function convertDateTimeToLdapTime(DateTime $date) + protected function convertDateTimeToLdapTime(DateTime $date): string { return $date->format( - $date->getOffset() == 0 ? 'YmdHis\Z' : 'YmdHisO' + $date->getOffset() == 0 + ? 'YmdHis\Z' + : 'YmdHisO' ); } /** * Converts standard windows timestamps to a date time object. - * - * @param string $value - * @return DateTime|false */ - protected function convertWindowsTimeToDateTime($value) + protected function convertWindowsTimeToDateTime(string $value): DateTime|false { - return DateTime::createFromFormat( - str_contains((string) $value, '0Z') ? 'YmdHis.0\Z' : 'YmdHis.0T', - $value, - new DateTimeZone('UTC') - ); + return DateTime::createFromFormat(match (true) { + str_ends_with($value, '.0Z') => 'YmdHis.0\Z', + default => 'YmdHis.0T' + }, $value, new DateTimeZone('UTC')); } /** * Converts date objects to a windows timestamp. - * - * @param DateTime $date - * @return string */ - protected function convertDateTimeToWindows(DateTime $date) + protected function convertDateTimeToWindows(DateTime $date): string { return $date->format( - $date->getOffset() == 0 ? 'YmdHis.0\Z' : 'YmdHis.0O' + $date->getOffset() == 0 + ? 'YmdHis.0\Z' + : 'YmdHis.0O' ); } /** * Converts standard windows integer dates to a date time object. * - * @param int $value - * @return DateTime|int|false - * * @throws \Exception */ - protected function convertWindowsIntegerTimeToDateTime($value) + protected function convertWindowsIntegerTimeToDateTime(string|int|null $value = null): DateTime|int|false { if (is_null($value) || $value === '') { return false; @@ -227,19 +189,16 @@ class Timestamp return (int) $value; } - return (new DateTime())->setTimestamp( - Utilities::convertWindowsTimeToUnixTime($value) + return (new DateTime)->setTimestamp( + (int) ($value / 10000000) - 11644473600 ); } /** * Converts date objects to a windows integer timestamp. - * - * @param DateTime $date - * @return float */ - protected function convertDateTimeToWindowsInteger(DateTime $date) + protected function convertDateTimeToWindowsInteger(DateTime $date): int { - return Utilities::convertUnixTimeToWindowsTime($date->getTimestamp()); + return ($date->getTimestamp() + 11644473600) * 10000000; } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/BatchModification.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/BatchModification.php index 954c58fcf..3b153095d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/BatchModification.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/BatchModification.php @@ -9,48 +9,38 @@ class BatchModification use DetectsResetIntegers; /** - * The array keys to be used in batch modifications. + * The array key identifiers. */ public const KEY_ATTRIB = 'attrib'; + public const KEY_MODTYPE = 'modtype'; + public const KEY_VALUES = 'values'; /** * The attribute of the modification. - * - * @var string|null */ - protected $attribute; + protected ?string $attribute = null; /** * The original value of the attribute before modification. - * - * @var array */ - protected $original = []; + protected array $original = []; /** * The values of the modification. - * - * @var array */ - protected $values = []; + protected array $values = []; /** * The modtype integer of the batch modification. - * - * @var int|null */ - protected $type; + protected ?int $type = null; /** * Constructor. - * - * @param string|null $attribute - * @param string|int|null $type - * @param array $values */ - public function __construct($attribute = null, $type = null, array $values = []) + public function __construct(?string $attribute = null, ?int $type = null, array $values = []) { $this->setAttribute($attribute) ->setType($type) @@ -58,12 +48,17 @@ class BatchModification } /** - * Set the original value of the attribute before modification. - * - * @param array|string $original - * @return $this + * Make a new batch modification instance. */ - public function setOriginal($original = []) + public static function make(?string $attribute = null, ?int $type = null, array $values = []): self + { + return new static($attribute, $type, $values); + } + + /** + * Set the original value of the attribute before modification. + */ + public function setOriginal(array|string $original = []): static { $this->original = $this->normalizeAttributeValues($original); @@ -71,22 +66,17 @@ class BatchModification } /** - * Returns the original value of the attribute before modification. - * - * @return array + * Get the original value of the attribute before modification. */ - public function getOriginal() + public function getOriginal(): array { return $this->original; } /** * Set the attribute of the modification. - * - * @param string $attribute - * @return $this */ - public function setAttribute($attribute) + public function setAttribute(?string $attribute = null): static { $this->attribute = $attribute; @@ -94,64 +84,51 @@ class BatchModification } /** - * Returns the attribute of the modification. - * - * @return string + * Get the attribute of the modification. */ - public function getAttribute() + public function getAttribute(): string { return $this->attribute; } /** * Set the values of the modification. - * - * @param array $values - * @return $this */ - public function setValues(array $values = []) + public function setValues(array $values = []): static { // Null and empty values must also not be added to a batch // modification. Passing null or empty values will result // in an exception when trying to save the modification. $this->values = array_filter($this->normalizeAttributeValues($values), function ($value) { - return is_numeric($value) && $this->valueIsResetInteger((int) $value) ?: ! empty($value); + return is_numeric($value) && $this->valueIsResetInteger((int) $value) || ! empty($value); }); return $this; } /** - * Normalize all of the attribute values. - * - * @param array|string $values - * @return array + * Normalize all the attribute values. */ - protected function normalizeAttributeValues($values = []) + protected function normalizeAttributeValues(array|string $values = []): array { - // We must convert all of the values to strings. Only strings can + // We must convert all the values to strings. Only strings can // be used in batch modifications, otherwise we will we will // receive an LDAP exception while attempting to save. return array_map('strval', (array) $values); } /** - * Returns the values of the modification. - * - * @return array + * Get the values of the modification. */ - public function getValues() + public function getValues(): array { return $this->values; } /** * Set the type of the modification. - * - * @param int|null $type - * @return $this */ - public function setType($type = null) + public function setType(?int $type = null): static { if (is_null($type)) { return $this; @@ -167,21 +144,17 @@ class BatchModification } /** - * Returns the type of the modification. - * - * @return int + * Get the type of the modification. */ - public function getType() + public function getType(): ?int { return $this->type; } /** - * Determines if the batch modification is valid in its current state. - * - * @return bool + * Determine if the batch modification is valid in its current state. */ - public function isValid() + public function isValid(): bool { return ! is_null($this->get()); } @@ -189,51 +162,43 @@ class BatchModification /** * Builds the type of modification automatically * based on the current and original values. - * - * @return $this */ - public function build() + public function build(): static { - switch (true) { - case empty($this->original) && empty($this->values): - return $this; - case ! empty($this->original) && empty($this->values): - return $this->setType(LDAP_MODIFY_BATCH_REMOVE_ALL); - case empty($this->original) && ! empty($this->values): - return $this->setType(LDAP_MODIFY_BATCH_ADD); - default: - return $this->determineBatchTypeFromOriginal(); - } + return match (true) { + empty($this->original) && empty($this->values) => $this, + + ! empty($this->original) && empty($this->values) => $this->setType(LDAP_MODIFY_BATCH_REMOVE_ALL), + + empty($this->original) && ! empty($this->values) => $this->setType(LDAP_MODIFY_BATCH_ADD), + + default => $this->determineBatchTypeFromOriginal(), + }; } /** * Determine the batch modification type from the original values. - * - * @return $this */ - protected function determineBatchTypeFromOriginal() + protected function determineBatchTypeFromOriginal(): static { $added = $this->getAddedValues(); $removed = $this->getRemovedValues(); - switch (true) { - case ! empty($added) && ! empty($removed): - return $this->setType(LDAP_MODIFY_BATCH_REPLACE); - case ! empty($added): - return $this->setValues($added)->setType(LDAP_MODIFY_BATCH_ADD); - case ! empty($removed): - return $this->setValues($removed)->setType(LDAP_MODIFY_BATCH_REMOVE); - default: - return $this; - } + return match (true) { + ! empty($added) && ! empty($removed) => $this->setType(LDAP_MODIFY_BATCH_REPLACE), + + ! empty($added) => $this->setValues($added)->setType(LDAP_MODIFY_BATCH_ADD), + + ! empty($removed) => $this->setValues($removed)->setType(LDAP_MODIFY_BATCH_REMOVE), + + default => $this, + }; } /** * Get the values that were added to the attribute. - * - * @return array */ - protected function getAddedValues() + protected function getAddedValues(): array { return array_values( array_diff($this->values, $this->original) @@ -242,10 +207,8 @@ class BatchModification /** * Get the values that were removed from the attribute. - * - * @return array */ - protected function getRemovedValues() + protected function getRemovedValues(): array { return array_values( array_diff($this->original, $this->values) @@ -253,43 +216,30 @@ class BatchModification } /** - * Returns the built batch modification array. - * - * @return array|null + * Get the batch modification array. */ - public function get() + public function get(): ?array { - switch ($this->type) { - case LDAP_MODIFY_BATCH_REMOVE_ALL: - // A values key cannot be provided when - // a remove all type is selected. - return [ - static::KEY_ATTRIB => $this->attribute, - static::KEY_MODTYPE => $this->type, - ]; - case LDAP_MODIFY_BATCH_REMOVE: - // Fallthrough. - case LDAP_MODIFY_BATCH_ADD: - // Fallthrough. - case LDAP_MODIFY_BATCH_REPLACE: - return [ - static::KEY_ATTRIB => $this->attribute, - static::KEY_MODTYPE => $this->type, - static::KEY_VALUES => $this->values, - ]; - default: - // If the modtype isn't recognized, we'll return null. - return; - } + return match ($this->type) { + LDAP_MODIFY_BATCH_REMOVE_ALL => [ + static::KEY_ATTRIB => $this->attribute, + static::KEY_MODTYPE => $this->type, + ], + + LDAP_MODIFY_BATCH_REMOVE, LDAP_MODIFY_BATCH_ADD, LDAP_MODIFY_BATCH_REPLACE => [ + static::KEY_ATTRIB => $this->attribute, + static::KEY_MODTYPE => $this->type, + static::KEY_VALUES => $this->values, + ], + + default => null, + }; } /** - * Determines if the given modtype is valid. - * - * @param int $type - * @return bool + * Determine if the given modtype is valid. */ - protected function isValidType($type) + protected function isValidType(int $type): bool { return in_array($type, [ LDAP_MODIFY_BATCH_REMOVE_ALL, diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Collection.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Collection.php index 11b4ca522..a0d8126a0 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Collection.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Collection.php @@ -11,10 +11,8 @@ class Collection extends QueryCollection { /** * Get a collection of the model's distinguished names. - * - * @return static */ - public function modelDns() + public function modelDns(): static { return $this->map(function (Model $model) { return $model->getDn(); @@ -22,12 +20,11 @@ class Collection extends QueryCollection } /** - * Determine if the collection contains all of the given models, or any models. + * Determine if the collection contains all the given models, or any models. * - * @param mixed $models - * @return bool + * @param QueryCollection|Model|array|string|null $models */ - public function exists($models = null) + public function exists(mixed $models = null): bool { $models = $this->getArrayableModels($models); @@ -57,19 +54,11 @@ class Collection extends QueryCollection /** * Determine if any of the given models are contained in the collection. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool */ - public function contains($key, $operator = null, $value = null) + public function contains($key, $operator = null, $value = null): bool { if (func_num_args() > 1 || $key instanceof Closure) { - // If we are supplied with more than one argument, or - // we were passed a closure, we will utilize the - // parents contains method, for compatibility. - return parent::contains($key, $operator, $value); + return parent::contains(...func_get_args()); } foreach ($this->getArrayableModels($key) as $model) { @@ -87,25 +76,20 @@ class Collection extends QueryCollection /** * Get the provided models as an array. - * - * @param mixed $models - * @return array */ - protected function getArrayableModels($models = null) + protected function getArrayableModels(mixed $models = null): array { - return $models instanceof QueryCollection - ? $models->toArray() - : Arr::wrap($models); + if ($models instanceof QueryCollection) { + return $models->all(); + } + + return Arr::wrap($models); } /** * Compare the related model with the given. - * - * @param Model|string $model - * @param Model $related - * @return bool */ - protected function compareModelWithRelated($model, $related) + protected function compareModelWithRelated(Model|string $model, Model $related): bool { if (is_string($model)) { return $this->isValidDn($model) @@ -118,11 +102,8 @@ class Collection extends QueryCollection /** * Determine if the given string is a valid distinguished name. - * - * @param string $dn - * @return bool */ - protected function isValidDn($dn) + protected function isValidDn(string $dn): bool { return ! empty((new DistinguishedName($dn))->components()); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/CanAuthenticate.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/CanAuthenticate.php index 6e8d4fb5d..e52657a44 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/CanAuthenticate.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/CanAuthenticate.php @@ -7,60 +7,58 @@ trait CanAuthenticate { /** * Get the name of the unique identifier for the user. - * - * @return string */ - public function getAuthIdentifierName() + public function getAuthIdentifierName(): string { return $this->guidKey; } /** * Get the unique identifier for the user. - * - * @return string */ - public function getAuthIdentifier() + public function getAuthIdentifier(): string { - return $this->getConvertedGuid(); + return $this->getConvertedGuid( + $this->getFirstAttribute($this->getAuthIdentifierName()) + ); } /** * Get the password for the user. - * - * @return string */ - public function getAuthPassword() + public function getAuthPassword(): string { return ''; } /** - * Get the token value for the "remember me" session. - * - * @return string + * Get the name of the password attribute for the user. */ - public function getRememberToken() + public function getAuthPasswordName(): string + { + return 'password'; + } + + /** + * Get the token value for the "remember me" session. + */ + public function getRememberToken(): string { return ''; } /** * Set the token value for the "remember me" session. - * - * @param string $value - * @return void */ - public function setRememberToken($value) + public function setRememberToken($value): void { + // Do nothing. } /** * Get the column name for the "remember me" token. - * - * @return string */ - public function getRememberTokenName() + public function getRememberTokenName(): string { return ''; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasAttributes.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasAttributes.php index a3d9df7d9..67b4cf703 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasAttributes.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasAttributes.php @@ -10,6 +10,7 @@ use LdapRecord\Models\Attributes\MbString; use LdapRecord\Models\Attributes\Timestamp; use LdapRecord\Models\DetectsResetIntegers; use LdapRecord\Support\Arr; +use RuntimeException; trait HasAttributes { @@ -17,69 +18,56 @@ trait HasAttributes /** * The models original attributes. - * - * @var array */ - protected $original = []; + protected array $original = []; + + /** + * The models changed attributes. + */ + protected array $changes = []; /** * The models attributes. - * - * @var array */ - protected $attributes = []; + protected array $attributes = []; /** * The attributes that should be mutated to dates. - * - * @var array */ - protected $dates = []; + protected array $dates = []; /** * The attributes that should be cast to their native types. - * - * @var array */ - protected $casts = []; + protected array $casts = []; /** * The accessors to append to the model's array form. - * - * @var array */ - protected $appends = []; + protected array $appends = []; /** * The format that dates must be output to for serialization. - * - * @var string */ - protected $dateFormat; + protected ?string $dateFormat = null; /** * The default attributes that should be mutated to dates. - * - * @var array */ - protected $defaultDates = [ + protected array $defaultDates = [ 'createtimestamp' => 'ldap', 'modifytimestamp' => 'ldap', ]; /** * The cache of the mutated attributes for each class. - * - * @var array */ - protected static $mutatorCache = []; + protected static array $mutatorCache = []; /** * Convert the model's original attributes to an array. - * - * @return array */ - public function originalToArray() + public function originalToArray(): array { return $this->encodeAttributes( $this->convertAttributesForJson($this->original) @@ -88,16 +76,14 @@ trait HasAttributes /** * Convert the model's attributes to an array. - * - * @return array */ - public function attributesToArray() + public function attributesToArray(): array { // Here we will replace our LDAP formatted dates with // properly formatted ones, so dates do not need to // be converted manually after being returned. $attributes = $this->addDateAttributesToArray( - $attributes = $this->getArrayableAttributes() + $this->getArrayableAttributes() ); $attributes = $this->addMutatedAttributesToArray( @@ -125,39 +111,30 @@ trait HasAttributes /** * Convert the model's serialized original attributes to their original form. - * - * @param array $attributes - * @return array */ - public function arrayToOriginal(array $attributes) + public function arrayToOriginal(array $attributes): array { - return $this->decodeAttributes( - $this->convertAttributesFromJson($attributes) - ); + $attributes = $this->decodeAttributes($attributes); + + return $this->convertAttributesFromJson($attributes); } /** * Convert the model's serialized attributes to their original form. - * - * @param array $attributes - * @return array */ - public function arrayToAttributes(array $attributes) + public function arrayToAttributes(array $attributes): array { $attributes = $this->restoreDateAttributesFromArray($attributes); - return $this->decodeAttributes( - $this->convertAttributesFromJson($attributes) - ); + $attributes = $this->decodeAttributes($attributes); + + return $this->convertAttributesFromJson($attributes); } /** * Add the date attributes to the attributes array. - * - * @param array $attributes - * @return array */ - protected function addDateAttributesToArray(array $attributes) + protected function addDateAttributesToArray(array $attributes): array { foreach ($this->getDates() as $attribute => $type) { if (! isset($attributes[$attribute])) { @@ -176,18 +153,15 @@ trait HasAttributes /** * Restore the date attributes to their true value from serialized attributes. - * - * @param array $attributes - * @return array */ - protected function restoreDateAttributesFromArray(array $attributes) + protected function restoreDateAttributesFromArray(array $attributes): array { foreach ($this->getDates() as $attribute => $type) { if (! isset($attributes[$attribute])) { continue; } - $date = $this->fromDateTime($type, $attributes[$attribute]); + $date = $this->fromDateTime($attributes[$attribute], $type); $attributes[$attribute] = Arr::wrap($date); } @@ -197,21 +171,16 @@ trait HasAttributes /** * Prepare a date for array / JSON serialization. - * - * @param DateTimeInterface $date - * @return string */ - protected function serializeDate(DateTimeInterface $date) + protected function serializeDate(DateTimeInterface $date): string { return $date->format($this->getDateFormat()); } /** * Recursively UTF-8 encode the given attributes. - * - * @return array */ - public function encodeAttributes($attributes) + protected function encodeAttributes($attributes): array { array_walk_recursive($attributes, function (&$value) { $value = $this->encodeValue($value); @@ -222,11 +191,8 @@ trait HasAttributes /** * Recursively UTF-8 decode the given attributes. - * - * @param array $attributes - * @return array */ - public function decodeAttributes($attributes) + public function decodeAttributes(array $attributes): array { array_walk_recursive($attributes, function (&$value) { $value = $this->decodeValue($value); @@ -237,11 +203,8 @@ trait HasAttributes /** * Encode the value for serialization. - * - * @param string $value - * @return string */ - protected function encodeValue($value) + protected function encodeValue(string $value): string { // If we are able to detect the encoding, we will // encode only the attributes that need to be, @@ -250,18 +213,15 @@ trait HasAttributes return $value; } - return utf8_encode($value); + return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); } /** * Decode the value from serialization. - * - * @param string $value - * @return string */ - protected function decodeValue($value) + protected function decodeValue(string $value): string { - if (MbString::isLoaded() && MbString::isUtf8($value)) { + if (MbString::isLoaded() && ! MbString::isUtf8($value)) { return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); } @@ -270,12 +230,8 @@ trait HasAttributes /** * Add the mutated attributes to the attributes array. - * - * @param array $attributes - * @param array $mutatedAttributes - * @return array */ - protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes) + protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes): array { foreach ($mutatedAttributes as $key) { // We want to spin through all the mutated attributes for this model and call @@ -299,10 +255,8 @@ trait HasAttributes /** * Set the model's original attributes with the model's current attributes. - * - * @return $this */ - public function syncOriginal() + public function syncOriginal(): static { $this->original = $this->attributes; @@ -310,12 +264,19 @@ trait HasAttributes } /** - * Fills the entry with the supplied attributes. - * - * @param array $attributes - * @return $this + * Sync the changed attributes. */ - public function fill(array $attributes = []) + public function syncChanges(): static + { + $this->changes = $this->getDirty(); + + return $this; + } + + /** + * Fills the entry with the supplied attributes. + */ + public function fill(array $attributes = []): static { foreach ($attributes as $key => $value) { $this->setAttribute($key, $value); @@ -325,29 +286,21 @@ trait HasAttributes } /** - * Returns the models attribute by its key. - * - * @param int|string $key - * @param mixed $default - * @return mixed + * Get the models attribute by its key. */ - public function getAttribute($key, $default = null) + public function getAttribute(?string $key = null, mixed $default = null): mixed { if (! $key) { - return; + return null; } return $this->getAttributeValue($key, $default); } /** - * Get an attributes value. - * - * @param string $key - * @param mixed $default - * @return mixed + * Get an attribute's value. */ - public function getAttributeValue($key, $default = null) + public function getAttributeValue(string $key, mixed $default = null): mixed { $key = $this->normalizeAttributeKey($key); $value = $this->getAttributeFromArray($key); @@ -368,42 +321,56 @@ trait HasAttributes } /** - * Determine if the given attribute is a date. - * - * @param string $key - * @return bool + * Get the model's raw attribute value. */ - public function isDateAttribute($key) + public function getRawAttribute(string $key, mixed $default = null): mixed + { + return Arr::get($this->attributes, $this->normalizeAttributeKey($key), $default); + } + + /** + * Determine if the given attribute is a date. + */ + public function isDateAttribute(string $key): bool { return array_key_exists($key, $this->getDates()); } /** * Get the attributes that should be mutated to dates. - * - * @return array */ - public function getDates() + public function getDates(): array { // Since array string keys can be unique depending // on casing differences, we need to normalize the // array key case so they are merged properly. return array_merge( - array_change_key_case($this->defaultDates, CASE_LOWER), - array_change_key_case($this->dates, CASE_LOWER) + array_change_key_case($this->defaultDates), + array_change_key_case($this->dates), + array_change_key_case($this->getDateCasts()), ); } + /** + * Get the attributes casts that should be mutated to dates. + */ + protected function getDateCasts(): array + { + return array_map(function (string $cast) { + return explode(':', $cast, 2)[1] ?? throw new RuntimeException( + "Invalid date cast [$cast]. A date cast must be in the format 'datetime:format'." + ); + }, array_filter($this->getCasts(), function ($cast) { + return $this->isDateTimeCast($cast); + })); + } + /** * Convert the given date value to an LDAP compatible value. * - * @param string $type - * @param mixed $value - * @return float|string - * * @throws LdapRecordException */ - public function fromDateTime($type, $value) + public function fromDateTime(mixed $value, string $type): float|int|string { return (new Timestamp($type))->fromDateTime($value); } @@ -411,28 +378,20 @@ trait HasAttributes /** * Convert the given LDAP date value to a Carbon instance. * - * @param mixed $value - * @param string $type - * @return Carbon|false - * * @throws LdapRecordException */ - public function asDateTime($value, $type) + public function asDateTime(mixed $value, string $type): Carbon|int|false { return (new Timestamp($type))->toDateTime($value); } /** * Determine whether an attribute should be cast to a native type. - * - * @param string $key - * @param array|string|null $types - * @return bool */ - public function hasCast($key, $types = null) + public function hasCast(string $key, array|string|null $types = null): bool { if (array_key_exists($key, $this->getCasts())) { - return $types ? in_array($this->getCastType($key), (array) $types, true) : true; + return ! $types || in_array($this->getCastType($key), (array) $types, true); } return false; @@ -440,32 +399,24 @@ trait HasAttributes /** * Get the attributes that should be cast to their native types. - * - * @return array */ - protected function getCasts() + protected function getCasts(): array { - return array_change_key_case($this->casts, CASE_LOWER); + return array_change_key_case($this->casts); } /** * Determine whether a value is JSON castable for inbound manipulation. - * - * @param string $key - * @return bool */ - protected function isJsonCastable($key) + protected function isJsonCastable(string $key): bool { return $this->hasCast($key, ['array', 'json', 'object', 'collection']); } /** * Get the type of cast for a model attribute. - * - * @param string $key - * @return string */ - protected function getCastType($key) + protected function getCastType(string $key): string { if ($this->isDecimalCast($this->getCasts()[$key])) { return 'decimal'; @@ -480,45 +431,32 @@ trait HasAttributes /** * Determine if the cast is a decimal. - * - * @param string $cast - * @return bool */ - protected function isDecimalCast($cast) + protected function isDecimalCast(string $cast): bool { return strncmp($cast, 'decimal:', 8) === 0; } /** * Determine if the cast is a datetime. - * - * @param string $cast - * @return bool */ - protected function isDateTimeCast($cast) + protected function isDateTimeCast(string $cast): bool { return strncmp($cast, 'datetime:', 8) === 0; } /** * Determine if the given attribute must be casted. - * - * @param string $key - * @return bool */ - protected function isCastedAttribute($key) + protected function isCastedAttribute(string $key): bool { - return array_key_exists($key, array_change_key_case($this->casts, CASE_LOWER)); + return array_key_exists($key, array_change_key_case($this->casts)); } /** * Cast an attribute to a native PHP type. - * - * @param string $key - * @param array|null $value - * @return mixed */ - protected function castAttribute($key, $value) + protected function castAttribute(string $key, ?array $value): mixed { $value = $this->castRequiresArrayValue($key) ? $value : Arr::first($value); @@ -542,7 +480,7 @@ trait HasAttributes case 'boolean': return $this->asBoolean($value); case 'object': - return $this->fromJson($value, $asObject = true); + return $this->fromJson($value, true); case 'array': case 'json': return $this->fromJson($value); @@ -557,22 +495,16 @@ trait HasAttributes /** * Determine if the cast type requires the first attribute value. - * - * @return bool */ - protected function castRequiresArrayValue($key) + protected function castRequiresArrayValue(string $key): bool { - return in_array($this->getCastType($key), ['collection']); + return $this->getCastType($key) === 'collection'; } /** * Cast the given attribute to JSON. - * - * @param string $key - * @param mixed $value - * @return string */ - protected function castAttributeAsJson($key, $value) + protected function castAttributeAsJson(string $key, mixed $value): string { $value = $this->asJson($value); @@ -580,107 +512,104 @@ trait HasAttributes $class = get_class($this); $message = json_last_error_msg(); - throw new Exception("Unable to encode attribute [{$key}] for model [{$class}] to JSON: {$message}."); + throw new Exception("Unable to encode attribute [$key] for model [$class] to JSON: $message."); } return $value; } /** - * Convert the model to its JSON representation. - * - * @return string + * Cast the given attribute to an LDAP primitive type. */ - public function toJson() + protected function castAttributeAsPrimitive(string $key, mixed $value): string + { + return match ($this->getCastType($key)) { + 'bool', 'boolean' => $this->fromBoolean($value), + default => (string) $value, + }; + } + + /** + * Convert the model to its JSON representation. + */ + public function toJson(): string { return json_encode($this); } /** * Encode the given value as JSON. - * - * @param mixed $value - * @return string */ - protected function asJson($value) + protected function asJson(mixed $value): string|false { return json_encode($value); } /** * Decode the given JSON back into an array or object. - * - * @param string $value - * @param bool $asObject - * @return mixed */ - public function fromJson($value, $asObject = false) + public function fromJson(string $value, bool $asObject = false): mixed { return json_decode($value, ! $asObject); } /** * Decode the given float. - * - * @param mixed $value - * @return mixed */ - public function fromFloat($value) + public function fromFloat(float $value): float { - switch ((string) $value) { - case 'Infinity': - return INF; - case '-Infinity': - return -INF; - case 'NaN': - return NAN; - default: - return (float) $value; - } + return match ((string) $value) { + 'NaN' => NAN, + 'Infinity' => INF, + '-Infinity' => -INF, + default => $value, + }; } /** - * Cast the value to a boolean. - * - * @param mixed $value - * @return bool + * Cast the value from an LDAP boolean string to a primitive boolean. */ - protected function asBoolean($value) + protected function asBoolean(mixed $value): bool { - $map = ['true' => true, 'false' => false]; + return match (strtolower($value)) { + 'true' => true, + 'false' => false, + default => (bool) $value, + }; + } - return $map[strtolower($value)] ?? (bool) $value; + /** + * Cast the value from a primitive boolean to an LDAP boolean string. + */ + protected function fromBoolean(mixed $value): string + { + if (is_string($value)) { + $value = $this->asBoolean($value); + } + + return $value ? 'TRUE' : 'FALSE'; } /** * Cast a decimal value as a string. - * - * @param float $value - * @param int $decimals - * @return string */ - protected function asDecimal($value, $decimals) + protected function asDecimal(float $value, int $decimals): string { return number_format($value, $decimals, '.', ''); } /** * Get an attribute array of all arrayable attributes. - * - * @return array */ - protected function getArrayableAttributes() + protected function getArrayableAttributes(): array { return $this->getArrayableItems($this->attributes); } /** * Get an attribute array of all arrayable values. - * - * @param array $values - * @return array */ - protected function getArrayableItems(array $values) + protected function getArrayableItems(array $values): array { if (count($visible = $this->getVisible()) > 0) { $values = array_intersect_key($values, array_flip($visible)); @@ -694,11 +623,9 @@ trait HasAttributes } /** - * Get all of the appendable values that are arrayable. - * - * @return array + * Get all the appendable values that are arrayable. */ - protected function getArrayableAppends() + protected function getArrayableAppends(): array { if (empty($this->appends)) { return []; @@ -711,21 +638,16 @@ trait HasAttributes /** * Get the format for date serialization. - * - * @return string */ - public function getDateFormat() + public function getDateFormat(): string { return $this->dateFormat ?: DateTimeInterface::ISO8601; } /** * Set the date format used by the model for serialization. - * - * @param string $format - * @return $this */ - public function setDateFormat($format) + public function setDateFormat(string $format): static { $this->dateFormat = $format; @@ -734,33 +656,24 @@ trait HasAttributes /** * Get an attribute from the $attributes array. - * - * @param string $key - * @return mixed */ - protected function getAttributeFromArray($key) + protected function getAttributeFromArray(string $key): mixed { return $this->getNormalizedAttributes()[$key] ?? null; } /** * Get the attributes with their keys normalized. - * - * @return array */ - protected function getNormalizedAttributes() + protected function getNormalizedAttributes(): array { - return array_change_key_case($this->attributes, CASE_LOWER); + return array_change_key_case($this->attributes); } /** - * Returns the first attribute by the specified key. - * - * @param string $key - * @param mixed $default - * @return mixed + * Get the first attribute by the specified key. */ - public function getFirstAttribute($key, $default = null) + public function getFirstAttribute(string $key, mixed $default = null): mixed { return Arr::first( Arr::wrap($this->getAttribute($key, $default)), @@ -768,38 +681,36 @@ trait HasAttributes } /** - * Returns all of the models attributes. - * - * @return array + * Returns all the model's attributes. */ - public function getAttributes() + public function getAttributes(): array { return $this->attributes; } /** * Set an attribute value by the specified key. - * - * @param string $key - * @param mixed $value - * @return $this */ - public function setAttribute($key, $value) + public function setAttribute(string $key, mixed $value): static { $key = $this->normalizeAttributeKey($key); if ($this->hasSetMutator($key)) { return $this->setMutatedAttributeValue($key, $value); - } elseif ( + } + + if ( $value && $this->isDateAttribute($key) && ! $this->valueIsResetInteger($value) ) { - $value = $this->fromDateTime($this->getDates()[$key], $value); + $value = (string) $this->fromDateTime($value, $this->getDates()[$key]); } if ($this->isJsonCastable($key) && ! is_null($value)) { $value = $this->castAttributeAsJson($key, $value); + } elseif ($this->hasCast($key) && ! is_null($value)) { + $value = $this->castAttributeAsPrimitive($key, $value); } $this->attributes[$key] = Arr::wrap($value); @@ -809,12 +720,8 @@ trait HasAttributes /** * Set an attribute on the model. No checking is done. - * - * @param string $key - * @param mixed $value - * @return $this */ - public function setRawAttribute($key, $value) + public function setRawAttribute(string $key, mixed $value): static { $key = $this->normalizeAttributeKey($key); @@ -825,75 +732,71 @@ trait HasAttributes /** * Set the models first attribute value. - * - * @param string $key - * @param mixed $value - * @return $this */ - public function setFirstAttribute($key, $value) + public function setFirstAttribute(string $key, mixed $value): static { return $this->setAttribute($key, Arr::wrap($value)); } /** * Add a unique value to the given attribute. - * - * @param string $key - * @param mixed $value - * @return $this */ - public function addAttributeValue($key, $value) + public function addAttributeValue(string $key, mixed $value): static { - return $this->setAttribute($key, array_unique( - array_merge( - Arr::wrap($this->getAttribute($key)), - Arr::wrap($value) - ) - )); + return $this->setRawAttribute($key, array_unique(array_merge( + $this->getRawAttribute($key, []), + Arr::wrap($value) + ))); + } + + /** + * Remove a unique value from the given attribute. + */ + public function removeAttributeValue(string $key, mixed $value): static + { + $values = $this->getRawAttribute($key, []); + + foreach (Arr::wrap($value) as $value) { + $index = array_search($value, $values); + + if ($index !== false) { + unset($values[$index]); + } + } + + return $this->setRawAttribute($key, array_values($values)); } /** * Determine if a get mutator exists for an attribute. - * - * @param string $key - * @return bool */ - public function hasGetMutator($key) + public function hasGetMutator(string $key): bool { return method_exists($this, 'get'.$this->getMutatorMethodName($key).'Attribute'); } /** * Determine if a set mutator exists for an attribute. - * - * @param string $key - * @return bool */ - public function hasSetMutator($key) + public function hasSetMutator(string $key): bool { return method_exists($this, 'set'.$this->getMutatorMethodName($key).'Attribute'); } /** * Set the value of an attribute using its mutator. - * - * @param string $key - * @param mixed $value - * @return mixed */ - protected function setMutatedAttributeValue($key, $value) + protected function setMutatedAttributeValue(string $key, mixed $value): static { - return $this->{'set'.$this->getMutatorMethodName($key).'Attribute'}($value); + $this->{'set'.$this->getMutatorMethodName($key).'Attribute'}($value); + + return $this; } /** * Get the value of an attribute using its mutator. - * - * @param string $key - * @param mixed $value - * @return mixed */ - protected function getMutatedAttributeValue($key, $value) + protected function getMutatedAttributeValue(string $key, mixed $value): mixed { return $this->{'get'.$this->getMutatorMethodName($key).'Attribute'}($value); } @@ -902,11 +805,8 @@ trait HasAttributes * Get the mutator attribute method name. * * Hyphenated attributes will use pascal cased methods. - * - * @param string $key - * @return mixed */ - protected function getMutatorMethodName($key) + protected function getMutatorMethodName(string $key): string { $key = ucwords(str_replace('-', ' ', $key)); @@ -915,12 +815,8 @@ trait HasAttributes /** * Get the value of an attribute using its mutator for array conversion. - * - * @param string $key - * @param mixed $value - * @return array */ - protected function mutateAttributeForArray($key, $value) + protected function mutateAttributeForArray(string $key, mixed $value): array { return Arr::wrap( $this->getMutatedAttributeValue($key, $value) @@ -928,27 +824,22 @@ trait HasAttributes } /** - * Set the attributes property. + * Set the raw model attributes. * * Used when constructing an existing LDAP record. - * - * @param array $attributes - * @return $this */ - public function setRawAttributes(array $attributes = []) + public function setRawAttributes(array $attributes = []): static { // We will filter out those annoying 'count' keys // returned with LDAP results and lowercase all // root array keys to prevent any casing issues. - $raw = array_change_key_case($this->filterRawAttributes($attributes), CASE_LOWER); + $raw = array_change_key_case($this->filterRawAttributes($attributes)); // Before setting the models attributes, we will filter // out the attributes that contain an integer key. LDAP // search results will contain integer keys that have // attribute names as values. We don't need these. - $this->attributes = array_filter($raw, function ($key) { - return ! is_int($key); - }, ARRAY_FILTER_USE_KEY); + $this->attributes = array_filter($raw, fn ($key) => ! is_int($key), ARRAY_FILTER_USE_KEY); // LDAP search results will contain the distinguished // name inside of the `dn` key. We will retrieve this, @@ -971,12 +862,8 @@ trait HasAttributes /** * Filters the count key recursively from raw LDAP attributes. - * - * @param array $attributes - * @param array $keys - * @return array */ - public function filterRawAttributes(array $attributes = [], array $keys = ['count', 'dn']) + public function filterRawAttributes(array $attributes = [], array $keys = ['count', 'dn']): array { foreach ($keys as $key) { unset($attributes[$key]); @@ -993,41 +880,40 @@ trait HasAttributes /** * Determine if the model has the given attribute. - * - * @param int|string $key - * @return bool */ - public function hasAttribute($key) + public function hasAttribute(int|string $key): bool { - return [] !== ($this->attributes[$this->normalizeAttributeKey($key)] ?? []); + return ($this->attributes[$this->normalizeAttributeKey($key)] ?? []) !== []; } /** - * Returns the number of attributes. - * - * @return int + * Get the number of attributes. */ - public function countAttributes() + public function countAttributes(): int { return count($this->getAttributes()); } /** - * Returns the models original attributes. - * - * @return array + * Get the model's original attributes. */ - public function getOriginal() + public function getOriginal(): array { return $this->original; } /** - * Get the attributes that have been changed since last sync. - * - * @return array + * Get the model's raw original attribute values. */ - public function getDirty() + public function getRawOriginal(string $key, mixed $default = null): mixed + { + return Arr::get($this->original, $key, $default); + } + + /** + * Get the attributes that have been changed since last sync. + */ + public function getDirty(): array { $dirty = []; @@ -1044,33 +930,69 @@ trait HasAttributes } /** - * Determine if the given attribute is dirty. - * - * @param string $key - * @return bool + * Get the attributes that have been changed since the model was last saved. */ - public function isDirty($key) + public function getChanges(): array + { + return $this->changes; + } + + /** + * Determine if the given attribute is dirty. + */ + public function isDirty(string $key): bool { return ! $this->originalIsEquivalent($key); } /** - * Get the accessors being appended to the models array form. - * - * @return array + * Determine if given attribute has remained the same. */ - public function getAppends() + public function isClean(string $key): bool + { + return ! $this->isDirty($key); + } + + /** + * Discard attribute changes and reset the attributes to their original state. + */ + public function discardChanges(): static + { + [$this->attributes, $this->changes] = [$this->original, []]; + + return $this; + } + + /** + * Determine if the model or any of the given attribute(s) were changed when the model was last saved. + */ + public function wasChanged(array|string|null $attributes = null): bool + { + if (func_num_args() === 0) { + return count($this->changes) > 0; + } + + foreach ((array) $attributes as $attribute) { + if (array_key_exists($attribute, $this->changes)) { + return true; + } + } + + return false; + } + + /** + * Get the accessors being appended to the models array form. + */ + public function getAppends(): array { return $this->appends; } /** * Set the accessors to append to model arrays. - * - * @param array $appends - * @return $this */ - public function setAppends(array $appends) + public function setAppends(array $appends): static { $this->appends = $appends; @@ -1079,22 +1001,16 @@ trait HasAttributes /** * Return whether the accessor attribute has been appended. - * - * @param string $attribute - * @return bool */ - public function hasAppended($attribute) + public function hasAppended(string $attribute): bool { return in_array($attribute, $this->appends); } /** * Returns a normalized attribute key. - * - * @param string $key - * @return string */ - public function normalizeAttributeKey($key) + public function normalizeAttributeKey(string $key): string { // Since LDAP supports hyphens in attribute names, // we'll convert attributes being retrieved by @@ -1106,11 +1022,8 @@ trait HasAttributes /** * Determine if the new and old values for a given key are equivalent. - * - * @param string $key - * @return bool */ - protected function originalIsEquivalent($key) + protected function originalIsEquivalent(string $key): bool { if (! array_key_exists($key, $this->original)) { return false; @@ -1123,17 +1036,15 @@ trait HasAttributes return true; } - return is_numeric($current) && + return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0; } /** * Get the mutated attributes for a given instance. - * - * @return array */ - public function getMutatedAttributes() + public function getMutatedAttributes(): array { $class = static::class; @@ -1146,26 +1057,19 @@ trait HasAttributes /** * Extract and cache all the mutated attributes of a class. - * - * @param string $class - * @return void */ - public static function cacheMutatedAttributes($class) + public static function cacheMutatedAttributes(string $class): void { - static::$mutatorCache[$class] = collect(static::getMutatorMethods($class))->reject(function ($match) { - return $match === 'First'; - })->map(function ($match) { - return lcfirst($match); - })->all(); + static::$mutatorCache[$class] = collect(static::getMutatorMethods($class)) + ->reject(fn ($match) => $match === 'First') + ->map(fn ($match) => lcfirst($match)) + ->all(); } /** * Get all of the attribute mutator methods. - * - * @param mixed $class - * @return array */ - protected static function getMutatorMethods($class) + protected static function getMutatorMethods(string $class): array { preg_match_all('/(?<=^|;)get([^;]+?)Attribute(;|$)/', implode(';', get_class_methods($class)), $matches); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasEvents.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasEvents.php index d9a41b892..7537f62f6 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasEvents.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasEvents.php @@ -13,18 +13,15 @@ trait HasEvents { /** * Execute the callback without raising any events. - * - * @param Closure $callback - * @return mixed */ - protected static function withoutEvents(Closure $callback) + protected static function withoutEvents(Closure $callback): mixed { $container = static::getConnectionContainer(); - $dispatcher = $container->getEventDispatcher(); + $dispatcher = $container->getDispatcher(); if ($dispatcher) { - $container->setEventDispatcher( + $container->setDispatcher( new NullDispatcher($dispatcher) ); } @@ -33,19 +30,15 @@ trait HasEvents return $callback(); } finally { if ($dispatcher) { - $container->setEventDispatcher($dispatcher); + $container->setDispatcher($dispatcher); } } } /** * Dispatch the given model events. - * - * @param string|array $events - * @param array $args - * @return void */ - protected function dispatch($events, array $args = []) + protected function dispatch(array|string $events, array $args = []): void { foreach (Arr::wrap($events) as $name) { $this->fireCustomModelEvent($name, $args); @@ -54,38 +47,27 @@ trait HasEvents /** * Fire a custom model event. - * - * @param string $name - * @param array $args - * @return mixed */ - protected function fireCustomModelEvent($name, array $args = []) + protected function fireCustomModelEvent(string $name, array $args = []): void { $event = implode('\\', [Events::class, ucfirst($name)]); - return $this->fireModelEvent(new $event($this, ...$args)); + $this->fireModelEvent(new $event($this, ...$args)); } /** * Fire a model event. - * - * @param Event $event - * @return mixed */ - protected function fireModelEvent(Event $event) + protected function fireModelEvent(Event $event): void { - return static::getConnectionContainer()->getEventDispatcher()->fire($event); + static::getConnectionContainer()->getDispatcher()->fire($event); } /** * Listen to a model event. - * - * @param string $event - * @param Closure $listener - * @return mixed */ - protected function listenForModelEvent($event, Closure $listener) + protected function listenForModelEvent(string $event, Closure $listener): void { - return static::getConnectionContainer()->getEventDispatcher()->listen($event, $listener); + static::getConnectionContainer()->getDispatcher()->listen($event, $listener); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasGlobalScopes.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasGlobalScopes.php index 4d0f2968f..855ba37c3 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasGlobalScopes.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasGlobalScopes.php @@ -12,43 +12,33 @@ trait HasGlobalScopes /** * Register a new global scope on the model. * - * @param Scope|Closure|string $scope - * @param Closure|null $implementation - * @return mixed - * * @throws InvalidArgumentException */ - public static function addGlobalScope($scope, Closure $implementation = null) + public static function addGlobalScope(Scope|Closure|string $scope, ?Closure $implementation = null): void { if (is_string($scope) && ! is_null($implementation)) { - return static::$globalScopes[static::class][$scope] = $implementation; + static::$globalScopes[static::class][$scope] = $implementation; } elseif ($scope instanceof Closure) { - return static::$globalScopes[static::class][spl_object_hash($scope)] = $scope; + static::$globalScopes[static::class][spl_object_hash($scope)] = $scope; } elseif ($scope instanceof Scope) { - return static::$globalScopes[static::class][get_class($scope)] = $scope; + static::$globalScopes[static::class][get_class($scope)] = $scope; + } else { + throw new InvalidArgumentException('Global scope must be an instance of Closure or Scope.'); } - - throw new InvalidArgumentException('Global scope must be an instance of Closure or Scope.'); } /** * Determine if a model has a global scope. - * - * @param Scope|string $scope - * @return bool */ - public static function hasGlobalScope($scope) + public static function hasGlobalScope(Scope|string $scope): bool { return ! is_null(static::getGlobalScope($scope)); } /** * Get a global scope registered with the model. - * - * @param Scope|string $scope - * @return Scope|Closure|null */ - public static function getGlobalScope($scope) + public static function getGlobalScope(Scope|string $scope): Scope|Closure|null { if (array_key_exists(static::class, static::$globalScopes)) { $scopeName = is_string($scope) ? $scope : get_class($scope); @@ -57,14 +47,14 @@ trait HasGlobalScopes ? static::$globalScopes[static::class][$scopeName] : null; } + + return null; } /** * Get the global scopes for this class instance. - * - * @return array */ - public function getGlobalScopes() + public function getGlobalScopes(): array { return array_key_exists(static::class, static::$globalScopes) ? static::$globalScopes[static::class] diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasPassword.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasPassword.php index 194efa8cf..c84bd977d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasPassword.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasPassword.php @@ -12,13 +12,11 @@ trait HasPassword /** * Set the password on the user. * - * @param string|array $password - * * @throws ConnectionException */ - public function setPasswordAttribute($password) + public function setPasswordAttribute(array|string $password): void { - $this->validateSecureConnection(); + $this->assertSecureConnection(); // Here we will attempt to determine the password hash method in use // by parsing the users hashed password (if it as available). If a @@ -49,31 +47,25 @@ trait HasPassword /** * Alias for setting the password on the user. * - * @param string|array $password - * * @throws ConnectionException */ - public function setUnicodepwdAttribute($password) + public function setUnicodepwdAttribute(array|string $password): void { $this->setPasswordAttribute($password); } /** * An accessor for retrieving the user's hashed password value. - * - * @return string|null */ - public function getPasswordAttribute() + public function getPasswordAttribute(): ?string { return $this->getAttribute($this->getPasswordAttributeName())[0] ?? null; } /** * Get the name of the attribute that contains the user's password. - * - * @return string */ - public function getPasswordAttributeName() + public function getPasswordAttributeName(): string { if (property_exists($this, 'passwordAttribute')) { return $this->passwordAttribute; @@ -88,10 +80,8 @@ trait HasPassword /** * Get the name of the method to use for hashing the user's password. - * - * @return string */ - public function getPasswordHashMethod() + public function getPasswordHashMethod(): string { if (property_exists($this, 'passwordHashMethod')) { return $this->passwordHashMethod; @@ -106,13 +96,8 @@ trait HasPassword /** * Set the changed password. - * - * @param string $oldPassword - * @param string $newPassword - * @param string $attribute - * @return void */ - protected function setChangedPassword($oldPassword, $newPassword, $attribute) + protected function setChangedPassword(string $oldPassword, string $newPassword, string $attribute): void { // Create batch modification for removing the old password. $this->addModification( @@ -135,13 +120,15 @@ trait HasPassword /** * Set the password on the model. - * - * @param string $password - * @param string $attribute - * @return void */ - protected function setPassword($password, $attribute) + protected function setPassword(string $password, string $attribute): void { + if (! $this->exists) { + $this->setRawAttribute($attribute, $password); + + return; + } + $this->addModification( $this->newBatchModification( $attribute, @@ -154,14 +141,9 @@ trait HasPassword /** * Encode / hash the given password. * - * @param string $method - * @param string $password - * @param string $salt - * @return string - * * @throws LdapRecordException */ - protected function getHashedPassword($method, $password, $salt = null) + protected function getHashedPassword(string $method, string $password, ?string $salt = null): string { if (! method_exists(Password::class, $method)) { throw new LdapRecordException("Password hashing method [{$method}] does not exist."); @@ -177,18 +159,22 @@ trait HasPassword /** * Validates that the current LDAP connection is secure. * - * @return void - * * @throws ConnectionException */ - protected function validateSecureConnection() + protected function assertSecureConnection(): void { $connection = $this->getConnection(); + $config = $connection->getConfiguration(); + + if ($config->get('allow_insecure_password_changes') === true) { + return; + } + if ($connection->isConnected()) { $secure = $connection->getLdapConnection()->canChangePasswords(); } else { - $secure = $connection->getConfiguration()->get('use_ssl') || $connection->getConfiguration()->get('use_tls'); + $secure = $config->get('use_ssl') || $config->get('use_tls'); } if (! $secure) { @@ -200,14 +186,11 @@ trait HasPassword /** * Attempt to retrieve the password's salt. - * - * @param string $method - * @return string|null */ - public function getPasswordSalt($method) + public function getPasswordSalt(string $method): ?string { if (! Password::hashMethodRequiresSalt($method)) { - return; + return null; } return Password::getSalt($this->password); @@ -234,15 +217,11 @@ trait HasPassword $value = null ); - switch ($algo) { - case Password::CRYPT_SALT_TYPE_MD5: - return 'md5'.$method; - case Password::CRYPT_SALT_TYPE_SHA256: - return 'sha256'.$method; - case Password::CRYPT_SALT_TYPE_SHA512: - return 'sha512'.$method; - default: - return $method; - } + return match ((int) $algo) { + Password::CRYPT_SALT_TYPE_MD5 => 'md5'.$method, + Password::CRYPT_SALT_TYPE_SHA256 => 'sha256'.$method, + Password::CRYPT_SALT_TYPE_SHA512 => 'sha512'.$method, + default => $method, + }; } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasRelationships.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasRelationships.php index 87c0a7cc8..e81c3a15e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasRelationships.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasRelationships.php @@ -12,61 +12,47 @@ trait HasRelationships { /** * Returns a new has one relationship. - * - * @param mixed $related - * @param string $relationKey - * @param string $foreignKey - * @return HasOne */ - public function hasOne($related, $relationKey, $foreignKey = 'dn') + public function hasOne(array|string $related, string $relationKey, string $foreignKey = 'dn'): HasOne { return new HasOne($this->newQuery(), $this, $related, $relationKey, $foreignKey); } /** * Returns a new has many relationship. - * - * @param mixed $related - * @param string $relationKey - * @param string $foreignKey - * @return HasMany */ - public function hasMany($related, $relationKey, $foreignKey = 'dn') + public function hasMany(array|string $related, string $relationKey, string $foreignKey = 'dn'): HasMany { return new HasMany($this->newQuery(), $this, $related, $relationKey, $foreignKey, $this->guessRelationshipName()); } /** * Returns a new has many in relationship. - * - * @param mixed $related - * @param string $relationKey - * @param string $foreignKey - * @return HasManyIn */ - public function hasManyIn($related, $relationKey, $foreignKey = 'dn') + public function hasManyIn(array|string $related, string $relationKey, string $foreignKey = 'dn'): HasManyIn { return new HasManyIn($this->newQuery(), $this, $related, $relationKey, $foreignKey, $this->guessRelationshipName()); } /** * Get a relationship by its name. - * - * @param string $relationName - * @return Relation|null */ - public function getRelation($relationName) + public function getRelation(?string $relationName = null): ?Relation { + if (is_null($relationName)) { + return null; + } + if (! method_exists($this, $relationName)) { - return; + return null; } if (! $relation = $this->{$relationName}()) { - return; + return null; } if (! $relation instanceof Relation) { - return; + return null; } return $relation; @@ -74,10 +60,8 @@ trait HasRelationships /** * Get the relationships name. - * - * @return string|null */ - protected function guessRelationshipName() + protected function guessRelationshipName(): ?string { return Arr::last(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3))['function']; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasScopes.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasScopes.php index cb227fecc..7420620ce 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasScopes.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasScopes.php @@ -2,38 +2,34 @@ namespace LdapRecord\Models\Concerns; +use LdapRecord\Query\Model\Builder; + /** @mixin \LdapRecord\Models\Model */ trait HasScopes { /** * Begin querying the direct descendants of the model. - * - * @return \LdapRecord\Query\Model\Builder */ - public function descendants() + public function descendants(): Builder { - return $this->in($this->getDn())->listing(); + return $this->in($this->getDn())->list(); } /** * Begin querying the direct ancestors of the model. - * - * @return \LdapRecord\Query\Model\Builder */ - public function ancestors() + public function ancestors(): Builder { $parent = $this->getParentDn($this->getDn()); - return $this->in($this->getParentDn($parent))->listing(); + return $this->in($this->getParentDn($parent))->list(); } /** * Begin querying the direct siblings of the model. - * - * @return \LdapRecord\Query\Model\Builder */ - public function siblings() + public function siblings(): Builder { - return $this->in($this->getParentDn($this->getDn()))->listing(); + return $this->in($this->getParentDn($this->getDn()))->list(); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HidesAttributes.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HidesAttributes.php index ecd328dab..533e8fc58 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HidesAttributes.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HidesAttributes.php @@ -13,37 +13,29 @@ trait HidesAttributes { /** * The attributes that should be hidden for serialization. - * - * @var array */ - protected $hidden = []; + protected array $hidden = []; /** * The attributes that should be visible in serialization. - * - * @var array */ - protected $visible = []; + protected array $visible = []; /** * Get the hidden attributes for the model. - * - * @return array */ - public function getHidden() + public function getHidden(): array { - return array_map(function ($key) { - return $this->normalizeAttributeKey($key); - }, $this->hidden); + return array_map( + $this->normalizeAttributeKey(...), + $this->hidden + ); } /** * Set the hidden attributes for the model. - * - * @param array $hidden - * @return $this */ - public function setHidden(array $hidden) + public function setHidden(array $hidden): static { $this->hidden = $hidden; @@ -52,11 +44,8 @@ trait HidesAttributes /** * Add hidden attributes for the model. - * - * @param array|string|null $attributes - * @return void */ - public function addHidden($attributes = null) + public function addHidden(array|string|null $attributes = null): void { $this->hidden = array_merge( $this->hidden, @@ -66,23 +55,19 @@ trait HidesAttributes /** * Get the visible attributes for the model. - * - * @return array */ - public function getVisible() + public function getVisible(): array { - return array_map(function ($key) { - return $this->normalizeAttributeKey($key); - }, $this->visible); + return array_map( + $this->normalizeAttributeKey(...), + $this->visible + ); } /** * Set the visible attributes for the model. - * - * @param array $visible - * @return $this */ - public function setVisible(array $visible) + public function setVisible(array $visible): static { $this->visible = $visible; @@ -91,11 +76,8 @@ trait HidesAttributes /** * Add visible attributes for the model. - * - * @param array|string|null $attributes - * @return void */ - public function addVisible($attributes = null) + public function addVisible(array|string|null $attributes = null): void { $this->visible = array_merge( $this->visible, @@ -105,11 +87,8 @@ trait HidesAttributes /** * Make the given, typically hidden, attributes visible. - * - * @param array|string $attributes - * @return $this */ - public function makeVisible($attributes) + public function makeVisible(array|string $attributes): static { $this->hidden = array_diff($this->hidden, (array) $attributes); @@ -122,11 +101,8 @@ trait HidesAttributes /** * Make the given, typically visible, attributes hidden. - * - * @param array|string $attributes - * @return $this */ - public function makeHidden($attributes) + public function makeHidden(array|string $attributes): static { $attributes = (array) $attributes; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesAndRestoresPropertyValues.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesAndRestoresPropertyValues.php index cf550c749..bc4fa28cd 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesAndRestoresPropertyValues.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesAndRestoresPropertyValues.php @@ -7,12 +7,8 @@ trait SerializesAndRestoresPropertyValues { /** * Get the property value prepared for serialization. - * - * @param string $property - * @param mixed $value - * @return mixed */ - protected function getSerializedPropertyValue($property, $value) + protected function getSerializedPropertyValue(string $property, mixed $value): mixed { if ($property === 'original') { return $this->originalToArray(); @@ -27,12 +23,8 @@ trait SerializesAndRestoresPropertyValues /** * Get the unserialized property value after deserialization. - * - * @param string $property - * @param mixed $value - * @return mixed */ - protected function getUnserializedPropertyValue($property, $value) + protected function getUnserializedPropertyValue(string $property, mixed $value): mixed { if ($property === 'original') { return $this->arrayToOriginal($value); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesProperties.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesProperties.php index 91f4bcfe3..65428fa40 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesProperties.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesProperties.php @@ -11,10 +11,8 @@ trait SerializesProperties /** * Prepare the attributes for serialization. - * - * @return array */ - public function __sleep() + public function __sleep(): array { $properties = (new ReflectionClass($this))->getProperties(); @@ -25,17 +23,15 @@ trait SerializesProperties )); } - return array_values(array_filter(array_map(function ($p) { - return $p->isStatic() ? null : $p->getName(); - }, $properties))); + return array_values(array_filter( + array_map(fn ($p) => $p->isStatic() ? null : $p->getName(), $properties) + )); } /** * Restore the attributes after serialization. - * - * @return void */ - public function __wakeup() + public function __wakeup(): void { foreach ((new ReflectionClass($this))->getProperties() as $property) { if ($property->isStatic()) { @@ -51,10 +47,8 @@ trait SerializesProperties /** * Prepare the model for serialization. - * - * @return array */ - public function __serialize() + public function __serialize(): array { $values = []; @@ -92,11 +86,8 @@ trait SerializesProperties /** * Restore the model after serialization. - * - * @param array $values - * @return void */ - public function __unserialize(array $values) + public function __unserialize(array $values): void { $properties = (new ReflectionClass($this))->getProperties(); @@ -130,11 +121,8 @@ trait SerializesProperties /** * Get the property value for the given property. - * - * @param ReflectionProperty $property - * @return mixed */ - protected function getPropertyValue(ReflectionProperty $property) + protected function getPropertyValue(ReflectionProperty $property): mixed { $property->setAccessible(true); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DetectsResetIntegers.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DetectsResetIntegers.php index 7b8d1830e..1f2e826bf 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DetectsResetIntegers.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DetectsResetIntegers.php @@ -10,12 +10,9 @@ trait DetectsResetIntegers * The integer values '0' and '-1' can be used on certain * LDAP attributes to instruct the server to reset the * value to an 'unset' or 'cleared' state. - * - * @param mixed $value - * @return bool */ - protected function valueIsResetInteger($value) + protected function valueIsResetInteger(mixed $value): bool { - return in_array($value, [0, -1], $strict = true); + return in_array($value, [0, -1], true); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Entry.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Entry.php index a67200e94..947daca1a 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Entry.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Entry.php @@ -9,8 +9,6 @@ class Entry extends Model implements DirectoryServer { /** * The attribute key that contains the models object GUID. - * - * @var string */ - protected $guidKey = 'gidNumber'; + protected string $guidKey = 'gidNumber'; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Group.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Group.php index 49a6e0aae..c5b0c17d8 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Group.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Group.php @@ -6,10 +6,8 @@ class Group extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'groupOfUniqueNames', 'posixGroup', diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/User.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/User.php index 430588b0a..4a3bc143c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/User.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/User.php @@ -6,10 +6,8 @@ class User extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'nsPerson', 'nsAccount', diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Event.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Event.php index 8dbd1d29a..340bc2ea5 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Event.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Event.php @@ -8,15 +8,11 @@ abstract class Event { /** * The model that the event is being triggered on. - * - * @var Model */ - protected $model; + protected Model $model; /** * Constructor. - * - * @param Model $model */ public function __construct(Model $model) { @@ -24,11 +20,9 @@ abstract class Event } /** - * Returns the model that generated the event. - * - * @return Model + * Get the model that generated the event. */ - public function getModel() + public function getModel(): Model { return $this->model; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Renaming.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Renaming.php index 5e7be97d7..4890e2bfc 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Renaming.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Renaming.php @@ -8,26 +8,18 @@ class Renaming extends Event { /** * The models RDN. - * - * @var string */ - protected $rdn; + protected string $rdn; /** * The models new parent DN. - * - * @var string */ - protected $newParentDn; + protected string $newParentDn; /** * Constructor. - * - * @param Model $model - * @param string $rdn - * @param string $newParentDn */ - public function __construct(Model $model, $rdn, $newParentDn) + public function __construct(Model $model, string $rdn, string $newParentDn) { parent::__construct($model); @@ -37,20 +29,16 @@ class Renaming extends Event /** * Get the models RDN. - * - * @return string */ - public function getRdn() + public function getRdn(): string { return $this->rdn; } /** * Get the models parent DN. - * - * @return string */ - public function getNewParentDn() + public function getNewParentDn(): string { return $this->newParentDn; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Entry.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Entry.php index be6322b15..4f29ef033 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Entry.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Entry.php @@ -13,41 +13,34 @@ class Entry extends BaseEntry implements FreeIPA { /** * The attribute key that contains the models object GUID. - * - * @var string */ - protected $guidKey = 'ipauniqueid'; + protected string $guidKey = 'ipauniqueid'; /** * The default attributes that should be mutated to dates. - * - * @var array */ - protected $defaultDates = [ + protected array $defaultDates = [ 'krblastpwdchange' => 'ldap', 'krbpasswordexpiration' => 'ldap', ]; /** - * @inheritdoc + * {@inheritdoc} */ - protected static function boot() + protected static function boot(): void { parent::boot(); // Here we'll add a global scope to all FreeIPA models to ensure the // Entry UUID is always selected on each query. This attribute is // virtual, so it must be manually selected to be included. - static::addGlobalScope(new AddEntryUuidToSelects()); + static::addGlobalScope(new AddEntryUuidToSelects); } /** * Create a new query builder. - * - * @param Connection $connection - * @return FreeIpaBuilder */ - public function newQueryBuilder(Connection $connection) + public function newQueryBuilder(Connection $connection): FreeIpaBuilder { return new FreeIpaBuilder($connection); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Group.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Group.php index 10fd93404..941977595 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Group.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Group.php @@ -2,14 +2,14 @@ namespace LdapRecord\Models\FreeIPA; +use LdapRecord\Models\Relations\HasMany; + class Group extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'groupofnames', 'nestedgroup', @@ -20,21 +20,17 @@ class Group extends Entry /** * The groups relationship. * - * Retrieves groups that the current group is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany + * Retrieves groups that the current group is a part of. */ - public function groups() + public function groups(): HasMany { return $this->hasMany(self::class, 'member'); } /** * Retrieve the members of the group. - * - * @return \LdapRecord\Models\Relations\HasMany */ - public function members() + public function members(): HasMany { return $this->hasMany(User::class, 'memberof')->using($this, 'member'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Scopes/AddEntryUuidToSelects.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Scopes/AddEntryUuidToSelects.php index 581b5beed..d35ed9141 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Scopes/AddEntryUuidToSelects.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Scopes/AddEntryUuidToSelects.php @@ -10,12 +10,8 @@ class AddEntryUuidToSelects implements Scope { /** * Add the entry UUID to the selected attributes. - * - * @param Builder $query - * @param Model $model - * @return void */ - public function apply(Builder $query, Model $model) + public function apply(Builder $query, Model $model): void { empty($query->columns) ? $query->addSelect(['*', $model->getGuidKey()]) diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/User.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/User.php index 24c7f3b7d..47964f010 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/User.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/User.php @@ -2,14 +2,14 @@ namespace LdapRecord\Models\FreeIPA; +use LdapRecord\Models\Relations\HasMany; + class User extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'person', 'inetorgperson', @@ -17,11 +17,9 @@ class User extends Entry ]; /** - * Retrieve groups that the current user is apart of. - * - * @return \LdapRecord\Models\Relations\HasMany + * Retrieve groups that the current user is a part of. */ - public function groups() + public function groups(): HasMany { return $this->hasMany(Group::class, 'member'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Model.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Model.php index 932c1a3b1..b17d0793a 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Model.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Model.php @@ -11,110 +11,92 @@ use LdapRecord\Container; use LdapRecord\EscapesValues; use LdapRecord\Models\Attributes\DistinguishedName; use LdapRecord\Models\Attributes\Guid; +use LdapRecord\Query\Builder as BaseBuilder; use LdapRecord\Query\Model\Builder; use LdapRecord\Support\Arr; +use RuntimeException; +use Stringable; use UnexpectedValueException; /** @mixin Builder */ -abstract class Model implements ArrayAccess, Arrayable, JsonSerializable +abstract class Model implements Arrayable, ArrayAccess, JsonSerializable, Stringable { - use EscapesValues; - use Concerns\HasEvents; - use Concerns\HasScopes; use Concerns\HasAttributes; + use Concerns\HasEvents; use Concerns\HasGlobalScopes; - use Concerns\HidesAttributes; use Concerns\HasRelationships; + use Concerns\HasScopes; + use Concerns\HidesAttributes; use Concerns\SerializesProperties; + use EscapesValues; /** * Indicates if the model exists in the directory. - * - * @var bool */ - public $exists = false; + public bool $exists = false; /** * Indicates whether the model was created during the current request lifecycle. - * - * @var bool */ - public $wasRecentlyCreated = false; + public bool $wasRecentlyCreated = false; /** * Indicates whether the model was renamed during the current request lifecycle. - * - * @var bool */ - public $wasRecentlyRenamed = false; + public bool $wasRecentlyRenamed = false; /** * The models distinguished name. - * - * @var string|null */ - protected $dn; + protected ?string $dn = null; /** * The base DN of where the model should be created in. - * - * @var string|null */ - protected $in; + protected ?string $in = null; /** * The object classes of the model. - * - * @var array */ - public static $objectClasses = []; + public static array $objectClasses = []; /** * The connection container instance. - * - * @var Container */ - protected static $container; + protected static ?Container $container = null; /** * The connection name for the model. - * - * @var string|null */ - protected $connection; + protected ?string $connection = null; /** - * The attribute key that contains the models object GUID. - * - * @var string + * The attribute key containing the models object GUID. */ - protected $guidKey = 'objectguid'; + protected string $guidKey = 'objectguid'; /** - * Contains the models modifications. - * - * @var array + * The array of the model's modifications. */ - protected $modifications = []; - - /** - * The array of global scopes on the model. - * - * @var array - */ - protected static $globalScopes = []; + protected array $modifications = []; /** * The array of booted models. - * - * @var array */ - protected static $booted = []; + protected static array $booted = []; + + /** + * The array of global scopes on the model. + */ + protected static array $globalScopes = []; + + /** + * The morph model cache containing object classes and their corresponding models. + */ + protected static array $morphCache = []; /** * Constructor. - * - * @param array $attributes */ public function __construct(array $attributes = []) { @@ -125,10 +107,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Check if the model needs to be booted and if so, do it. - * - * @return void */ - protected function bootIfNotBooted() + protected function bootIfNotBooted(): void { if (! isset(static::$booted[static::class])) { static::$booted[static::class] = true; @@ -139,20 +119,16 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * The "boot" method of the model. - * - * @return void */ - protected static function boot() + protected static function boot(): void { // } /** - * Clear the list of booted models so they will be re-booted. - * - * @return void + * Clear the list of booted models, so they will be re-booted. */ - public static function clearBootedModels() + public static function clearBootedModels(): void { static::$booted = []; @@ -161,12 +137,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Handle dynamic method calls into the model. - * - * @param string $method - * @param array $parameters - * @return mixed */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters): mixed { if (method_exists($this, $method)) { return $this->$method(...$parameters); @@ -177,88 +149,66 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Handle dynamic static method calls into the method. - * - * @param string $method - * @param array $parameters - * @return mixed */ - public static function __callStatic($method, $parameters) + public static function __callStatic(string $method, array $parameters): mixed { - return (new static())->$method(...$parameters); + return (new static)->$method(...$parameters); } /** - * Returns the models distinguished name. - * - * @return string|null + * Get the models distinguished name. */ - public function getDn() + public function getDn(): ?string { return $this->dn; } /** - * Set the models distinguished name. - * - * @param string $dn - * @return $this + * Set the model's distinguished name. */ - public function setDn($dn) + public function setDn(?string $dn = null): static { - $this->dn = (string) $dn; + $this->dn = $dn; return $this; } /** - * A mutator for setting the models distinguished name. - * - * @param string $dn - * @return $this + * A mutator for setting the model's distinguished name. */ - public function setDnAttribute($dn) + public function setDnAttribute(string $dn): static { return $this->setRawAttribute('dn', $dn)->setDn($dn); } /** - * A mutator for setting the models distinguished name. - * - * @param string $dn - * @return $this + * A mutator for setting the model's distinguished name. */ - public function setDistinguishedNameAttribute($dn) + public function setDistinguishedNameAttribute(string $dn): static { return $this->setRawAttribute('distinguishedname', $dn)->setDn($dn); } /** * Get the connection for the model. - * - * @return Connection */ - public function getConnection() + public function getConnection(): Connection { return static::resolveConnection($this->getConnectionName()); } /** * Get the current connection name for the model. - * - * @return string */ - public function getConnectionName() + public function getConnectionName(): ?string { return $this->connection; } /** * Set the connection associated with the model. - * - * @param string $name - * @return $this */ - public function setConnection($name) + public function setConnection(?string $name = null): static { $this->connection = $name; @@ -267,24 +217,18 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Make a new model instance. - * - * @param array $attributes - * @return static */ - public static function make($attributes = []) + public static function make(array $attributes = []): static { return new static($attributes); } /** * Begin querying the model on a given connection. - * - * @param string|null $connection - * @return Builder */ - public static function on($connection = null) + public static function on(?string $connection = null): Builder { - $instance = new static(); + $instance = new static; $instance->setConnection($connection); @@ -293,11 +237,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Get all the models from the directory. - * - * @param array|mixed $attributes - * @return Collection|static[] */ - public static function all($attributes = ['*']) + public static function all(array|string $attributes = ['*']): array|Collection { return static::query()->select($attributes)->paginate(); } @@ -305,17 +246,15 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Get the RootDSE (AD schema) record from the directory. * - * @param string|null $connection - * @return Model - * * @throws \LdapRecord\Models\ModelNotFoundException */ - public static function getRootDse($connection = null) + public static function getRootDse(?string $connection = null): Model { + /** @var Model $model */ $model = static::getRootDseModel(); return $model::on($connection ?? (new $model)->getConnectionName()) - ->in(null) + ->in() ->read() ->whereHas('objectclass') ->firstOrFail(); @@ -326,40 +265,31 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable * * @return class-string */ - protected static function getRootDseModel() + protected static function getRootDseModel(): string { - $instance = (new static); + $instance = new static; - switch (true) { - case $instance instanceof Types\ActiveDirectory: - return ActiveDirectory\Entry::class; - case $instance instanceof Types\DirectoryServer: - return OpenLDAP\Entry::class; - case $instance instanceof Types\OpenLDAP: - return OpenLDAP\Entry::class; - case $instance instanceof Types\FreeIPA: - return FreeIPA\Entry::class; - default: - return Entry::class; - } + return match (true) { + $instance instanceof Types\ActiveDirectory => ActiveDirectory\Entry::class, + $instance instanceof Types\DirectoryServer => DirectoryServer\Entry::class, + $instance instanceof Types\OpenLDAP => OpenLDAP\Entry::class, + $instance instanceof Types\FreeIPA => FreeIPA\Entry::class, + default => Entry::class, + }; } /** * Begin querying the model. - * - * @return Builder */ - public static function query() + public static function query(): Builder { - return (new static())->newQuery(); + return (new static)->newQuery(); } /** * Get a new query for builder filtered by the current models object classes. - * - * @return Builder */ - public function newQuery() + public function newQuery(): Builder { return $this->registerModelScopes( $this->newQueryWithoutScopes() @@ -368,10 +298,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Get a new query builder that doesn't have any global scopes. - * - * @return Builder */ - public function newQueryWithoutScopes() + public function newQueryWithoutScopes(): Builder { return static::resolveConnection( $this->getConnectionName() @@ -380,85 +308,64 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Create a new query builder. - * - * @param Connection $connection - * @return Builder */ - public function newQueryBuilder(Connection $connection) + public function newQueryBuilder(Connection $connection): Builder { return new Builder($connection); } /** * Create a new model instance. - * - * @param array $attributes - * @return static */ - public function newInstance(array $attributes = []) + public function newInstance(array $attributes = []): static { return (new static($attributes))->setConnection($this->getConnectionName()); } /** * Resolve a connection instance. - * - * @param string|null $connection - * @return Connection */ - public static function resolveConnection($connection = null) + public static function resolveConnection(?string $connection = null): Connection { - return static::getConnectionContainer()->get($connection); + return static::getConnectionContainer()->getConnection($connection); } /** * Get the connection container. - * - * @return Container */ - public static function getConnectionContainer() + public static function getConnectionContainer(): Container { return static::$container ?? static::getDefaultConnectionContainer(); } /** * Get the default singleton container instance. - * - * @return Container */ - public static function getDefaultConnectionContainer() + public static function getDefaultConnectionContainer(): Container { return Container::getInstance(); } /** * Set the connection container. - * - * @param Container $container - * @return void */ - public static function setConnectionContainer(Container $container) + public static function setConnectionContainer(Container $container): void { static::$container = $container; } /** * Unset the connection container. - * - * @return void */ - public static function unsetConnectionContainer() + public static function unsetConnectionContainer(): void { static::$container = null; } /** * Register the query scopes for this builder instance. - * - * @param Builder $builder - * @return Builder */ - public function registerModelScopes($builder) + public function registerModelScopes(Builder $builder): Builder { $this->applyObjectClassScopes($builder); @@ -469,11 +376,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Register the global model scopes. - * - * @param Builder $builder - * @return Builder */ - public function registerGlobalScopes($builder) + public function registerGlobalScopes(Builder $builder): Builder { foreach ($this->getGlobalScopes() as $identifier => $scope) { $builder->withGlobalScope($identifier, $scope); @@ -484,11 +388,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Apply the model object class scopes to the given builder instance. - * - * @param Builder $query - * @return void */ - public function applyObjectClassScopes(Builder $query) + public function applyObjectClassScopes(Builder $query): void { foreach (static::$objectClasses as $objectClass) { $query->where('objectclass', '=', $objectClass); @@ -496,164 +397,121 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable } /** - * Returns the models distinguished name when the model is converted to a string. - * - * @return null|string + * Get the models distinguished name when the model is converted to a string. */ - public function __toString() + public function __toString(): string { - return $this->getDn(); + return (string) $this->getDn(); } /** * Returns a new batch modification. - * - * @param string|null $attribute - * @param string|int|null $type - * @param array $values - * @return BatchModification */ - public function newBatchModification($attribute = null, $type = null, $values = []) + public function newBatchModification(?string $attribute = null, ?int $type = null, array $values = []): BatchModification { return new BatchModification($attribute, $type, $values); } /** * Returns a new collection with the specified items. - * - * @param mixed $items - * @return Collection */ - public function newCollection($items = []) + public function newCollection(mixed $items = []): Collection { return new Collection($items); } /** * Dynamically retrieve attributes on the object. - * - * @param string $key - * @return mixed */ - public function __get($key) + public function __get(string $key): mixed { return $this->getAttribute($key); } /** * Dynamically set attributes on the object. - * - * @param string $key - * @param mixed $value - * @return $this */ - public function __set($key, $value) + public function __set(string $key, mixed $value): void { - return $this->setAttribute($key, $value); + $this->setAttribute($key, $value); } /** * Determine if the given offset exists. - * - * @param string $offset - * @return bool */ #[\ReturnTypeWillChange] - public function offsetExists($offset) + public function offsetExists(mixed $offset): bool { return ! is_null($this->getAttribute($offset)); } /** * Get the value for a given offset. - * - * @param string $offset - * @return mixed */ #[\ReturnTypeWillChange] - public function offsetGet($offset) + public function offsetGet(mixed $offset): mixed { return $this->getAttribute($offset); } /** * Set the value at the given offset. - * - * @param string $offset - * @param mixed $value - * @return void */ #[\ReturnTypeWillChange] - public function offsetSet($offset, $value) + public function offsetSet(mixed $offset, mixed $value): void { $this->setAttribute($offset, $value); } /** * Unset the value at the given offset. - * - * @param string $offset - * @return void */ #[\ReturnTypeWillChange] - public function offsetUnset($offset) + public function offsetUnset(mixed $offset): void { unset($this->attributes[$offset]); } /** * Determine if an attribute exists on the model. - * - * @param string $key - * @return bool */ - public function __isset($key) + public function __isset(string $key): bool { return $this->offsetExists($key); } /** * Unset an attribute on the model. - * - * @param string $key - * @return void */ - public function __unset($key) + public function __unset(string $key): void { $this->offsetUnset($key); } /** * Convert the model to its JSON encodeable array form. - * - * @return array */ - public function toArray() + public function toArray(): array { return $this->attributesToArray(); } /** * Convert the model's attributes into JSON encodeable values. - * - * @return array */ #[\ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return $this->toArray(); } /** * Convert the attributes for JSON serialization. - * - * @param array $attributes - * @return array */ - protected function convertAttributesForJson(array $attributes = []) + protected function convertAttributesForJson(array $attributes = []): array { // If the model has a GUID set, we need to convert it to its - // string format, due to it being in binary. Otherwise + // string format, due to it being in binary. Otherwise, // we will receive a JSON serialization exception. if (isset($attributes[$this->guidKey])) { $attributes[$this->guidKey] = [$this->getConvertedGuid( @@ -666,21 +524,25 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Convert the attributes from JSON serialization. - * - * @param array $attributes - * @return array */ - protected function convertAttributesFromJson(array $attributes = []) + protected function convertAttributesFromJson(array $attributes = []): array { + // Here we are converting the model's GUID and SID attributes + // back to their original values from serialization, so that + // their original value may be used and compared against. + if (isset($attributes[$this->guidKey])) { + $attributes[$this->guidKey] = [$this->getBinaryGuid( + Arr::first($attributes[$this->guidKey]) + )]; + } + return $attributes; } /** * Reload a fresh model instance from the directory. - * - * @return static|false */ - public function fresh() + public function fresh(): static|false { if (! $this->exists) { return false; @@ -691,50 +553,106 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Determine if two models have the same distinguished name and belong to the same connection. - * - * @param Model|null $model - * @return bool */ - public function is($model) + public function is(?Model $model = null): bool { return ! is_null($model) - && $this->dn == $model->getDn() - && $this->getConnectionName() == $model->getConnectionName(); + && ! empty($this->dn) + && ! empty($model->getDn()) + && $this->dn == $model->getDn() + && $this->getConnectionName() == $model->getConnectionName(); } /** * Determine if two models are not the same. - * - * @param Model|null $model - * @return bool */ - public function isNot($model) + public function isNot(?Model $model = null): bool { return ! $this->is($model); } /** * Hydrate a new collection of models from search results. - * - * @param array $records - * @return Collection */ - public function hydrate($records) + public function hydrate(array $records): Collection { return $this->newCollection($records)->transform(function ($attributes) { - return $attributes instanceof static - ? $attributes - : static::newInstance()->setRawAttributes($attributes); + if ($attributes instanceof static) { + return $attributes; + } + + return static::newInstance()->setRawAttributes($attributes); }); } /** - * Converts the current model into the given model. - * - * @param Model $into - * @return Model + * Morph the model into a one of matching models using their object classes. */ - public function convert(self $into) + public function morphInto(array $models, ?callable $resolver = null): Model + { + if (class_exists($model = $this->determineMorphModel($this, $models, $resolver))) { + return $this->convert(new $model); + } + + return $this; + } + + /** + * Morph the model into a one of matching models or throw an exception. + */ + public function morphIntoOrFail(array $models, ?callable $resolver = null): Model + { + $model = $this->morphInto($models, $resolver); + + if ($model instanceof $this) { + throw new RuntimeException( + 'The model could not be morphed into any of the given models.' + ); + } + + return $model; + } + + /** + * Determine the model to morph into from the given models. + * + * @return class-string|bool + */ + protected function determineMorphModel(Model $model, array $models, ?callable $resolver = null): string|bool + { + $morphModelMap = []; + + foreach ($models as $modelClass) { + $morphModelMap[$modelClass] = static::$morphCache[$modelClass] ??= $this->normalizeObjectClasses( + $modelClass::$objectClasses + ); + } + + $objectClasses = $this->normalizeObjectClasses( + $model->getObjectClasses() + ); + + $resolver ??= function (array $objectClasses, array $morphModelMap) { + return array_search($objectClasses, $morphModelMap); + }; + + return $resolver($objectClasses, $morphModelMap); + } + + /** + * Sort and normalize the object classes. + */ + protected function normalizeObjectClasses(array $classes): array + { + sort($classes); + + return array_map('strtolower', $classes); + } + + /** + * Converts the current model into the given model. + */ + public function convert(self $into): Model { $into->setDn($this->getDn()); $into->setConnection($this->getConnectionName()); @@ -748,10 +666,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Refreshes the current models attributes with the directory values. - * - * @return bool */ - public function refresh() + public function refresh(): bool { if ($model = $this->fresh()) { $this->setRawAttributes($model->getAttributes()); @@ -764,10 +680,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Get the model's batch modifications to be processed. - * - * @return array */ - public function getModifications() + public function getModifications(): array { $builtModifications = []; @@ -780,11 +694,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Set the models batch modifications. - * - * @param array $modifications - * @return $this */ - public function setModifications(array $modifications = []) + public function setModifications(array $modifications = []): static { $this->modifications = []; @@ -798,12 +709,9 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Adds a batch modification to the model. * - * @param array|BatchModification $mod - * @return $this - * * @throws InvalidArgumentException */ - public function addModification($mod = []) + public function addModification(BatchModification|array $mod = []): static { if ($mod instanceof BatchModification) { $mod = $mod->get(); @@ -822,85 +730,68 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Get the model's guid attribute key name. - * - * @return string */ - public function getGuidKey() + public function getGuidKey(): string { return $this->guidKey; } /** * Get the model's ANR attributes for querying when incompatible with ANR. - * - * @return array */ - public function getAnrAttributes() + public function getAnrAttributes(): array { return ['cn', 'sn', 'uid', 'name', 'mail', 'givenname', 'displayname']; } /** * Get the name of the model, or the given DN. - * - * @param string|null $dn - * @return string|null */ - public function getName($dn = null) + public function getName(?string $dn = null): ?string { return $this->newDn($dn ?? $this->dn)->name(); } /** * Get the head attribute of the model, or the given DN. - * - * @param string|null $dn - * @return string|null */ - public function getHead($dn = null) + public function getHead(?string $dn = null): ?string { return $this->newDn($dn ?? $this->dn)->head(); } /** * Get the RDN of the model, of the given DN. - * - * @param string|null - * @return string|null */ - public function getRdn($dn = null) + public function getRdn(?string $dn = null): ?string { return $this->newDn($dn ?? $this->dn)->relative(); } /** * Get the parent distinguished name of the model, or the given DN. - * - * @param string|null - * @return string|null */ - public function getParentDn($dn = null) + public function getParentDn(?string $dn = null): ?string { return $this->newDn($dn ?? $this->dn)->parent(); } /** - * Create a new Distinguished Name object. - * - * @param string|null $dn - * @return DistinguishedName + * Create a new distinguished name. */ - public function newDn($dn = null) + public function newDn(?string $dn = null): DistinguishedName { + if (! is_null($dn) && str_contains($dn, BaseBuilder::BASE_DN_PLACEHOLDER)) { + $dn = $this->newQuery()->substituteBaseDn($dn); + } + return new DistinguishedName($dn); } /** * Get the model's object GUID key. - * - * @return string */ - public function getObjectGuidKey() + public function getObjectGuidKey(): string { return $this->guidKey; } @@ -909,76 +800,60 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable * Get the model's binary object GUID. * * @see https://msdn.microsoft.com/en-us/library/ms679021(v=vs.85).aspx - * - * @return string|null */ - public function getObjectGuid() + public function getObjectGuid(): ?string { return $this->getFirstAttribute($this->guidKey); } /** * Get the model's object classes. - * - * @return array */ - public function getObjectClasses() + public function getObjectClasses(): array { return $this->getAttribute('objectclass', static::$objectClasses); } /** * Get the model's string GUID. - * - * @param string|null $guid - * @return string|null */ - public function getConvertedGuid($guid = null) + public function getConvertedGuid(?string $guid = null): ?string { try { - return (string) $this->newObjectGuid( - $guid ?? $this->getObjectGuid() + return $this->newObjectGuid( + (string) ($guid ?? $this->getObjectGuid()) ); - } catch (InvalidArgumentException $e) { - return; + } catch (InvalidArgumentException) { + return null; } } /** * Get the model's binary GUID. - * - * @param string|null $guid - * @return string|null */ - public function getBinaryGuid($guid = null) + public function getBinaryGuid(?string $guid = null): ?string { try { return $this->newObjectGuid( $guid ?? $this->getObjectGuid() )->getBinary(); - } catch (InvalidArgumentException $e) { - return; + } catch (InvalidArgumentException) { + return null; } } /** * Make a new object Guid instance. - * - * @param string $value - * @return Guid */ - protected function newObjectGuid($value) + protected function newObjectGuid(string $value): Guid { return new Guid($value); } /** * Determine if the current model is a direct descendant of the given. - * - * @param static|string $parent - * @return bool */ - public function isChildOf($parent) + public function isChildOf(Model|string|null $parent = null): bool { return $this->newDn($this->getDn())->isChildOf( $this->newDn((string) $parent) @@ -986,12 +861,19 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable } /** - * Determine if the current model is a direct ascendant of the given. - * - * @param static|string $child - * @return bool + * Determine if the current model is a sibling of the given. */ - public function isParentOf($child) + public function isSiblingOf(Model|string|null $model = null): bool + { + return $this->newDn($this->getDn())->isSiblingOf( + $this->newDn((string) $model) + ); + } + + /** + * Determine if the current model is a direct ascendant of the given. + */ + public function isParentOf(Model|string|null $child = null): bool { return $this->newDn($this->getDn())->isParentOf( $this->newDn((string) $child) @@ -1000,34 +882,24 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Determine if the current model is a descendant of the given. - * - * @param static|string $model - * @return bool */ - public function isDescendantOf($model) + public function isDescendantOf(Model|string|null $model = null): bool { return $this->dnIsInside($this->getDn(), $model); } /** * Determine if the current model is a ancestor of the given. - * - * @param static|string $model - * @return bool */ - public function isAncestorOf($model) + public function isAncestorOf(Model|string|null $model = null): bool { return $this->dnIsInside($model, $this->getDn()); } /** - * Determines if the DN is inside of the parent DN. - * - * @param static|string $dn - * @param static|string $parentDn - * @return bool + * Determine if the DN is inside the parent DN. */ - protected function dnIsInside($dn, $parentDn) + protected function dnIsInside(Model|string|null $dn = null, Model|string|null $parentDn = null): bool { return $this->newDn((string) $dn)->isDescendantOf( $this->newDn($parentDn) @@ -1036,11 +908,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Set the base DN of where the model should be created in. - * - * @param static|string $dn - * @return $this */ - public function inside($dn) + public function inside(Model|string $dn): static { $this->in = $dn instanceof self ? $dn->getDn() : $dn; @@ -1050,12 +919,9 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Save the model to the directory without raising any events. * - * @param array $attributes - * @return void - * * @throws \LdapRecord\LdapRecordException */ - public function saveQuietly(array $attributes = []) + public function saveQuietly(array $attributes = []): void { static::withoutEvents(function () use ($attributes) { $this->save($attributes); @@ -1065,12 +931,9 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Save the model to the directory. * - * @param array $attributes The attributes to update or create for the current entry. - * @return void - * * @throws \LdapRecord\LdapRecordException */ - public function save(array $attributes = []) + public function save(array $attributes = []): void { $this->fill($attributes); @@ -1088,11 +951,9 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Inserts the model into the directory. * - * @return void - * * @throws \LdapRecord\LdapRecordException */ - protected function performInsert() + protected function performInsert(): void { // Here we will populate the models object classes if it // does not already have any set. An LDAP object cannot @@ -1112,11 +973,18 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable $this->dispatch('creating'); + // Some PHP versions prevent non-numerically indexed arrays + // from being sent to the server. To resolve this, we will + // convert the attributes to numerically indexed arrays. + $attributes = array_map('array_values', array_filter($this->getAttributes())); + // Here we perform the insert of new object in the directory, // but filter out any empty attributes before sending them // to the server. LDAP servers will throw an exception if // attributes have been given empty or null values. - $query->insert($this->getDn(), array_filter($this->getAttributes())); + $this->dn = $query->insertAndGetDn($this->getDn(), $attributes); + + $this->attributes = $attributes; $this->dispatch('created'); @@ -1130,11 +998,9 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Updates the model in the directory. * - * @return void - * * @throws \LdapRecord\LdapRecordException */ - protected function performUpdate() + protected function performUpdate(): void { if (! count($modifications = $this->getModifications())) { return; @@ -1146,18 +1012,17 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable $this->dispatch('updated'); + $this->syncChanges(); + $this->syncOriginal(); } /** * Create the model in the directory. * - * @param array $attributes The attributes for the new entry. - * @return Model - * * @throws \LdapRecord\LdapRecordException */ - public static function create(array $attributes = []) + public static function create(array $attributes = []): static { $instance = new static($attributes); @@ -1167,22 +1032,18 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable } /** - * Create an attribute on the model. - * - * @param string $attribute The attribute to create - * @param mixed $value The value of the new attribute - * @return void + * Add an attribute on the model with the given value. * * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function createAttribute($attribute, $value) + public function addAttribute(string $attribute, mixed $value): void { $this->assertExists(); $this->dispatch(['saving', 'updating']); - $this->newQuery()->insertAttributes($this->dn, [$attribute => (array) $value]); + $this->newQuery()->add($this->dn, [$attribute => (array) $value]); $this->addAttributeValue($attribute, $value); @@ -1192,13 +1053,10 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Update the model. * - * @param array $attributes The attributes to update for the current entry. - * @return void - * * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function update(array $attributes = []) + public function update(array $attributes = []): void { $this->assertExists(); @@ -1208,20 +1066,16 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Update the model attribute with the specified value. * - * @param string $attribute The attribute to modify - * @param mixed $value The new value for the attribute - * @return void - * * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function updateAttribute($attribute, $value) + public function replaceAttribute(string $attribute, mixed $value): void { $this->assertExists(); $this->dispatch(['saving', 'updating']); - $this->newQuery()->updateAttributes($this->dn, [$attribute => (array) $value]); + $this->newQuery()->replace($this->dn, [$attribute => (array) $value]); $this->addAttributeValue($attribute, $value); @@ -1231,17 +1085,13 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Destroy the models for the given distinguished names. * - * @param Collection|array|string $dns - * @param bool $recursive - * @return int - * * @throws \LdapRecord\LdapRecordException */ - public static function destroy($dns, $recursive = false) + public static function destroy(mixed $dns, bool $recursive = false): int { $count = 0; - $instance = new static(); + $instance = new static; if ($dns instanceof Collection) { $dns = $dns->modelDns()->toArray(); @@ -1270,13 +1120,10 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable * Throws a ModelNotFoundException if the current model does * not exist or does not contain a distinguished name. * - * @param bool $recursive Whether to recursively delete leaf nodes (models that are children). - * @return void - * * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function delete($recursive = false) + public function delete(bool $recursive = false): void { $this->assertExists(); @@ -1299,38 +1146,44 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Deletes leaf nodes that are attached to the model. * - * @return void - * * @throws \LdapRecord\LdapRecordException */ - protected function deleteLeafNodes() + protected function deleteLeafNodes(): void { $this->newQueryWithoutScopes() ->in($this->dn) - ->listing() + ->list() ->each(function (Model $model) { - $model->delete($recursive = true); + $model->delete(recursive: true); }); } /** - * Delete an attribute on the model. - * - * @param string|array $attributes The attribute(s) to delete - * - * Delete specific values in attributes: - * - * ["memberuid" => "jdoe"] - * - * Delete an entire attribute: - * - * ["memberuid" => []] - * @return void + * Remove an attribute on the model. * * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function deleteAttribute($attributes) + public function removeAttribute(string $attribute, mixed $value = null): void + { + $this->removeAttributes([$attribute => $value]); + } + + /** + * Remove an attribute on the model. + * + * Remove specific values in attributes: + * + * ["memberuid" => "jdoe"] + * + * Remove an entire attribute: + * + * ["memberuid" => []] + * + * @throws ModelDoesNotExistException + * @throws \LdapRecord\LdapRecordException + */ + public function removeAttributes(array|string $attributes): void { $this->assertExists(); @@ -1338,7 +1191,7 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable $this->dispatch(['saving', 'updating']); - $this->newQuery()->deleteAttributes($this->dn, $attributes); + $this->newQuery()->remove($this->dn, $attributes); foreach ($attributes as $attribute => $value) { // If the attribute value is empty, we can assume the @@ -1364,11 +1217,8 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Make a deletable attribute array. - * - * @param string|array $attributes - * @return array */ - protected function makeDeletableAttributes($attributes) + protected function makeDeletableAttributes(string|array $attributes): array { $delete = []; @@ -1386,15 +1236,11 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable * * For example: $user->move($ou); * - * @param static|string $newParentDn The new parent of the current model. - * @param bool $deleteOldRdn Whether to delete the old models relative distinguished name once renamed / moved. - * @return void - * * @throws UnexpectedValueException * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function move($newParentDn, $deleteOldRdn = true) + public function move(Model|string $newParentDn, bool $deleteOldRdn = true): void { $this->assertExists(); @@ -1408,15 +1254,10 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Rename the model to a new RDN and new parent. * - * @param string $rdn The models new relative distinguished name. Example: "cn=JohnDoe" - * @param static|string|null $newParentDn The models new parent distinguished name (if moving). Leave this null if you are only renaming. Example: "ou=MovedUsers,dc=acme,dc=org" - * @param bool|true $deleteOldRdn Whether to delete the old models relative distinguished name once renamed / moved. - * @return void - * * @throws ModelDoesNotExistException * @throws \LdapRecord\LdapRecordException */ - public function rename($rdn, $newParentDn = null, $deleteOldRdn = true) + public function rename(string $rdn, Model|string|null $newParentDn = null, bool $deleteOldRdn = true): void { $this->assertExists(); @@ -1428,6 +1269,13 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable $newParentDn = $this->getParentDn($this->dn); } + // If the RDN we have been given is empty when parsed, we must + // have been given a string, with no attribute. In this case, + // we will create a new RDN using the current DN's head. + if ($this->newDn($rdn)->isEmpty()) { + $rdn = $this->getUpdateableRdn($rdn); + } + // If the RDN and the new parent DN are the same as the current, // we will simply return here to prevent a rename operation // being sent, which would fail anyway in such case. @@ -1438,21 +1286,12 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable return; } - // If the RDN we have been given is empty when parsed, we must - // have been given a string, with no attribute. In this case, - // we will create a new RDN using the current DN's head. - if ($this->newDn($rdn)->isEmpty()) { - $rdn = $this->getUpdateableRdn($rdn); - } - $this->dispatch('renaming', [$rdn, $newParentDn]); - $this->newQuery()->rename($this->dn, $rdn, $newParentDn, $deleteOldRdn); - // If the model was successfully renamed, we will set // its new DN so any further updates to the model // can be performed without any issues. - $this->dn = implode(',', [$rdn, $newParentDn]); + $this->dn = $this->newQuery()->renameAndGetDn($this->dn, $rdn, $newParentDn, $deleteOldRdn); $map = $this->newDn($this->dn)->assoc(); @@ -1471,24 +1310,17 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable } /** - * Get an updateable RDN for the model. - * - * @param string $name - * @return string + * Get an updatable RDN for the model. */ - public function getUpdateableRdn($name) + public function getUpdateableRdn(string $name): string { return $this->getCreatableRdn($name, $this->newDn($this->dn)->head()); } /** * Get a distinguished name that is creatable for the model. - * - * @param string|null $name - * @param string|null $attribute - * @return string */ - public function getCreatableDn($name = null, $attribute = null) + public function getCreatableDn(?string $name = null, ?string $attribute = null): string { return implode(',', [ $this->getCreatableRdn($name, $attribute), @@ -1498,39 +1330,30 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Get a creatable (escaped) RDN for the model. - * - * @param string|null $name - * @param string|null $attribute - * @return string */ - public function getCreatableRdn($name = null, $attribute = null) + public function getCreatableRdn(?string $name = null, ?string $attribute = null): string { $attribute = $attribute ?? $this->getCreatableRdnAttribute(); $name = $this->escape( $name ?? $this->getFirstAttribute($attribute) - )->dn(); + )->forDn(); return "$attribute=$name"; } /** * Get the creatable RDN attribute name. - * - * @return string */ - protected function getCreatableRdnAttribute() + protected function getCreatableRdnAttribute(): string { return 'cn'; } /** - * Determines if the given modification is valid. - * - * @param mixed $mod - * @return bool + * Determine if the given modification is valid. */ - protected function isValidModification($mod) + protected function isValidModification(mixed $mod): bool { return Arr::accessible($mod) && Arr::exists($mod, BatchModification::KEY_MODTYPE) @@ -1542,7 +1365,7 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable * * @return BatchModification[] */ - protected function buildModificationsFromDirty() + protected function buildModificationsFromDirty(): array { $modifications = []; @@ -1569,25 +1392,9 @@ abstract class Model implements ArrayAccess, Arrayable, JsonSerializable /** * Throw an exception if the model does not exist. * - * @deprecated - * - * @return void - * * @throws ModelDoesNotExistException */ - protected function requireExistence() - { - return $this->assertExists(); - } - - /** - * Throw an exception if the model does not exist. - * - * @return void - * - * @throws ModelDoesNotExistException - */ - protected function assertExists() + protected function assertExists(): void { if (! $this->exists || is_null($this->dn)) { throw ModelDoesNotExistException::forModel($this); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ModelDoesNotExistException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ModelDoesNotExistException.php index 508414911..98efa9865 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ModelDoesNotExistException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ModelDoesNotExistException.php @@ -7,30 +7,22 @@ use LdapRecord\LdapRecordException; class ModelDoesNotExistException extends LdapRecordException { /** - * The class name of the model that does not exist. - * - * @var Model + * The instance of the model that does not exist. */ - protected $model; + protected Model $model; /** * Create a new exception for the given model. - * - * @param Model $model - * @return ModelDoesNotExistException */ - public static function forModel(Model $model) + public static function forModel(Model $model): static { - return (new static())->setModel($model); + return (new static)->setModel($model); } /** - * Set the model that does not exist. - * - * @param Model $model - * @return ModelDoesNotExistException + * Set the model instance that does not exist. */ - public function setModel(Model $model) + public function setModel(Model $model): static { $this->model = $model; @@ -40,4 +32,12 @@ class ModelDoesNotExistException extends LdapRecordException return $this; } + + /** + * Get the instance of the model that does not exist. + */ + public function getModel(): Model + { + return $this->model; + } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Entry.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Entry.php index c11ca72d4..e20016a45 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Entry.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Entry.php @@ -13,31 +13,26 @@ class Entry extends BaseEntry implements OpenLDAP { /** * The attribute key that contains the models object GUID. - * - * @var string */ - protected $guidKey = 'entryuuid'; + protected string $guidKey = 'entryuuid'; /** - * @inheritdoc + * {@inheritdoc} */ - protected static function boot() + protected static function boot(): void { parent::boot(); // Here we'll add a global scope to all OpenLDAP models to ensure the // Entry UUID is always selected on each query. This attribute is // virtual, so it must be manually selected to be included. - static::addGlobalScope(new AddEntryUuidToSelects()); + static::addGlobalScope(new AddEntryUuidToSelects); } /** * Create a new query builder. - * - * @param Connection $connection - * @return OpenLdapBuilder */ - public function newQueryBuilder(Connection $connection) + public function newQueryBuilder(Connection $connection): OpenLdapBuilder { return new OpenLdapBuilder($connection); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Group.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Group.php index a9a682a06..e7d81ef28 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Group.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Group.php @@ -2,27 +2,23 @@ namespace LdapRecord\Models\OpenLDAP; +use LdapRecord\Models\Relations\HasManyIn; + class Group extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'groupofuniquenames', ]; /** * The members relationship. - * - * Retrieves members that are apart of the group. - * - * @return \LdapRecord\Models\Relations\HasMany */ - public function members() + public function members(): HasManyIn { - return $this->hasMany([static::class, User::class], 'memberUid'); + return $this->hasManyIn([static::class, User::class], 'uniquemember')->using($this, 'uniquemember'); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/OrganizationalUnit.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/OrganizationalUnit.php index 7ae0a37a4..e304237e7 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/OrganizationalUnit.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/OrganizationalUnit.php @@ -6,20 +6,16 @@ class OrganizationalUnit extends Entry { /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'organizationalunit', ]; /** * Get the creatable RDN attribute name. - * - * @return string */ - public function getCreatableRdnAttribute() + public function getCreatableRdnAttribute(): string { return 'ou'; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Scopes/AddEntryUuidToSelects.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Scopes/AddEntryUuidToSelects.php index 780a633f3..78efa95f4 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Scopes/AddEntryUuidToSelects.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/Scopes/AddEntryUuidToSelects.php @@ -10,12 +10,8 @@ class AddEntryUuidToSelects implements Scope { /** * Add the entry UUID to the selected attributes. - * - * @param Builder $query - * @param Model $model - * @return void */ - public function apply(Builder $query, Model $model) + public function apply(Builder $query, Model $model): void { empty($query->columns) ? $query->addSelect(['*', $model->getGuidKey()]) diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/User.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/User.php index c8626de6d..beaba9e4c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/User.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/OpenLDAP/User.php @@ -5,32 +5,27 @@ namespace LdapRecord\Models\OpenLDAP; use Illuminate\Contracts\Auth\Authenticatable; use LdapRecord\Models\Concerns\CanAuthenticate; use LdapRecord\Models\Concerns\HasPassword; +use LdapRecord\Models\Relations\HasMany; class User extends Entry implements Authenticatable { - use HasPassword; use CanAuthenticate; + use HasPassword; /** * The password's attribute name. - * - * @var string */ - protected $passwordAttribute = 'userpassword'; + protected string $passwordAttribute = 'userpassword'; /** * The password's hash method. - * - * @var string */ - protected $passwordHashMethod = 'ssha'; + protected string $passwordHashMethod = 'ssha'; /** * The object classes of the LDAP model. - * - * @var array */ - public static $objectClasses = [ + public static array $objectClasses = [ 'top', 'person', 'organizationalperson', @@ -38,14 +33,18 @@ class User extends Entry implements Authenticatable ]; /** - * The groups relationship. - * - * Retrieve groups that the user is a part of. - * - * @return \LdapRecord\Models\Relations\HasMany + * Get the unique identifier for the user. */ - public function groups() + public function getAuthIdentifier(): string { - return $this->hasMany(Group::class, 'memberuid', 'uid'); + return $this->getFirstAttribute($this->guidKey); + } + + /** + * The groups relationship. + */ + public function groups(): HasMany + { + return $this->hasMany(Group::class, 'uniquemember'); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasMany.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasMany.php index 583f81c30..16e65b14c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasMany.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasMany.php @@ -3,73 +3,21 @@ namespace LdapRecord\Models\Relations; use Closure; -use LdapRecord\DetectsErrors; -use LdapRecord\LdapRecordException; use LdapRecord\Models\Collection; use LdapRecord\Models\Model; -use LdapRecord\Models\ModelNotFoundException; +use LdapRecord\Query\Model\Builder; class HasMany extends OneToMany { - use DetectsErrors; - - /** - * The model to use for attaching / detaching. - * - * @var Model - */ - protected $using; - - /** - * The attribute key to use for attaching / detaching. - * - * @var string - */ - protected $usingKey; - /** * The pagination page size. - * - * @var int */ - protected $pageSize = 1000; - - /** - * The exceptions to bypass for each relation operation. - * - * @var array - */ - protected $bypass = [ - 'attach' => [ - 'Already exists', 'Type or value exists', - ], - 'detach' => [ - 'No such attribute', 'Server is unwilling to perform', - ], - ]; - - /** - * Set the model and attribute to use for attaching / detaching. - * - * @param Model $using - * @param string $usingKey - * @return $this - */ - public function using(Model $using, $usingKey) - { - $this->using = $using; - $this->usingKey = $usingKey; - - return $this; - } + protected int $pageSize = 1000; /** * Set the pagination page size of the relation query. - * - * @param int $pageSize - * @return $this */ - public function setPageSize($pageSize) + public function setPageSize(int $pageSize): static { $this->pageSize = $pageSize; @@ -78,22 +26,16 @@ class HasMany extends OneToMany /** * Paginate the relation using the given page size. - * - * @param int $pageSize - * @return Collection */ - public function paginate($pageSize = 1000) + public function paginate(int $pageSize = 1000): Collection { return $this->paginateOnceUsing($pageSize); } /** * Paginate the relation using the page size once. - * - * @param int $pageSize - * @return Collection */ - protected function paginateOnceUsing($pageSize) + protected function paginateOnceUsing(int $pageSize): Collection { $size = $this->pageSize; @@ -106,44 +48,32 @@ class HasMany extends OneToMany /** * Execute a callback over each result while chunking. - * - * @param Closure $callback - * @param int $pageSize - * @return bool */ - public function each(Closure $callback, $pageSize = 1000) + public function each(Closure $callback, int $pageSize = 1000): bool { - $this->chunk($pageSize, function ($results) use ($callback) { + return $this->chunk($pageSize, function ($results) use ($callback) { foreach ($results as $key => $value) { if ($callback($value, $key) === false) { return false; } } + + return true; }); } /** * Chunk the relation results using the given callback. - * - * @param int $pageSize - * @param Closure $callback - * @param array $loaded - * @return bool */ - public function chunk($pageSize, Closure $callback) + public function chunk(int $pageSize, Closure $callback): bool { return $this->chunkRelation($pageSize, $callback); } /** * Execute the callback over chunks of relation results. - * - * @param int $pageSize - * @param Closure $callback - * @param array $loaded - * @return bool */ - protected function chunkRelation($pageSize, Closure $callback, $loaded = []) + protected function chunkRelation(int $pageSize, Closure $callback, array $loaded = []): bool { return $this->getRelationQuery()->chunk($pageSize, function (Collection $results) use ($pageSize, $callback, $loaded) { $models = $this->transformResults($results)->when($this->recursive, function (Collection $models) use ($loaded) { @@ -165,15 +95,15 @@ class HasMany extends OneToMany } }); }); + + return true; }); } /** * Get the relationships results. - * - * @return Collection */ - public function getRelationResults() + public function getRelationResults(): Collection { return $this->transformResults( $this->getRelationQuery()->paginate($this->pageSize) @@ -182,10 +112,8 @@ class HasMany extends OneToMany /** * Get the prepared relationship query. - * - * @return \LdapRecord\Query\Model\Builder */ - public function getRelationQuery() + public function getRelationQuery(): Builder { $columns = $this->query->getSelects(); @@ -209,212 +137,4 @@ class HasMany extends OneToMany $this->getEscapedForeignValueFromModel($this->parent) ); } - - /** - * Attach a model to the relation. - * - * @param Model|string $model - * @return Model|string|false - */ - public function attach($model) - { - return $this->attemptFailableOperation( - $this->buildAttachCallback($model), - $this->bypass['attach'], - $model - ); - } - - /** - * Build the attach callback. - * - * @param Model|string $model - * @return \Closure - */ - protected function buildAttachCallback($model) - { - return function () use ($model) { - $foreign = $this->getAttachableForeignValue($model); - - if ($this->using) { - return $this->using->createAttribute($this->usingKey, $foreign); - } - - if (! $model instanceof Model) { - $model = $this->getForeignModelByValueOrFail($model); - } - - return $model->createAttribute($this->relationKey, $foreign); - }; - } - - /** - * Attach a collection of models to the parent instance. - * - * @param iterable $models - * @return iterable - */ - public function attachMany($models) - { - foreach ($models as $model) { - $this->attach($model); - } - - return $models; - } - - /** - * Detach the model from the relation. - * - * @param Model|string $model - * @return Model|string|false - */ - public function detach($model) - { - return $this->attemptFailableOperation( - $this->buildDetachCallback($model), - $this->bypass['detach'], - $model - ); - } - - /** - * Detach the model or delete the parent if the relation is empty. - * - * @param Model|string $model - * @return void - */ - public function detachOrDeleteParent($model) - { - $count = $this->onceWithoutMerging(function () { - return $this->count(); - }); - - if ($count <= 1) { - return $this->getParent()->delete(); - } - - return $this->detach($model); - } - - /** - * Build the detach callback. - * - * @param Model|string $model - * @return \Closure - */ - protected function buildDetachCallback($model) - { - return function () use ($model) { - $foreign = $this->getAttachableForeignValue($model); - - if ($this->using) { - return $this->using->deleteAttribute([$this->usingKey => $foreign]); - } - - if (! $model instanceof Model) { - $model = $this->getForeignModelByValueOrFail($model); - } - - return $model->deleteAttribute([$this->relationKey => $foreign]); - }; - } - - /** - * Get the attachable foreign value from the model. - * - * @param Model|string $model - * @return string - */ - protected function getAttachableForeignValue($model) - { - if ($model instanceof Model) { - return $this->using - ? $this->getForeignValueFromModel($model) - : $this->getParentForeignValue(); - } - - return $this->using ? $model : $this->getParentForeignValue(); - } - - /** - * Get the foreign model by the given value, or fail. - * - * @param string $model - * @return Model - * - * @throws ModelNotFoundException - */ - protected function getForeignModelByValueOrFail($model) - { - if (! is_null($model = $this->getForeignModelByValue($model))) { - return $model; - } - - throw ModelNotFoundException::forQuery( - $this->query->getUnescapedQuery(), - $this->query->getDn() - ); - } - - /** - * Attempt a failable operation and return the value if successful. - * - * If a bypassable exception is encountered, the value will be returned. - * - * @param callable $operation - * @param string|array $bypass - * @param mixed $value - * @return mixed - * - * @throws LdapRecordException - */ - protected function attemptFailableOperation($operation, $bypass, $value) - { - try { - $operation(); - - return $value; - } catch (LdapRecordException $e) { - if ($this->errorContainsMessage($e->getMessage(), $bypass)) { - return $value; - } - - throw $e; - } - } - - /** - * Detach all relation models. - * - * @return Collection - */ - public function detachAll() - { - return $this->onceWithoutMerging(function () { - return $this->get()->each(function (Model $model) { - $this->detach($model); - }); - }); - } - - /** - * Detach all relation models or delete the model if its relation is empty. - * - * @return Collection - */ - public function detachAllOrDelete() - { - return $this->onceWithoutMerging(function () { - return $this->get()->each(function (Model $model) { - $relation = $model->getRelation($this->relationName); - - if ($relation && $relation->count() >= 1) { - $model->delete(); - } else { - $this->detach($model); - } - }); - }); - } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasManyIn.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasManyIn.php index 303a1440d..8b5d561f0 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasManyIn.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasManyIn.php @@ -2,21 +2,19 @@ namespace LdapRecord\Models\Relations; -use LdapRecord\Query\Collection; +use LdapRecord\Models\Collection; class HasManyIn extends OneToMany { /** * Get the relationships results. - * - * @return Collection */ - public function getRelationResults() + public function getRelationResults(): Collection { $results = $this->parent->newCollection(); foreach ((array) $this->parent->getAttribute($this->relationKey) as $value) { - if ($foreign = $this->getForeignModelByValue($value)) { + if ($value && $foreign = $this->getForeignModelByValue($value)) { $results->push($foreign); } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasOne.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasOne.php index 243cd2ed9..0199f2178 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasOne.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/HasOne.php @@ -2,20 +2,19 @@ namespace LdapRecord\Models\Relations; +use LdapRecord\Models\Collection; use LdapRecord\Models\Model; class HasOne extends Relation { /** * Get the results of the relationship. - * - * @return \LdapRecord\Query\Collection */ - public function getResults() + public function getResults(): Collection { - $model = $this->getForeignModelByValue( - $this->getFirstAttributeValue($this->parent, $this->relationKey) - ); + $relationValue = $this->getFirstAttributeValue($this->parent, $this->relationKey); + + $model = $relationValue ? $this->getForeignModelByValue($relationValue) : null; return $this->transformResults( $this->parent->newCollection($model ? [$model] : null) @@ -24,31 +23,20 @@ class HasOne extends Relation /** * Attach a model instance to the parent model. - * - * @param Model|string $model - * @return Model|string - * - * @throws \LdapRecord\LdapRecordException */ - public function attach($model) + public function attach(Model|string $model): void { $foreign = $model instanceof Model ? $this->getForeignValueFromModel($model) : $model; $this->parent->setAttribute($this->relationKey, $foreign)->save(); - - return $model; } /** * Detach the related model from the parent. - * - * @return void - * - * @throws \LdapRecord\LdapRecordException */ - public function detach() + public function detach(): void { $this->parent->setAttribute($this->relationKey, null)->save(); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/OneToMany.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/OneToMany.php index aa873b60b..8eb88cbcf 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/OneToMany.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/OneToMany.php @@ -2,44 +2,59 @@ namespace LdapRecord\Models\Relations; +use Closure; +use LdapRecord\DetectsErrors; +use LdapRecord\LdapRecordException; +use LdapRecord\Models\Collection; use LdapRecord\Models\Model; -use LdapRecord\Query\Collection; +use LdapRecord\Models\ModelNotFoundException; use LdapRecord\Query\Model\Builder; abstract class OneToMany extends Relation { + use DetectsErrors; + + /** + * The model to use for attaching / detaching. + */ + protected ?Model $using = null; + /** * The relation to merge results with. - * - * @var OneToMany|null */ - protected $with; + protected ?Relation $with = null; + + /** + * The attribute key to use for attaching / detaching. + */ + protected ?string $usingKey = null; /** * The name of the relationship. - * - * @var string */ - protected $relationName; + protected ?string $relationName = null; /** * Whether to include recursive results. - * - * @var bool */ - protected $recursive = false; + protected bool $recursive = false; + + /** + * The exceptions to bypass for each relation operation. + */ + protected array $bypass = [ + 'attach' => [ + 'Already exists', 'Type or value exists', + ], + 'detach' => [ + 'No such attribute', 'Server is unwilling to perform', + ], + ]; /** * Constructor. - * - * @param Builder $query - * @param Model $parent - * @param string $related - * @param string $relationKey - * @param string $foreignKey - * @param string $relationName */ - public function __construct(Builder $query, Model $parent, $related, $relationKey, $foreignKey, $relationName) + public function __construct(Builder $query, Model $parent, array|string $related, string $relationKey, string $foreignKey, string $relationName) { $this->relationName = $relationName; @@ -47,12 +62,20 @@ abstract class OneToMany extends Relation } /** - * Set the relation to load with its parent. - * - * @param Relation $relation - * @return $this + * Set the model and attribute to use for attaching / detaching. */ - public function with(Relation $relation) + public function using(Model $using, string $usingKey): static + { + $this->using = $using; + $this->usingKey = $usingKey; + + return $this; + } + + /** + * Set the relation to load with its parent. + */ + public function with(Relation $relation): static { $this->with = $relation; @@ -61,30 +84,23 @@ abstract class OneToMany extends Relation /** * Whether to include recursive results. - * - * @param bool $enable - * @return $this */ - public function recursive($enable = true) + public function recursive(bool $recursive = true): static { - $this->recursive = $enable; + $this->recursive = $recursive; return $this; } /** * Get the immediate relationships results. - * - * @return Collection */ - abstract public function getRelationResults(); + abstract public function getRelationResults(): Collection; /** * Get the results of the relationship. - * - * @return Collection */ - public function getResults() + public function getResults(): Collection { $results = $this->recursive ? $this->getRecursiveResults() @@ -97,11 +113,8 @@ abstract class OneToMany extends Relation /** * Execute the callback excluding the merged query result. - * - * @param callable $callback - * @return mixed */ - protected function onceWithoutMerging($callback) + protected function onceWithoutMerging(Closure $callback): mixed { $merging = $this->with; @@ -116,20 +129,262 @@ abstract class OneToMany extends Relation /** * Get the relation name. - * - * @return string */ - public function getRelationName() + public function getRelationName(): string { return $this->relationName; } /** - * Get the results of the merging 'with' relation. - * - * @return Collection + * Attach the model in the relation. */ - protected function getMergingRelationResults() + public function attach(mixed $model): void + { + if (is_iterable($model)) { + array_map($this->attach(...), [...$model]); + + return; + } + + $this->attemptFailableOperation( + $this->buildAttachCallback($model), + $this->bypass['attach'], + $model + ); + } + + /** + * Build the attach callback. + */ + protected function buildAttachCallback(Model|string $model): Closure + { + return function () use ($model) { + $foreign = $this->getAttachableForeignValue($model); + + if ($this->using) { + $this->using->addAttribute($this->usingKey, $foreign); + + return; + } + + if (! $model instanceof Model) { + $model = $this->getForeignModelByValueOrFail($model); + } + + $model->addAttribute($this->relationKey, $foreign); + }; + } + + /** + * Get the foreign model by the given value, or fail. + * + * @throws ModelNotFoundException + */ + protected function getForeignModelByValueOrFail(string $model): Model + { + if (! is_null($model = $this->getForeignModelByValue($model))) { + return $model; + } + + throw ModelNotFoundException::forQuery( + $this->query->getUnescapedQuery(), + $this->query->getDn() + ); + } + + /** + * Detach the model from the relation. + */ + public function detach(mixed $model): void + { + if (is_iterable($model)) { + array_map($this->detach(...), [...$model]); + + return; + } + + $this->attemptFailableOperation( + $this->buildDetachCallback($model), + $this->bypass['detach'], + $model + ); + } + + /** + * Detach the model or delete the parent if the relation is empty. + */ + public function detachOrDeleteParent(Model|string $model): void + { + /** @var Collection $related */ + $related = $this->onceWithoutMerging(function () { + return $this->get('dn'); + }); + + if (! $related->exists($model)) { + return; + } + + if ($related->count() <= 1) { + $this->getParent()->delete(); + + return; + } + + $this->detach($model); + } + + /** + * Build the detach callback. + */ + protected function buildDetachCallback(Model|string $model): Closure + { + return function () use ($model) { + $foreign = $this->getAttachableForeignValue($model); + + if ($this->using) { + $this->using->removeAttribute($this->usingKey, $foreign); + + return; + } + + if (! $model instanceof Model) { + $model = $this->getForeignModelByValueOrFail($model); + } + + $model->removeAttribute($this->relationKey, $foreign); + }; + } + + /** + * Associate the model in the relation. + */ + public function associate(mixed $model): void + { + if (is_iterable($model)) { + array_map($this->associate(...), [...$model]); + + return; + } + + $foreign = $this->getAttachableForeignValue($model); + + if ($this->using) { + $this->using->addAttributeValue($this->usingKey, $foreign); + + return; + } + + if (! $model instanceof Model) { + $model = $this->getForeignModelByValueOrFail($model); + } + + $model->addAttributeValue($this->relationKey, $foreign); + } + + /** + * Dissociate the model in the relation. + */ + public function dissociate(mixed $model): void + { + if (is_iterable($model)) { + array_map($this->dissociate(...), [...$model]); + + return; + } + + $foreign = $this->getAttachableForeignValue($model); + + if ($this->using) { + $this->using->removeAttributeValue($this->usingKey, $foreign); + + return; + } + + if (! $model instanceof Model) { + $model = $this->getForeignModelByValueOrFail($model); + } + + $model->removeAttributeValue($this->relationKey, $foreign); + } + + /** + * Alias of "dissociate" method. + */ + public function disassociate(Model|string $model): void + { + $this->dissociate($model); + } + + /** + * Get the attachable foreign value from the model. + */ + protected function getAttachableForeignValue(Model|string $model): string + { + if ($model instanceof Model) { + return $this->using + ? $this->getForeignValueFromModel($model) + : $this->getParentForeignValue(); + } + + return $this->using ? $model : $this->getParentForeignValue(); + } + + /** + * Attempt a failable operation and return the value if successful. + * + * If a bypassable exception is encountered, the value will be returned. + * + * @throws LdapRecordException + */ + protected function attemptFailableOperation(Closure $operation, string|array $bypass, mixed $value): mixed + { + try { + $operation(); + + return $value; + } catch (LdapRecordException $e) { + if ($this->errorContainsMessage($e->getMessage(), $bypass)) { + return $value; + } + + throw $e; + } + } + + /** + * Detach all relation models. + */ + public function detachAll(): Collection + { + return $this->onceWithoutMerging( + fn () => $this->get()->each(function (Model $model) { + $this->detach($model); + }) + ); + } + + /** + * Detach all relation models or delete the model if its relation is empty. + */ + public function detachAllOrDelete(): Collection + { + return $this->onceWithoutMerging( + fn () => $this->get()->each(function (Model $model) { + $relation = $model->getRelation($this->relationName); + + if ($relation && $relation->count() >= 1) { + $model->delete(); + } else { + $this->detach($model); + } + }) + ); + } + + /** + * Get the results of the merging 'with' relation. + */ + protected function getMergingRelationResults(): Collection { return $this->with ? $this->with->recursive($this->recursive)->get() @@ -137,24 +392,23 @@ abstract class OneToMany extends Relation } /** - * Get the results for the models relation recursively. + * Get the results for the model's relation recursively. * * @param string[] $loaded The distinguished names of models already loaded - * @return Collection */ - protected function getRecursiveResults(array $loaded = []) + protected function getRecursiveResults(array $loaded = []): Collection { - $results = $this->getRelationResults()->reject(function (Model $model) use ($loaded) { + $results = $this->getRelationResults()->reject(fn (Model $model) => // Here we will exclude the models that we have already // loaded the recursive results for so we don't run // into issues with circular relations in LDAP. - return in_array($model->getDn(), $loaded); - }); + in_array($model->getDn(), $loaded) + ); foreach ($results as $model) { $loaded[] = $model->getDn(); - // Finally, we will fetch the related models relations, + // Finally, we will fetch the related model's relations, // passing along our loaded models, to ensure we do // not attempt fetching already loaded relations. $results = $results->merge( @@ -167,12 +421,8 @@ abstract class OneToMany extends Relation /** * Get the recursive relation results for given model. - * - * @param Model $model - * @param array $loaded - * @return Collection */ - protected function getRecursiveRelationResults(Model $model, array $loaded) + protected function getRecursiveRelationResults(Model $model, array $loaded): Collection { return ($relation = $model->getRelation($this->relationName)) ? $relation->getRecursiveResults($loaded) diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/Relation.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/Relation.php index 31b0969c9..b2d26100e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/Relation.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Relations/Relation.php @@ -3,13 +3,13 @@ namespace LdapRecord\Models\Relations; use Closure; +use LdapRecord\Models\Collection; use LdapRecord\Models\Entry; use LdapRecord\Models\Model; -use LdapRecord\Query\Collection; use LdapRecord\Query\Model\Builder; /** - * @method bool exists($models = null) Determine if the relation contains all of the given models, or any models + * @method bool exists($models = null) Determine if the relation contains all the given models, or any models * @method bool contains($models) Determine if any of the given models are contained in the relation * @method bool count() Retrieve the "count" result of the query. */ @@ -17,70 +17,48 @@ abstract class Relation { /** * The underlying LDAP query. - * - * @var Builder */ - protected $query; + protected Builder $query; /** * The parent model instance. - * - * @var Model */ - protected $parent; + protected Model $parent; /** * The related model class names. - * - * @var array */ - protected $related; + protected array $related; /** * The relation key. - * - * @var string */ - protected $relationKey; + protected string $relationKey; /** * The foreign key. - * - * @var string */ - protected $foreignKey; + protected string $foreignKey; /** * The default relation model. - * - * @var string */ - protected $default = Entry::class; + protected string $default = Entry::class; /** * The callback to use for resolving relation models. - * - * @var Closure */ - protected static $modelResolver; + protected static ?Closure $modelResolver = null; /** * The methods that should be passed along to a relation collection. - * - * @var string[] */ - protected $passthru = ['count', 'exists', 'contains']; + protected array $passthru = ['count', 'exists', 'contains']; /** * Constructor. - * - * @param Builder $query - * @param Model $parent - * @param string|array $related - * @param string $relationKey - * @param string $foreignKey */ - public function __construct(Builder $query, Model $parent, $related, $relationKey, $foreignKey) + public function __construct(Builder $query, Model $parent, array|string $related, string $relationKey, string $foreignKey) { $this->query = $query; $this->parent = $parent; @@ -88,21 +66,13 @@ abstract class Relation $this->relationKey = $relationKey; $this->foreignKey = $foreignKey; - static::$modelResolver = static::$modelResolver ?? function (array $modelObjectClasses, array $relationMap) { - return array_search($modelObjectClasses, $relationMap); - }; - $this->initRelation(); } /** * Handle dynamic method calls to the relationship. - * - * @param string $method - * @param array $parameters - * @return mixed */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters): mixed { if (in_array($method, $this->passthru)) { return $this->get('objectclass')->$method(...$parameters); @@ -119,21 +89,16 @@ abstract class Relation /** * Set the callback to use for resolving models from relation results. - * - * @param Closure $callback - * @return void */ - public static function resolveModelsUsing(Closure $callback) + public static function resolveModelsUsing(?Closure $callback = null): void { static::$modelResolver = $callback; } /** * Only return objects matching the related model's object classes. - * - * @return $this */ - public function onlyRelated() + public function onlyRelated(): static { $relations = []; @@ -158,18 +123,13 @@ abstract class Relation /** * Get the results of the relationship. - * - * @return Collection */ - abstract public function getResults(); + abstract public function getResults(): Collection; /** * Execute the relationship query. - * - * @param array|string $columns - * @return Collection */ - public function get($columns = ['*']) + public function get(array|string $columns = ['*']): Collection { return $this->getResultsWithColumns($columns); } @@ -178,11 +138,8 @@ abstract class Relation * Get the results of the relationship while selecting the given columns. * * If the query columns are empty, the given columns are applied. - * - * @param array $columns - * @return Collection */ - protected function getResultsWithColumns($columns) + protected function getResultsWithColumns(array|string $columns): Collection { if (is_null($this->query->columns)) { $this->query->select($columns); @@ -193,21 +150,16 @@ abstract class Relation /** * Get the first result of the relationship. - * - * @param array|string $columns - * @return Model|null */ - public function first($columns = ['*']) + public function first(array|string $columns = ['*']): ?Model { return $this->get($columns)->first(); } /** * Prepare the relation query. - * - * @return static */ - public function initRelation() + public function initRelation(): static { $this->query ->clearFilters() @@ -219,11 +171,8 @@ abstract class Relation /** * Set the underlying query for the relation. - * - * @param Builder $query - * @return $this */ - public function setQuery(Builder $query) + public function setQuery(Builder $query): static { $this->query = $query; @@ -234,72 +183,58 @@ abstract class Relation /** * Get the underlying query for the relation. - * - * @return Builder */ - public function getQuery() + public function getQuery(): Builder { return $this->query; } /** * Get the parent model of the relation. - * - * @return Model */ - public function getParent() + public function getParent(): Model { return $this->parent; } /** * Get the relation attribute key. - * - * @return string */ - public function getRelationKey() + public function getRelationKey(): string { return $this->relationKey; } /** * Get the related model classes for the relation. - * - * @return array */ - public function getRelated() + public function getRelated(): array { return $this->related; } /** * Get the relation foreign attribute key. - * - * @return string */ - public function getForeignKey() + public function getForeignKey(): string { return $this->foreignKey; } /** * Get the class name of the default model. - * - * @return string */ - public function getDefaultModel() + public function getDefaultModel(): string { return $this->default; } /** * Get a new instance of the default model on the relation. - * - * @return Model */ - public function getNewDefaultModel() + public function getNewDefaultModel(): Model { - $model = new $this->default(); + $model = new $this->default; $model->setConnection($this->parent->getConnectionName()); @@ -308,132 +243,65 @@ abstract class Relation /** * Get the foreign model by the given value. - * - * @param string $value - * @return Model|null */ - protected function getForeignModelByValue($value) + protected function getForeignModelByValue(string $value): ?Model { return $this->foreignKeyIsDistinguishedName() - ? $this->query->find($value) - : $this->query->findBy($this->foreignKey, $value); + ? $this->query->clearFilters()->find($value) + : $this->query->clearFilters()->findBy($this->foreignKey, $value); } /** - * Returns the escaped foreign key value for use in an LDAP filter from the model. - * - * @param Model $model - * @return string + * Get the escaped foreign key value for use in an LDAP filter from the model. */ - protected function getEscapedForeignValueFromModel(Model $model) + protected function getEscapedForeignValueFromModel(Model $model): string { return $this->query->escape( $this->getForeignValueFromModel($model) - )->both(); + )->forDnAndFilter(); } /** * Get the relation parents foreign value. - * - * @return string */ - protected function getParentForeignValue() + protected function getParentForeignValue(): ?string { return $this->getForeignValueFromModel($this->parent); } /** * Get the foreign key value from the model. - * - * @param Model $model - * @return string */ - protected function getForeignValueFromModel(Model $model) + protected function getForeignValueFromModel(Model $model): ?string { return $this->foreignKeyIsDistinguishedName() - ? $model->getDn() - : $this->getFirstAttributeValue($model, $this->foreignKey); + ? $model->getDn() + : $this->getFirstAttributeValue($model, $this->foreignKey); } /** * Get the first attribute value from the model. - * - * @param Model $model - * @param string $attribute - * @return string|null */ - protected function getFirstAttributeValue(Model $model, $attribute) + protected function getFirstAttributeValue(Model $model, string $attribute): mixed { return $model->getFirstAttribute($attribute); } /** * Transforms the results by converting the models into their related. - * - * @param Collection $results - * @return Collection */ - protected function transformResults(Collection $results) + protected function transformResults(Collection $results): Collection { - $relationMap = []; - - foreach ($this->related as $relation) { - $relationMap[$relation] = $this->normalizeObjectClasses( - $relation::$objectClasses - ); - } - - return $results->transform(function (Model $entry) use ($relationMap) { - $model = $this->determineModelFromRelated($entry, $relationMap); - - return class_exists($model) ? $entry->convert(new $model()) : $entry; - }); + return $results->transform( + fn (Model $entry) => $entry->morphInto($this->related, static::$modelResolver) + ); } /** - * Determines if the foreign key is a distinguished name. - * - * @return bool + * Determine if the foreign key is a distinguished name. */ - protected function foreignKeyIsDistinguishedName() + protected function foreignKeyIsDistinguishedName(): bool { return in_array($this->foreignKey, ['dn', 'distinguishedname']); } - - /** - * Determines the model from the given relation map. - * - * @param Model $model - * @param array $relationMap - * @return class-string|bool - */ - protected function determineModelFromRelated(Model $model, array $relationMap) - { - // We must normalize all the related models object class - // names to the same case so we are able to properly - // determine the owning model from search results. - $modelObjectClasses = $this->normalizeObjectClasses( - $model->getObjectClasses() - ); - - return call_user_func( - static::$modelResolver, - $modelObjectClasses, - $relationMap, - $model, - ); - } - - /** - * Sort and normalize the object classes. - * - * @param array $classes - * @return array - */ - protected function normalizeObjectClasses($classes) - { - sort($classes); - - return array_map('strtolower', $classes); - } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Scope.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Scope.php index f61cf686b..09c0c0c2c 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Scope.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Scope.php @@ -8,10 +8,6 @@ interface Scope { /** * Apply the scope to the given query. - * - * @param Builder $query - * @param Model $model - * @return void */ - public function apply(Builder $query, Model $model); + public function apply(Builder $query, Model $model): void; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Types/ActiveDirectory.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Types/ActiveDirectory.php index 3edcf88c9..2fc54e4a4 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Types/ActiveDirectory.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Types/ActiveDirectory.php @@ -5,34 +5,24 @@ namespace LdapRecord\Models\Types; interface ActiveDirectory extends TypeInterface { /** - * Returns the models object SID key. - * - * @return string + * Get the models object SID key. */ - public function getObjectSidKey(); + public function getObjectSidKey(): string; /** - * Returns the model's hex object SID. + * Get the model's hex object SID. * * @see https://msdn.microsoft.com/en-us/library/ms679024(v=vs.85).aspx - * - * @return string */ - public function getObjectSid(); + public function getObjectSid(): ?string; /** - * Returns the model's SID. - * - * @param string|null $sid - * @return string|null + * Get the model's SID. */ - public function getConvertedSid($sid = null); + public function getConvertedSid(?string $sid = null): ?string; /** - * Returns the model's binary SID. - * - * @param string|null $sid - * @return string|null + * Get the model's binary SID. */ - public function getBinarySid($sid = null); + public function getBinarySid(?string $sid = null): ?string; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ArrayCacheStore.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ArrayCacheStore.php index 2fb9c9a72..d4a9137c0 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ArrayCacheStore.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ArrayCacheStore.php @@ -10,15 +10,13 @@ class ArrayCacheStore implements CacheInterface /** * An array of stored values. - * - * @var array */ - protected $storage = []; + protected array $storage = []; /** - * @inheritdoc + * {@inheritdoc} */ - public function get($key, $default = null) + public function get($key, $default = null): mixed { if (! isset($this->storage[$key])) { return $default; @@ -38,9 +36,9 @@ class ArrayCacheStore implements CacheInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function set($key, $value, $ttl = null) + public function set($key, $value, $ttl = null): bool { $this->storage[$key] = [ 'value' => $value, @@ -52,30 +50,24 @@ class ArrayCacheStore implements CacheInterface /** * Get the expiration time of the key. - * - * @param int $seconds - * @return int */ - protected function calculateExpiration($seconds) + protected function calculateExpiration($seconds = null): int { return $this->toTimestamp($seconds); } /** * Get the UNIX timestamp for the given number of seconds. - * - * @param int $seconds - * @return int */ - protected function toTimestamp($seconds) + protected function toTimestamp($seconds = null): int { return $seconds > 0 ? $this->availableAt($seconds) : 0; } /** - * @inheritdoc + * {@inheritdoc} */ - public function delete($key) + public function delete($key): bool { unset($this->storage[$key]); @@ -83,9 +75,9 @@ class ArrayCacheStore implements CacheInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function clear() + public function clear(): bool { $this->storage = []; @@ -93,9 +85,9 @@ class ArrayCacheStore implements CacheInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function getMultiple($keys, $default = null) + public function getMultiple($keys, $default = null): iterable { $values = []; @@ -107,9 +99,9 @@ class ArrayCacheStore implements CacheInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function setMultiple($values, $ttl = null) + public function setMultiple($values, $ttl = null): bool { foreach ($values as $key => $value) { $this->set($key, $value, $ttl); @@ -119,9 +111,9 @@ class ArrayCacheStore implements CacheInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function deleteMultiple($keys) + public function deleteMultiple($keys): bool { foreach ($keys as $key) { $this->delete($key); @@ -131,9 +123,9 @@ class ArrayCacheStore implements CacheInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function has($key) + public function has($key): bool { return isset($this->storage[$key]); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Builder.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Builder.php index c93c17809..642cf32af 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Builder.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Builder.php @@ -5,6 +5,7 @@ namespace LdapRecord\Query; use BadMethodCallException; use Closure; use DateTimeInterface; +use Generator; use InvalidArgumentException; use LDAP\Result; use LdapRecord\Connection; @@ -12,31 +13,42 @@ use LdapRecord\Container; use LdapRecord\EscapesValues; use LdapRecord\LdapInterface; use LdapRecord\LdapRecordException; +use LdapRecord\Models\Attributes\EscapedValue; use LdapRecord\Models\Model; use LdapRecord\Query\Events\QueryExecuted; use LdapRecord\Query\Model\Builder as ModelBuilder; use LdapRecord\Query\Pagination\LazyPaginator; use LdapRecord\Query\Pagination\Paginator; use LdapRecord\Support\Arr; -use LdapRecord\Utilities; class Builder { use EscapesValues; + public const TYPE_SEARCH = 'search'; + + public const TYPE_READ = 'read'; + + public const TYPE_CHUNK = 'chunk'; + + public const TYPE_LIST = 'list'; + + public const TYPE_PAGINATE = 'paginate'; + + /** + * The base distinguished name placeholder. + */ + public const BASE_DN_PLACEHOLDER = '{base}'; + /** * The selected columns to retrieve on the query. - * - * @var array */ - public $columns; + public ?array $columns = null; /** * The query filters. - * - * @var array */ - public $filters = [ + public array $filters = [ 'and' => [], 'or' => [], 'raw' => [], @@ -44,120 +56,92 @@ class Builder /** * The LDAP server controls to be sent. - * - * @var array */ - public $controls = []; + public array $controls = []; /** * The LDAP server controls that were processed. - * - * @var array */ - public $controlsResponse = []; + public array $controlsResponse = []; /** * The size limit of the query. - * - * @var int */ - public $limit = 0; + public int $limit = 0; /** - * Determines whether the current query is paginated. - * - * @var bool + * Determine whether the current query is paginated. */ - public $paginated = false; + public bool $paginated = false; /** * The distinguished name to perform searches upon. - * - * @var string|null */ - protected $dn; + protected ?string $dn = null; /** * The base distinguished name to perform searches inside. - * - * @var string|null */ - protected $baseDn; + protected ?string $baseDn = null; /** * The default query type. - * - * @var string */ - protected $type = 'search'; + protected string $type = self::TYPE_SEARCH; /** - * Determines whether the query is nested. - * - * @var bool + * Determine whether the query is nested. */ - protected $nested = false; + protected bool $nested = false; /** - * Determines whether the query should be cached. - * - * @var bool + * Determine whether the query should be cached. */ - protected $caching = false; + protected bool $caching = false; + + /** + * The custom cache key to use when caching results. + */ + protected ?string $cacheKey = null; /** * How long the query should be cached until. - * - * @var DateTimeInterface|null */ - protected $cacheUntil = null; + protected ?DateTimeInterface $cacheUntil = null; /** - * Determines whether the query cache must be flushed. - * - * @var bool + * Determine whether the query cache must be flushed. */ - protected $flushCache = false; + protected bool $flushCache = false; /** * The current connection instance. - * - * @var Connection */ - protected $connection; + protected Connection $connection; /** * The current grammar instance. - * - * @var Grammar */ - protected $grammar; + protected Grammar $grammar; /** * The current cache instance. - * - * @var Cache|null */ - protected $cache; + protected ?Cache $cache = null; /** * Constructor. - * - * @param Connection $connection */ public function __construct(Connection $connection) { $this->connection = $connection; - $this->grammar = new Grammar(); + $this->grammar = new Grammar; } /** * Set the current connection. - * - * @param Connection $connection - * @return $this */ - public function setConnection(Connection $connection) + public function setConnection(Connection $connection): static { $this->connection = $connection; @@ -166,11 +150,8 @@ class Builder /** * Set the current filter grammar. - * - * @param Grammar $grammar - * @return $this */ - public function setGrammar(Grammar $grammar) + public function setGrammar(Grammar $grammar): static { $this->grammar = $grammar; @@ -179,11 +160,8 @@ class Builder /** * Set the cache to store query results. - * - * @param Cache|null $cache - * @return $this */ - public function setCache(Cache $cache = null) + public function setCache(?Cache $cache = null): static { $this->cache = $cache; @@ -192,26 +170,18 @@ class Builder /** * Returns a new Query Builder instance. - * - * @param string $baseDn - * @return $this */ - public function newInstance($baseDn = null) + public function newInstance(?string $baseDn = null): Builder { - // We'll set the base DN of the new Builder so - // developers don't need to do this manually. - $dn = is_null($baseDn) ? $this->getDn() : $baseDn; - - return (new static($this->connection))->setDn($dn); + return (new static($this->connection))->setDn( + is_null($baseDn) ? $this->getDn() : $baseDn + ); } /** * Returns a new nested Query Builder instance. - * - * @param Closure|null $closure - * @return $this */ - public function newNestedInstance(Closure $closure = null) + public function newNestedInstance(?Closure $closure = null): Builder { $query = $this->newInstance()->nested(); @@ -224,27 +194,20 @@ class Builder /** * Executes the LDAP query. - * - * @param string|array $columns - * @return Collection|array */ - public function get($columns = ['*']) + public function get(array|string $columns = ['*']): Collection|array { - return $this->onceWithColumns(Arr::wrap($columns), function () { - return $this->query($this->getQuery()); - }); + return $this->onceWithColumns( + Arr::wrap($columns), fn () => $this->query($this->getQuery()) + ); } /** * Execute the given callback while selecting the given columns. * * After running the callback, the columns are reset to the original value. - * - * @param array $columns - * @param Closure $callback - * @return mixed */ - protected function onceWithColumns($columns, Closure $callback) + protected function onceWithColumns(array $columns, Closure $callback): mixed { $original = $this->columns; @@ -260,11 +223,9 @@ class Builder } /** - * Compiles and returns the current query string. - * - * @return string + * Compile the query into an LDAP filter string. */ - public function getQuery() + public function getQuery(): string { // We need to ensure we have at least one filter, as // no query results will be returned otherwise. @@ -276,134 +237,101 @@ class Builder } /** - * Returns the unescaped query. - * - * @return string + * Get the unescaped query. */ - public function getUnescapedQuery() + public function getUnescapedQuery(): string { - return Utilities::unescape($this->getQuery()); + return EscapedValue::unescape($this->getQuery()); } /** - * Returns the current Grammar instance. - * - * @return Grammar + * Get the current Grammar instance. */ - public function getGrammar() + public function getGrammar(): Grammar { return $this->grammar; } /** - * Returns the current Cache instance. - * - * @return Cache|null + * Get the current Cache instance. */ - public function getCache() + public function getCache(): ?Cache { return $this->cache; } /** - * Returns the current Connection instance. - * - * @return Connection + * Get the current Connection instance. */ - public function getConnection() + public function getConnection(): Connection { return $this->connection; } /** - * Returns the query type. - * - * @return string + * Get the query type. */ - public function getType() + public function getType(): string { return $this->type; } /** * Set the base distinguished name of the query. - * - * @param Model|string $dn - * @return $this */ - public function setBaseDn($dn) + public function setBaseDn(Model|string|null $dn = null): static { - $this->baseDn = $this->substituteBaseInDn($dn); + $this->baseDn = $this->substituteBaseDn($dn); return $this; } /** * Get the base distinguished name of the query. - * - * @return string|null */ - public function getBaseDn() + public function getBaseDn(): ?string { return $this->baseDn; } /** * Get the distinguished name of the query. - * - * @return string */ - public function getDn() + public function getDn(): ?string { return $this->dn; } /** * Set the distinguished name for the query. - * - * @param string|Model|null $dn - * @return $this */ - public function setDn($dn = null) + public function setDn(Model|string|null $dn = null): static { - $this->dn = $this->substituteBaseInDn($dn); + $this->dn = $this->substituteBaseDn($dn); return $this; } /** * Substitute the base DN string template for the current base. - * - * @param Model|string $dn - * @return string */ - protected function substituteBaseInDn($dn) + public function substituteBaseDn(Model|string|null $dn = null): string { - return str_replace( - '{base}', - $this->baseDn ?: '', - (string) ($dn instanceof Model ? $dn->getDn() : $dn) - ); + return str_replace(static::BASE_DN_PLACEHOLDER, $this->baseDn ?? '', (string) $dn); } /** * Alias for setting the distinguished name for the query. - * - * @param string|Model|null $dn - * @return $this */ - public function in($dn = null) + public function in(Model|string|null $dn = null): static { return $this->setDn($dn); } /** * Set the size limit of the current query. - * - * @param int $limit - * @return $this */ - public function limit($limit = 0) + public function limit(int $limit = 0): static { $this->limit = $limit; @@ -412,11 +340,8 @@ class Builder /** * Returns a new query for the given model. - * - * @param Model $model - * @return ModelBuilder */ - public function model(Model $model) + public function model(Model $model): ModelBuilder { return $model->newQueryBuilder($this->connection) ->setCache($this->connection->getCache()) @@ -426,20 +351,15 @@ class Builder /** * Performs the specified query on the current LDAP connection. - * - * @param string $query - * @return Collection|array */ - public function query($query) + public function query(string $query): Collection|array { $start = microtime(true); // Here we will create the execution callback. This allows us // to only execute an LDAP request if caching is disabled // or if no cache of the given query exists yet. - $callback = function () use ($query) { - return $this->parse($this->run($query)); - }; + $callback = fn () => $this->parse($this->run($query)); $results = $this->getCachedResponse($query, $callback); @@ -450,12 +370,8 @@ class Builder /** * Paginates the current LDAP query. - * - * @param int $pageSize - * @param bool $isCritical - * @return Collection|array */ - public function paginate($pageSize = 1000, $isCritical = false) + public function paginate(int $pageSize = 1000, bool $isCritical = false): Collection|array { $this->paginated = true; @@ -466,42 +382,37 @@ class Builder // Here we will create the pagination callback. This allows us // to only execute an LDAP request if caching is disabled // or if no cache of the given query exists yet. - $callback = function () use ($query, $pageSize, $isCritical) { - return $this->runPaginate($query, $pageSize, $isCritical); - }; + $callback = fn () => $this->runPaginate($query, $pageSize, $isCritical); $pages = $this->getCachedResponse($query, $callback); - $this->logQuery($this, 'paginate', $this->getElapsedTime($start)); + $this->logQuery($this, self::TYPE_PAGINATE, $this->getElapsedTime($start)); return $this->process($pages); } /** * Runs the paginate operation with the given filter. - * - * @param string $filter - * @param int $perPage - * @param bool $isCritical - * @return array */ - protected function runPaginate($filter, $perPage, $isCritical) + protected function runPaginate(string $filter, int $perPage, bool $isCritical): array { - return $this->connection->run(function (LdapInterface $ldap) use ($filter, $perPage, $isCritical) { - return (new Paginator($this, $filter, $perPage, $isCritical))->execute($ldap); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $this->newPaginator($filter, $perPage, $isCritical)->execute($ldap) + ); + } + + /** + * Make a new paginator instance. + */ + protected function newPaginator(string $filter, int $perPage, bool $isCritical): Paginator + { + return new Paginator($this, $filter, $perPage, $isCritical); } /** * Execute a callback over each item while chunking. - * - * @param Closure $callback - * @param int $pageSize - * @param bool $isCritical - * @param bool $isolate - * @return bool */ - public function each(Closure $callback, $pageSize = 1000, $isCritical = false, $isolate = false) + public function each(Closure $callback, int $pageSize = 1000, bool $isCritical = false, bool $isolate = false): bool { return $this->chunk($pageSize, function ($results) use ($callback) { foreach ($results as $key => $value) { @@ -514,15 +425,11 @@ class Builder /** * Chunk the results of a paginated LDAP query. - * - * @param int $pageSize - * @param Closure $callback - * @param bool $isCritical - * @param bool $isolate - * @return bool */ - public function chunk($pageSize, Closure $callback, $isCritical = false, $isolate = false) + public function chunk(int $pageSize, Closure $callback, bool $isCritical = false, bool $isolate = false): bool { + $this->limit(0); + $start = microtime(true); $chunk = function (Builder $query) use ($pageSize, $callback, $isCritical) { @@ -537,40 +444,40 @@ class Builder } }; - $isolate ? $this->connection->isolate(function (Connection $replicate) use ($chunk) { - $chunk($this->clone()->setConnection($replicate)); - }) : $chunk($this); + // Connection isolation creates a new, temporary connection for the pagination + // request to occur on. This allows connections that do not support executing + // other queries during a pagination request, to do so without interruption. + $isolate ? $this->connection->isolate( + fn (Connection $replicate) => $chunk($this->clone()->setConnection($replicate)) + ) : $chunk($this); - $this->logQuery($this, 'chunk', $this->getElapsedTime($start)); + $this->logQuery($this, self::TYPE_CHUNK, $this->getElapsedTime($start)); return true; } /** * Runs the chunk operation with the given filter. - * - * @param string $filter - * @param int $perPage - * @param bool $isCritical - * @return \Generator */ - protected function runChunk($filter, $perPage, $isCritical) + protected function runChunk(string $filter, int $perPage, bool $isCritical): Generator { - return $this->connection->run(function (LdapInterface $ldap) use ($filter, $perPage, $isCritical) { - return (new LazyPaginator($this, $filter, $perPage, $isCritical))->execute($ldap); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $this->newLazyPaginator($filter, $perPage, $isCritical)->execute($ldap) + ); + } + + /** + * Make a new lazy paginator instance. + */ + protected function newLazyPaginator(string $filter, int $perPage, bool $isCritical): LazyPaginator + { + return new LazyPaginator($this, $filter, $perPage, $isCritical); } /** * Create a slice of the LDAP query into a page. - * - * @param int $page - * @param int $perPage - * @param string $orderBy - * @param string $orderByDir - * @return Slice */ - public function slice($page = 1, $perPage = 100, $orderBy = 'cn', $orderByDir = 'asc') + public function slice(int $page = 1, int $perPage = 100, string $orderBy = 'cn', string $orderByDir = 'asc'): Slice { $results = $this->forPage($page, $perPage, $orderBy, $orderByDir); @@ -589,14 +496,8 @@ class Builder /** * Get the results of a query for a given page. - * - * @param int $page - * @param int $perPage - * @param string $orderBy - * @param string $orderByDir - * @return Collection|array */ - public function forPage($page = 1, $perPage = 100, $orderBy = 'cn', $orderByDir = 'asc') + public function forPage(int $page = 1, int $perPage = 100, string $orderBy = 'cn', string $orderByDir = 'asc'): Collection|array { if (! $this->hasOrderBy()) { $this->orderBy($orderBy, $orderByDir); @@ -614,11 +515,8 @@ class Builder /** * Processes and converts the given LDAP results into models. - * - * @param array $results - * @return array */ - protected function process(array $results) + protected function process(array $results): mixed { unset($results['count']); @@ -631,11 +529,8 @@ class Builder /** * Flattens LDAP paged results into a single array. - * - * @param array $pages - * @return array */ - protected function flattenPages(array $pages) + protected function flattenPages(array $pages): array { $records = []; @@ -650,15 +545,11 @@ class Builder /** * Get the cached response or execute and cache the callback value. - * - * @param string $query - * @param Closure $callback - * @return mixed */ - protected function getCachedResponse($query, Closure $callback) + protected function getCachedResponse(string $query, Closure $callback): mixed { if ($this->cache && $this->caching) { - $key = $this->getCacheKey($query); + $key = $this->cacheKey ?? $this->getCacheKey($query); if ($this->flushCache) { $this->cache->delete($key); @@ -667,16 +558,20 @@ class Builder return $this->cache->remember($key, $this->cacheUntil, $callback); } - return $callback(); + try { + return $callback(); + } finally { + $this->caching = false; + $this->cacheKey = null; + $this->cacheUntil = null; + $this->flushCache = false; + } } /** * Runs the query operation with the given filter. - * - * @param string $filter - * @return resource */ - public function run($filter) + public function run(string $filter): mixed { return $this->connection->run(function (LdapInterface $ldap) use ($filter) { // We will avoid setting the controls during any pagination @@ -690,7 +585,7 @@ class Builder } return $ldap->{$this->type}( - $this->dn ?? $this->baseDn, + (string) ($this->dn ?? $this->baseDn), $filter, $this->getSelects(), $onlyAttributes = false, @@ -701,11 +596,8 @@ class Builder /** * Parses the given LDAP resource by retrieving its entries. - * - * @param resource $resource - * @return array */ - public function parse($resource) + public function parse(mixed $resource): array { if (! $resource) { return []; @@ -714,17 +606,10 @@ class Builder return $this->connection->run(function (LdapInterface $ldap) use ($resource) { $this->controlsResponse = $this->controls; - $errorCode = 0; - $dn = $errorMessage = $refs = null; - // Process the server controls response. $ldap->parseResult( - $resource, - $errorCode, - $dn, - $errorMessage, - $refs, - $this->controlsResponse + result: $resource, + controls: $this->controlsResponse ); $entries = $ldap->getEntries($resource); @@ -739,16 +624,13 @@ class Builder } /** - * Returns the cache key. - * - * @param string $query - * @return string + * Get the cache key. */ - protected function getCacheKey($query) + protected function getCacheKey(string $query): string { - $host = $this->connection->run(function (LdapInterface $ldap) { - return $ldap->getHost(); - }); + $host = $this->connection->run( + fn (LdapInterface $ldap) => $ldap->getHost() + ); $key = $host .$this->type @@ -762,12 +644,9 @@ class Builder } /** - * Returns the first entry in a search result. - * - * @param array|string $columns - * @return Model|null + * Get the first entry in a search result. */ - public function first($columns = ['*']) + public function first(array|string $columns = ['*']): Model|array|null { return Arr::first( $this->limit(1)->get($columns) @@ -775,16 +654,13 @@ class Builder } /** - * Returns the first entry in a search result. + * Get the first entry in a search result. * * If no entry is found, an exception is thrown. * - * @param array|string $columns - * @return Model|array - * * @throws ObjectNotFoundException */ - public function firstOrFail($columns = ['*']) + public function firstOrFail(array|string $columns = ['*']): Model|array { if (! $record = $this->first($columns)) { $this->throwNotFoundException($this->getUnescapedQuery(), $this->dn); @@ -794,12 +670,9 @@ class Builder } /** - * Return the first entry in a result, or execute the callback. - * - * @param Closure $callback - * @return Model|mixed + * Get the first entry in a result, or execute the callback. */ - public function firstOr(Closure $callback) + public function firstOr(Closure $callback): mixed { return $this->first() ?: $callback(); } @@ -807,13 +680,10 @@ class Builder /** * Execute the query and get the first result if it's the sole matching record. * - * @param array|string $columns - * @return Model|array - * * @throws ObjectsNotFoundException * @throws MultipleObjectsFoundException */ - public function sole($columns = ['*']) + public function sole(array|string $columns = ['*']): Model|array { $result = $this->limit(2)->get($columns); @@ -830,31 +700,24 @@ class Builder /** * Determine if any results exist for the current query. - * - * @return bool */ - public function exists() + public function exists(): bool { return ! is_null($this->first()); } /** * Determine if no results exist for the current query. - * - * @return bool */ - public function doesntExist() + public function doesntExist(): bool { return ! $this->exists(); } /** * Execute the given callback if no rows exist for the current query. - * - * @param Closure $callback - * @return bool|mixed */ - public function existsOr(Closure $callback) + public function existsOr(Closure $callback): mixed { return $this->exists() ? true : $callback(); } @@ -862,12 +725,9 @@ class Builder /** * Throws a not found exception. * - * @param string $query - * @param string $dn - * * @throws ObjectNotFoundException */ - protected function throwNotFoundException($query, $dn) + protected function throwNotFoundException(string $query, ?string $dn = null): void { throw ObjectNotFoundException::forQuery($query, $dn); } @@ -875,17 +735,14 @@ class Builder /** * Finds a record by the specified attribute and value. * - * @param string $attribute - * @param string $value - * @param array|string $columns * @return Model|static|null */ - public function findBy($attribute, $value, $columns = ['*']) + public function findBy(string $attribute, string $value, array|string $columns = ['*']): Model|array|null { try { return $this->findByOrFail($attribute, $value, $columns); } catch (ObjectNotFoundException $e) { - return; + return null; } } @@ -894,26 +751,17 @@ class Builder * * If no record is found an exception is thrown. * - * @param string $attribute - * @param string $value - * @param array|string $columns - * @return Model - * * @throws ObjectNotFoundException */ - public function findByOrFail($attribute, $value, $columns = ['*']) + public function findByOrFail(string $attribute, string $value, array|string $columns = ['*']): Model|array { return $this->whereEquals($attribute, $value)->firstOrFail($columns); } /** * Find many records by distinguished name. - * - * @param string|array $dns - * @param array $columns - * @return array|Collection */ - public function findMany($dns, $columns = ['*']) + public function findMany(array|string $dns, array|string $columns = ['*']): Collection|array { if (empty($dns)) { return $this->process([]); @@ -932,13 +780,8 @@ class Builder /** * Finds many records by the specified attribute. - * - * @param string $attribute - * @param array $values - * @param array $columns - * @return Collection */ - public function findManyBy($attribute, array $values = [], $columns = ['*']) + public function findManyBy(string $attribute, array $values = [], array|string $columns = ['*']): Collection|array { $query = $this->select($columns); @@ -951,12 +794,8 @@ class Builder /** * Finds a record by its distinguished name. - * - * @param string|array $dn - * @param array|string $columns - * @return Model|static|array|Collection|null */ - public function find($dn, $columns = ['*']) + public function find(array|string $dn, array|string $columns = ['*']): Collection|Model|array|null { if (is_array($dn)) { return $this->findMany($dn, $columns); @@ -965,7 +804,7 @@ class Builder try { return $this->findOrFail($dn, $columns); } catch (ObjectNotFoundException $e) { - return; + return null; } } @@ -974,13 +813,9 @@ class Builder * * Fails upon no records returned. * - * @param string $dn - * @param array|string $columns - * @return Model|static - * * @throws ObjectNotFoundException */ - public function findOrFail($dn, $columns = ['*']) + public function findOrFail(string $dn, array|string $columns = ['*']): Model|array { return $this->setDn($dn) ->read() @@ -990,11 +825,8 @@ class Builder /** * Adds the inserted fields to query on the current LDAP connection. - * - * @param array|string $columns - * @return $this */ - public function select($columns = ['*']) + public function select(array|string $columns = ['*']): static { $columns = is_array($columns) ? $columns : func_get_args(); @@ -1007,11 +839,8 @@ class Builder /** * Add a new select column to the query. - * - * @param array|mixed $column - * @return $this */ - public function addSelect($column) + public function addSelect(array|string $column): static { $column = is_array($column) ? $column : func_get_args(); @@ -1022,46 +851,38 @@ class Builder /** * Add an order by control to the query. - * - * @param string $attribute - * @param string $direction - * @return $this */ - public function orderBy($attribute, $direction = 'asc') + public function orderBy(string $attribute, string $direction = 'asc', array $options = []): static { return $this->addControl(LDAP_CONTROL_SORTREQUEST, true, [ - ['attr' => $attribute, 'reverse' => $direction === 'desc'], + [ + ...$options, + 'attr' => $attribute, + 'reverse' => $direction === 'desc', + ], ]); } /** * Add an order by descending control to the query. - * - * @param string $attribute - * @return $this */ - public function orderByDesc($attribute) + public function orderByDesc(string $attribute, array $options = []): static { - return $this->orderBy($attribute, 'desc'); + return $this->orderBy($attribute, 'desc', $options); } /** * Determine if the query has a sotr request control header. - * - * @return bool */ - public function hasOrderBy() + public function hasOrderBy(): bool { return $this->hasControl(LDAP_CONTROL_SORTREQUEST); } /** * Adds a raw filter to the current query. - * - * @param array|string $filters - * @return $this */ - public function rawFilter($filters = []) + public function rawFilter(array|string $filters = []): static { $filters = is_array($filters) ? $filters : func_get_args(); @@ -1074,11 +895,8 @@ class Builder /** * Adds a nested 'and' filter to the current query. - * - * @param Closure $closure - * @return $this */ - public function andFilter(Closure $closure) + public function andFilter(Closure $closure): static { $query = $this->newNestedInstance($closure); @@ -1089,11 +907,8 @@ class Builder /** * Adds a nested 'or' filter to the current query. - * - * @param Closure $closure - * @return $this */ - public function orFilter(Closure $closure) + public function orFilter(Closure $closure): static { $query = $this->newNestedInstance($closure); @@ -1104,11 +919,8 @@ class Builder /** * Adds a nested 'not' filter to the current query. - * - * @param Closure $closure - * @return $this */ - public function notFilter(Closure $closure) + public function notFilter(Closure $closure): static { $query = $this->newNestedInstance($closure); @@ -1120,30 +932,23 @@ class Builder /** * Adds a where clause to the current query. * - * @param string|array $field - * @param string $operator - * @param string $value - * @param string $boolean - * @param bool $raw - * @return $this - * * @throws InvalidArgumentException */ - public function where($field, $operator = null, $value = null, $boolean = 'and', $raw = false) + public function where(array|string $field, mixed $operator = null, mixed $value = null, string $boolean = 'and', bool $raw = false): static { if (is_array($field)) { // If the field is an array, we will assume we have been // provided with an array of key-value pairs and can - // add them each as their own seperate where clause. + // add them each as their own separate where clause. return $this->addArrayOfWheres($field, $boolean, $raw); } // If we have been provided with two arguments not a "has" or // "not has" operator, we'll assume the developer is creating // an "equals" clause and set the proper operator in place. - if (func_num_args() === 2 && ! in_array($operator, ['*', '!*'])) { - [$value, $operator] = [$operator, '=']; - } + [$value, $operator] = $this->prepareValueAndOperator( + $value, $operator, func_num_args() === 2 && ! $this->operatorRequiresValue($operator) + ); if (! in_array($operator, $this->grammar->getOperators())) { throw new InvalidArgumentException("Invalid LDAP filter operator [$operator]"); @@ -1152,7 +957,7 @@ class Builder // We'll escape the value if raw isn't requested. $value = $this->prepareWhereValue($field, $value, $raw); - $field = $this->escape($field)->both()->get(); + $field = $this->escape($field)->forDnAndFilter()->get(); $this->addFilter($boolean, compact('field', 'operator', 'value')); @@ -1160,125 +965,114 @@ class Builder } /** - * Prepare the value for being queried. - * - * @param string $field - * @param string $value - * @param bool $raw - * @return string + * Prepare the value and operator for a where clause. */ - protected function prepareWhereValue($field, $value, $raw = false) + public function prepareValueAndOperator(mixed $value, mixed $operator, bool $useDefault = false): array { - return $raw ? $value : $this->escape($value); + if ($useDefault) { + return [$operator, '=']; + } + + return [$value, $operator]; + } + + /** + * Determine if the operator requires a value to be present. + */ + protected function operatorRequiresValue(mixed $operator): bool + { + return in_array($operator, ['*', '!*']); + } + + /** + * Prepare the value for being queried. + */ + protected function prepareWhereValue(string $field, mixed $value = null, bool $raw = false): string + { + return $raw ? $value : $this->escape($value)->get(); } /** * Adds a raw where clause to the current query. * * Values given to this method are not escaped. - * - * @param string|array $field - * @param string $operator - * @param string $value - * @return $this */ - public function whereRaw($field, $operator = null, $value = null) + public function whereRaw(array|string $field, ?string $operator = null, mixed $value = null): static { return $this->where($field, $operator, $value, 'and', true); } /** * Adds a 'where equals' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereEquals($field, $value) + public function whereEquals(string $field, string $value): static { return $this->where($field, '=', $value); } /** * Adds a 'where not equals' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereNotEquals($field, $value) + public function whereNotEquals(string $field, string $value): static { return $this->where($field, '!', $value); } /** * Adds a 'where approximately equals' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereApproximatelyEquals($field, $value) + public function whereApproximatelyEquals(string $field, string $value): static { return $this->where($field, '~=', $value); } /** * Adds a 'where has' clause to the current query. - * - * @param string $field - * @return $this */ - public function whereHas($field) + public function whereHas(string $field): static { return $this->where($field, '*'); } /** * Adds a 'where not has' clause to the current query. - * - * @param string $field - * @return $this */ - public function whereNotHas($field) + public function whereNotHas(string $field): static { return $this->where($field, '!*'); } /** * Adds a 'where contains' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereContains($field, $value) + public function whereContains(string $field, string $value): static { return $this->where($field, 'contains', $value); } /** * Adds a 'where contains' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereNotContains($field, $value) + public function whereNotContains(string $field, string $value): static { return $this->where($field, 'not_contains', $value); } /** * Query for entries that match any of the values provided for the given field. - * - * @param string $field - * @param array $values - * @return $this */ - public function whereIn($field, array $values) + public function whereIn(string $field, array $values): static { - return $this->orFilter(function (self $query) use ($field, $values) { + if (empty($values)) { + // If the array of values is empty, we will + // add an empty OR filter to the query to + // ensure that no results are returned. + $this->rawFilter('(|)'); + + return $this; + } + + return $this->orFilter(function (Builder $query) use ($field, $values) { foreach ($values as $value) { $query->whereEquals($field, $value); } @@ -1287,12 +1081,8 @@ class Builder /** * Adds a 'between' clause to the current query. - * - * @param string $field - * @param array $values - * @return $this */ - public function whereBetween($field, array $values) + public function whereBetween(string $field, array $values): static { return $this->where([ [$field, '>=', $values[0]], @@ -1302,81 +1092,56 @@ class Builder /** * Adds a 'where starts with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereStartsWith($field, $value) + public function whereStartsWith(string $field, string $value): static { return $this->where($field, 'starts_with', $value); } /** * Adds a 'where *not* starts with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereNotStartsWith($field, $value) + public function whereNotStartsWith(string $field, string $value): static { return $this->where($field, 'not_starts_with', $value); } /** * Adds a 'where ends with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereEndsWith($field, $value) + public function whereEndsWith(string $field, string $value): static { return $this->where($field, 'ends_with', $value); } /** * Adds a 'where *not* ends with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function whereNotEndsWith($field, $value) + public function whereNotEndsWith(string $field, string $value): static { return $this->where($field, 'not_ends_with', $value); } /** * Only include deleted models in the results. - * - * @return $this */ - public function whereDeleted() + public function whereDeleted(): static { return $this->withDeleted()->whereEquals('isDeleted', 'TRUE'); } /** * Set the LDAP control option to include deleted LDAP models. - * - * @return $this */ - public function withDeleted() + public function withDeleted(): static { return $this->addControl(LdapInterface::OID_SERVER_SHOW_DELETED, $isCritical = true); } /** * Add a server control to the query. - * - * @param string $oid - * @param bool $isCritical - * @param mixed $value - * @return $this */ - public function addControl($oid, $isCritical = false, $value = null) + public function addControl(string $oid, bool $isCritical = false, mixed $value = null): static { $this->controls[$oid] = compact('oid', 'isCritical', 'value'); @@ -1385,25 +1150,21 @@ class Builder /** * Determine if the server control exists on the query. - * - * @param string $oid - * @return bool */ - public function hasControl($oid) + public function hasControl(string $oid): bool { return array_key_exists($oid, $this->controls); } /** * Adds an 'or where' clause to the current query. - * - * @param array|string $field - * @param string|null $operator - * @param string|null $value - * @return $this */ - public function orWhere($field, $operator = null, $value = null) + public function orWhere(array|string $field, ?string $operator = null, ?string $value = null): static { + [$value, $operator] = $this->prepareValueAndOperator( + $value, $operator, func_num_args() === 2 && ! $this->operatorRequiresValue($operator) + ); + return $this->where($field, $operator, $value, 'or'); } @@ -1411,143 +1172,96 @@ class Builder * Adds a raw or where clause to the current query. * * Values given to this method are not escaped. - * - * @param string $field - * @param string $operator - * @param string $value - * @return $this */ - public function orWhereRaw($field, $operator = null, $value = null) + public function orWhereRaw(array|string $field, ?string $operator = null, ?string $value = null): static { return $this->where($field, $operator, $value, 'or', true); } /** * Adds an 'or where has' clause to the current query. - * - * @param string $field - * @return $this */ - public function orWhereHas($field) + public function orWhereHas(string $field): static { return $this->orWhere($field, '*'); } /** * Adds a 'where not has' clause to the current query. - * - * @param string $field - * @return $this */ - public function orWhereNotHas($field) + public function orWhereNotHas(string $field): static { return $this->orWhere($field, '!*'); } /** * Adds an 'or where equals' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereEquals($field, $value) + public function orWhereEquals(string $field, string $value): static { return $this->orWhere($field, '=', $value); } /** * Adds an 'or where not equals' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereNotEquals($field, $value) + public function orWhereNotEquals(string $field, string $value): static { return $this->orWhere($field, '!', $value); } /** * Adds a 'or where approximately equals' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereApproximatelyEquals($field, $value) + public function orWhereApproximatelyEquals(string $field, string $value): static { return $this->orWhere($field, '~=', $value); } /** * Adds an 'or where contains' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereContains($field, $value) + public function orWhereContains(string $field, string $value): static { return $this->orWhere($field, 'contains', $value); } /** * Adds an 'or where *not* contains' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereNotContains($field, $value) + public function orWhereNotContains(string $field, string $value): static { return $this->orWhere($field, 'not_contains', $value); } /** * Adds an 'or where starts with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereStartsWith($field, $value) + public function orWhereStartsWith(string $field, string $value): static { return $this->orWhere($field, 'starts_with', $value); } /** * Adds an 'or where *not* starts with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereNotStartsWith($field, $value) + public function orWhereNotStartsWith(string $field, string $value): static { return $this->orWhere($field, 'not_starts_with', $value); } /** * Adds an 'or where ends with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereEndsWith($field, $value) + public function orWhereEndsWith(string $field, string $value): static { return $this->orWhere($field, 'ends_with', $value); } /** * Adds an 'or where *not* ends with' clause to the current query. - * - * @param string $field - * @param string $value - * @return $this */ - public function orWhereNotEndsWith($field, $value) + public function orWhereNotEndsWith(string $field, string $value): static { return $this->orWhere($field, 'not_ends_with', $value); } @@ -1555,13 +1269,9 @@ class Builder /** * Adds a filter binding onto the current query. * - * @param string $type The type of filter to add. - * @param array $bindings The bindings of the filter. - * @return $this - * * @throws InvalidArgumentException */ - public function addFilter($type, array $bindings) + public function addFilter(string $type, array $bindings): static { if (! array_key_exists($type, $this->filters)) { throw new InvalidArgumentException("Filter type: [$type] is invalid."); @@ -1583,11 +1293,8 @@ class Builder /** * Extract any missing required binding keys. - * - * @param array $bindings - * @return array */ - protected function missingBindingKeys($bindings) + protected function missingBindingKeys(array $bindings): array { $required = array_flip(['field', 'operator', 'value']); @@ -1598,20 +1305,16 @@ class Builder /** * Get all the filters on the query. - * - * @return array */ - public function getFilters() + public function getFilters(): array { return $this->filters; } /** * Clear the query filters. - * - * @return $this */ - public function clearFilters() + public function clearFilters(): static { foreach (array_keys($this->filters) as $type) { $this->filters[$type] = []; @@ -1622,20 +1325,16 @@ class Builder /** * Determine if the query has attributes selected. - * - * @return bool */ - public function hasSelects() + public function hasSelects(): bool { - return count($this->columns) > 0; + return count($this->columns ?? []) > 0; } /** * Get the attributes to select on the search. - * - * @return array */ - public function getSelects() + public function getSelects(): array { $selects = $this->columns ?? ['*']; @@ -1659,49 +1358,48 @@ class Builder * Set the query to search on the base distinguished name. * * This will result in one record being returned. - * - * @return $this */ - public function read() + public function read(): static { - $this->type = 'read'; + $this->type = self::TYPE_READ; return $this; } /** * Set the query to search one level on the base distinguished name. - * - * @return $this */ - public function listing() + public function list(): static { - $this->type = 'listing'; + $this->type = self::TYPE_LIST; return $this; } /** - * Set the query to search the entire directory on the base distinguished name. - * - * @return $this + * Alias for the "search" method. */ - public function recursive() + public function recursive(): static { - $this->type = 'search'; + return $this->search(); + } + + /** + * Set the query to search the entire directory on the base distinguished name. + */ + public function search(): static + { + $this->type = self::TYPE_SEARCH; return $this; } /** * Whether to mark the current query as nested. - * - * @param bool $nested - * @return $this */ - public function nested($nested = true) + public function nested(bool $nested = true): static { - $this->nested = (bool) $nested; + $this->nested = $nested; return $this; } @@ -1710,14 +1408,11 @@ class Builder * Enables caching on the current query until the given date. * * If flushing is enabled, the query cache will be flushed and then re-cached. - * - * @param DateTimeInterface $until When to expire the query cache. - * @param bool $flush Whether to force-flush the query cache. - * @return $this */ - public function cache(DateTimeInterface $until = null, $flush = false) + public function cache(?DateTimeInterface $until = null, bool $flush = false, ?string $key = null): static { $this->caching = true; + $this->cacheKey = $key; $this->cacheUntil = $until; $this->flushCache = $flush; @@ -1726,20 +1421,16 @@ class Builder /** * Determine if the query is nested. - * - * @return bool */ - public function isNested() + public function isNested(): bool { return $this->nested === true; } /** * Determine whether the query is paginated. - * - * @return bool */ - public function isPaginated() + public function isPaginated(): bool { return $this->paginated; } @@ -1747,14 +1438,22 @@ class Builder /** * Insert an entry into the directory. * - * @param string $dn - * @param array $attributes - * @return bool + * @throws LdapRecordException + */ + public function insert(string $dn, array $attributes): bool + { + return (bool) $this->insertAndGetDn($dn, $attributes); + } + + /** + * Insert an entry into the directory and get the inserted distinguished name. * * @throws LdapRecordException */ - public function insert($dn, array $attributes) + public function insertAndGetDn(string $dn, array $attributes): string|false { + $dn = $this->substituteBaseDn($dn); + if (empty($dn)) { throw new LdapRecordException('A new LDAP object must have a distinguished name (dn).'); } @@ -1765,102 +1464,85 @@ class Builder ); } - return $this->connection->run(function (LdapInterface $ldap) use ($dn, $attributes) { - return $ldap->add($dn, $attributes); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->add($dn, $attributes) + ) ? $dn : false; } /** - * Create attributes on the entry in the directory. - * - * @param string $dn - * @param array $attributes - * @return bool + * Add attributes to an entry in the directory. */ - public function insertAttributes($dn, array $attributes) + public function add(string $dn, array $attributes): bool { - return $this->connection->run(function (LdapInterface $ldap) use ($dn, $attributes) { - return $ldap->modAdd($dn, $attributes); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->modAdd($dn, $attributes) + ); } /** * Update the entry with the given modifications. - * - * @param string $dn - * @param array $modifications - * @return bool */ - public function update($dn, array $modifications) + public function update(string $dn, array $modifications): bool { - return $this->connection->run(function (LdapInterface $ldap) use ($dn, $modifications) { - return $ldap->modifyBatch($dn, $modifications); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->modifyBatch($dn, $modifications) + ); } /** - * Update an entries attribute in the directory. - * - * @param string $dn - * @param array $attributes - * @return bool + * Replace an entry's attributes in the directory. */ - public function updateAttributes($dn, array $attributes) + public function replace(string $dn, array $attributes): bool { - return $this->connection->run(function (LdapInterface $ldap) use ($dn, $attributes) { - return $ldap->modReplace($dn, $attributes); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->modReplace($dn, $attributes) + ); } /** * Delete an entry from the directory. - * - * @param string $dn - * @return bool */ - public function delete($dn) + public function delete(string $dn): bool { - return $this->connection->run(function (LdapInterface $ldap) use ($dn) { - return $ldap->delete($dn); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->delete($dn) + ); } /** - * Delete attributes on the entry in the directory. - * - * @param string $dn - * @param array $attributes - * @return bool + * Remove attributes on the entry in the directory. */ - public function deleteAttributes($dn, array $attributes) + public function remove(string $dn, array $attributes): bool { - return $this->connection->run(function (LdapInterface $ldap) use ($dn, $attributes) { - return $ldap->modDelete($dn, $attributes); - }); + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->modDelete($dn, $attributes) + ); } /** * Rename an entry in the directory. - * - * @param string $dn - * @param string $rdn - * @param string $newParentDn - * @param bool $deleteOldRdn - * @return bool */ - public function rename($dn, $rdn, $newParentDn, $deleteOldRdn = true) + public function rename(string $dn, string $rdn, string $newParentDn, bool $deleteOldRdn = true): bool { - return $this->connection->run(function (LdapInterface $ldap) use ($dn, $rdn, $newParentDn, $deleteOldRdn) { - return $ldap->rename($dn, $rdn, $newParentDn, $deleteOldRdn); - }); + return (bool) $this->renameAndGetDn($dn, $rdn, $newParentDn, $deleteOldRdn); + } + + /** + * Rename an entry in the directory and get the new distinguished name. + */ + public function renameAndGetDn(string $dn, string $rdn, string $newParentDn, bool $deleteOldRdn = true): string|false + { + $newParentDn = $this->substituteBaseDn($newParentDn); + + return $this->connection->run( + fn (LdapInterface $ldap) => $ldap->rename($dn, $rdn, $newParentDn, $deleteOldRdn) + ) ? implode(',', [$rdn, $newParentDn]) : false; } /** * Clone the query. - * - * @return static */ - public function clone() + public function clone(): static { return clone $this; } @@ -1868,18 +1550,14 @@ class Builder /** * Handle dynamic method calls on the query builder. * - * @param string $method - * @param array $parameters - * @return mixed - * * @throws BadMethodCallException */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters): static { // If the beginning of the method being called contains // 'where', we will assume a dynamic 'where' clause is // being performed and pass the parameters to it. - if (substr($method, 0, 5) === 'where') { + if (str_starts_with($method, 'where')) { return $this->dynamicWhere($method, $parameters); } @@ -1893,11 +1571,9 @@ class Builder /** * Handles dynamic "where" clauses to the query. * - * @param string $method - * @param array $parameters * @return $this */ - public function dynamicWhere($method, $parameters) + public function dynamicWhere(string $method, array $parameters): static { $finder = substr($method, 5); @@ -1933,13 +1609,8 @@ class Builder /** * Adds an array of wheres to the current query. - * - * @param array $wheres - * @param string $boolean - * @param bool $raw - * @return $this */ - protected function addArrayOfWheres($wheres, $boolean, $raw) + protected function addArrayOfWheres(array $wheres, string $boolean, bool $raw): static { foreach ($wheres as $key => $value) { if (is_numeric($key) && is_array($value)) { @@ -1964,14 +1635,8 @@ class Builder /** * Add a single dynamic where clause statement to the query. - * - * @param string $segment - * @param string $connector - * @param array $parameters - * @param int $index - * @return void */ - protected function addDynamic($segment, $connector, $parameters, $index) + protected function addDynamic(string $segment, string $connector, array $parameters, int $index): void { // If no parameters were given to the dynamic where clause, // we can assume a "has" attribute filter is being added. @@ -1984,55 +1649,34 @@ class Builder /** * Logs the given executed query information by firing its query event. - * - * @param Builder $query - * @param string $type - * @param null|float $time - * @return void */ - protected function logQuery($query, $type, $time = null) + protected function logQuery(Builder $query, string $type, ?float $time = null): void { $args = [$query, $time]; - switch ($type) { - case 'listing': - $event = new Events\Listing(...$args); - break; - case 'read': - $event = new Events\Read(...$args); - break; - case 'chunk': - $event = new Events\Chunk(...$args); - break; - case 'paginate': - $event = new Events\Paginate(...$args); - break; - default: - $event = new Events\Search(...$args); - break; - } - - $this->fireQueryEvent($event); + $this->fireQueryEvent( + match ($type) { + self::TYPE_READ => new Events\Read(...$args), + self::TYPE_CHUNK => new Events\Chunk(...$args), + self::TYPE_LIST => new Events\Listing(...$args), + self::TYPE_PAGINATE => new Events\Paginate(...$args), + default => new Events\Search(...$args), + } + ); } /** * Fires the given query event. - * - * @param QueryExecuted $event - * @return void */ - protected function fireQueryEvent(QueryExecuted $event) + protected function fireQueryEvent(QueryExecuted $event): void { - Container::getInstance()->getEventDispatcher()->fire($event); + Container::getInstance()->getDispatcher()->fire($event); } /** * Get the elapsed time since a given starting point. - * - * @param int $start - * @return float */ - protected function getElapsedTime($start) + protected function getElapsedTime(float $start): float { return round((microtime(true) - $start) * 1000, 2); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Cache.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Cache.php index d6057bfb3..184455ced 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Cache.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Cache.php @@ -13,15 +13,11 @@ class Cache /** * The cache driver. - * - * @var CacheInterface */ - protected $store; + protected CacheInterface $store; /** * Constructor. - * - * @param CacheInterface $store */ public function __construct(CacheInterface $store) { @@ -30,47 +26,26 @@ class Cache /** * Get an item from the cache. - * - * @param string $key - * @return mixed */ - public function get($key) + public function get(string $key): mixed { return $this->store->get($key); } /** * Store an item in the cache. - * - * @param string $key - * @param mixed $value - * @param DateTimeInterface|DateInterval|int|null $ttl - * @return bool */ - public function put($key, $value, $ttl = null) + public function put(string $key, mixed $value, DateTimeInterface|DateInterval|int|null $ttl = null): bool { - $seconds = $this->secondsUntil($ttl); - - if ($seconds <= 0) { - return $this->delete($key); - } - - return $this->store->set($key, $value, $seconds); + return $this->store->set($key, $value, $this->secondsUntil($ttl)); } /** * Get an item from the cache, or execute the given Closure and store the result. - * - * @param string $key - * @param DateTimeInterface|DateInterval|int|null $ttl - * @param Closure $callback - * @return mixed */ - public function remember($key, $ttl, Closure $callback) + public function remember(string $key, DateTimeInterface|DateInterval|int|null $ttl, Closure $callback): mixed { - $value = $this->get($key); - - if (! is_null($value)) { + if (! is_null($value = $this->get($key))) { return $value; } @@ -81,21 +56,16 @@ class Cache /** * Delete an item from the cache. - * - * @param string $key - * @return bool */ - public function delete($key) + public function delete(string $key): bool { return $this->store->delete($key); } /** * Get the underlying cache store. - * - * @return CacheInterface */ - public function store() + public function store(): CacheInterface { return $this->store; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Collection.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Collection.php index 036affa22..9cda10cdb 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Collection.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Collection.php @@ -2,25 +2,23 @@ namespace LdapRecord\Query; +use Illuminate\Support\Collection as BaseCollection; use LdapRecord\Models\Model; -use Tightenco\Collect\Support\Collection as BaseCollection; class Collection extends BaseCollection { /** - * @inheritdoc + * {@inheritdoc} */ - protected function valueRetriever($value) + protected function valueRetriever(mixed $value): callable { if ($this->useAsCallable($value)) { /** @var callable $value */ return $value; } - return function ($item) use ($value) { - return $item instanceof Model + return fn ($item) => $item instanceof Model ? $item->getFirstAttribute($value) : data_get($item, $value); - }; } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Events/QueryExecuted.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Events/QueryExecuted.php index 8ed8f835d..95cadd6ae 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Events/QueryExecuted.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Events/QueryExecuted.php @@ -8,46 +8,35 @@ class QueryExecuted { /** * The LDAP filter that was used for the query. - * - * @var Builder */ - protected $query; + protected Builder $query; /** * The number of milliseconds it took to execute the query. - * - * @var ?float */ - protected $time; + protected ?float $time; /** * Constructor. - * - * @param Builder $query - * @param null|float $time */ - public function __construct(Builder $query, $time = null) + public function __construct(Builder $query, ?float $time = null) { $this->query = $query; $this->time = $time; } /** - * Returns the LDAP filter that was used for the query. - * - * @return Builder + * Get the LDAP filter that was used for the query. */ - public function getQuery() + public function getQuery(): Builder { return $this->query; } /** - * Returns the number of milliseconds it took to execute the query. - * - * @return float|null + * Get the number of milliseconds it took to execute the query. */ - public function getTime() + public function getTime(): ?float { return $this->time; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ConditionNode.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ConditionNode.php index 6203360c5..4dfed722b 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ConditionNode.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ConditionNode.php @@ -8,38 +8,28 @@ class ConditionNode extends Node { /** * The condition's attribute. - * - * @var string */ - protected $attribute; + protected string $attribute; /** * The condition's operator. - * - * @var string */ - protected $operator; + protected string $operator; /** * The condition's value. - * - * @var string */ - protected $value; + protected string $value; /** * The available condition operators. - * - * @var array */ - protected $operators = ['>=', '<=', '~=', '=']; + protected array $operators = ['>=', '<=', '~=', '=']; /** * Constructor. - * - * @param string $filter */ - public function __construct($filter) + public function __construct(string $filter) { $this->raw = $filter; @@ -48,51 +38,38 @@ class ConditionNode extends Node /** * Get the condition's attribute. - * - * @return string */ - public function getAttribute() + public function getAttribute(): string { return $this->attribute; } /** * Get the condition's operator. - * - * @return string */ - public function getOperator() + public function getOperator(): string { return $this->operator; } /** * Get the condition's value. - * - * @return string */ - public function getValue() + public function getValue(): string { return $this->value; } /** * Extract the condition components from the filter. - * - * @param string $filter - * @return array */ - protected function extractComponents($filter) + protected function extractComponents(string $filter): array { $components = Str::whenContains( $filter, $this->operators, - function ($operator, $filter) { - return explode($this->operator = $operator, $filter, 2); - }, - function ($filter) { - throw new ParserException("Invalid query condition. No operator found in [$filter]"); - }, + fn ($operator, $filter) => explode($this->operator = $operator, $filter, 2), + fn ($filter) => throw new ParserException("Invalid query condition. No operator found in [$filter]"), ); return $components; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/GroupNode.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/GroupNode.php index 1a3596f3e..1af71369e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/GroupNode.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/GroupNode.php @@ -6,24 +6,20 @@ class GroupNode extends Node { /** * The group's operator. - * - * @var string */ - protected $operator; + protected string $operator; /** * The group's sub-nodes. * * @var Node[] */ - protected $nodes = []; + protected array $nodes = []; /** * Constructor. - * - * @param string $filter */ - public function __construct($filter) + public function __construct(string $filter) { $this->raw = $filter; @@ -34,10 +30,8 @@ class GroupNode extends Node /** * Get the group's operator. - * - * @return string */ - public function getOperator() + public function getOperator(): string { return $this->operator; } @@ -47,7 +41,7 @@ class GroupNode extends Node * * @return Node[] */ - public function getNodes() + public function getNodes(): array { return $this->nodes; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Node.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Node.php index faf88d41f..3e9c039f1 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Node.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Node.php @@ -6,24 +6,18 @@ abstract class Node { /** * The raw value of the node. - * - * @var string */ - protected $raw; + protected string $raw; /** * Create a new filter node. - * - * @param string $filter */ - abstract public function __construct($filter); + abstract public function __construct(string $filter); /** * Get the raw value of the node. - * - * @return string */ - public function getRaw() + public function getRaw(): string { return $this->raw; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Parser.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Parser.php index 7717ac522..1fea1810d 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Parser.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/Parser.php @@ -10,12 +10,11 @@ class Parser /** * Parse an LDAP filter into nodes. * - * @param string $string * @return (ConditionNode|GroupNode)[] * * @throws ParserException */ - public static function parse($string) + public static function parse(string $string): array { [$open, $close] = static::countParenthesis($string); @@ -34,11 +33,8 @@ class Parser /** * Perform a match for all filters in the string. - * - * @param string $string - * @return array */ - protected static function match($string) + protected static function match(string $string): array { preg_match_all("/\((((?>[^()]+)|(?R))*)\)/", trim($string), $matches); @@ -49,22 +45,18 @@ class Parser * Assemble the parsed nodes into a single filter. * * @param Node|Node[] $nodes - * @return string */ - public static function assemble($nodes = []) + public static function assemble(Node|array $nodes = []): string { - return array_reduce(Arr::wrap($nodes), function ($carry, Node $node) { - return $carry .= static::compileNode($node); - }); + return array_reduce(Arr::wrap($nodes), fn ($carry, Node $node) => ( + $carry .= static::compileNode($node) + )); } /** * Assemble the node into its string based format. - * - * @param GroupNode|ConditionNode $node - * @return string */ - protected static function compileNode(Node $node) + protected static function compileNode(Node $node): string { switch (true) { case $node instanceof GroupNode: @@ -84,7 +76,7 @@ class Parser * * @throws ParserException */ - protected static function buildNodes(array $filters = []) + protected static function buildNodes(array $filters = []): array { return array_map(function ($filter) { if (static::isWrapped($filter)) { @@ -103,33 +95,24 @@ class Parser /** * Count the open and close parenthesis of the sting. - * - * @param string $string - * @return array */ - protected static function countParenthesis($string) + protected static function countParenthesis(string $string): array { return [Str::substrCount($string, '('), Str::substrCount($string, ')')]; } /** * Wrap the value in parentheses. - * - * @param string $value - * @return string */ - protected static function wrap($value) + protected static function wrap(string $value): string { return "($value)"; } /** * Recursively unwrwap the value from its parentheses. - * - * @param string $value - * @return string */ - protected static function unwrap($value) + protected static function unwrap(string $value): string { $nodes = static::parse($value); @@ -140,22 +123,16 @@ class Parser /** * Determine if the filter is wrapped. - * - * @param string $filter - * @return bool */ - protected static function isWrapped($filter) + protected static function isWrapped(string $filter): bool { return Str::startsWith($filter, '(') && Str::endsWith($filter, ')'); } /** * Determine if the filter is a group. - * - * @param string $filter - * @return bool */ - protected static function isGroup($filter) + protected static function isGroup(string $filter): bool { return Str::startsWith($filter, ['&', '|', '!']); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ParserException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ParserException.php index 68a4ca171..3c3d68e4f 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ParserException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Filter/ParserException.php @@ -4,6 +4,4 @@ namespace LdapRecord\Query\Filter; use LdapRecord\LdapRecordException; -class ParserException extends LdapRecordException -{ -} +class ParserException extends LdapRecordException {} diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Grammar.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Grammar.php index 84c4b8d0c..0506c983e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Grammar.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Grammar.php @@ -8,10 +8,8 @@ class Grammar { /** * The query operators and their method names. - * - * @var array */ - public $operators = [ + public array $operators = [ '*' => 'has', '!*' => 'notHas', '=' => 'equals', @@ -30,17 +28,13 @@ class Grammar /** * The query wrapper. - * - * @var string|null */ - protected $wrapper; + protected ?string $wrapper = null; /** * Get all the available operators. - * - * @return array */ - public function getOperators() + public function getOperators(): array { return array_keys($this->operators); } @@ -49,24 +43,16 @@ class Grammar * Wraps a query string in brackets. * * Produces: (query) - * - * @param string $query - * @param string $prefix - * @param string $suffix - * @return string */ - public function wrap($query, $prefix = '(', $suffix = ')') + public function wrap(string $query, ?string $prefix = '(', ?string $suffix = ')'): string { return $prefix.$query.$suffix; } /** * Compiles the Builder instance into an LDAP query string. - * - * @param Builder $query - * @return string */ - public function compile(Builder $query) + public function compile(Builder $query): string { if ($this->queryMustBeWrapped($query)) { $this->wrapper = 'and'; @@ -76,50 +62,37 @@ class Grammar .$this->compileWheres($query) .$this->compileOrWheres($query); - switch ($this->wrapper) { - case 'and': - return $this->compileAnd($filter); - case 'or': - return $this->compileOr($filter); - default: - return $filter; - } + return match ($this->wrapper) { + 'and' => $this->compileAnd($filter), + 'or' => $this->compileOr($filter), + default => $filter, + }; } /** * Determine if the query must be wrapped in an encapsulating statement. - * - * @param Builder $query - * @return bool */ - protected function queryMustBeWrapped(Builder $query) + protected function queryMustBeWrapped(Builder $query): bool { return ! $query->isNested() && $this->hasMultipleFilters($query); } /** - * Assembles all of the "raw" filters on the query. - * - * @param Builder $builder - * @return string + * Assembles all the "raw" filters on the query. */ - protected function compileRaws(Builder $builder) + protected function compileRaws(Builder $builder): string { return $this->concatenate($builder->filters['raw']); } /** * Assembles all where clauses in the current wheres property. - * - * @param Builder $builder - * @param string $type - * @return string */ - protected function compileWheres(Builder $builder, $type = 'and') + protected function compileWheres(Builder $builder, string $type = 'and'): string { $filter = ''; - foreach ($builder->filters[$type] as $where) { + foreach ($builder->filters[$type] ?? [] as $where) { $filter .= $this->compileWhere($where); } @@ -128,11 +101,8 @@ class Grammar /** * Assembles all or where clauses in the current orWheres property. - * - * @param Builder $query - * @return string */ - protected function compileOrWheres(Builder $query) + protected function compileOrWheres(Builder $query): string { $filter = $this->compileWheres($query, 'or'); @@ -154,24 +124,18 @@ class Grammar /** * Determine if the query can be wrapped in a single or statement. - * - * @param Builder $query - * @return bool */ - protected function queryCanBeWrappedInSingleOrStatement(Builder $query) + protected function queryCanBeWrappedInSingleOrStatement(Builder $query): bool { - return $this->has($query, 'or', '>=', 1) && - $this->has($query, 'and', '<=', 1) && - $this->has($query, 'raw', '=', 0); + return $this->has($query, 'or', '>=', 1) + && $this->has($query, 'and', '<=', 1) + && $this->has($query, 'raw', '=', 0); } /** * Concatenates filters into a single string. - * - * @param array $bindings - * @return string */ - public function concatenate(array $bindings = []) + public function concatenate(array $bindings = []): string { // Filter out empty query segments. return implode( @@ -181,69 +145,48 @@ class Grammar /** * Determine if the binding value is not empty. - * - * @param string $value - * @return bool */ - protected function bindingValueIsNotEmpty($value) + protected function bindingValueIsNotEmpty(string $value): bool { return ! empty($value); } /** * Determine if the query is using multiple filters. - * - * @param Builder $query - * @return bool */ - protected function hasMultipleFilters(Builder $query) + protected function hasMultipleFilters(Builder $query): bool { return $this->has($query, ['and', 'or', 'raw'], '>', 1); } /** * Determine if the query contains the given filter statement type. - * - * @param Builder $query - * @param string|array $type - * @param string $operator - * @param int $count - * @return bool */ - protected function has(Builder $query, $type, $operator = '>=', $count = 1) + protected function has(Builder $query, array|string $type, string $operator = '>=', int $count = 1): bool { $types = (array) $type; $filters = 0; foreach ($types as $type) { - $filters += count($query->filters[$type]); + $filters += count($query->filters[$type] ?? []); } - switch ($operator) { - case '>': - return $filters > $count; - case '>=': - return $filters >= $count; - case '<': - return $filters < $count; - case '<=': - return $filters <= $count; - default: - return $filters == $count; - } + return match ($operator) { + '>' => $filters > $count, + '>=' => $filters >= $count, + '<' => $filters < $count, + '<=' => $filters <= $count, + default => $filters == $count, + }; } /** * Returns a query string for equals. * * Produces: (field=value) - * - * @param string $field - * @param string $value - * @return string */ - public function compileEquals($field, $value) + public function compileEquals(string $field, string $value): string { return $this->wrap($field.'='.$value); } @@ -252,12 +195,8 @@ class Grammar * Returns a query string for does not equal. * * Produces: (!(field=value)) - * - * @param string $field - * @param string $value - * @return string */ - public function compileDoesNotEqual($field, $value) + public function compileDoesNotEqual(string $field, string $value): string { return $this->compileNot( $this->compileEquals($field, $value) @@ -268,12 +207,8 @@ class Grammar * Alias for does not equal operator (!=) operator. * * Produces: (!(field=value)) - * - * @param string $field - * @param string $value - * @return string */ - public function compileDoesNotEqualAlias($field, $value) + public function compileDoesNotEqualAlias(string $field, string $value): string { return $this->compileDoesNotEqual($field, $value); } @@ -282,12 +217,8 @@ class Grammar * Returns a query string for greater than or equals. * * Produces: (field>=value) - * - * @param string $field - * @param string $value - * @return string */ - public function compileGreaterThanOrEquals($field, $value) + public function compileGreaterThanOrEquals(string $field, string $value): string { return $this->wrap("$field>=$value"); } @@ -296,12 +227,8 @@ class Grammar * Returns a query string for less than or equals. * * Produces: (field<=value) - * - * @param string $field - * @param string $value - * @return string */ - public function compileLessThanOrEquals($field, $value) + public function compileLessThanOrEquals(string $field, string $value): string { return $this->wrap("$field<=$value"); } @@ -310,12 +237,8 @@ class Grammar * Returns a query string for approximately equals. * * Produces: (field~=value) - * - * @param string $field - * @param string $value - * @return string */ - public function compileApproximatelyEquals($field, $value) + public function compileApproximatelyEquals(string $field, string $value): string { return $this->wrap("$field~=$value"); } @@ -324,12 +247,8 @@ class Grammar * Returns a query string for starts with. * * Produces: (field=value*) - * - * @param string $field - * @param string $value - * @return string */ - public function compileStartsWith($field, $value) + public function compileStartsWith(string $field, string $value): string { return $this->wrap("$field=$value*"); } @@ -338,12 +257,8 @@ class Grammar * Returns a query string for does not start with. * * Produces: (!(field=*value)) - * - * @param string $field - * @param string $value - * @return string */ - public function compileNotStartsWith($field, $value) + public function compileNotStartsWith(string $field, string $value): string { return $this->compileNot( $this->compileStartsWith($field, $value) @@ -354,12 +269,8 @@ class Grammar * Returns a query string for ends with. * * Produces: (field=*value) - * - * @param string $field - * @param string $value - * @return string */ - public function compileEndsWith($field, $value) + public function compileEndsWith(string $field, string $value): string { return $this->wrap("$field=*$value"); } @@ -368,12 +279,8 @@ class Grammar * Returns a query string for does not end with. * * Produces: (!(field=value*)) - * - * @param string $field - * @param string $value - * @return string */ - public function compileNotEndsWith($field, $value) + public function compileNotEndsWith(string $field, string $value): string { return $this->compileNot($this->compileEndsWith($field, $value)); } @@ -382,12 +289,8 @@ class Grammar * Returns a query string for contains. * * Produces: (field=*value*) - * - * @param string $field - * @param string $value - * @return string */ - public function compileContains($field, $value) + public function compileContains(string $field, string $value): string { return $this->wrap("$field=*$value*"); } @@ -396,12 +299,8 @@ class Grammar * Returns a query string for does not contain. * * Produces: (!(field=*value*)) - * - * @param string $field - * @param string $value - * @return string */ - public function compileNotContains($field, $value) + public function compileNotContains(string $field, string $value): string { return $this->compileNot( $this->compileContains($field, $value) @@ -412,11 +311,8 @@ class Grammar * Returns a query string for a where has. * * Produces: (field=*) - * - * @param string $field - * @return string */ - public function compileHas($field) + public function compileHas(string $field): string { return $this->wrap("$field=*"); } @@ -425,11 +321,8 @@ class Grammar * Returns a query string for a where does not have. * * Produces: (!(field=*)) - * - * @param string $field - * @return string */ - public function compileNotHas($field) + public function compileNotHas(string $field): string { return $this->compileNot( $this->compileHas($field) @@ -440,11 +333,8 @@ class Grammar * Wraps the inserted query inside an AND operator. * * Produces: (&query) - * - * @param string $query - * @return string */ - public function compileAnd($query) + public function compileAnd(string $query): string { return $query ? $this->wrap($query, '(&') : ''; } @@ -453,22 +343,16 @@ class Grammar * Wraps the inserted query inside an OR operator. * * Produces: (|query) - * - * @param string $query - * @return string */ - public function compileOr($query) + public function compileOr(string $query): string { return $query ? $this->wrap($query, '(|') : ''; } /** * Wraps the inserted query inside an NOT operator. - * - * @param string $query - * @return string */ - public function compileNot($query) + public function compileNot(string $query): string { return $query ? $this->wrap($query, '(!') : ''; } @@ -476,12 +360,9 @@ class Grammar /** * Assembles a single where query. * - * @param array $where - * @return string - * * @throws UnexpectedValueException */ - protected function compileWhere(array $where) + protected function compileWhere(array $where): string { $method = $this->makeCompileMethod($where['operator']); @@ -491,12 +372,9 @@ class Grammar /** * Make the compile method name for the operator. * - * @param string $operator - * @return string - * * @throws UnexpectedValueException */ - protected function makeCompileMethod($operator) + protected function makeCompileMethod(string $operator): string { if (! $this->operatorExists($operator)) { throw new UnexpectedValueException("Invalid LDAP filter operator ['$operator']"); @@ -507,11 +385,8 @@ class Grammar /** * Determine if the operator exists. - * - * @param string $operator - * @return bool */ - protected function operatorExists($operator) + protected function operatorExists(string $operator): bool { return array_key_exists($operator, $this->operators); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/InteractsWithTime.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/InteractsWithTime.php index e8024c033..111c2c201 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/InteractsWithTime.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/InteractsWithTime.php @@ -15,11 +15,8 @@ trait InteractsWithTime { /** * Get the number of seconds until the given DateTime. - * - * @param DateTimeInterface|DateInterval|int $delay - * @return int */ - protected function secondsUntil($delay) + protected function secondsUntil(DateTimeInterface|DateInterval|int|null $delay = null): int { $delay = $this->parseDateInterval($delay); @@ -30,11 +27,8 @@ trait InteractsWithTime /** * Get the "available at" UNIX timestamp. - * - * @param DateTimeInterface|DateInterval|int $delay - * @return int */ - protected function availableAt($delay = 0) + protected function availableAt(DateTimeInterface|DateInterval|int $delay = 0): int { $delay = $this->parseDateInterval($delay); @@ -45,11 +39,8 @@ trait InteractsWithTime /** * If the given value is an interval, convert it to a DateTime instance. - * - * @param DateTimeInterface|DateInterval|int $delay - * @return DateTimeInterface|int */ - protected function parseDateInterval($delay) + protected function parseDateInterval(DateTimeInterface|DateInterval|int|null $delay = null): DateTimeInterface|int|null { if ($delay instanceof DateInterval) { $delay = Carbon::now()->add($delay); @@ -60,10 +51,8 @@ trait InteractsWithTime /** * Get the current system time as a UNIX timestamp. - * - * @return int */ - protected function currentTime() + protected function currentTime(): int { return Carbon::now()->getTimestamp(); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/ActiveDirectoryBuilder.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/ActiveDirectoryBuilder.php index 3b0e96f79..7fb8ab682 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/ActiveDirectoryBuilder.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/ActiveDirectoryBuilder.php @@ -5,23 +5,20 @@ namespace LdapRecord\Query\Model; use Closure; use LdapRecord\LdapInterface; use LdapRecord\Models\Attributes\AccountControl; +use LdapRecord\Models\Model; use LdapRecord\Models\ModelNotFoundException; class ActiveDirectoryBuilder extends Builder { /** * Finds a record by its Object SID. - * - * @param string $sid - * @param array|string $columns - * @return \LdapRecord\Models\ActiveDirectory\Entry|static|null */ - public function findBySid($sid, $columns = []) + public function findBySid(string $sid, array|string $columns = ['*']): ?Model { try { return $this->findBySidOrFail($sid, $columns); - } catch (ModelNotFoundException $e) { - return; + } catch (ModelNotFoundException) { + return null; } } @@ -30,190 +27,157 @@ class ActiveDirectoryBuilder extends Builder * * Fails upon no records returned. * - * @param string $sid - * @param array|string $columns - * @return \LdapRecord\Models\ActiveDirectory\Entry|static - * * @throws ModelNotFoundException */ - public function findBySidOrFail($sid, $columns = []) + public function findBySidOrFail(string $sid, array $columns = ['*']): Model { return $this->findByOrFail('objectsid', $sid, $columns); } /** * Adds a enabled filter to the current query. - * - * @return $this */ - public function whereEnabled() + public function whereEnabled(): static { - return $this->notFilter(function ($query) { - return $query->whereDisabled(); - }); + return $this->notFilter( + fn (self $query) => $query->whereDisabled() + ); } /** * Adds a disabled filter to the current query. - * - * @return $this */ - public function whereDisabled() + public function whereDisabled(): static { return $this->rawFilter( - (new AccountControl())->accountIsDisabled()->filter() + (new AccountControl)->setAccountIsDisabled()->filter() ); } /** * Adds a 'where member' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function whereMember($dn, $nested = false) + public function whereMember(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->whereEquals($attribute, $dn); - }, 'member', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->whereEquals($attribute, $this->substituteBaseDn($dn)), + 'member', + $nested + ); } /** * Adds an 'or where member' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function orWhereMember($dn, $nested = false) + public function orWhereMember(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->orWhereEquals($attribute, $dn); - }, 'member', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->orWhereEquals($attribute, $this->substituteBaseDn($dn)), + 'member', + $nested + ); } /** * Adds a 'where member of' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function whereMemberOf($dn, $nested = false) + public function whereMemberOf(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->whereEquals($attribute, $dn); - }, 'memberof', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->whereEquals($attribute, $this->substituteBaseDn($dn)), + 'memberof', + $nested + ); } /** * Adds a 'where not member of' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function whereNotMemberof($dn, $nested = false) + public function whereNotMemberof(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->whereNotEquals($attribute, $dn); - }, 'memberof', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->whereNotEquals($attribute, $this->substituteBaseDn($dn)), + 'memberof', + $nested + ); } /** * Adds an 'or where member of' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function orWhereMemberOf($dn, $nested = false) + public function orWhereMemberOf(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->orWhereEquals($attribute, $dn); - }, 'memberof', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->orWhereEquals($attribute, $this->substituteBaseDn($dn)), + 'memberof', + $nested + ); } /** * Adds a 'or where not member of' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function orWhereNotMemberof($dn, $nested = false) + public function orWhereNotMemberof(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->orWhereNotEquals($attribute, $dn); - }, 'memberof', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->orWhereNotEquals($attribute, $this->substituteBaseDn($dn)), + 'memberof', + $nested + ); } /** * Adds a 'where manager' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function whereManager($dn, $nested = false) + public function whereManager(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->whereEquals($attribute, $dn); - }, 'manager', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->whereEquals($attribute, $this->substituteBaseDn($dn)), + 'manager', + $nested + ); } /** * Adds a 'where not manager' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function whereNotManager($dn, $nested = false) + public function whereNotManager(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->whereNotEquals($attribute, $dn); - }, 'manager', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->whereNotEquals($attribute, $this->substituteBaseDn($dn)), + 'manager', + $nested + ); } /** * Adds an 'or where manager' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function orWhereManager($dn, $nested = false) + public function orWhereManager(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->orWhereEquals($attribute, $dn); - }, 'manager', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->orWhereEquals($attribute, $this->substituteBaseDn($dn)), + 'manager', + $nested + ); } /** * Adds an 'or where not manager' filter to the current query. - * - * @param string $dn - * @param bool $nested - * @return $this */ - public function orWhereNotManager($dn, $nested = false) + public function orWhereNotManager(string $dn, bool $nested = false): static { - return $this->nestedMatchQuery(function ($attribute) use ($dn) { - return $this->orWhereNotEquals($attribute, $dn); - }, 'manager', $nested); + return $this->nestedMatchQuery( + fn (string $attribute) => $this->orWhereNotEquals($attribute, $this->substituteBaseDn($dn)), + 'manager', + $nested + ); } /** * Execute the callback with a nested match attribute. - * - * @param Closure $callback - * @param string $attribute - * @param bool $nested - * @return $this */ - protected function nestedMatchQuery(Closure $callback, $attribute, $nested = false) + protected function nestedMatchQuery(Closure $callback, string $attribute, bool $nested = false): static { return $callback( $nested ? $this->makeNestedMatchAttribute($attribute) : $attribute @@ -222,11 +186,8 @@ class ActiveDirectoryBuilder extends Builder /** * Make a "nested match" filter attribute for querying descendants. - * - * @param string $attribute - * @return string */ - protected function makeNestedMatchAttribute($attribute) + protected function makeNestedMatchAttribute(string $attribute): string { return sprintf('%s:%s:', $attribute, LdapInterface::OID_MATCHING_RULE_IN_CHAIN); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/Builder.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/Builder.php index a6886ea96..741646e7e 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/Builder.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/Builder.php @@ -4,51 +4,41 @@ namespace LdapRecord\Query\Model; use Closure; use DateTime; +use LdapRecord\Models\Attributes\Guid; +use LdapRecord\Models\Collection; use LdapRecord\Models\Model; use LdapRecord\Models\ModelNotFoundException; use LdapRecord\Models\Scope; use LdapRecord\Models\Types\ActiveDirectory; use LdapRecord\Query\Builder as BaseBuilder; -use LdapRecord\Utilities; +use UnexpectedValueException; class Builder extends BaseBuilder { /** * The model being queried. - * - * @var Model */ - protected $model; + protected Model $model; /** * The global scopes to be applied. - * - * @var array */ - protected $scopes = []; + protected array $scopes = []; /** * The removed global scopes. - * - * @var array */ - protected $removedScopes = []; + protected array $removedScopes = []; /** * The applied global scopes. - * - * @var array */ - protected $appliedScopes = []; + protected array $appliedScopes = []; /** * Dynamically handle calls into the query instance. - * - * @param string $method - * @param array $parameters - * @return mixed */ - public function __call($method, $parameters) + public function __call(string $method, array $parameters): static { if (method_exists($this->model, $scope = 'scope'.ucfirst($method))) { return $this->callScope([$this->model, $scope], $parameters); @@ -59,12 +49,8 @@ class Builder extends BaseBuilder /** * Apply the given scope on the current builder instance. - * - * @param callable $scope - * @param array $parameters - * @return mixed */ - protected function callScope(callable $scope, $parameters = []) + protected function callScope(callable $scope, array $parameters = []): static { array_unshift($parameters, $this); @@ -73,10 +59,8 @@ class Builder extends BaseBuilder /** * Get the attributes to select on the search. - * - * @return array */ - public function getSelects() + public function getSelects(): array { // Here we will ensure the models GUID attribute is always // selected. In some LDAP directories, the attribute is @@ -88,11 +72,8 @@ class Builder extends BaseBuilder /** * Set the model instance for the model being queried. - * - * @param Model $model - * @return $this */ - public function setModel(Model $model) + public function setModel(Model $model): static { $this->model = $model; @@ -100,48 +81,97 @@ class Builder extends BaseBuilder } /** - * Returns the model being queried for. - * - * @return Model + * Get the model being queried for. */ - public function getModel() + public function getModel(): Model { return $this->model; } /** * Get a new model query builder instance. - * - * @param string|null $baseDn - * @return static */ - public function newInstance($baseDn = null) + public function newInstance(?string $baseDn = null): BaseBuilder { return parent::newInstance($baseDn)->model($this->model); } /** - * Finds a model by its distinguished name. - * - * @param array|string $dn - * @param array|string|string[] $columns - * @return Model|\LdapRecord\Query\Collection|static|null + * {@inheritDoc} */ - public function find($dn, $columns = ['*']) + public function first(array|string $columns = ['*']): ?Model { - return $this->afterScopes(function () use ($dn, $columns) { - return parent::find($dn, $columns); - }); + return parent::first($columns); + } + + /** + * {@inheritDoc} + */ + public function firstOrFail(array|string $columns = ['*']): Model + { + return parent::firstOrFail($columns); + } + + /** + * {@inheritDoc} + */ + public function sole(array|string $columns = ['*']): Model + { + return parent::sole($columns); + } + + /** + * {@inheritDoc} + */ + public function find(array|string $dn, array|string $columns = ['*']): Model|Collection|null + { + return $this->afterScopes(fn () => parent::find($dn, $columns)); + } + + /** + * {@inheritDoc} + */ + public function findOrFail(string $dn, array|string $columns = ['*']): Model + { + return parent::findOrFail($dn, $columns); + } + + /** + * {@inheritDoc} + */ + public function findByOrFail(string $attribute, string $value, array|string $columns = ['*']): Model + { + return parent::findByOrFail($attribute, $value, $columns); + } + + /** + * {@inheritDoc} + */ + public function findBy(string $attribute, string $value, array|string $columns = ['*']): ?Model + { + return parent::findBy($attribute, $value, $columns); + } + + /** + * {@inheritDoc} + */ + public function findMany(array|string $dns, array|string $columns = ['*']): Collection + { + return parent::findMany($dns, $columns); + } + + /** + * {@inheritDoc} + */ + public function findManyBy(string $attribute, array $values = [], array|string $columns = ['*']): Collection + { + return parent::findManyBy($attribute, $values, $columns); } /** * Finds a record using ambiguous name resolution. - * - * @param string|array $value - * @param array|string $columns - * @return Model|\LdapRecord\Query\Collection|static|null */ - public function findByAnr($value, $columns = ['*']) + public function findByAnr(array|string $value, array|string $columns = ['*']): Model|Collection|null { if (is_array($value)) { return $this->findManyByAnr($value, $columns); @@ -159,10 +189,8 @@ class Builder extends BaseBuilder /** * Determine if the current model is compatible with ANR filters. - * - * @return bool */ - protected function modelIsCompatibleWithAnr() + protected function modelIsCompatibleWithAnr(): bool { return $this->model instanceof ActiveDirectory; } @@ -172,13 +200,9 @@ class Builder extends BaseBuilder * * If a record is not found, an exception is thrown. * - * @param string $value - * @param array|string $columns - * @return Model - * * @throws ModelNotFoundException */ - public function findByAnrOrFail($value, $columns = ['*']) + public function findByAnrOrFail(string $value, array|string $columns = ['*']): Model { if (! $entry = $this->findByAnr($value, $columns)) { $this->throwNotFoundException($this->getUnescapedQuery(), $this->dn); @@ -190,24 +214,17 @@ class Builder extends BaseBuilder /** * Throws a not found exception. * - * @param string $query - * @param string $dn - * * @throws ModelNotFoundException */ - protected function throwNotFoundException($query, $dn) + protected function throwNotFoundException(string $query, ?string $dn = null): void { throw ModelNotFoundException::forQuery($query, $dn); } /** * Finds multiple records using ambiguous name resolution. - * - * @param array $values - * @param array $columns - * @return \LdapRecord\Query\Collection */ - public function findManyByAnr(array $values = [], $columns = ['*']) + public function findManyByAnr(array $values = [], array|string $columns = ['*']): Collection { $this->select($columns); @@ -224,13 +241,10 @@ class Builder extends BaseBuilder /** * Creates an ANR equivalent query for LDAP distributions that do not support ANR. - * - * @param string $value - * @return $this */ - protected function prepareAnrEquivalentQuery($value) + protected function prepareAnrEquivalentQuery(string $value): static { - return $this->orFilter(function (self $query) use ($value) { + return $this->orFilter(function (BaseBuilder $query) use ($value) { foreach ($this->model->getAnrAttributes() as $attribute) { $query->whereEquals($attribute, $value); } @@ -239,35 +253,25 @@ class Builder extends BaseBuilder /** * Finds a record by its string GUID. - * - * @param string $guid - * @param array|string $columns - * @return Model|static|null */ - public function findByGuid($guid, $columns = ['*']) + public function findByGuid(string $guid, array|string $columns = ['*']): ?Model { try { return $this->findByGuidOrFail($guid, $columns); } catch (ModelNotFoundException $e) { - return; + return null; } } /** - * Finds a record by its string GUID. - * - * Fails upon no records returned. - * - * @param string $guid - * @param array|string $columns - * @return Model|static + * Finds a record by its string GUID or throw an exception. * * @throws ModelNotFoundException */ - public function findByGuidOrFail($guid, $columns = ['*']) + public function findByGuidOrFail(string $guid, array|string $columns = ['*']): Model { if ($this->model instanceof ActiveDirectory) { - $guid = Utilities::stringGuidToHex($guid); + $guid = (new Guid($guid))->getEncodedHex(); } return $this->whereRaw([ @@ -276,22 +280,17 @@ class Builder extends BaseBuilder } /** - * @inheritdoc + * {@inheritdoc} */ - public function getQuery() + public function getQuery(): string { - return $this->afterScopes(function () { - return parent::getQuery(); - }); + return $this->afterScopes(fn () => parent::getQuery()); } /** * Apply the query scopes and execute the callback. - * - * @param Closure $callback - * @return mixed */ - protected function afterScopes(Closure $callback) + protected function afterScopes(Closure $callback): mixed { $this->applyScopes(); @@ -300,10 +299,8 @@ class Builder extends BaseBuilder /** * Apply the global query scopes. - * - * @return $this */ - public function applyScopes() + public function applyScopes(): static { if (! $this->scopes) { return $this; @@ -326,12 +323,8 @@ class Builder extends BaseBuilder /** * Register a new global scope. - * - * @param string $identifier - * @param Scope|\Closure $scope - * @return $this */ - public function withGlobalScope($identifier, $scope) + public function withGlobalScope(string $identifier, Scope|Closure $scope): static { $this->scopes[$identifier] = $scope; @@ -340,11 +333,8 @@ class Builder extends BaseBuilder /** * Remove a registered global scope. - * - * @param Scope|string $scope - * @return $this */ - public function withoutGlobalScope($scope) + public function withoutGlobalScope(Scope|string $scope): static { if (! is_string($scope)) { $scope = get_class($scope); @@ -359,11 +349,8 @@ class Builder extends BaseBuilder /** * Remove all or passed registered global scopes. - * - * @param array|null $scopes - * @return $this */ - public function withoutGlobalScopes(array $scopes = null) + public function withoutGlobalScopes(?array $scopes = null): static { if (! is_array($scopes)) { $scopes = array_keys($this->scopes); @@ -378,51 +365,44 @@ class Builder extends BaseBuilder /** * Get an array of global scopes that were removed from the query. - * - * @return array */ - public function removedScopes() + public function removedScopes(): array { return $this->removedScopes; } /** * Get an array of the global scopes that were applied to the query. - * - * @return array */ - public function appliedScopes() + public function appliedScopes(): array { return $this->appliedScopes; } /** * Processes and converts the given LDAP results into models. - * - * @param array $results - * @return \LdapRecord\Query\Collection */ - protected function process(array $results) + protected function process(array $results): Collection { return $this->model->hydrate(parent::process($results)); } /** - * @inheritdoc + * {@inheritdoc} */ - protected function prepareWhereValue($field, $value, $raw = false) + protected function prepareWhereValue(string $field, mixed $value = null, $raw = false): string { if ($value instanceof DateTime) { $field = $this->model->normalizeAttributeKey($field); if (! $this->model->isDateAttribute($field)) { - throw new \UnexpectedValueException( + throw new UnexpectedValueException( "Cannot convert field [$field] to an LDAP timestamp. You must add this field as a model date." - .' Refer to https://ldaprecord.com/docs/core/v2/model-mutators/#date-mutators' + .' Refer to https://ldaprecord.com/docs/core/v3/model-mutators/#date-mutators' ); } - $value = $this->model->fromDateTime($this->model->getDates()[$field], $value); + $value = $this->model->fromDateTime($value, $this->model->getDates()[$field]); } return parent::prepareWhereValue($field, $value, $raw); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/FreeIpaBuilder.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/FreeIpaBuilder.php index 5e3d43fef..cac5ac30b 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/FreeIpaBuilder.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/FreeIpaBuilder.php @@ -6,20 +6,16 @@ class FreeIpaBuilder extends Builder { /** * Adds a enabled filter to the current query. - * - * @return $this */ - public function whereEnabled() + public function whereEnabled(): static { return $this->rawFilter('(!(pwdAccountLockedTime=*))'); } /** * Adds a disabled filter to the current query. - * - * @return $this */ - public function whereDisabled() + public function whereDisabled(): static { return $this->rawFilter('(pwdAccountLockedTime=*)'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/OpenLdapBuilder.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/OpenLdapBuilder.php index dd41344b7..e0a8d5ebc 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/OpenLdapBuilder.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Model/OpenLdapBuilder.php @@ -6,20 +6,16 @@ class OpenLdapBuilder extends Builder { /** * Adds a enabled filter to the current query. - * - * @return $this */ - public function whereEnabled() + public function whereEnabled(): static { return $this->rawFilter('(!(pwdAccountLockedTime=*))'); } /** * Adds a disabled filter to the current query. - * - * @return $this */ - public function whereDisabled() + public function whereDisabled(): static { return $this->rawFilter('(pwdAccountLockedTime=*)'); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/MultipleObjectsFoundException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/MultipleObjectsFoundException.php index 0ece752cf..79ed1e3ef 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/MultipleObjectsFoundException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/MultipleObjectsFoundException.php @@ -4,6 +4,4 @@ namespace LdapRecord\Query; use LdapRecord\LdapRecordException; -class MultipleObjectsFoundException extends LdapRecordException -{ -} +class MultipleObjectsFoundException extends LdapRecordException {} diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectNotFoundException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectNotFoundException.php index 99c2e85e3..8fb22a218 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectNotFoundException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectNotFoundException.php @@ -8,38 +8,26 @@ class ObjectNotFoundException extends LdapRecordException { /** * The query filter that was used. - * - * @var string */ - protected $query; + protected string $query; /** * The base DN of the query that was used. - * - * @var string */ - protected $baseDn; + protected ?string $baseDn; /** * Create a new exception for the executed filter. - * - * @param string $query - * @param ?string $baseDn - * @return static */ - public static function forQuery($query, $baseDn = null) + public static function forQuery(string $query, ?string $baseDn = null): static { - return (new static())->setQuery($query, $baseDn); + return (new static)->setQuery($query, $baseDn); } /** * Set the query that was used. - * - * @param string $query - * @param string|null $baseDn - * @return $this */ - public function setQuery($query, $baseDn = null) + public function setQuery(string $query, ?string $baseDn = null): static { $this->query = $query; $this->baseDn = $baseDn; diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectsNotFoundException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectsNotFoundException.php index a9846005c..85edb7f00 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectsNotFoundException.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/ObjectsNotFoundException.php @@ -4,6 +4,4 @@ namespace LdapRecord\Query; use LdapRecord\LdapRecordException; -class ObjectsNotFoundException extends LdapRecordException -{ -} +class ObjectsNotFoundException extends LdapRecordException {} diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/AbstractPaginator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/AbstractPaginator.php index 11e2dc547..c55677314 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/AbstractPaginator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/AbstractPaginator.php @@ -9,38 +9,28 @@ abstract class AbstractPaginator { /** * The query builder instance. - * - * @var Builder */ - protected $query; + protected Builder $query; /** * The filter to execute. - * - * @var string */ - protected $filter; + protected string $filter; /** * The amount of objects to fetch per page. - * - * @var int */ - protected $perPage; + protected int $perPage; /** * Whether the operation is critical. - * - * @var bool */ - protected $isCritical; + protected bool $isCritical; /** * Constructor. - * - * @param Builder $query */ - public function __construct(Builder $query, $filter, $perPage, $isCritical) + public function __construct(Builder $query, string $filter, int $perPage, bool $isCritical = false) { $this->query = $query; $this->filter = $filter; @@ -50,11 +40,8 @@ abstract class AbstractPaginator /** * Execute the pagination request. - * - * @param LdapInterface $ldap - * @return array */ - public function execute(LdapInterface $ldap) + public function execute(LdapInterface $ldap): mixed { $pages = []; @@ -78,53 +65,37 @@ abstract class AbstractPaginator } /** - * Whether the paginater should continue iterating. - * - * @return bool + * Whether the paginator should continue iterating. */ - protected function shouldContinue() + protected function shouldContinue(): bool { - $cookie = (string) $this->fetchCookie(); + $cookie = $this->fetchCookie(); return $cookie !== ''; } /** * Fetch the pagination cookie. - * - * @return string */ - abstract protected function fetchCookie(); + abstract protected function fetchCookie(): ?string; /** * Prepare the server controls before executing the pagination request. - * - * @return void */ - abstract protected function prepareServerControls(); + abstract protected function prepareServerControls(): void; /** * Apply the server controls. - * - * @param LdapInterface $ldap - * @return void */ - abstract protected function applyServerControls(LdapInterface $ldap); + abstract protected function applyServerControls(LdapInterface $ldap): void; /** * Reset the server controls. - * - * @param LdapInterface $ldap - * @return void */ - abstract protected function resetServerControls(LdapInterface $ldap); + abstract protected function resetServerControls(LdapInterface $ldap): void; /** * Update the server controls. - * - * @param LdapInterface $ldap - * @param resource $resource - * @return void */ - abstract protected function updateServerControls(LdapInterface $ldap, $resource); + abstract protected function updateServerControls(LdapInterface $ldap, mixed $resource): void; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/DeprecatedPaginator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/DeprecatedPaginator.php deleted file mode 100644 index b4a7f8db9..000000000 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/DeprecatedPaginator.php +++ /dev/null @@ -1,58 +0,0 @@ -cookie; - } - - /** - * @inheritdoc - */ - protected function prepareServerControls() - { - $this->cookie = ''; - } - - /** - * @inheritdoc - */ - protected function applyServerControls(LdapInterface $ldap) - { - $ldap->controlPagedResult($this->perPage, $this->isCritical, $this->cookie); - } - - /** - * @inheritdoc - */ - protected function updateServerControls(LdapInterface $ldap, $resource) - { - $ldap->controlPagedResultResponse($resource, $this->cookie); - } - - /** - * @inheritdoc - */ - protected function resetServerControls(LdapInterface $ldap) - { - $ldap->controlPagedResult(); - } -} diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/LazyPaginator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/LazyPaginator.php index 8b12d526c..d770259ed 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/LazyPaginator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/LazyPaginator.php @@ -2,17 +2,15 @@ namespace LdapRecord\Query\Pagination; +use Generator; use LdapRecord\LdapInterface; class LazyPaginator extends Paginator { /** * Execute the pagination request. - * - * @param LdapInterface $ldap - * @return \Generator */ - public function execute(LdapInterface $ldap) + public function execute(LdapInterface $ldap): Generator { $this->prepareServerControls(); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/Paginator.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/Paginator.php index c0a31afd9..50671a77a 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/Paginator.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Pagination/Paginator.php @@ -7,17 +7,17 @@ use LdapRecord\LdapInterface; class Paginator extends AbstractPaginator { /** - * @inheritdoc + * {@inheritdoc} */ - protected function fetchCookie() + protected function fetchCookie(): ?string { return $this->query->controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? null; } /** - * @inheritdoc + * {@inheritdoc} */ - protected function prepareServerControls() + protected function prepareServerControls(): void { $this->query->addControl(LDAP_CONTROL_PAGEDRESULTS, $this->isCritical, [ 'size' => $this->perPage, 'cookie' => '', @@ -25,31 +25,27 @@ class Paginator extends AbstractPaginator } /** - * @inheritdoc + * {@inheritdoc} */ - protected function applyServerControls(LdapInterface $ldap) + protected function applyServerControls(LdapInterface $ldap): void { $ldap->setOption(LDAP_OPT_SERVER_CONTROLS, $this->query->controls); } /** - * @inheritdoc + * {@inheritdoc} */ - protected function updateServerControls(LdapInterface $ldap, $resource) + protected function updateServerControls(LdapInterface $ldap, mixed $resource): void { - $errorCode = 0; - $dn = $errorMessage = $refs = null; $controls = $this->query->controls; - $ldap->parseResult( - $resource, - $errorCode, - $dn, - $errorMessage, - $refs, - $controls + $response = $ldap->parseResult( + result: $resource, + controls: $controls ); + $controls = array_merge($controls, $response->controls ?? []); + $cookie = $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? ''; $this->query->controls[LDAP_CONTROL_PAGEDRESULTS]['value'] = [ @@ -59,9 +55,9 @@ class Paginator extends AbstractPaginator } /** - * @inheritdoc + * {@inheritdoc} */ - protected function resetServerControls(LdapInterface $ldap) + protected function resetServerControls(LdapInterface $ldap): void { unset($this->query->controls[LDAP_CONTROL_PAGEDRESULTS]); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Slice.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Slice.php index 12faa5bd1..2921724ca 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Slice.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Query/Slice.php @@ -10,49 +10,34 @@ use JsonSerializable; class Slice implements ArrayAccess, IteratorAggregate, JsonSerializable { /** - * All of the items being paginated. - * - * @var \LdapRecord\Query\Collection|array + * All the items in the slice. */ - protected $items; + protected Collection|array $items; /** * The number of items to be shown per page. - * - * @var int */ - protected $perPage; + protected int $perPage; /** * The total number of items before slicing. - * - * @var int */ - protected $total; + protected int $total; /** * The last available page. - * - * @var int */ - protected $lastPage; + protected int $lastPage; /** * The current page being "viewed". - * - * @var int */ - protected $currentPage; + protected int $currentPage; /** * Constructor. - * - * @param \LdapRecord\Query\Collection|array $items - * @param int $total - * @param int $perPage - * @param int|null $currentPage */ - public function __construct($items, $total, $perPage, $currentPage = null) + public function __construct(Collection|array $items, int $total, int $perPage, ?int $currentPage = null) { $this->items = $items; $this->total = $total; @@ -63,202 +48,159 @@ class Slice implements ArrayAccess, IteratorAggregate, JsonSerializable /** * Get the slice of items being paginated. - * - * @return \LdapRecord\Query\Collection|array */ - public function items() + public function items(): Collection|array { return $this->items; } /** * Get the total number of items being paginated. - * - * @return int */ - public function total() + public function total(): int { return $this->total; } /** * Get the number of items shown per page. - * - * @return int */ - public function perPage() + public function perPage(): int { return $this->perPage; } /** * Determine if there are more items in the data source. - * - * @return bool */ - public function hasMorePages() + public function hasMorePages(): bool { return $this->currentPage() < $this->lastPage(); } /** * Determine if there are enough items to split into multiple pages. - * - * @return bool */ - public function hasPages() + public function hasPages(): bool { return $this->currentPage() != 1 || $this->hasMorePages(); } /** * Determine if the paginator is on the first page. - * - * @return bool */ - public function onFirstPage() + public function onFirstPage(): bool { return $this->currentPage() <= 1; } /** * Determine if the paginator is on the last page. - * - * @return bool */ - public function onLastPage() + public function onLastPage(): bool { return ! $this->hasMorePages(); } /** * Get the current page. - * - * @return int */ - public function currentPage() + public function currentPage(): int { return $this->currentPage; } /** * Get the last page. - * - * @return int */ - public function lastPage() + public function lastPage(): int { return $this->lastPage; } /** * Get an iterator for the items. - * - * @return \ArrayIterator */ #[\ReturnTypeWillChange] - public function getIterator() + public function getIterator(): ArrayIterator { return new ArrayIterator($this->items); } /** * Determine if the list of items is empty. - * - * @return bool */ - public function isEmpty() + public function isEmpty(): bool { return empty($this->items); } /** * Determine if the list of items is not empty. - * - * @return bool */ - public function isNotEmpty() + public function isNotEmpty(): bool { return ! $this->isEmpty(); } /** * Get the number of items for the current page. - * - * @return int */ #[\ReturnTypeWillChange] - public function count() + public function count(): int { return count($this->items); } /** * Determine if the given item exists. - * - * @param mixed $key - * @return bool */ #[\ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists(mixed $offset): bool { - return array_key_exists($key, $this->items); + return array_key_exists($offset, $this->items); } /** * Get the item at the given offset. - * - * @param mixed $key - * @return mixed */ #[\ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet(mixed $offset): mixed { - return $this->items[$key] ?? null; + return $this->items[$offset] ?? null; } /** * Set the item at the given offset. - * - * @param mixed $key - * @param mixed $value - * @return void */ #[\ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet(mixed $offset, mixed $value): void { - $this->items[$key] = $value; + $this->items[$offset] = $value; } /** * Unset the item at the given key. - * - * @param mixed $key - * @return void */ #[\ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset(mixed $offset): void { - unset($this->items[$key]); + unset($this->items[$offset]); } /** * Convert the object into something JSON serializable. - * - * @return array */ #[\ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return $this->toArray(); } /** * Get the arrayable items. - * - * @return array */ - public function getArrayableItems() + public function getArrayableItems(): array { return $this->items instanceof Collection ? $this->items->all() @@ -267,10 +209,8 @@ class Slice implements ArrayAccess, IteratorAggregate, JsonSerializable /** * Get the instance as an array. - * - * @return array */ - public function toArray() + public function toArray(): array { return [ 'current_page' => $this->currentPage(), diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Arr.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Arr.php index 7475859ae..6c3217a50 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Arr.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Arr.php @@ -8,23 +8,16 @@ class Arr { /** * Determine whether the given value is array accessible. - * - * @param mixed $value - * @return bool */ - public static function accessible($value) + public static function accessible(mixed $value): bool { return is_array($value) || $value instanceof ArrayAccess; } /** * Determine if the given key exists in the provided array. - * - * @param \ArrayAccess|array $array - * @param string|int $key - * @return bool */ - public static function exists($array, $key) + public static function exists(ArrayAccess|array $array, string|int $key): bool { if ($array instanceof ArrayAccess) { return $array->offsetExists($key); @@ -35,11 +28,8 @@ class Arr /** * If the given value is not an array and not null, wrap it in one. - * - * @param mixed $value - * @return array */ - public static function wrap($value) + public static function wrap(mixed $value): array { if (is_null($value)) { return []; @@ -49,14 +39,9 @@ class Arr } /** - * Return the first element in an array passing a given truth test. - * - * @param iterable $array - * @param callable|null $callback - * @param mixed $default - * @return mixed + * Get the first element in an array passing a given truth test. */ - public static function first($array, callable $callback = null, $default = null) + public static function first(iterable $array, ?callable $callback = null, mixed $default = null): mixed { if (is_null($callback)) { if (empty($array)) { @@ -78,14 +63,11 @@ class Arr } /** - * Return the last element in an array passing a given truth test. + * Get the last element in an array passing a given truth test. * * @param array $array - * @param callable|null $callback - * @param mixed $default - * @return mixed */ - public static function last($array, callable $callback = null, $default = null) + public static function last(iterable $array, ?callable $callback = null, mixed $default = null): mixed { if (is_null($callback)) { return empty($array) ? Helpers::value($default) : end($array); @@ -96,13 +78,8 @@ class Arr /** * Get an item from an array using "dot" notation. - * - * @param ArrayAccess|array $array - * @param string|int|null $key - * @param mixed $default - * @return mixed */ - public static function get($array, $key, $default = null) + public static function get(ArrayAccess|array $array, string|int|null $key, mixed $default = null): mixed { if (! static::accessible($array)) { return Helpers::value($default); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Helpers.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Helpers.php index ab217d6ff..5b4bd46fb 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Helpers.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Helpers.php @@ -7,13 +7,9 @@ use Closure; class Helpers { /** - * Return the default value of the given value. - * - * @param mixed $value - * @param mixed $args - * @return mixed + * Get the default value of the given value. */ - public static function value($value, ...$args) + public static function value(mixed $value, mixed ...$args): mixed { return $value instanceof Closure ? $value(...$args) : $value; } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Str.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Str.php index 820834e21..ced2a5fd1 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Str.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Support/Str.php @@ -5,19 +5,49 @@ namespace LdapRecord\Support; class Str { /** - * Return the remainder of a string after the last occurrence of a given value. - * - * @param string $subject - * @param string $search - * @return string + * Determine if a given string matches a given pattern. */ - public static function afterLast($subject, $search) + public static function is(string|iterable $pattern, string $value): bool + { + if (! is_iterable($pattern)) { + $pattern = [$pattern]; + } + + foreach ($pattern as $pattern) { + $pattern = (string) $pattern; + + // If the given value is an exact match we can of course return true right + // from the beginning. Otherwise, we will translate asterisks and do an + // actual pattern match against the two strings to see if they match. + if ($pattern === $value) { + return true; + } + + $pattern = preg_quote($pattern, '#'); + + // Asterisks are translated into zero-or-more regular expression wildcards + // to make it convenient to check if the strings starts with the given + // pattern such as "library/*", making any string check convenient. + $pattern = str_replace('\*', '.*', $pattern); + + if (preg_match('#^'.$pattern.'\z#u', $value) === 1) { + return true; + } + } + + return false; + } + + /** + * Get the remainder of a string after the last occurrence of a given value. + */ + public static function afterLast(string $subject, string $search): string { if ($search === '') { return $subject; } - $position = strrpos($subject, (string) $search); + $position = strrpos($subject, $search); if ($position === false) { return $subject; @@ -28,12 +58,8 @@ class Str /** * Determine if a given string starts with a given substring. - * - * @param string $haystack - * @param string|string[] $needles - * @return bool */ - public static function startsWith($haystack, $needles) + public static function startsWith(string $haystack, array|string $needles): bool { foreach ((array) $needles as $needle) { if ((string) $needle !== '' && str_starts_with($haystack, $needle)) { @@ -46,12 +72,8 @@ class Str /** * Determine if a given string ends with a given substring. - * - * @param string $haystack - * @param string|string[] $needles - * @return bool */ - public static function endsWith($haystack, $needles) + public static function endsWith(string $haystack, array|string $needles): bool { foreach ((array) $needles as $needle) { if ( @@ -67,14 +89,8 @@ class Str /** * Execute a callback when a needle is found in the haystack. - * - * @param string $haystack - * @param string|string[] $needles - * @param \Closure|mixed $callback - * @param \Closure|mixed $default - * @return mixed */ - public static function whenContains($haystack, $needles, $callback, $default = null) + public static function whenContains(string $haystack, array|string $needles, mixed $callback, mixed $default = null): mixed { foreach ((array) $needles as $needle) { if (static::contains($haystack, $needle)) { @@ -87,13 +103,8 @@ class Str /** * Determine if a given string contains a given substring. - * - * @param string $haystack - * @param string|string[] $needles - * @param bool $ignoreCase - * @return bool */ - public static function contains($haystack, $needles, $ignoreCase = false) + public static function contains(string $haystack, array|string $needles, bool $ignoreCase = false): bool { if ($ignoreCase) { $haystack = mb_strtolower($haystack); @@ -110,15 +121,9 @@ class Str } /** - * Returns the number of substring occurrences. - * - * @param string $haystack - * @param string $needle - * @param int $offset - * @param int|null $length - * @return int + * Get the number of substring occurrences. */ - public static function substrCount($haystack, $needle, $offset = 0, $length = null) + public static function substrCount(string $haystack, string $needle, int $offset = 0, ?int $length = null): int { if (! is_null($length)) { return substr_count($haystack, $needle, $offset, $length); diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/AuthGuardFake.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/AuthGuardFake.php index 4a691506d..36574c0d5 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/AuthGuardFake.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/AuthGuardFake.php @@ -8,11 +8,6 @@ class AuthGuardFake extends Guard { /** * Always allow binding as configured user. - * - * @return bool */ - public function bindAsConfiguredUser() - { - return true; - } + public function bindAsConfiguredUser(): void {} } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/ConnectionFake.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/ConnectionFake.php index 09530f5c7..2c76a06d5 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/ConnectionFake.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/ConnectionFake.php @@ -4,6 +4,7 @@ namespace LdapRecord\Testing; use LdapRecord\Auth\Guard; use LdapRecord\Connection; +use LdapRecord\LdapInterface; use LdapRecord\Models\Model; class ConnectionFake extends Connection @@ -13,25 +14,19 @@ class ConnectionFake extends Connection * * @var LdapFake */ - protected $ldap; + protected LdapInterface $ldap; /** * Whether the fake is connected. - * - * @var bool */ - protected $connected = false; + protected bool $connected = false; /** * Make a new fake LDAP connection instance. - * - * @param array $config - * @param string $ldap - * @return static */ - public static function make(array $config = [], $ldap = LdapFake::class) + public static function make(array $config = [], string $ldap = LdapFake::class): static { - $connection = new static($config, new $ldap()); + $connection = new static($config, new $ldap); $connection->configure(); @@ -40,13 +35,10 @@ class ConnectionFake extends Connection /** * Set the user to authenticate as. - * - * @param Model|string $user - * @return $this */ - public function actingAs($user) + public function actingAs(Model|string $user): static { - $this->ldap->shouldAuthenticateWith( + $this->ldap->shouldAllowBindWith( $user instanceof Model ? $user->getDn() : $user ); @@ -55,10 +47,8 @@ class ConnectionFake extends Connection /** * Set the connection to bypass bind attempts as the configured user. - * - * @return $this */ - public function shouldBeConnected() + public function shouldBeConnected(): static { $this->connected = true; @@ -71,10 +61,8 @@ class ConnectionFake extends Connection /** * Set the connection to attempt binding as the configured user. - * - * @return $this */ - public function shouldNotBeConnected() + public function shouldNotBeConnected(): static { $this->connected = false; @@ -86,10 +74,20 @@ class ConnectionFake extends Connection } /** - * @inheritdoc + * {@inheritdoc} */ - public function isConnected() + public function isConnected(): bool { return $this->connected ?: parent::isConnected(); } + + /** + * Perform tear down tasks on the fake. + * + * @throws LdapExpectationException + */ + public function tearDown(): void + { + $this->ldap->assertMinimumExpectationCounts(); + } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/DirectoryFake.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/DirectoryFake.php index 72be02af3..90cbf7ae6 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/DirectoryFake.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/DirectoryFake.php @@ -7,44 +7,50 @@ use LdapRecord\Container; class DirectoryFake { /** - * Setup the fake connection. + * The LDAP connections that were replaced with fakes. * - * @param string|null $name - * @return ConnectionFake + * @var \LdapRecord\Connection[] + */ + protected static array $replaced = []; + + /** + * Replace a connection a fake. * * @throws \LdapRecord\ContainerException */ - public static function setup($name = null) + public static function setup(?string $name = null): ConnectionFake { - $connection = Container::getConnection($name); + $name = $name ?? Container::getDefaultConnectionName(); + + $connection = static::$replaced[$name] = Container::getConnection($name); $fake = static::makeConnectionFake( $connection->getConfiguration()->all() ); - // Replace the connection with a fake. Container::addConnection($fake, $name); return $fake; } /** - * Reset the container. - * - * @return void + * Replace all faked connections with their original. */ - public static function tearDown() + public static function tearDown(): void { - Container::reset(); + foreach (static::$replaced as $name => $connection) { + Container::getConnection($name)->tearDown(); + } + + Container::flush(); + + static::$replaced = []; } /** * Make a connection fake. - * - * @param array $config - * @return ConnectionFake */ - public static function makeConnectionFake(array $config = []) + public static function makeConnectionFake(array $config = []): ConnectionFake { return ConnectionFake::make($config)->shouldBeConnected(); } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectation.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectation.php index 693459149..2e010d7d7 100644 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectation.php +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectation.php @@ -3,7 +3,9 @@ namespace LdapRecord\Testing; use Closure; +use Exception; use LdapRecord\LdapRecordException; +use LdapRecord\LdapResultResponse; use PHPUnit\Framework\Constraint\Callback; use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\Constraint\IsEqual; @@ -13,91 +15,76 @@ class LdapExpectation { /** * The value to return from the expectation. - * - * @var mixed */ - protected $value; + protected mixed $value = null; /** * The exception to throw from the expectation. - * - * @var null|LdapRecordException|\Exception */ - protected $exception; + protected ?Exception $exception = null; /** - * The amount of times the expectation should be called. - * - * @var int + * The tracked number of times the expectation should be called. */ - protected $count = 1; + protected int $count = 1; + + /** + * The original number of times the expectation should be called. + */ + protected int $originalCount = 1; + + /** + * The actual number of times the expectation was called. + */ + protected int $called = 0; /** * The method that the expectation belongs to. - * - * @var string */ - protected $method; + protected ?string $method = null; /** * The methods argument's. - * - * @var array */ - protected $args = []; + protected array $args = []; /** * Whether the same expectation should be returned indefinitely. - * - * @var bool */ - protected $indefinitely = true; + protected bool $indefinitely = true; /** - * Whether the expectation should return errors. - * - * @var bool + * Whether the expectation should return an error. */ - protected $errors = false; + protected bool $errors = false; /** - * The error number to return. - * - * @var int + * The error code to return. */ - protected $errorCode = 1; + protected int $errorCode = 1; /** - * The last error string to return. - * - * @var string + * The error message to return. */ - protected $errorMessage = ''; + protected string $errorMessage = 'Unknown error'; /** * The diagnostic message string to return. - * - * @var string */ - protected $errorDiagnosticMessage = ''; + protected ?string $errorDiagnosticMessage = null; /** * Constructor. - * - * @param string $method */ - public function __construct($method) + public function __construct(string $method) { $this->method = $method; } /** * Set the arguments that the operation should receive. - * - * @param mixed $args - * @return $this */ - public function with($args) + public function with(mixed $args): static { $this->args = array_map(function ($arg) { if ($arg instanceof Closure) { @@ -116,11 +103,8 @@ class LdapExpectation /** * Set the expected value to return. - * - * @param mixed $value - * @return $this */ - public function andReturn($value) + public function andReturn(mixed $value): static { $this->value = $value; @@ -128,31 +112,62 @@ class LdapExpectation } /** - * The error message to return from the expectation. - * - * @param int $code - * @param string $error - * @param string $diagnosticMessage - * @return $this + * Set the expected value to return true. */ - public function andReturnError($code = 1, $error = '', $diagnosticMessage = '') + public function andReturnTrue(): static + { + return $this->andReturn(true); + } + + /** + * Set the expected value to return false. + */ + public function andReturnFalse(): static + { + return $this->andReturn(false); + } + + /** + * The error message to return from the expectation. + */ + public function andReturnError(int $errorCode = 1, string $errorMessage = 'Unknown error', ?string $diagnosticMessage = null): static { $this->errors = true; - $this->errorCode = $code; - $this->errorMessage = $error; + $this->errorCode = $errorCode; + $this->errorMessage = $errorMessage; $this->errorDiagnosticMessage = $diagnosticMessage; return $this; } /** - * Set the expected exception to throw. - * - * @param string|\Exception|LdapRecordException $exception - * @return $this + * Return an error LDAP result response. */ - public function andThrow($exception) + public function andReturnErrorResponse(int $code = 1, ?string $errorMessage = null): static + { + return $this->andReturnResponse($code, $errorMessage); + } + + /** + * Return an LDAP result response. + */ + public function andReturnResponse( + int $errorCode = 0, + ?string $matchedDn = null, + ?string $errorMessage = null, + array $referrals = [], + array $controls = [] + ): static { + return $this->andReturn( + new LdapResultResponse($errorCode, $matchedDn, $errorMessage, $referrals, $controls) + ); + } + + /** + * Set the expected exception to throw. + */ + public function andThrow(string|Exception $exception): static { if (is_string($exception)) { $exception = new LdapRecordException($exception); @@ -165,45 +180,36 @@ class LdapExpectation /** * Set the expectation to be only called once. - * - * @return $this */ - public function once() + public function once(): static { - return $this->times(1); + return $this->times(); } /** * Set the expectation to be only called twice. - * - * @return $this */ - public function twice() + public function twice(): static { return $this->times(2); } /** * Set the expectation to be called the given number of times. - * - * @param int $count - * @return $this */ - public function times($count = 1) + public function times(int $count = 1): static { $this->indefinitely = false; - $this->count = $count; + $this->originalCount = $this->count = $count; return $this; } /** * Get the method the expectation belongs to. - * - * @return string */ - public function getMethod() + public function getMethod(): string { if (is_null($this->method)) { throw new UnexpectedValueException('An expectation must have a method.'); @@ -214,89 +220,105 @@ class LdapExpectation /** * Get the expected call count. - * - * @return int */ - public function getExpectedCount() + public function getExpectedCount(): int { return $this->count; } + /** + * Get the original expected call count. + */ + public function getOriginalExpectedCount(): int + { + return $this->originalCount; + } + + /** + * Get the count that the expectation was called. + */ + public function getCalledCount(): int + { + return $this->called; + } + /** * Get the expected arguments. * * @return Constraint[] */ - public function getExpectedArgs() + public function getExpectedArgs(): array { return $this->args; } /** * Get the expected exception. - * - * @return null|\Exception|LdapRecordException */ - public function getExpectedException() + public function getExpectedException(): ?Exception { return $this->exception; } /** * Get the expected value. - * - * @return mixed */ - public function getExpectedValue() + public function getExpectedValue(): mixed { return $this->value; } /** - * Determine whether the expectation is returning an error. - * - * @return bool + * Determine whether the expectation can be called indefinitely. */ - public function isReturningError() + public function isIndefinite(): bool + { + return $this->indefinitely; + } + + /** + * Determine whether the expectation is returning an error. + */ + public function isReturningError(): bool { return $this->errors; } /** - * @return int + * Get the expected error code. */ - public function getExpectedErrorCode() + public function getExpectedErrorCode(): int { return $this->errorCode; } /** - * @return string + * Get the expected error message. */ - public function getExpectedErrorMessage() + public function getExpectedErrorMessage(): ?string { return $this->errorMessage; } /** - * @return string + * Get the expected diagnostic message. */ - public function getExpectedErrorDiagnosticMessage() + public function getExpectedErrorDiagnosticMessage(): ?string { return $this->errorDiagnosticMessage; } /** - * Decrement the call count of the expectation. - * - * @return $this + * Decrement the expected count of the expectation. */ - public function decrementCallCount() + public function decrementExpectedCount(): static { if (! $this->indefinitely) { $this->count -= 1; } + $this->called++; + return $this; } } diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectationException.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectationException.php new file mode 100644 index 000000000..74a3ebe33 --- /dev/null +++ b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Testing/LdapExpectationException.php @@ -0,0 +1,7 @@ + */ - protected $expectations = []; + protected array $expectations = []; /** * The default fake error number. - * - * @var int */ - protected $errNo = 1; + protected int $errNo = 1; /** * The default fake last error string. - * - * @var string */ - protected $lastError = ''; + protected string $lastError = 'Unknown error'; /** * The default fake diagnostic message string. - * - * @var string */ - protected $diagnosticMessage = ''; + protected ?string $diagnosticMessage = null; /** * Create a new expected operation. - * - * @param string $method - * @return LdapExpectation */ - public static function operation($method) + public static function operation(string $method): LdapExpectation { return new LdapExpectation($method); } /** - * Set the user that will pass binding. - * - * @param string $dn - * @return $this + * Set the fake LDAP host. */ - public function shouldAuthenticateWith($dn) + public function setHost(string $host): void { - return $this->expect( - static::operation('bind')->with($dn, PHPUnit::anything())->andReturn(true) - ); + $this->host = $host; } /** * Add an LDAP method expectation. - * - * @param LdapExpectation|array $expectations - * @return $this */ - public function expect($expectations = []) + public function expect(LdapExpectation|array $expectations = []): static { $expectations = Arr::wrap($expectations); @@ -101,11 +85,8 @@ class LdapFake implements LdapInterface /** * Determine if the method has any expectations. - * - * @param string $method - * @return bool */ - public function hasExpectations($method) + public function hasExpectations(string $method): bool { return count($this->getExpectations($method)) > 0; } @@ -113,33 +94,55 @@ class LdapFake implements LdapInterface /** * Get expectations by method. * - * @param string $method - * @return LdapExpectation[]|mixed + * @return LdapExpectation[] */ - public function getExpectations($method) + public function getExpectations(string $method): array { return $this->expectations[$method] ?? []; } /** * Remove an expectation by method and key. - * - * @param string $method - * @param int $key - * @return void */ - public function removeExpectation($method, $key) + public function removeExpectation(string $method, int $key): void { unset($this->expectations[$method][$key]); } /** - * Set the error number of a failed bind attempt. - * - * @param int $number - * @return $this + * Set the fake to allow any bind attempt. */ - public function shouldReturnErrorNumber($number = 1) + public function shouldAllowAnyBind(): static + { + return $this->expect( + static::operation('bind')->andReturnResponse() + ); + } + + /** + * Set the fake to allow any bind attempt with the given DN. + */ + public function shouldAllowBindWith(string $dn): static + { + return $this->expect( + static::operation('bind')->with($dn, PHPUnit::anything())->andReturnResponse() + ); + } + + /** + * Set the user that will pass binding. + * + * @deprecated Use shouldAllowBindWith instead. + */ + public function shouldAuthenticateWith(string $dn): static + { + return $this->shouldAllowBindWith($dn); + } + + /** + * Set the error number of a failed bind attempt. + */ + public function shouldReturnErrorNumber(int $number = 1): static { $this->errNo = $number; @@ -148,11 +151,8 @@ class LdapFake implements LdapInterface /** * Set the last error of a failed bind attempt. - * - * @param string $message - * @return $this */ - public function shouldReturnError($message = '') + public function shouldReturnError(string $message): static { $this->lastError = $message; @@ -161,11 +161,8 @@ class LdapFake implements LdapInterface /** * Set the diagnostic message of a failed bind attempt. - * - * @param string $message - * @return $this */ - public function shouldReturnDiagnosticMessage($message = '') + public function shouldReturnDiagnosticMessage(?string $message): static { $this->diagnosticMessage = $message; @@ -174,38 +171,32 @@ class LdapFake implements LdapInterface /** * Return a fake error number. - * - * @return int */ - public function errNo() + public function errNo(): int { return $this->errNo; } /** * Return a fake error. - * - * @return string */ - public function getLastError() + public function getLastError(): string { return $this->lastError; } /** - * @inheritdoc + * Return a fake diagnostic message. */ - public function getDiagnosticMessage() + public function getDiagnosticMessage(): ?string { return $this->diagnosticMessage; } /** * Return a fake detailed error. - * - * @return DetailedError */ - public function getDetailedError() + public function getDetailedError(): DetailedError { return new DetailedError( $this->errNo(), @@ -215,258 +206,309 @@ class LdapFake implements LdapInterface } /** - * @inheritdoc + * {@inheritdoc} */ - public function getEntries($searchResults) + public function getEntries(mixed $result): array { - return $searchResults; + return $result; } /** - * @inheritdoc + * {@inheritDoc} */ - public function isUsingSSL() + public function getValuesLen(mixed $entry, string $attribute): array|false { - return $this->hasExpectations('isUsingSSL') - ? $this->resolveExpectation('isUsingSSL') + return $this->resolveExpectation(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritDoc} + */ + public function compare(string $dn, string $attribute, string $value, ?array $controls = null): bool|int + { + return $this->resolveExpectation(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritDoc} + */ + public function setRebindCallback(callable $callback): bool + { + return $this->resolveExpectation(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritDoc} + */ + public function getFirstEntry(mixed $result): mixed + { + return $this->executeFailableOperation(function () use ($result) { + return ldap_first_entry($this->connection, $result); + }); + } + + /** + * {@inheritDoc} + */ + public function getNextEntry(mixed $entry): mixed + { + return $this->executeFailableOperation(function () use ($entry) { + return ldap_next_entry($this->connection, $entry); + }); + } + + /** + * {@inheritDoc} + */ + public function getAttributes(mixed $entry): array|false + { + return $this->executeFailableOperation(function () use ($entry) { + return ldap_get_attributes($this->connection, $entry); + }); + } + + /** + * {@inheritDoc} + */ + public function countEntries(mixed $result): int + { + return $this->executeFailableOperation(function () use ($result) { + return ldap_count_entries($this->connection, $result); + }); + } + + /** + * {@inheritdoc} + */ + public function isUsingSSL(): bool + { + return $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__) : $this->useSSL; } /** - * @inheritdoc + * {@inheritdoc} */ - public function isUsingTLS() + public function isUsingTLS(): bool { - return $this->hasExpectations('isUsingTLS') - ? $this->resolveExpectation('isUsingTLS') + return $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__) : $this->useTLS; } /** - * @inheritdoc + * {@inheritdoc} */ - public function isBound() + public function isBound(): bool { - return $this->hasExpectations('isBound') - ? $this->resolveExpectation('isBound') + return $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__) : $this->bound; } /** - * @inheritdoc + * {@inheritdoc} */ - public function getHost() + public function setOption(int $option, mixed $value): bool { - return $this->hasExpectations('getHost') - ? $this->resolveExpectation('getHost') - : $this->host; - } - - /** - * @inheritdoc - */ - public function setOption($option, $value) - { - return $this->hasExpectations('setOption') - ? $this->resolveExpectation('setOption', func_get_args()) + return $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__, func_get_args()) : true; } /** - * @inheritdoc + * {@inheritdoc} */ - public function getOption($option, &$value = null) + public function getOption(int $option, mixed &$value = null): mixed { - return $this->resolveExpectation('getOption', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function startTLS() + public function startTLS(): bool { - return $this->resolveExpectation('startTLS', func_get_args()); + return $this->secure = $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function connect($hosts = [], $port = 389) + public function connect(string|array $hosts = [], int $port = 389, ?string $protocol = null): bool { $this->bound = false; - + $this->protocol = $protocol; $this->host = $this->makeConnectionUris($hosts, $port); - return $this->connection = $this->hasExpectations('connect') - ? $this->resolveExpectation('connect', func_get_args()) + return $this->connection = $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__, func_get_args()) : true; } /** - * @inheritdoc + * {@inheritdoc} */ - public function close() + public function close(): bool { - $this->connection = null; $this->bound = false; - $this->host = null; + $this->secure = false; - return $this->hasExpectations('close') - ? $this->resolveExpectation('close') + $this->host = null; + $this->protocol = null; + $this->connection = null; + + return $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__) : true; } /** - * @inheritdoc + * {@inheritdoc} */ - public function bind($username, $password) + public function bind(?string $dn = null, ?string $password = null, ?array $controls = null): LdapResultResponse { - return $this->bound = $this->resolveExpectation('bind', func_get_args()); + $result = $this->resolveExpectation(__FUNCTION__, func_get_args()); + + $this->handleBindResponse($result); + + return $result; } /** - * @inheritdoc + * {@inheritDoc} */ - public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []) + public function saslBind(?string $dn = null, ?string $password = null, ?array $options = null): bool { - return $this->resolveExpectation('search', func_get_args()); + return $this->bound = $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []) + public function search(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed { - return $this->resolveExpectation('listing', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []) + public function list(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed { - return $this->resolveExpectation('read', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = []) + public function read(string $dn, string $filter, array $fields, bool $onlyAttributes = false, int $size = 0, int $time = 0, int $deref = LDAP_DEREF_NEVER, ?array $controls = null): mixed { - return $this->resolveExpectation('parseResult', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function add($dn, array $entry) + public function parseResult(mixed $result, int &$errorCode = 0, ?string &$dn = null, ?string &$errorMessage = null, ?array &$referrals = null, ?array &$controls = null): LdapResultResponse|false { - return $this->resolveExpectation('add', func_get_args()); + return $this->hasExpectations(__FUNCTION__) + ? $this->resolveExpectation(__FUNCTION__, func_get_args()) + : new LdapResultResponse; } /** - * @inheritdoc + * {@inheritdoc} */ - public function delete($dn) + public function add(string $dn, array $entry): bool { - return $this->resolveExpectation('delete', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false) + public function delete(string $dn): bool { - return $this->resolveExpectation('rename', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function modify($dn, array $entry) + public function rename(string $dn, string $newRdn, string $newParent, bool $deleteOldRdn = false): bool { - return $this->resolveExpectation('modify', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function modifyBatch($dn, array $values) + public function modify(string $dn, array $entry): bool { - return $this->resolveExpectation('modifyBatch', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function modAdd($dn, array $entry) + public function modifyBatch(string $dn, array $values): bool { - return $this->resolveExpectation('modAdd', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function modReplace($dn, array $entry) + public function modAdd(string $dn, array $entry): bool { - return $this->resolveExpectation('modReplace', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function modDelete($dn, array $entry) + public function modReplace(string $dn, array $entry): bool { - return $this->resolveExpectation('modDelete', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '') + public function modDelete(string $dn, array $entry): bool { - return $this->resolveExpectation('controlPagedResult', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function controlPagedResultResponse($result, &$cookie) + public function freeResult(mixed $result): bool { - return $this->resolveExpectation('controlPagedResultResponse', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** - * @inheritdoc + * {@inheritdoc} */ - public function freeResult($result) + public function err2Str(int $number): string { - return $this->resolveExpectation('freeResult', func_get_args()); - } - - /** - * @inheritdoc - */ - public function err2Str($number) - { - return $this->resolveExpectation('err2Str', func_get_args()); + return $this->resolveExpectation(__FUNCTION__, func_get_args()); } /** * Resolve the methods expectations. * - * @param string $method - * @param array $args - * @return mixed - * - * @throws Exception + * @throws LdapExpectationException */ - protected function resolveExpectation($method, array $args = []) + protected function resolveExpectation(string $method, array $args = []): mixed { foreach ($this->getExpectations($method) as $key => $expectation) { $this->assertMethodArgumentsMatch($method, $expectation->getExpectedArgs(), $args); - $expectation->decrementCallCount(); + $expectation->decrementExpectedCount(); if ($expectation->getExpectedCount() === 0) { $this->removeExpectation($method, $key); @@ -483,31 +525,43 @@ class LdapFake implements LdapInterface return $expectation->getExpectedValue(); } - throw new Exception("LDAP method [$method] was unexpected."); + throw new LdapExpectationException("LDAP method [$method] was unexpected."); } /** * Apply the expectation error to the fake. - * - * @param LdapExpectation $expectation - * @return void */ - protected function applyExpectationError(LdapExpectation $expectation) + protected function applyExpectationError(LdapExpectation $expectation): void { $this->shouldReturnError($expectation->getExpectedErrorMessage()); $this->shouldReturnErrorNumber($expectation->getExpectedErrorCode()); $this->shouldReturnDiagnosticMessage($expectation->getExpectedErrorDiagnosticMessage()); } + /** + * Assert that the expectations have been called their minimum amount of times. + * + * @throws LdapExpectationException + */ + public function assertMinimumExpectationCounts(): void + { + foreach ($this->expectations as $method => $expectations) { + foreach ($expectations as $expectation) { + if (! $expectation->isIndefinite() && $expectation->getExpectedCount()) { + $remaining = ($original = $expectation->getOriginalExpectedCount()) - $expectation->getExpectedCount(); + + throw new LdapExpectationException("Method [$method] should be called $original times but was called $remaining times."); + } + } + } + } + /** * Assert that the expected arguments match the operations arguments. * - * @param string $method * @param Constraint[] $expectedArgs - * @param array $methodArgs - * @return void */ - protected function assertMethodArgumentsMatch($method, array $expectedArgs = [], array $methodArgs = []) + protected function assertMethodArgumentsMatch(string $method, array $expectedArgs = [], array $methodArgs = []): void { foreach ($expectedArgs as $key => $constraint) { $argNumber = $key + 1; @@ -515,7 +569,7 @@ class LdapFake implements LdapInterface PHPUnit::assertArrayHasKey( $key, $methodArgs, - "LDAP method [$method] argument #{$argNumber} does not exist." + "LDAP method [$method] argument #$argNumber does not exist." ); $constraint->evaluate( diff --git a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Utilities.php b/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Utilities.php deleted file mode 100644 index c4f5190d8..000000000 --- a/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Utilities.php +++ /dev/null @@ -1,187 +0,0 @@ - $value) { - $dn[$rdn] = static::unescape($value); - } - - return $dn; - } - - /** - * Un-escapes a hexadecimal string into its original string representation. - * - * @param string $value - * @return string - */ - public static function unescape($value) - { - return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/', function ($matches) { - return chr(hexdec($matches[1])); - }, $value); - } - - /** - * Convert a binary SID to a string SID. - * - * @author Chad Sikorra - * - * @see https://github.com/ChadSikorra - * @see https://stackoverflow.com/questions/39533560/php-ldap-get-user-sid - * - * @param string $value The Binary SID - * @return string|null - */ - public static function binarySidToString($value) - { - // Revision - 8bit unsigned int (C1) - // Count - 8bit unsigned int (C1) - // 2 null bytes - // ID - 32bit unsigned long, big-endian order - $sid = @unpack('C1rev/C1count/x2/N1id', $value); - - if (! isset($sid['id']) || ! isset($sid['rev'])) { - return; - } - - $revisionLevel = $sid['rev']; - - $identifierAuthority = $sid['id']; - - $subs = isset($sid['count']) ? $sid['count'] : 0; - - $sidHex = $subs ? bin2hex($value) : ''; - - $subAuthorities = []; - - // The sub-authorities depend on the count, so only get as - // many as the count, regardless of data beyond it. - for ($i = 0; $i < $subs; $i++) { - $data = implode(array_reverse( - str_split( - substr($sidHex, 16 + ($i * 8), 8), - 2 - ) - )); - - $subAuthorities[] = hexdec($data); - } - - // Tack on the 'S-' and glue it all together... - return 'S-'.$revisionLevel.'-'.$identifierAuthority.implode( - preg_filter('/^/', '-', $subAuthorities) - ); - } - - /** - * Convert a binary GUID to a string GUID. - * - * @param string $binGuid - * @return string|null - */ - public static function binaryGuidToString($binGuid) - { - if (is_null($binGuid) || trim($binGuid) == '') { - return; - } - - $hex = unpack('H*hex', $binGuid)['hex']; - - $hex1 = substr($hex, -26, 2).substr($hex, -28, 2).substr($hex, -30, 2).substr($hex, -32, 2); - $hex2 = substr($hex, -22, 2).substr($hex, -24, 2); - $hex3 = substr($hex, -18, 2).substr($hex, -20, 2); - $hex4 = substr($hex, -16, 4); - $hex5 = substr($hex, -12, 12); - - return sprintf('%s-%s-%s-%s-%s', $hex1, $hex2, $hex3, $hex4, $hex5); - } - - /** - * Converts a string GUID to it's hex variant. - * - * @param string $string - * @return string - */ - public static function stringGuidToHex($string) - { - $hex = '\\'.substr($string, 6, 2).'\\'.substr($string, 4, 2).'\\'.substr($string, 2, 2).'\\'.substr($string, 0, 2); - $hex = $hex.'\\'.substr($string, 11, 2).'\\'.substr($string, 9, 2); - $hex = $hex.'\\'.substr($string, 16, 2).'\\'.substr($string, 14, 2); - $hex = $hex.'\\'.substr($string, 19, 2).'\\'.substr($string, 21, 2); - $hex = $hex.'\\'.substr($string, 24, 2).'\\'.substr($string, 26, 2).'\\'.substr($string, 28, 2).'\\'.substr($string, 30, 2).'\\'.substr($string, 32, 2).'\\'.substr($string, 34, 2); - - return $hex; - } - - /** - * Round a Windows timestamp down to seconds and remove - * the seconds between 1601-01-01 and 1970-01-01. - * - * @param int $windowsTime - * @return int - */ - public static function convertWindowsTimeToUnixTime($windowsTime) - { - return (int) (round($windowsTime / 10000000) - 11644473600); - } - - /** - * Convert a Unix timestamp to Windows timestamp. - * - * @param int $unixTime - * @return int - */ - public static function convertUnixTimeToWindowsTime($unixTime) - { - return ($unixTime + 11644473600) * 10000000; - } - - /** - * Validates that the inserted string is an object SID. - * - * @param string $sid - * @return bool - */ - public static function isValidSid($sid) - { - return (bool) preg_match("/^S-\d(-\d{1,10}){1,16}$/i", (string) $sid); - } - - /** - * Validates that the inserted string is an object GUID. - * - * @param string $guid - * @return bool - */ - public static function isValidGuid($guid) - { - return (bool) preg_match('/^([0-9a-fA-F]){8}(-([0-9a-fA-F]){4}){3}-([0-9a-fA-F]){12}$/', (string) $guid); - } -} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Arr.php b/data/web/inc/lib/vendor/illuminate/collections/Arr.php similarity index 88% rename from data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Arr.php rename to data/web/inc/lib/vendor/illuminate/collections/Arr.php index f3a4b042e..d83cf5f73 100644 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Arr.php +++ b/data/web/inc/lib/vendor/illuminate/collections/Arr.php @@ -1,10 +1,10 @@ $value) { @@ -214,7 +216,7 @@ class Arr * @param mixed $default * @return mixed */ - public static function last($array, callable $callback = null, $default = null) + public static function last($array, ?callable $callback = null, $default = null) { if (is_null($callback)) { return empty($array) ? value($default) : end($array); @@ -223,6 +225,22 @@ class Arr return static::first(array_reverse($array, true), $callback, $default); } + /** + * Take the first or last {$limit} items from an array. + * + * @param array $array + * @param int $limit + * @return array + */ + public static function take($array, $limit) + { + if ($limit < 0) { + return array_slice($array, $limit, abs($limit)); + } + + return array_slice($array, 0, $limit); + } + /** * Flatten a multi-dimensional array into a single level. * @@ -410,9 +428,7 @@ class Arr */ public static function isAssoc(array $array) { - $keys = array_keys($array); - - return array_keys($keys) !== $keys; + return ! array_is_list($array); } /** @@ -425,7 +441,7 @@ class Arr */ public static function isList($array) { - return ! self::isAssoc($array); + return array_is_list($array); } /** @@ -476,9 +492,7 @@ class Arr */ public static function prependKeysWith($array, $prependWith) { - return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) { - return [$prependWith.$key => $item]; - })->all(); + return static::mapWithKeys($array, fn ($item, $key) => [$prependWith.$key => $item]); } /** @@ -493,6 +507,32 @@ class Arr return array_intersect_key($array, array_flip((array) $keys)); } + /** + * Select an array of values from an array. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function select($array, $keys) + { + $keys = static::wrap($keys); + + return static::map($array, function ($item) use ($keys) { + $result = []; + + foreach ($keys as $key) { + if (Arr::accessible($item) && Arr::exists($item, $key)) { + $result[$key] = $item[$key]; + } elseif (is_object($item) && isset($item->{$key})) { + $result[$key] = $item->{$key}; + } + } + + return $result; + }); + } + /** * Pluck an array of values from an array. * @@ -565,6 +605,35 @@ class Arr return array_combine($keys, $items); } + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TKey + * @template TValue + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param array $array + * @param callable(TValue, TKey): array $callback + * @return array + */ + public static function mapWithKeys(array $array, callable $callback) + { + $result = []; + + foreach ($array as $key => $value) { + $assoc = $callback($value, $key); + + foreach ($assoc as $mapKey => $mapValue) { + $result[$mapKey] = $mapValue; + } + } + + return $result; + } + /** * Push an item onto the beginning of an array. * @@ -759,7 +828,7 @@ class Arr } } - if (static::isAssoc($array)) { + if (! array_is_list($array)) { $descending ? krsort($array, $options) : ksort($array, $options); @@ -772,6 +841,18 @@ class Arr return $array; } + /** + * Recursively sort an array by keys and values in descending order. + * + * @param array $array + * @param int $options + * @return array + */ + public static function sortRecursiveDesc($array, $options = SORT_REGULAR) + { + return static::sortRecursive($array, $options, true); + } + /** * Conditionally compile classes from an array into a CSS class list. * diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Collection.php b/data/web/inc/lib/vendor/illuminate/collections/Collection.php similarity index 86% rename from data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Collection.php rename to data/web/inc/lib/vendor/illuminate/collections/Collection.php index e705ee0d0..2f0c768f1 100644 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Collection.php +++ b/data/web/inc/lib/vendor/illuminate/collections/Collection.php @@ -1,26 +1,28 @@ - * @implements \Tightenco\Collect\Support\Enumerable + * @implements \Illuminate\Support\Enumerable */ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable { /** - * @use \Tightenco\Collect\Support\Traits\EnumeratesValues + * @use \Illuminate\Support\Traits\EnumeratesValues */ use EnumeratesValues, Macroable; @@ -34,7 +36,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Create a new collection. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable|null $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items * @return void */ public function __construct($items = []) @@ -67,7 +69,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get a lazy collection for the items in this collection. * - * @return \Tightenco\Collect\Support\LazyCollection + * @return \Illuminate\Support\LazyCollection */ public function lazy() { @@ -220,7 +222,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * @template TCrossJoinKey * @template TCrossJoinValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable ...$lists + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists * @return static> */ public function crossJoin(...$lists) @@ -233,7 +235,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items in the collection that are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diff($items) @@ -244,7 +246,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items in the collection that are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -256,7 +258,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items in the collection whose keys and values are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diffAssoc($items) @@ -267,7 +269,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items in the collection whose keys and values are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TKey, TKey): int $callback * @return static */ @@ -279,7 +281,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items in the collection whose keys are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diffKeys($items) @@ -290,7 +292,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items in the collection whose keys are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TKey, TKey): int $callback * @return static */ @@ -356,11 +358,15 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get all items except for those with the specified keys. * - * @param \Tightenco\Collect\Support\Enumerable|array $keys + * @param \Illuminate\Support\Enumerable|array|string $keys * @return static */ public function except($keys) { + if (is_null($keys)) { + return new static($this->items); + } + if ($keys instanceof Enumerable) { $keys = $keys->all(); } elseif (! is_array($keys)) { @@ -373,10 +379,10 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Run a filter over each of the items. * - * @param (callable(TValue, TKey): bool)|null $callback + * @param (callable(TValue, TKey): bool)|null $callback * @return static */ - public function filter(callable $callback = null) + public function filter(?callable $callback = null) { if ($callback) { return new static(Arr::where($this->items, $callback)); @@ -394,7 +400,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * @param TFirstDefault|(\Closure(): TFirstDefault) $default * @return TValue|TFirstDefault */ - public function first(callable $callback = null, $default = null) + public function first(?callable $callback = null, $default = null) { return Arr::first($this->items, $callback, $default); } @@ -423,12 +429,13 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Remove an item from the collection by key. * - * @param TKey|array $keys + * \Illuminate\Contracts\Support\Arrayable|iterable|TKey $keys + * * @return $this */ public function forget($keys) { - foreach ((array) $keys as $key) { + foreach ($this->getArrayableItems($keys) as $key) { $this->offsetUnset($key); } @@ -456,9 +463,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get an item from the collection by key or add it to collection if it does not exist. * + * @template TGetOrPutValue + * * @param mixed $key - * @param mixed $value - * @return mixed + * @param TGetOrPutValue|(\Closure(): TGetOrPutValue) $value + * @return TValue|TGetOrPutValue */ public function getOrPut($key, $value) { @@ -500,6 +509,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl foreach ($groupKeys as $groupKey) { $groupKey = match (true) { is_bool($groupKey) => (int) $groupKey, + $groupKey instanceof \BackedEnum => $groupKey->value, $groupKey instanceof \Stringable => (string) $groupKey, default => $groupKey, }; @@ -603,7 +613,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl $first = $this->first(); - if (is_array($first) || (is_object($first) && ! $first instanceof \Illuminate\Support\Stringable)) { + if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) { return implode($glue ?? '', $this->pluck($value)->all()); } @@ -613,7 +623,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Intersect the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersect($items) @@ -624,7 +634,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Intersect the collection with the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -636,7 +646,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Intersect the collection with the given items with additional index check. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersectAssoc($items) @@ -647,7 +657,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Intersect the collection with the given items with additional index check, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -659,7 +669,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Intersect the collection with the given items by key. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersectByKeys($items) @@ -738,7 +748,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * @param TLastDefault|(\Closure(): TLastDefault) $default * @return TValue|TLastDefault */ - public function last(callable $callback = null, $default = null) + public function last(?callable $callback = null, $default = null) { return Arr::last($this->items, $callback, $default); } @@ -813,23 +823,13 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl */ public function mapWithKeys(callable $callback) { - $result = []; - - foreach ($this->items as $key => $value) { - $assoc = $callback($value, $key); - - foreach ($assoc as $mapKey => $mapValue) { - $result[$mapKey] = $mapValue; - } - } - - return new static($result); + return new static(Arr::mapWithKeys($this->items, $callback)); } /** * Merge the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function merge($items) @@ -842,7 +842,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * * @template TMergeRecursiveValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function mergeRecursive($items) @@ -855,7 +855,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * * @template TCombineValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function combine($values) @@ -866,7 +866,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Union the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function union($items) @@ -901,7 +901,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get the items with the specified keys. * - * @param \Tightenco\Collect\Support\Enumerable|array|string|null $keys + * @param \Illuminate\Support\Enumerable|array|string|null $keys * @return static */ public function only($keys) @@ -919,6 +919,27 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl return new static(Arr::only($this->items, $keys)); } + /** + * Select specific values from the items within the collection. + * + * @param \Illuminate\Support\Enumerable|array|string|null $keys + * @return static + */ + public function select($keys) + { + if (is_null($keys)) { + return new static($this->items); + } + + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } + + $keys = is_array($keys) ? $keys : func_get_args(); + + return new static(Arr::select($this->items, $keys)); + } + /** * Get and remove the last N items from the collection. * @@ -978,8 +999,11 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static */ public function concat($source) { @@ -1024,27 +1048,28 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * Get one or a specified number of items randomly from the collection. * * @param (callable(self): int)|int|null $number + * @param bool $preserveKeys * @return static|TValue * * @throws \InvalidArgumentException */ - public function random($number = null) + public function random($number = null, $preserveKeys = false) { if (is_null($number)) { return Arr::random($this->items); } if (is_callable($number)) { - return new static(Arr::random($this->items, $number($this))); + return new static(Arr::random($this->items, $number($this), $preserveKeys)); } - return new static(Arr::random($this->items, $number)); + return new static(Arr::random($this->items, $number, $preserveKeys)); } /** * Replace the collection items with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function replace($items) @@ -1055,7 +1080,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Recursively replace the collection items with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function replaceRecursive($items) @@ -1078,7 +1103,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * * @param TValue|(callable(TValue,TKey): bool) $value * @param bool $strict - * @return TKey|bool + * @return TKey|false */ public function search($value, $strict = false) { @@ -1100,17 +1125,27 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * * @param int $count * @return static|TValue|null + * + * @throws \InvalidArgumentException */ public function shift($count = 1) { - if ($count === 1) { - return array_shift($this->items); + if ($count < 0) { + throw new InvalidArgumentException('Number of shifted items may not be less than zero.'); } if ($this->isEmpty()) { + return null; + } + + if ($count === 0) { return new static; } + if ($count === 1) { + return array_shift($this->items); + } + $results = []; $collectionCount = $this->count(); @@ -1144,9 +1179,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl { $chunks = floor(($this->count() - $size) / $step) + 1; - return static::times($chunks, function ($number) use ($size, $step) { - return $this->slice(($number - 1) * $step, $size); - }); + return static::times($chunks, fn ($number) => $this->slice(($number - 1) * $step, $size)); } /** @@ -1250,8 +1283,8 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * @param mixed $value * @return TValue * - * @throws \Tightenco\Collect\Support\ItemNotFoundException - * @throws \Tightenco\Collect\Support\MultipleItemsFoundException + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException */ public function sole($key = null, $operator = null, $value = null) { @@ -1282,7 +1315,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * @param mixed $value * @return TValue * - * @throws \Tightenco\Collect\Support\ItemNotFoundException + * @throws \Illuminate\Support\ItemNotFoundException */ public function firstOrFail($key = null, $operator = null, $value = null) { @@ -1378,7 +1411,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl public function sortBy($callback, $options = SORT_REGULAR, $descending = false) { if (is_array($callback) && ! is_callable($callback)) { - return $this->sortByMany($callback); + return $this->sortByMany($callback, $options); } $results = []; @@ -1409,13 +1442,14 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * Sort the collection using multiple comparisons. * * @param array $comparisons + * @param int $options * @return static */ - protected function sortByMany(array $comparisons = []) + protected function sortByMany(array $comparisons = [], int $options = SORT_REGULAR) { $items = $this->items; - uasort($items, function ($a, $b) use ($comparisons) { + uasort($items, function ($a, $b) use ($comparisons, $options) { foreach ($comparisons as $comparison) { $comparison = Arr::wrap($comparison); @@ -1433,7 +1467,21 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl $values = array_reverse($values); } - $result = $values[0] <=> $values[1]; + if (($options & SORT_FLAG_CASE) === SORT_FLAG_CASE) { + if (($options & SORT_NATURAL) === SORT_NATURAL) { + $result = strnatcasecmp($values[0], $values[1]); + } else { + $result = strcasecmp($values[0], $values[1]); + } + } else { + $result = match ($options) { + SORT_NUMERIC => intval($values[0]) <=> intval($values[1]), + SORT_STRING => strcmp($values[0], $values[1]), + SORT_NATURAL => strnatcmp($values[0], $values[1]), + SORT_LOCALE_STRING => strcoll($values[0], $values[1]), + default => $values[0] <=> $values[1], + }; + } } if ($result === 0) { @@ -1456,6 +1504,16 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl */ public function sortByDesc($callback, $options = SORT_REGULAR) { + if (is_array($callback) && ! is_callable($callback)) { + foreach ($callback as $index => $key) { + $comparison = Arr::wrap($key); + + $comparison[1] = 'desc'; + + $callback[$index] = $comparison; + } + } + return $this->sortBy($callback, $options, true); } @@ -1568,6 +1626,16 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl return $this; } + /** + * Flatten a multi-dimensional associative array with dots. + * + * @return static + */ + public function dot() + { + return new static(Arr::dot($this->all())); + } + /** * Convert a flatten "dot" notation array into an expanded array. * @@ -1622,7 +1690,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl * * @template TZipValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable ...$items + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items * @return static> */ public function zip($items) @@ -1695,7 +1763,7 @@ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerabl /** * Get a base Support collection instance from this collection. * - * @return \Tightenco\Collect\Support\Collection + * @return \Illuminate\Support\Collection */ public function toBase() { diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Enumerable.php b/data/web/inc/lib/vendor/illuminate/collections/Enumerable.php similarity index 88% rename from data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Enumerable.php rename to data/web/inc/lib/vendor/illuminate/collections/Enumerable.php index 1f0db03ae..806a6923f 100644 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Enumerable.php +++ b/data/web/inc/lib/vendor/illuminate/collections/Enumerable.php @@ -1,20 +1,21 @@ + * @template-covariant TValue + * + * @extends \Illuminate\Contracts\Support\Arrayable * @extends \IteratorAggregate */ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable @@ -25,7 +26,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @template TMakeKey of array-key * @template TMakeValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable|null $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items * @return static */ public static function make($items = []); @@ -37,7 +38,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param callable|null $callback * @return static */ - public static function times($number, callable $callback = null); + public static function times($number, ?callable $callback = null); /** * Create a collection with the given range. @@ -167,7 +168,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @template TCrossJoinKey * @template TCrossJoinValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable ...$lists + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists * @return static> */ public function crossJoin(...$lists); @@ -190,7 +191,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items that are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diff($items); @@ -198,7 +199,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items that are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -207,7 +208,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items whose keys and values are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diffAssoc($items); @@ -215,7 +216,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items whose keys and values are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TKey, TKey): int $callback * @return static */ @@ -224,7 +225,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items whose keys are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diffKeys($items); @@ -232,7 +233,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items whose keys are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TKey, TKey): int $callback * @return static */ @@ -284,7 +285,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get all items except for those with the specified keys. * - * @param \Tightenco\Collect\Support\Enumerable|array $keys + * @param \Illuminate\Support\Enumerable|array $keys * @return static */ public function except($keys); @@ -295,7 +296,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable(TValue): bool)|null $callback * @return static */ - public function filter(callable $callback = null); + public function filter(?callable $callback = null); /** * Apply the callback if the given "value" is (or resolves to) truthy. @@ -307,7 +308,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable($this): TWhenReturnType)|null $default * @return $this|TWhenReturnType */ - public function when($value, callable $callback = null, callable $default = null); + public function when($value, ?callable $callback = null, ?callable $default = null); /** * Apply the callback if the collection is empty. @@ -318,7 +319,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable($this): TWhenEmptyReturnType)|null $default * @return $this|TWhenEmptyReturnType */ - public function whenEmpty(callable $callback, callable $default = null); + public function whenEmpty(callable $callback, ?callable $default = null); /** * Apply the callback if the collection is not empty. @@ -329,7 +330,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable($this): TWhenNotEmptyReturnType)|null $default * @return $this|TWhenNotEmptyReturnType */ - public function whenNotEmpty(callable $callback, callable $default = null); + public function whenNotEmpty(callable $callback, ?callable $default = null); /** * Apply the callback if the given "value" is (or resolves to) truthy. @@ -341,7 +342,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable($this): TUnlessReturnType)|null $default * @return $this|TUnlessReturnType */ - public function unless($value, callable $callback, callable $default = null); + public function unless($value, callable $callback, ?callable $default = null); /** * Apply the callback unless the collection is empty. @@ -352,7 +353,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable($this): TUnlessEmptyReturnType)|null $default * @return $this|TUnlessEmptyReturnType */ - public function unlessEmpty(callable $callback, callable $default = null); + public function unlessEmpty(callable $callback, ?callable $default = null); /** * Apply the callback unless the collection is not empty. @@ -363,7 +364,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param (callable($this): TUnlessNotEmptyReturnType)|null $default * @return $this|TUnlessNotEmptyReturnType */ - public function unlessNotEmpty(callable $callback, callable $default = null); + public function unlessNotEmpty(callable $callback, ?callable $default = null); /** * Filter items by the given key value pair. @@ -404,7 +405,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * Filter items by the given key value pair. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict * @return static */ @@ -414,7 +415,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereInStrict($key, $values); @@ -423,7 +424,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * Filter items such that the value of the given key is between the given values. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereBetween($key, $values); @@ -432,7 +433,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * Filter items such that the value of the given key is not between the given values. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereNotBetween($key, $values); @@ -441,7 +442,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * Filter items by the given key value pair. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict * @return static */ @@ -451,7 +452,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereNotInStrict($key, $values); @@ -475,7 +476,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param TFirstDefault|(\Closure(): TFirstDefault) $default * @return TValue|TFirstDefault */ - public function first(callable $callback = null, $default = null); + public function first(?callable $callback = null, $default = null); /** * Get the first item by the given key value pair. @@ -549,7 +550,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Concatenate values of a given key as a string. * - * @param string $value + * @param callable|string $value * @param string|null $glue * @return string */ @@ -558,7 +559,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Intersect the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersect($items); @@ -566,7 +567,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Intersect the collection with the given items by key. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersectByKeys($items); @@ -617,7 +618,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param TLastDefault|(\Closure(): TLastDefault) $default * @return TValue|TLastDefault */ - public function last(callable $callback = null, $default = null); + public function last(?callable $callback = null, $default = null); /** * Run a map over each of the items. @@ -682,7 +683,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @template TFlatMapKey of array-key * @template TFlatMapValue * - * @param callable(TValue, TKey): (\Tightenco\Collect\Support\Collection|array) $callback + * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback * @return static */ public function flatMap(callable $callback); @@ -700,7 +701,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Merge the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function merge($items); @@ -710,7 +711,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * * @template TMergeRecursiveValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function mergeRecursive($items); @@ -720,7 +721,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * * @template TCombineValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function combine($values); @@ -728,7 +729,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Union the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function union($items); @@ -761,7 +762,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Get the items with the specified keys. * - * @param \Tightenco\Collect\Support\Enumerable|array|string $keys + * @param \Illuminate\Support\Enumerable|array|string $keys * @return static */ public function only($keys); @@ -788,8 +789,11 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static */ public function concat($source); @@ -829,7 +833,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Replace the collection items with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function replace($items); @@ -837,7 +841,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Recursively replace the collection items with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function replaceRecursive($items); @@ -924,8 +928,8 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param mixed $value * @return TValue * - * @throws \Tightenco\Collect\Support\ItemNotFoundException - * @throws \Tightenco\Collect\Support\MultipleItemsFoundException + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException */ public function sole($key = null, $operator = null, $value = null); @@ -937,7 +941,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * @param mixed $value * @return TValue * - * @throws \Tightenco\Collect\Support\ItemNotFoundException + * @throws \Illuminate\Support\ItemNotFoundException */ public function firstOrFail($key = null, $operator = null, $value = null); @@ -1078,8 +1082,10 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Pass the collection into a new class. * - * @param class-string $class - * @return mixed + * @template TPipeIntoValue + * + * @param class-string $class + * @return TPipeIntoValue */ public function pipeInto($class); @@ -1180,7 +1186,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, * * @template TZipValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable ...$items + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items * @return static> */ public function zip($items); @@ -1188,7 +1194,7 @@ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, /** * Collect the values into a collection. * - * @return \Tightenco\Collect\Support\Collection + * @return \Illuminate\Support\Collection */ public function collect(); diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/HigherOrderCollectionProxy.php b/data/web/inc/lib/vendor/illuminate/collections/HigherOrderCollectionProxy.php similarity index 86% rename from data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/HigherOrderCollectionProxy.php rename to data/web/inc/lib/vendor/illuminate/collections/HigherOrderCollectionProxy.php index 01ac43f96..106356c3a 100644 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/HigherOrderCollectionProxy.php +++ b/data/web/inc/lib/vendor/illuminate/collections/HigherOrderCollectionProxy.php @@ -1,16 +1,16 @@ + * @template-covariant TValue + * + * @implements \Illuminate\Support\Enumerable */ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable { /** - * @use \Tightenco\Collect\Support\Traits\EnumeratesValues + * @use \Illuminate\Support\Traits\EnumeratesValues */ use EnumeratesValues, Macroable; @@ -37,7 +38,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Create a new lazy collection instance. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $source + * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $source * @return void */ public function __construct($source = null) @@ -61,7 +62,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @template TMakeKey of array-key * @template TMakeValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items * @return static */ public static function make($items = []) @@ -281,7 +282,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @template TCrossJoinKey * @template TCrossJoinValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable ...$arrays + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$arrays * @return static> */ public function crossJoin(...$arrays) @@ -321,7 +322,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items that are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diff($items) @@ -332,7 +333,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items that are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -344,7 +345,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items whose keys and values are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diffAssoc($items) @@ -355,7 +356,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items whose keys and values are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TKey, TKey): int $callback * @return static */ @@ -367,7 +368,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items whose keys are not present in the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function diffKeys($items) @@ -378,7 +379,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items whose keys are not present in the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TKey, TKey): int $callback * @return static */ @@ -413,7 +414,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get all items except for those with the specified keys. * - * @param \Tightenco\Collect\Support\Enumerable|array $keys + * @param \Illuminate\Support\Enumerable|array $keys * @return static */ public function except($keys) @@ -427,7 +428,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @param (callable(TValue, TKey): bool)|null $callback * @return static */ - public function filter(callable $callback = null) + public function filter(?callable $callback = null) { if (is_null($callback)) { $callback = fn ($value) => (bool) $value; @@ -451,7 +452,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @param TFirstDefault|(\Closure(): TFirstDefault) $default * @return TValue|TFirstDefault */ - public function first(callable $callback = null, $default = null) + public function first(?callable $callback = null, $default = null) { $iterator = $this->getIterator(); @@ -622,7 +623,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Intersect the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersect($items) @@ -633,7 +634,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Intersect the collection with the given items, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -645,7 +646,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Intersect the collection with the given items with additional index check. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersectAssoc($items) @@ -656,7 +657,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Intersect the collection with the given items with additional index check, using the callback. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @param callable(TValue, TValue): int $callback * @return static */ @@ -668,7 +669,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Intersect the collection with the given items by key. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function intersectByKeys($items) @@ -731,7 +732,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @param TLastDefault|(\Closure(): TLastDefault) $default * @return TValue|TLastDefault */ - public function last(callable $callback = null, $default = null) + public function last(?callable $callback = null, $default = null) { $needle = $placeholder = new stdClass; @@ -830,7 +831,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Merge the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function merge($items) @@ -843,7 +844,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * * @template TMergeRecursiveValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function mergeRecursive($items) @@ -887,7 +888,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Union the collection with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function union($items) @@ -920,7 +921,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Get the items with the specified keys. * - * @param \Tightenco\Collect\Support\Enumerable|array|string $keys + * @param \Illuminate\Support\Enumerable|array|string $keys * @return static */ public function only($keys) @@ -952,11 +953,49 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable }); } + /** + * Select specific values from the items within the collection. + * + * @param \Illuminate\Support\Enumerable|array|string $keys + * @return static + */ + public function select($keys) + { + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } elseif (! is_null($keys)) { + $keys = is_array($keys) ? $keys : func_get_args(); + } + + return new static(function () use ($keys) { + if (is_null($keys)) { + yield from $this; + } else { + foreach ($this as $item) { + $result = []; + + foreach ($keys as $key) { + if (Arr::accessible($item) && Arr::exists($item, $key)) { + $result[$key] = $item[$key]; + } elseif (is_object($item) && isset($item->{$key})) { + $result[$key] = $item->{$key}; + } + } + + yield $result; + } + } + }); + } + /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static */ public function concat($source) { @@ -984,7 +1023,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Replace the collection items with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function replace($items) @@ -1011,7 +1050,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable /** * Recursively replace the collection items with the given items. * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items * @return static */ public function replaceRecursive($items) @@ -1034,7 +1073,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * * @param TValue|(callable(TValue,TKey): bool) $value * @param bool $strict - * @return TKey|bool + * @return TKey|false */ public function search($value, $strict = false) { @@ -1202,8 +1241,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @param mixed $value * @return TValue * - * @throws \Tightenco\Collect\Support\ItemNotFoundException - * @throws \Tightenco\Collect\Support\MultipleItemsFoundException + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException */ public function sole($key = null, $operator = null, $value = null) { @@ -1227,7 +1266,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * @param mixed $value * @return TValue * - * @throws \Tightenco\Collect\Support\ItemNotFoundException + * @throws \Illuminate\Support\ItemNotFoundException */ public function firstOrFail($key = null, $operator = null, $value = null) { @@ -1420,7 +1459,21 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable public function take($limit) { if ($limit < 0) { - return $this->passthru('take', func_get_args()); + return new static(function () use ($limit) { + $limit = abs($limit); + $ringBuffer = []; + $position = 0; + + foreach ($this as $key => $value) { + $ringBuffer[$position] = [$key, $value]; + $position = ($position + 1) % $limit; + } + + for ($i = 0, $end = min($limit, count($ringBuffer)); $i < $end; $i++) { + $pointer = ($position + $i) % $limit; + yield $ringBuffer[$pointer][0] => $ringBuffer[$pointer][1]; + } + }); } return new static(function () use ($limit) { @@ -1518,6 +1571,16 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable }); } + /** + * Flatten a multi-dimensional associative array with dots. + * + * @return static + */ + public function dot() + { + return $this->passthru('dot', []); + } + /** * Convert a flatten "dot" notation array into an expanded array. * @@ -1574,7 +1637,7 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable * * @template TZipValue * - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable ...$items + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items * @return static> */ public function zip($items) @@ -1715,6 +1778,8 @@ class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable */ protected function now() { - return time(); + return class_exists(Carbon::class) + ? Carbon::now()->timestamp + : time(); } } diff --git a/data/web/inc/lib/vendor/illuminate/collections/MultipleItemsFoundException.php b/data/web/inc/lib/vendor/illuminate/collections/MultipleItemsFoundException.php new file mode 100644 index 000000000..d90d835b4 --- /dev/null +++ b/data/web/inc/lib/vendor/illuminate/collections/MultipleItemsFoundException.php @@ -0,0 +1,40 @@ +count = $count; + + parent::__construct("$count items were found.", $code, $previous); + } + + /** + * Get the number of items found. + * + * @return int + */ + public function getCount() + { + return $this->count; + } +} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Traits/EnumeratesValues.php b/data/web/inc/lib/vendor/illuminate/collections/Traits/EnumeratesValues.php similarity index 85% rename from data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Traits/EnumeratesValues.php rename to data/web/inc/lib/vendor/illuminate/collections/Traits/EnumeratesValues.php index 25cde2216..9718f5bc5 100644 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Traits/EnumeratesValues.php +++ b/data/web/inc/lib/vendor/illuminate/collections/Traits/EnumeratesValues.php @@ -1,25 +1,28 @@ |iterable|null $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items * @return static */ public static function make($items = []) @@ -159,7 +164,7 @@ trait EnumeratesValues * @param (callable(int): TTimesValue)|null $callback * @return static */ - public static function times($number, callable $callback = null) + public static function times($number, ?callable $callback = null) { if ($number < 1) { return new static; @@ -296,9 +301,11 @@ trait EnumeratesValues /** * Get a single key's value from the first matching item in the collection. * + * @template TValueDefault + * * @param string $key - * @param mixed $default - * @return mixed + * @param TValueDefault|(\Closure(): TValueDefault) $default + * @return TValue|TValueDefault */ public function value($key, $default = null) { @@ -309,6 +316,35 @@ trait EnumeratesValues return value($default); } + /** + * Ensure that every item in the collection is of the expected type. + * + * @template TEnsureOfType + * + * @param class-string|array> $type + * @return static + * + * @throws \UnexpectedValueException + */ + public function ensure($type) + { + $allowedTypes = is_array($type) ? $type : [$type]; + + return $this->each(function ($item) use ($allowedTypes) { + $itemType = get_debug_type($item); + + foreach ($allowedTypes as $allowedType) { + if ($itemType === $allowedType || $item instanceof $allowedType) { + return true; + } + } + + throw new UnexpectedValueException( + sprintf("Collection should only include [%s] items, but '%s' found.", implode(', ', $allowedTypes), $itemType) + ); + }); + } + /** * Determine if the collection is not empty. * @@ -324,7 +360,7 @@ trait EnumeratesValues * * @template TMapSpreadValue * - * @param callable(mixed): TMapSpreadValue $callback + * @param callable(mixed...): TMapSpreadValue $callback * @return static */ public function mapSpread(callable $callback) @@ -360,7 +396,7 @@ trait EnumeratesValues * @template TFlatMapKey of array-key * @template TFlatMapValue * - * @param callable(TValue, TKey): (\Tightenco\Collect\Support\Collection|array) $callback + * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback * @return static */ public function flatMap(callable $callback) @@ -455,6 +491,25 @@ trait EnumeratesValues return new static([new static($passed), new static($failed)]); } + /** + * Calculate the percentage of items that pass a given truth test. + * + * @param (callable(TValue, TKey): bool) $callback + * @param int $precision + * @return float|null + */ + public function percentage(callable $callback, int $precision = 2) + { + if ($this->isEmpty()) { + return null; + } + + return round( + $this->filter($callback)->count() / $this->count() * 100, + $precision + ); + } + /** * Get the sum of the given values. * @@ -479,7 +534,7 @@ trait EnumeratesValues * @param (callable($this): TWhenEmptyReturnType)|null $default * @return $this|TWhenEmptyReturnType */ - public function whenEmpty(callable $callback, callable $default = null) + public function whenEmpty(callable $callback, ?callable $default = null) { return $this->when($this->isEmpty(), $callback, $default); } @@ -493,7 +548,7 @@ trait EnumeratesValues * @param (callable($this): TWhenNotEmptyReturnType)|null $default * @return $this|TWhenNotEmptyReturnType */ - public function whenNotEmpty(callable $callback, callable $default = null) + public function whenNotEmpty(callable $callback, ?callable $default = null) { return $this->when($this->isNotEmpty(), $callback, $default); } @@ -507,7 +562,7 @@ trait EnumeratesValues * @param (callable($this): TUnlessEmptyReturnType)|null $default * @return $this|TUnlessEmptyReturnType */ - public function unlessEmpty(callable $callback, callable $default = null) + public function unlessEmpty(callable $callback, ?callable $default = null) { return $this->whenNotEmpty($callback, $default); } @@ -521,7 +576,7 @@ trait EnumeratesValues * @param (callable($this): TUnlessNotEmptyReturnType)|null $default * @return $this|TUnlessNotEmptyReturnType */ - public function unlessNotEmpty(callable $callback, callable $default = null) + public function unlessNotEmpty(callable $callback, ?callable $default = null) { return $this->whenEmpty($callback, $default); } @@ -577,7 +632,7 @@ trait EnumeratesValues * Filter items by the given key value pair. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict * @return static */ @@ -592,7 +647,7 @@ trait EnumeratesValues * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereInStrict($key, $values) @@ -604,7 +659,7 @@ trait EnumeratesValues * Filter items such that the value of the given key is between the given values. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereBetween($key, $values) @@ -616,7 +671,7 @@ trait EnumeratesValues * Filter items such that the value of the given key is not between the given values. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereNotBetween($key, $values) @@ -630,7 +685,7 @@ trait EnumeratesValues * Filter items by the given key value pair. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict * @return static */ @@ -645,7 +700,7 @@ trait EnumeratesValues * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param \Tightenco\Collect\Contracts\Support\Arrayable|iterable $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @return static */ public function whereNotInStrict($key, $values) @@ -694,8 +749,10 @@ trait EnumeratesValues /** * Pass the collection into a new class. * - * @param class-string $class - * @return mixed + * @template TPipeIntoValue + * + * @param class-string $class + * @return TPipeIntoValue */ public function pipeInto($class) { @@ -764,6 +821,21 @@ trait EnumeratesValues return $result; } + /** + * Reduce an associative collection to a single value. + * + * @template TReduceWithKeysInitial + * @template TReduceWithKeysReturnType + * + * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback + * @param TReduceWithKeysInitial $initial + * @return TReduceWithKeysReturnType + */ + public function reduceWithKeys(callable $callback, $initial = null) + { + return $this->reduce($callback, $initial); + } + /** * Create a collection of all elements that do not pass a given truth test. * @@ -830,7 +902,7 @@ trait EnumeratesValues /** * Collect the values into a collection. * - * @return \Tightenco\Collect\Support\Collection + * @return \Illuminate\Support\Collection */ public function collect() { @@ -952,21 +1024,18 @@ trait EnumeratesValues { if (is_array($items)) { return $items; - } elseif ($items instanceof Enumerable) { - return $items->all(); - } elseif ($items instanceof Arrayable) { - return $items->toArray(); - } elseif ($items instanceof Traversable) { - return iterator_to_array($items); - } elseif ($items instanceof Jsonable) { - return json_decode($items->toJson(), true); - } elseif ($items instanceof JsonSerializable) { - return (array) $items->jsonSerialize(); - } elseif ($items instanceof UnitEnum) { - return [$items]; } - return (array) $items; + return match (true) { + $items instanceof WeakMap => throw new InvalidArgumentException('Collections can not be created using instances of WeakMap.'), + $items instanceof Enumerable => $items->all(), + $items instanceof Arrayable => $items->toArray(), + $items instanceof Traversable => iterator_to_array($items), + $items instanceof Jsonable => json_decode($items->toJson(), true), + $items instanceof JsonSerializable => (array) $items->jsonSerialize(), + $items instanceof UnitEnum => [$items], + default => (array) $items, + }; } /** diff --git a/data/web/inc/lib/vendor/illuminate/collections/composer.json b/data/web/inc/lib/vendor/illuminate/collections/composer.json new file mode 100644 index 000000000..5e11a788e --- /dev/null +++ b/data/web/inc/lib/vendor/illuminate/collections/composer.json @@ -0,0 +1,42 @@ +{ + "name": "illuminate/collections", + "description": "The Illuminate Collections package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^8.1", + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0" + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "helpers.php" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.2)." + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/data/web/inc/lib/vendor/illuminate/collections/functions.php b/data/web/inc/lib/vendor/illuminate/collections/functions.php new file mode 100644 index 000000000..6ccd9b3ed --- /dev/null +++ b/data/web/inc/lib/vendor/illuminate/collections/functions.php @@ -0,0 +1,27 @@ + $value->value, + $value instanceof \UnitEnum => $value->name, + + default => $value ?? value($default), + }; + } +} diff --git a/data/web/inc/lib/vendor/illuminate/collections/helpers.php b/data/web/inc/lib/vendor/illuminate/collections/helpers.php new file mode 100644 index 000000000..235edceb1 --- /dev/null +++ b/data/web/inc/lib/vendor/illuminate/collections/helpers.php @@ -0,0 +1,226 @@ +|iterable|null $value + * @return \Illuminate\Support\Collection + */ + function collect($value = []) + { + return new Collection($value); + } +} + +if (! function_exists('data_fill')) { + /** + * Fill in data where it's missing. + * + * @param mixed $target + * @param string|array $key + * @param mixed $value + * @return mixed + */ + function data_fill(&$target, $key, $value) + { + return data_set($target, $key, $value, false); + } +} + +if (! function_exists('data_get')) { + /** + * Get an item from an array or object using "dot" notation. + * + * @param mixed $target + * @param string|array|int|null $key + * @param mixed $default + * @return mixed + */ + function data_get($target, $key, $default = null) + { + if (is_null($key)) { + return $target; + } + + $key = is_array($key) ? $key : explode('.', $key); + + foreach ($key as $i => $segment) { + unset($key[$i]); + + if (is_null($segment)) { + return $target; + } + + if ($segment === '*') { + if ($target instanceof Collection) { + $target = $target->all(); + } elseif (! is_iterable($target)) { + return value($default); + } + + $result = []; + + foreach ($target as $item) { + $result[] = data_get($item, $key); + } + + return in_array('*', $key) ? Arr::collapse($result) : $result; + } + + if (Arr::accessible($target) && Arr::exists($target, $segment)) { + $target = $target[$segment]; + } elseif (is_object($target) && isset($target->{$segment})) { + $target = $target->{$segment}; + } else { + return value($default); + } + } + + return $target; + } +} + +if (! function_exists('data_set')) { + /** + * Set an item on an array or object using dot notation. + * + * @param mixed $target + * @param string|array $key + * @param mixed $value + * @param bool $overwrite + * @return mixed + */ + function data_set(&$target, $key, $value, $overwrite = true) + { + $segments = is_array($key) ? $key : explode('.', $key); + + if (($segment = array_shift($segments)) === '*') { + if (! Arr::accessible($target)) { + $target = []; + } + + if ($segments) { + foreach ($target as &$inner) { + data_set($inner, $segments, $value, $overwrite); + } + } elseif ($overwrite) { + foreach ($target as &$inner) { + $inner = $value; + } + } + } elseif (Arr::accessible($target)) { + if ($segments) { + if (! Arr::exists($target, $segment)) { + $target[$segment] = []; + } + + data_set($target[$segment], $segments, $value, $overwrite); + } elseif ($overwrite || ! Arr::exists($target, $segment)) { + $target[$segment] = $value; + } + } elseif (is_object($target)) { + if ($segments) { + if (! isset($target->{$segment})) { + $target->{$segment} = []; + } + + data_set($target->{$segment}, $segments, $value, $overwrite); + } elseif ($overwrite || ! isset($target->{$segment})) { + $target->{$segment} = $value; + } + } else { + $target = []; + + if ($segments) { + data_set($target[$segment], $segments, $value, $overwrite); + } elseif ($overwrite) { + $target[$segment] = $value; + } + } + + return $target; + } +} + +if (! function_exists('data_forget')) { + /** + * Remove / unset an item from an array or object using "dot" notation. + * + * @param mixed $target + * @param string|array|int|null $key + * @return mixed + */ + function data_forget(&$target, $key) + { + $segments = is_array($key) ? $key : explode('.', $key); + + if (($segment = array_shift($segments)) === '*' && Arr::accessible($target)) { + if ($segments) { + foreach ($target as &$inner) { + data_forget($inner, $segments); + } + } + } elseif (Arr::accessible($target)) { + if ($segments && Arr::exists($target, $segment)) { + data_forget($target[$segment], $segments); + } else { + Arr::forget($target, $segment); + } + } elseif (is_object($target)) { + if ($segments && isset($target->{$segment})) { + data_forget($target->{$segment}, $segments); + } elseif (isset($target->{$segment})) { + unset($target->{$segment}); + } + } + + return $target; + } +} + +if (! function_exists('head')) { + /** + * Get the first element of an array. Useful for method chaining. + * + * @param array $array + * @return mixed + */ + function head($array) + { + return reset($array); + } +} + +if (! function_exists('last')) { + /** + * Get the last element from an array. + * + * @param array $array + * @return mixed + */ + function last($array) + { + return end($array); + } +} + +if (! function_exists('value')) { + /** + * Return the default value of the given value. + * + * @param mixed $value + * @param mixed ...$args + * @return mixed + */ + function value($value, ...$args) + { + return $value instanceof Closure ? $value(...$args) : $value; + } +} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Conditionable/HigherOrderWhenProxy.php b/data/web/inc/lib/vendor/illuminate/conditionable/HigherOrderWhenProxy.php similarity index 98% rename from data/web/inc/lib/vendor/tightenco/collect/src/Collect/Conditionable/HigherOrderWhenProxy.php rename to data/web/inc/lib/vendor/illuminate/conditionable/HigherOrderWhenProxy.php index eaf24812b..579114cf1 100644 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Conditionable/HigherOrderWhenProxy.php +++ b/data/web/inc/lib/vendor/illuminate/conditionable/HigherOrderWhenProxy.php @@ -1,6 +1,6 @@ name)) { - $method->setAccessible(true); static::macro($method->name, $method->invoke($mixin)); } } diff --git a/data/web/inc/lib/vendor/illuminate/macroable/composer.json b/data/web/inc/lib/vendor/illuminate/macroable/composer.json new file mode 100644 index 000000000..231d2333a --- /dev/null +++ b/data/web/inc/lib/vendor/illuminate/macroable/composer.json @@ -0,0 +1,33 @@ +{ + "name": "illuminate/macroable", + "description": "The Illuminate Macroable package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^8.1" + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/CHANGELOG.md b/data/web/inc/lib/vendor/symfony/var-dumper/CHANGELOG.md deleted file mode 100644 index 7481cff1a..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/CHANGELOG.md +++ /dev/null @@ -1,91 +0,0 @@ -CHANGELOG -========= - -6.4 ---- - - * Dump uninitialized properties - -6.3 ---- - - * Add caster for `WeakMap` - * Add support of named arguments to `dd()` and `dump()` to display the argument name - * Add support for `Relay\Relay` - * Add display of invisible characters - -6.2 ---- - - * Add support for `FFI\CData` and `FFI\CType` - * Deprecate calling `VarDumper::setHandler()` without arguments - -5.4 ---- - - * Add ability to style integer and double values independently - * Add casters for Symfony's UUIDs and ULIDs - * Add support for `Fiber` - -5.2.0 ------ - - * added support for PHPUnit `--colors` option - * added `VAR_DUMPER_FORMAT=server` env var value support - * prevent replacing the handler when the `VAR_DUMPER_FORMAT` env var is set - -5.1.0 ------ - - * added `RdKafka` support - -4.4.0 ------ - - * added `VarDumperTestTrait::setUpVarDumper()` and `VarDumperTestTrait::tearDownVarDumper()` - to configure casters & flags to use in tests - * added `ImagineCaster` and infrastructure to dump images - * added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data - * added `UuidCaster` - * made all casters final - * added support for the `NO_COLOR` env var (https://no-color.org/) - -4.3.0 ------ - - * added `DsCaster` to support dumping the contents of data structures from the Ds extension - -4.2.0 ------ - - * support selecting the format to use by setting the environment variable `VAR_DUMPER_FORMAT` to `html` or `cli` - -4.1.0 ------ - - * added a `ServerDumper` to send serialized Data clones to a server - * added a `ServerDumpCommand` and `DumpServer` to run a server collecting - and displaying dumps on a single place with multiple formats support - * added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support - -4.0.0 ------ - - * support for passing `\ReflectionClass` instances to the `Caster::castObject()` - method has been dropped, pass class names as strings instead - * the `Data::getRawData()` method has been removed - * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$filter = 0` - argument and moves `$message = ''` argument at 4th position. - * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$filter = 0` - argument and moves `$message = ''` argument at 4th position. - -3.4.0 ------ - - * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth - * deprecated `MongoCaster` - -2.7.0 ------ - - * deprecated `Cloner\Data::getLimitedClone()`. Use `withMaxDepth`, `withMaxItemsPerDepth` or `withRefHandles` instead. diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/AmqpCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/AmqpCaster.php deleted file mode 100644 index 22026f46a..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/AmqpCaster.php +++ /dev/null @@ -1,227 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Amqp related classes to array representation. - * - * @author Grégoire Pineau - * - * @final - */ -class AmqpCaster -{ - private const FLAGS = [ - \AMQP_DURABLE => 'AMQP_DURABLE', - \AMQP_PASSIVE => 'AMQP_PASSIVE', - \AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', - \AMQP_AUTODELETE => 'AMQP_AUTODELETE', - \AMQP_INTERNAL => 'AMQP_INTERNAL', - \AMQP_NOLOCAL => 'AMQP_NOLOCAL', - \AMQP_AUTOACK => 'AMQP_AUTOACK', - \AMQP_IFEMPTY => 'AMQP_IFEMPTY', - \AMQP_IFUNUSED => 'AMQP_IFUNUSED', - \AMQP_MANDATORY => 'AMQP_MANDATORY', - \AMQP_IMMEDIATE => 'AMQP_IMMEDIATE', - \AMQP_MULTIPLE => 'AMQP_MULTIPLE', - \AMQP_NOWAIT => 'AMQP_NOWAIT', - \AMQP_REQUEUE => 'AMQP_REQUEUE', - ]; - - private const EXCHANGE_TYPES = [ - \AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', - \AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', - \AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', - \AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', - ]; - - /** - * @return array - */ - public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'is_connected' => $c->isConnected(), - ]; - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPConnection\x00login"])) { - return $a; - } - - // BC layer in the amqp lib - if (method_exists($c, 'getReadTimeout')) { - $timeout = $c->getReadTimeout(); - } else { - $timeout = $c->getTimeout(); - } - - $a += [ - $prefix.'is_connected' => $c->isConnected(), - $prefix.'login' => $c->getLogin(), - $prefix.'password' => $c->getPassword(), - $prefix.'host' => $c->getHost(), - $prefix.'vhost' => $c->getVhost(), - $prefix.'port' => $c->getPort(), - $prefix.'read_timeout' => $timeout, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'is_connected' => $c->isConnected(), - $prefix.'channel_id' => $c->getChannelId(), - ]; - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPChannel\x00connection"])) { - return $a; - } - - $a += [ - $prefix.'connection' => $c->getConnection(), - $prefix.'prefetch_size' => $c->getPrefetchSize(), - $prefix.'prefetch_count' => $c->getPrefetchCount(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'flags' => self::extractFlags($c->getFlags()), - ]; - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPQueue\x00name"])) { - return $a; - } - - $a += [ - $prefix.'connection' => $c->getConnection(), - $prefix.'channel' => $c->getChannel(), - $prefix.'name' => $c->getName(), - $prefix.'arguments' => $c->getArguments(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'flags' => self::extractFlags($c->getFlags()), - ]; - - $type = isset(self::EXCHANGE_TYPES[$c->getType()]) ? new ConstStub(self::EXCHANGE_TYPES[$c->getType()], $c->getType()) : $c->getType(); - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPExchange\x00name"])) { - $a["\x00AMQPExchange\x00type"] = $type; - - return $a; - } - - $a += [ - $prefix.'connection' => $c->getConnection(), - $prefix.'channel' => $c->getChannel(), - $prefix.'name' => $c->getName(), - $prefix.'type' => $type, - $prefix.'arguments' => $c->getArguments(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode()); - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPEnvelope\x00body"])) { - $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode; - - return $a; - } - - if (!($filter & Caster::EXCLUDE_VERBOSE)) { - $a += [$prefix.'body' => $c->getBody()]; - } - - $a += [ - $prefix.'delivery_tag' => $c->getDeliveryTag(), - $prefix.'is_redelivery' => $c->isRedelivery(), - $prefix.'exchange_name' => $c->getExchangeName(), - $prefix.'routing_key' => $c->getRoutingKey(), - $prefix.'content_type' => $c->getContentType(), - $prefix.'content_encoding' => $c->getContentEncoding(), - $prefix.'headers' => $c->getHeaders(), - $prefix.'delivery_mode' => $deliveryMode, - $prefix.'priority' => $c->getPriority(), - $prefix.'correlation_id' => $c->getCorrelationId(), - $prefix.'reply_to' => $c->getReplyTo(), - $prefix.'expiration' => $c->getExpiration(), - $prefix.'message_id' => $c->getMessageId(), - $prefix.'timestamp' => $c->getTimeStamp(), - $prefix.'type' => $c->getType(), - $prefix.'user_id' => $c->getUserId(), - $prefix.'app_id' => $c->getAppId(), - ]; - - return $a; - } - - private static function extractFlags(int $flags): ConstStub - { - $flagsArray = []; - - foreach (self::FLAGS as $value => $name) { - if ($flags & $value) { - $flagsArray[] = $name; - } - } - - if (!$flagsArray) { - $flagsArray = ['AMQP_NOPARAM']; - } - - return new ConstStub(implode('|', $flagsArray), $flags); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ArgsStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ArgsStub.php deleted file mode 100644 index 9dc24c1b1..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ArgsStub.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a list of function arguments. - * - * @author Nicolas Grekas - */ -class ArgsStub extends EnumStub -{ - private static array $parameters = []; - - public function __construct(array $args, string $function, ?string $class) - { - [$variadic, $params] = self::getParameters($function, $class); - - $values = []; - foreach ($args as $k => $v) { - $values[$k] = !\is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; - } - if (null === $params) { - parent::__construct($values, false); - - return; - } - if (\count($values) < \count($params)) { - $params = \array_slice($params, 0, \count($values)); - } elseif (\count($values) > \count($params)) { - $values[] = new EnumStub(array_splice($values, \count($params)), false); - $params[] = $variadic; - } - if (['...'] === $params) { - $this->dumpKeys = false; - $this->value = $values[0]->value; - } else { - $this->value = array_combine($params, $values); - } - } - - private static function getParameters(string $function, ?string $class): array - { - if (isset(self::$parameters[$k = $class.'::'.$function])) { - return self::$parameters[$k]; - } - - try { - $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); - } catch (\ReflectionException) { - return [null, null]; - } - - $variadic = '...'; - $params = []; - foreach ($r->getParameters() as $v) { - $k = '$'.$v->name; - if ($v->isPassedByReference()) { - $k = '&'.$k; - } - if ($v->isVariadic()) { - $variadic .= $k; - } else { - $params[] = $k; - } - } - - return self::$parameters[$k] = [$variadic, $params]; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/Caster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/Caster.php deleted file mode 100644 index d9577e7ae..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/Caster.php +++ /dev/null @@ -1,198 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Helper for filtering out properties in casters. - * - * @author Nicolas Grekas - * - * @final - */ -class Caster -{ - public const EXCLUDE_VERBOSE = 1; - public const EXCLUDE_VIRTUAL = 2; - public const EXCLUDE_DYNAMIC = 4; - public const EXCLUDE_PUBLIC = 8; - public const EXCLUDE_PROTECTED = 16; - public const EXCLUDE_PRIVATE = 32; - public const EXCLUDE_NULL = 64; - public const EXCLUDE_EMPTY = 128; - public const EXCLUDE_NOT_IMPORTANT = 256; - public const EXCLUDE_STRICT = 512; - public const EXCLUDE_UNINITIALIZED = 1024; - - public const PREFIX_VIRTUAL = "\0~\0"; - public const PREFIX_DYNAMIC = "\0+\0"; - public const PREFIX_PROTECTED = "\0*\0"; - // usage: sprintf(Caster::PATTERN_PRIVATE, $class, $property) - public const PATTERN_PRIVATE = "\0%s\0%s"; - - private static array $classProperties = []; - - /** - * Casts objects to arrays and adds the dynamic property prefix. - * - * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not - */ - public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, ?string $debugClass = null): array - { - if ($hasDebugInfo) { - try { - $debugInfo = $obj->__debugInfo(); - } catch (\Throwable) { - // ignore failing __debugInfo() - $hasDebugInfo = false; - } - } - - $a = $obj instanceof \Closure ? [] : (array) $obj; - - if ($obj instanceof \__PHP_Incomplete_Class) { - return $a; - } - - $classProperties = self::$classProperties[$class] ??= self::getClassProperties(new \ReflectionClass($class)); - $a = array_replace($classProperties, $a); - - if ($a) { - $debugClass ??= get_debug_type($obj); - - $i = 0; - $prefixedKeys = []; - foreach ($a as $k => $v) { - if ("\0" !== ($k[0] ?? '')) { - if (!isset($classProperties[$k])) { - $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; - } - } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { - $prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0"); - } - ++$i; - } - if ($prefixedKeys) { - $keys = array_keys($a); - foreach ($prefixedKeys as $i => $k) { - $keys[$i] = $k; - } - $a = array_combine($keys, $a); - } - } - - if ($hasDebugInfo && \is_array($debugInfo)) { - foreach ($debugInfo as $k => $v) { - if (!isset($k[0]) || "\0" !== $k[0]) { - if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) { - continue; - } - $k = self::PREFIX_VIRTUAL.$k; - } - - unset($a[$k]); - $a[$k] = $v; - } - } - - return $a; - } - - /** - * Filters out the specified properties. - * - * By default, a single match in the $filter bit field filters properties out, following an "or" logic. - * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed. - * - * @param array $a The array containing the properties to filter - * @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out - * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set - * @param int|null &$count Set to the number of removed properties - */ - public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array - { - $count = 0; - - foreach ($a as $k => $v) { - $type = self::EXCLUDE_STRICT & $filter; - - if (null === $v) { - $type |= self::EXCLUDE_NULL & $filter; - $type |= self::EXCLUDE_EMPTY & $filter; - } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { - $type |= self::EXCLUDE_EMPTY & $filter; - } elseif ($v instanceof UninitializedStub) { - $type |= self::EXCLUDE_UNINITIALIZED & $filter; - } - if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { - $type |= self::EXCLUDE_NOT_IMPORTANT; - } - if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) { - $type |= self::EXCLUDE_VERBOSE; - } - - if (!isset($k[1]) || "\0" !== $k[0]) { - $type |= self::EXCLUDE_PUBLIC & $filter; - } elseif ('~' === $k[1]) { - $type |= self::EXCLUDE_VIRTUAL & $filter; - } elseif ('+' === $k[1]) { - $type |= self::EXCLUDE_DYNAMIC & $filter; - } elseif ('*' === $k[1]) { - $type |= self::EXCLUDE_PROTECTED & $filter; - } else { - $type |= self::EXCLUDE_PRIVATE & $filter; - } - - if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) { - unset($a[$k]); - ++$count; - } - } - - return $a; - } - - public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array - { - if (isset($a['__PHP_Incomplete_Class_Name'])) { - $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; - unset($a['__PHP_Incomplete_Class_Name']); - } - - return $a; - } - - private static function getClassProperties(\ReflectionClass $class): array - { - $classProperties = []; - $className = $class->name; - - if ($parent = $class->getParentClass()) { - $classProperties += self::$classProperties[$parent->name] ??= self::getClassProperties($parent); - } - - foreach ($class->getProperties() as $p) { - if ($p->isStatic()) { - continue; - } - - $classProperties[match (true) { - $p->isPublic() => $p->name, - $p->isProtected() => self::PREFIX_PROTECTED.$p->name, - default => "\0".$className."\0".$p->name, - }] = new UninitializedStub($p); - } - - return $classProperties; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ClassStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ClassStub.php deleted file mode 100644 index 914728663..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ClassStub.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a PHP class identifier. - * - * @author Nicolas Grekas - */ -class ClassStub extends ConstStub -{ - /** - * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name - * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier - */ - public function __construct(string $identifier, callable|array|string|null $callable = null) - { - $this->value = $identifier; - - try { - if (null !== $callable) { - if ($callable instanceof \Closure) { - $r = new \ReflectionFunction($callable); - } elseif (\is_object($callable)) { - $r = [$callable, '__invoke']; - } elseif (\is_array($callable)) { - $r = $callable; - } elseif (false !== $i = strpos($callable, '::')) { - $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; - } else { - $r = new \ReflectionFunction($callable); - } - } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { - $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; - } else { - $r = new \ReflectionClass($identifier); - } - - if (\is_array($r)) { - try { - $r = new \ReflectionMethod($r[0], $r[1]); - } catch (\ReflectionException) { - $r = new \ReflectionClass($r[0]); - } - } - - if (str_contains($identifier, "@anonymous\0")) { - $this->value = $identifier = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $identifier); - } - - if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { - $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); - $s = ReflectionCaster::getSignature($s); - - if (str_ends_with($identifier, '()')) { - $this->value = substr_replace($identifier, $s, -2); - } else { - $this->value .= $s; - } - } - } catch (\ReflectionException) { - return; - } finally { - if (0 < $i = strrpos($this->value, '\\')) { - $this->attr['ellipsis'] = \strlen($this->value) - $i; - $this->attr['ellipsis-type'] = 'class'; - $this->attr['ellipsis-tail'] = 1; - } - } - - if ($f = $r->getFileName()) { - $this->attr['file'] = $f; - $this->attr['line'] = $r->getStartLine(); - } - } - - /** - * @return mixed - */ - public static function wrapCallable(mixed $callable) - { - if (\is_object($callable) || !\is_callable($callable)) { - return $callable; - } - - if (!\is_array($callable)) { - $callable = new static($callable, $callable); - } elseif (\is_string($callable[0])) { - $callable[0] = new static($callable[0], $callable); - } else { - $callable[1] = new static($callable[1], $callable); - } - - return $callable; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ConstStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ConstStub.php deleted file mode 100644 index 587c6c398..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ConstStub.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a PHP constant and its value. - * - * @author Nicolas Grekas - */ -class ConstStub extends Stub -{ - public function __construct(string $name, string|int|float|null $value = null) - { - $this->class = $name; - $this->value = 1 < \func_num_args() ? $value : $name; - } - - public function __toString(): string - { - return (string) $this->value; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/CutArrayStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/CutArrayStub.php deleted file mode 100644 index 0e4fb363d..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/CutArrayStub.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents a cut array. - * - * @author Nicolas Grekas - */ -class CutArrayStub extends CutStub -{ - public $preservedSubset; - - public function __construct(array $value, array $preservedKeys) - { - parent::__construct($value); - - $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); - $this->cut -= \count($this->preservedSubset); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/CutStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/CutStub.php deleted file mode 100644 index 772399ef6..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/CutStub.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents the main properties of a PHP variable, pre-casted by a caster. - * - * @author Nicolas Grekas - */ -class CutStub extends Stub -{ - public function __construct(mixed $value) - { - $this->value = $value; - - switch (\gettype($value)) { - case 'object': - $this->type = self::TYPE_OBJECT; - $this->class = $value::class; - - if ($value instanceof \Closure) { - ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); - } - - $this->cut = -1; - break; - - case 'array': - $this->type = self::TYPE_ARRAY; - $this->class = self::ARRAY_ASSOC; - $this->cut = $this->value = \count($value); - break; - - case 'resource': - case 'unknown type': - case 'resource (closed)': - $this->type = self::TYPE_RESOURCE; - $this->handle = (int) $value; - if ('Unknown' === $this->class = @get_resource_type($value)) { - $this->class = 'Closed'; - } - $this->cut = -1; - break; - - case 'string': - $this->type = self::TYPE_STRING; - $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; - $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8'); - $this->value = ''; - break; - } - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DOMCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DOMCaster.php deleted file mode 100644 index d2d3fc129..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DOMCaster.php +++ /dev/null @@ -1,312 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts DOM related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class DOMCaster -{ - private const ERROR_CODES = [ - \DOM_PHP_ERR => 'DOM_PHP_ERR', - \DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', - \DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', - \DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR', - \DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR', - \DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR', - \DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR', - \DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR', - \DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR', - \DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR', - \DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR', - \DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR', - \DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR', - \DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR', - \DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', - \DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', - \DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', - ]; - - private const NODE_TYPES = [ - \XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', - \XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', - \XML_TEXT_NODE => 'XML_TEXT_NODE', - \XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE', - \XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE', - \XML_ENTITY_NODE => 'XML_ENTITY_NODE', - \XML_PI_NODE => 'XML_PI_NODE', - \XML_COMMENT_NODE => 'XML_COMMENT_NODE', - \XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE', - \XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE', - \XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE', - \XML_NOTATION_NODE => 'XML_NOTATION_NODE', - \XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE', - \XML_DTD_NODE => 'XML_DTD_NODE', - \XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE', - \XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', - \XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', - \XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', - ]; - - /** - * @return array - */ - public static function castException(\DOMException $e, array $a, Stub $stub, bool $isNested) - { - $k = Caster::PREFIX_PROTECTED.'code'; - if (isset($a[$k], self::ERROR_CODES[$a[$k]])) { - $a[$k] = new ConstStub(self::ERROR_CODES[$a[$k]], $a[$k]); - } - - return $a; - } - - /** - * @return array - */ - public static function castLength($dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'length' => $dom->length, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castImplementation(\DOMImplementation $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'Core' => '1.0', - Caster::PREFIX_VIRTUAL.'XML' => '2.0', - ]; - - return $a; - } - - /** - * @return array - */ - public static function castNode(\DOMNode $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'nodeName' => $dom->nodeName, - 'nodeValue' => new CutStub($dom->nodeValue), - 'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType), - 'parentNode' => new CutStub($dom->parentNode), - 'childNodes' => $dom->childNodes, - 'firstChild' => new CutStub($dom->firstChild), - 'lastChild' => new CutStub($dom->lastChild), - 'previousSibling' => new CutStub($dom->previousSibling), - 'nextSibling' => new CutStub($dom->nextSibling), - 'attributes' => $dom->attributes, - 'ownerDocument' => new CutStub($dom->ownerDocument), - 'namespaceURI' => $dom->namespaceURI, - 'prefix' => $dom->prefix, - 'localName' => $dom->localName, - 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, - 'textContent' => new CutStub($dom->textContent), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'nodeName' => $dom->nodeName, - 'nodeValue' => new CutStub($dom->nodeValue), - 'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType), - 'prefix' => $dom->prefix, - 'localName' => $dom->localName, - 'namespaceURI' => $dom->namespaceURI, - 'ownerDocument' => new CutStub($dom->ownerDocument), - 'parentNode' => new CutStub($dom->parentNode), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - 'doctype' => $dom->doctype, - 'implementation' => $dom->implementation, - 'documentElement' => new CutStub($dom->documentElement), - 'actualEncoding' => $dom->actualEncoding, - 'encoding' => $dom->encoding, - 'xmlEncoding' => $dom->xmlEncoding, - 'standalone' => $dom->standalone, - 'xmlStandalone' => $dom->xmlStandalone, - 'version' => $dom->version, - 'xmlVersion' => $dom->xmlVersion, - 'strictErrorChecking' => $dom->strictErrorChecking, - 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, - 'config' => $dom->config, - 'formatOutput' => $dom->formatOutput, - 'validateOnParse' => $dom->validateOnParse, - 'resolveExternals' => $dom->resolveExternals, - 'preserveWhiteSpace' => $dom->preserveWhiteSpace, - 'recover' => $dom->recover, - 'substituteEntities' => $dom->substituteEntities, - ]; - - if (!($filter & Caster::EXCLUDE_VERBOSE)) { - $formatOutput = $dom->formatOutput; - $dom->formatOutput = true; - $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; - $dom->formatOutput = $formatOutput; - } - - return $a; - } - - /** - * @return array - */ - public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'data' => $dom->data, - 'length' => $dom->length, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'name' => $dom->name, - 'specified' => $dom->specified, - 'value' => $dom->value, - 'ownerElement' => $dom->ownerElement, - 'schemaTypeInfo' => $dom->schemaTypeInfo, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castElement(\DOMElement $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'tagName' => $dom->tagName, - 'schemaTypeInfo' => $dom->schemaTypeInfo, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castText(\DOMText $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'wholeText' => $dom->wholeText, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'name' => $dom->name, - 'entities' => $dom->entities, - 'notations' => $dom->notations, - 'publicId' => $dom->publicId, - 'systemId' => $dom->systemId, - 'internalSubset' => $dom->internalSubset, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'publicId' => $dom->publicId, - 'systemId' => $dom->systemId, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'publicId' => $dom->publicId, - 'systemId' => $dom->systemId, - 'notationName' => $dom->notationName, - 'actualEncoding' => $dom->actualEncoding, - 'encoding' => $dom->encoding, - 'version' => $dom->version, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'target' => $dom->target, - 'data' => $dom->data, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'document' => $dom->document, - ]; - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DateCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DateCaster.php deleted file mode 100644 index a0cbddb76..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DateCaster.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts DateTimeInterface related classes to array representation. - * - * @author Dany Maillard - * - * @final - */ -class DateCaster -{ - private const PERIOD_LIMIT = 3; - - /** - * @return array - */ - public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter) - { - $prefix = Caster::PREFIX_VIRTUAL; - $location = $d->getTimezone() ? $d->getTimezone()->getLocation() : null; - $fromNow = (new \DateTimeImmutable())->diff($d); - - $title = $d->format('l, F j, Y') - ."\n".self::formatInterval($fromNow).' from now' - .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') - ; - - unset( - $a[Caster::PREFIX_DYNAMIC.'date'], - $a[Caster::PREFIX_DYNAMIC.'timezone'], - $a[Caster::PREFIX_DYNAMIC.'timezone_type'] - ); - $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); - - $stub->class .= $d->format(' @U'); - - return $a; - } - - /** - * @return array - */ - public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter) - { - $now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); - $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); - $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; - - $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; - - return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; - } - - private static function formatInterval(\DateInterval $i): string - { - $format = '%R '; - - if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { - $d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); - $i = $d->diff($d->add($i)); // recalculate carry over points - $format .= 0 < $i->days ? '%ad ' : ''; - } else { - $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); - } - - $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; - $format = '%R ' === $format ? '0s' : $format; - - return $i->format(rtrim($format)); - } - - /** - * @return array - */ - public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter) - { - $location = $timeZone->getLocation(); - $formatted = (new \DateTimeImmutable('now', $timeZone))->format($location ? 'e (P)' : 'P'); - $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; - - $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; - - return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; - } - - /** - * @return array - */ - public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter) - { - $dates = []; - foreach (clone $p as $i => $d) { - if (self::PERIOD_LIMIT === $i) { - $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - $dates[] = sprintf('%s more', ($end = $p->getEndDate()) - ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) - : $p->recurrences - $i - ); - break; - } - $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d)); - } - - $period = sprintf( - 'every %s, from %s%s %s', - self::formatInterval($p->getDateInterval()), - $p->include_start_date ? '[' : ']', - self::formatDateTime($p->getStartDate()), - ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end).(\PHP_VERSION_ID >= 80200 && $p->include_end_date ? ']' : '[') : 'recurring '.$p->recurrences.' time/s' - ); - - $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; - - return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; - } - - private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string - { - return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); - } - - private static function formatSeconds(string $s, string $us): string - { - return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DoctrineCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DoctrineCaster.php deleted file mode 100644 index 3120c3d91..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DoctrineCaster.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Doctrine\Common\Proxy\Proxy as CommonProxy; -use Doctrine\ORM\PersistentCollection; -use Doctrine\ORM\Proxy\Proxy as OrmProxy; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Doctrine related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class DoctrineCaster -{ - /** - * @return array - */ - public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $isNested) - { - foreach (['__cloner__', '__initializer__'] as $k) { - if (\array_key_exists($k, $a)) { - unset($a[$k]); - ++$stub->cut; - } - } - - return $a; - } - - /** - * @return array - */ - public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, bool $isNested) - { - foreach (['_entityPersister', '_identifier'] as $k) { - if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { - unset($a[$k]); - ++$stub->cut; - } - } - - return $a; - } - - /** - * @return array - */ - public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, bool $isNested) - { - foreach (['snapshot', 'association', 'typeClass'] as $k) { - if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { - $a[$k] = new CutStub($a[$k]); - } - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DsCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DsCaster.php deleted file mode 100644 index b34b67004..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DsCaster.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Ds\Collection; -use Ds\Map; -use Ds\Pair; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Ds extension classes to array representation. - * - * @author Jáchym Toušek - * - * @final - */ -class DsCaster -{ - public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array - { - $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count(); - $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity(); - - if (!$c instanceof Map) { - $a += $c->toArray(); - } - - return $a; - } - - public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array - { - foreach ($c as $k => $v) { - $a[] = new DsPairStub($k, $v); - } - - return $a; - } - - public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array - { - foreach ($c->toArray() as $k => $v) { - $a[Caster::PREFIX_VIRTUAL.$k] = $v; - } - - return $a; - } - - public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array - { - if ($isNested) { - $stub->class = Pair::class; - $stub->value = null; - $stub->handle = 0; - - $a = $c->value; - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DsPairStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DsPairStub.php deleted file mode 100644 index afa2727b1..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/DsPairStub.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - */ -class DsPairStub extends Stub -{ - public function __construct(mixed $key, mixed $value) - { - $this->value = [ - Caster::PREFIX_VIRTUAL.'key' => $key, - Caster::PREFIX_VIRTUAL.'value' => $value, - ]; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/EnumStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/EnumStub.php deleted file mode 100644 index 7a4e98a21..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/EnumStub.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents an enumeration of values. - * - * @author Nicolas Grekas - */ -class EnumStub extends Stub -{ - public $dumpKeys = true; - - public function __construct(array $values, bool $dumpKeys = true) - { - $this->value = $values; - $this->dumpKeys = $dumpKeys; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ExceptionCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ExceptionCaster.php deleted file mode 100644 index 02efb1b02..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ExceptionCaster.php +++ /dev/null @@ -1,419 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\ErrorHandler\Exception\FlattenException; -use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; -use Symfony\Component\VarDumper\Cloner\Stub; -use Symfony\Component\VarDumper\Exception\ThrowingCasterException; - -/** - * Casts common Exception classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class ExceptionCaster -{ - public static int $srcContext = 1; - public static bool $traceArgs = true; - public static array $errorTypes = [ - \E_DEPRECATED => 'E_DEPRECATED', - \E_USER_DEPRECATED => 'E_USER_DEPRECATED', - \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', - \E_ERROR => 'E_ERROR', - \E_WARNING => 'E_WARNING', - \E_PARSE => 'E_PARSE', - \E_NOTICE => 'E_NOTICE', - \E_CORE_ERROR => 'E_CORE_ERROR', - \E_CORE_WARNING => 'E_CORE_WARNING', - \E_COMPILE_ERROR => 'E_COMPILE_ERROR', - \E_COMPILE_WARNING => 'E_COMPILE_WARNING', - \E_USER_ERROR => 'E_USER_ERROR', - \E_USER_WARNING => 'E_USER_WARNING', - \E_USER_NOTICE => 'E_USER_NOTICE', - \E_STRICT => 'E_STRICT', - ]; - - private static array $framesCache = []; - - /** - * @return array - */ - public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); - } - - /** - * @return array - */ - public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); - } - - /** - * @return array - */ - public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $isNested) - { - if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) { - $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); - } - - return $a; - } - - /** - * @return array - */ - public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, bool $isNested) - { - $trace = Caster::PREFIX_VIRTUAL.'trace'; - $prefix = Caster::PREFIX_PROTECTED; - $xPrefix = "\0Exception\0"; - - if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { - $b = (array) $a[$xPrefix.'previous']; - $class = get_debug_type($a[$xPrefix.'previous']); - self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']); - $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); - } - - unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); - - return $a; - } - - /** - * @return array - */ - public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, bool $isNested) - { - $sPrefix = "\0".SilencedErrorContext::class."\0"; - - if (!isset($a[$s = $sPrefix.'severity'])) { - return $a; - } - - if (isset(self::$errorTypes[$a[$s]])) { - $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); - } - - $trace = [[ - 'file' => $a[$sPrefix.'file'], - 'line' => $a[$sPrefix.'line'], - ]]; - - if (isset($a[$sPrefix.'trace'])) { - $trace = array_merge($trace, $a[$sPrefix.'trace']); - } - - unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']); - $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); - - return $a; - } - - /** - * @return array - */ - public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, bool $isNested) - { - if (!$isNested) { - return $a; - } - $stub->class = ''; - $stub->handle = 0; - $frames = $trace->value; - $prefix = Caster::PREFIX_VIRTUAL; - - $a = []; - $j = \count($frames); - if (0 > $i = $trace->sliceOffset) { - $i = max(0, $j + $i); - } - if (!isset($trace->value[$i])) { - return []; - } - $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; - $frames[] = ['function' => '']; - $collapse = false; - - for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { - $f = $frames[$i]; - $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; - - $frame = new FrameStub( - [ - 'object' => $f['object'] ?? null, - 'class' => $f['class'] ?? null, - 'type' => $f['type'] ?? null, - 'function' => $f['function'] ?? null, - ] + $frames[$i - 1], - false, - true - ); - $f = self::castFrameStub($frame, [], $frame, true); - if (isset($f[$prefix.'src'])) { - foreach ($f[$prefix.'src']->value as $label => $frame) { - if (str_starts_with($label, "\0~collapse=0")) { - if ($collapse) { - $label = substr_replace($label, '1', 11, 1); - } else { - $collapse = true; - } - } - $label = substr_replace($label, "title=Stack level $j.&", 2, 0); - } - $f = $frames[$i - 1]; - if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) { - $frame->value['arguments'] = new ArgsStub($f['args'], $f['function'] ?? null, $f['class'] ?? null); - } - } elseif ('???' !== $lastCall) { - $label = new ClassStub($lastCall); - if (isset($label->attr['ellipsis'])) { - $label->attr['ellipsis'] += 2; - $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()'; - } else { - $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()'; - } - } else { - $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall; - } - $a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame; - - $lastCall = $call; - } - if (null !== $trace->sliceLength) { - $a = \array_slice($a, 0, $trace->sliceLength, true); - } - - return $a; - } - - /** - * @return array - */ - public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, bool $isNested) - { - if (!$isNested) { - return $a; - } - $f = $frame->value; - $prefix = Caster::PREFIX_VIRTUAL; - - if (isset($f['file'], $f['line'])) { - $cacheKey = $f; - unset($cacheKey['object'], $cacheKey['args']); - $cacheKey[] = self::$srcContext; - $cacheKey = implode('-', $cacheKey); - - if (isset(self::$framesCache[$cacheKey])) { - $a[$prefix.'src'] = self::$framesCache[$cacheKey]; - } else { - if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { - $f['file'] = substr($f['file'], 0, -\strlen($match[0])); - $f['line'] = (int) $match[1]; - } - $src = $f['line']; - $srcKey = $f['file']; - $ellipsis = new LinkStub($srcKey, 0); - $srcAttr = 'collapse='.(int) $ellipsis->inVendor; - $ellipsisTail = $ellipsis->attr['ellipsis-tail'] ?? 0; - $ellipsis = $ellipsis->attr['ellipsis'] ?? 0; - - if (is_file($f['file']) && 0 <= self::$srcContext) { - if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) { - $template = null; - if (isset($f['object'])) { - $template = $f['object']; - } elseif ((new \ReflectionClass($f['class']))->isInstantiable()) { - $template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); - } - if (null !== $template) { - $ellipsis = 0; - $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); - $templateInfo = $template->getDebugInfo(); - if (isset($templateInfo[$f['line']])) { - if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) { - $templatePath = null; - } - if ($templateSrc) { - $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); - $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; - } - } - } - } - if ($srcKey == $f['file']) { - $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); - $srcKey .= ':'.$f['line']; - if ($ellipsis) { - $ellipsis += 1 + \strlen($f['line']); - } - } - $srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']); - } else { - $srcAttr .= '&separator=:'; - } - $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; - self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); - } - } - - unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); - if ($frame->inTraceStub) { - unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); - } - foreach ($a as $k => $v) { - if (!$v) { - unset($a[$k]); - } - } - if ($frame->keepArgs && !empty($f['args'])) { - $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); - } - - return $a; - } - - /** - * @return array - */ - public static function castFlattenException(FlattenException $e, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $k = sprintf(Caster::PATTERN_PRIVATE, FlattenException::class, 'traceAsString'); - $a[$k] = new CutStub($a[$k]); - } - - return $a; - } - - private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array - { - if (isset($a[$xPrefix.'trace'])) { - $trace = $a[$xPrefix.'trace']; - unset($a[$xPrefix.'trace']); // Ensures the trace is always last - } else { - $trace = []; - } - - if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { - if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { - self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); - } - $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); - } - if (empty($a[$xPrefix.'previous'])) { - unset($a[$xPrefix.'previous']); - } - unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message']); - - if (isset($a[Caster::PREFIX_PROTECTED.'message']) && str_contains($a[Caster::PREFIX_PROTECTED.'message'], "@anonymous\0")) { - $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $a[Caster::PREFIX_PROTECTED.'message']); - } - - if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { - $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); - } - - return $a; - } - - private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void - { - if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { - return; - } - array_unshift($trace, [ - 'function' => $class ? 'new '.$class : null, - 'file' => $file, - 'line' => $line, - ]); - } - - private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub - { - $srcLines = explode("\n", $srcLines); - $src = []; - - for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { - $src[] = ($srcLines[$i] ?? '')."\n"; - } - - if ($frame['function'] ?? false) { - $stub = new CutStub(new \stdClass()); - $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; - $stub->type = Stub::TYPE_OBJECT; - $stub->attr['cut_hash'] = true; - $stub->attr['file'] = $frame['file']; - $stub->attr['line'] = $frame['line']; - - try { - $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); - $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); - - if ($f = $caller->getFileName()) { - $stub->attr['file'] = $f; - $stub->attr['line'] = $caller->getStartLine(); - } - } catch (\ReflectionException) { - // ignore fake class/function - } - - $srcLines = ["\0~separator=\0" => $stub]; - } else { - $stub = null; - $srcLines = []; - } - - $ltrim = 0; - do { - $pad = null; - for ($i = $srcContext << 1; $i >= 0; --$i) { - if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { - $pad ??= $c; - if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { - break; - } - } - } - ++$ltrim; - } while (0 > $i && null !== $pad); - - --$ltrim; - - foreach ($src as $i => $c) { - if ($ltrim) { - $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t"); - } - $c = substr($c, 0, -1); - if ($i !== $srcContext) { - $c = new ConstStub('default', $c); - } else { - $c = new ConstStub($c, $stub ? 'in '.$stub->class : ''); - if (null !== $file) { - $c->attr['file'] = $file; - $c->attr['line'] = $line; - } - } - $c->attr['lang'] = $lang; - $srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c; - } - - return new EnumStub($srcLines); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FFICaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FFICaster.php deleted file mode 100644 index f1984eef3..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FFICaster.php +++ /dev/null @@ -1,161 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use FFI\CData; -use FFI\CType; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts FFI extension classes to array representation. - * - * @author Nesmeyanov Kirill - */ -final class FFICaster -{ - /** - * In case of "char*" contains a string, the length of which depends on - * some other parameter, then during the generation of the string it is - * possible to go beyond the allowable memory area. - * - * This restriction serves to ensure that processing does not take - * up the entire allowable PHP memory limit. - */ - private const MAX_STRING_LENGTH = 255; - - public static function castCTypeOrCData(CData|CType $data, array $args, Stub $stub): array - { - if ($data instanceof CType) { - $type = $data; - $data = null; - } else { - $type = \FFI::typeof($data); - } - - $stub->class = sprintf('%s<%s> size %d align %d', ($data ?? $type)::class, $type->getName(), $type->getSize(), $type->getAlignment()); - - return match ($type->getKind()) { - CType::TYPE_FLOAT, - CType::TYPE_DOUBLE, - \defined('\FFI\CType::TYPE_LONGDOUBLE') ? CType::TYPE_LONGDOUBLE : -1, - CType::TYPE_UINT8, - CType::TYPE_SINT8, - CType::TYPE_UINT16, - CType::TYPE_SINT16, - CType::TYPE_UINT32, - CType::TYPE_SINT32, - CType::TYPE_UINT64, - CType::TYPE_SINT64, - CType::TYPE_BOOL, - CType::TYPE_CHAR, - CType::TYPE_ENUM => null !== $data ? [Caster::PREFIX_VIRTUAL.'cdata' => $data->cdata] : [], - CType::TYPE_POINTER => self::castFFIPointer($stub, $type, $data), - CType::TYPE_STRUCT => self::castFFIStructLike($type, $data), - CType::TYPE_FUNC => self::castFFIFunction($stub, $type), - default => $args, - }; - } - - private static function castFFIFunction(Stub $stub, CType $type): array - { - $arguments = []; - - for ($i = 0, $count = $type->getFuncParameterCount(); $i < $count; ++$i) { - $param = $type->getFuncParameterType($i); - - $arguments[] = $param->getName(); - } - - $abi = match ($type->getFuncABI()) { - CType::ABI_DEFAULT, - CType::ABI_CDECL => '[cdecl]', - CType::ABI_FASTCALL => '[fastcall]', - CType::ABI_THISCALL => '[thiscall]', - CType::ABI_STDCALL => '[stdcall]', - CType::ABI_PASCAL => '[pascal]', - CType::ABI_REGISTER => '[register]', - CType::ABI_MS => '[ms]', - CType::ABI_SYSV => '[sysv]', - CType::ABI_VECTORCALL => '[vectorcall]', - default => '[unknown abi]' - }; - - $returnType = $type->getFuncReturnType(); - - $stub->class = $abi.' callable('.implode(', ', $arguments).'): ' - .$returnType->getName(); - - return [Caster::PREFIX_VIRTUAL.'returnType' => $returnType]; - } - - private static function castFFIPointer(Stub $stub, CType $type, ?CData $data = null): array - { - $ptr = $type->getPointerType(); - - if (null === $data) { - return [Caster::PREFIX_VIRTUAL.'0' => $ptr]; - } - - return match ($ptr->getKind()) { - CType::TYPE_CHAR => [Caster::PREFIX_VIRTUAL.'cdata' => self::castFFIStringValue($data)], - CType::TYPE_FUNC => self::castFFIFunction($stub, $ptr), - default => [Caster::PREFIX_VIRTUAL.'cdata' => $data[0]], - }; - } - - private static function castFFIStringValue(CData $data): string|CutStub - { - $result = []; - - for ($i = 0; $i < self::MAX_STRING_LENGTH; ++$i) { - $result[$i] = $data[$i]; - - if ("\0" === $result[$i]) { - return implode('', $result); - } - } - - $string = implode('', $result); - $stub = new CutStub($string); - $stub->cut = -1; - $stub->value = $string; - - return $stub; - } - - private static function castFFIStructLike(CType $type, ?CData $data = null): array - { - $isUnion = ($type->getAttributes() & CType::ATTR_UNION) === CType::ATTR_UNION; - - $result = []; - - foreach ($type->getStructFieldNames() as $name) { - $field = $type->getStructFieldType($name); - - // Retrieving the value of a field from a union containing - // a pointer is not a safe operation, because may contain - // incorrect data. - $isUnsafe = $isUnion && CType::TYPE_POINTER === $field->getKind(); - - if ($isUnsafe) { - $result[Caster::PREFIX_VIRTUAL.$name.'?'] = $field; - } elseif (null === $data) { - $result[Caster::PREFIX_VIRTUAL.$name] = $field; - } else { - $fieldName = $data->{$name} instanceof CData ? '' : $field->getName().' '; - $result[Caster::PREFIX_VIRTUAL.$fieldName.$name] = $data->{$name}; - } - } - - return $result; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FiberCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FiberCaster.php deleted file mode 100644 index b797dbd63..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FiberCaster.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Fiber related classes to array representation. - * - * @author Grégoire Pineau - */ -final class FiberCaster -{ - /** - * @return array - */ - public static function castFiber(\Fiber $fiber, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if ($fiber->isTerminated()) { - $status = 'terminated'; - } elseif ($fiber->isRunning()) { - $status = 'running'; - } elseif ($fiber->isSuspended()) { - $status = 'suspended'; - } elseif ($fiber->isStarted()) { - $status = 'started'; - } else { - $status = 'not started'; - } - - $a[$prefix.'status'] = $status; - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FrameStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FrameStub.php deleted file mode 100644 index 878675528..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/FrameStub.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). - * - * @author Nicolas Grekas - */ -class FrameStub extends EnumStub -{ - public $keepArgs; - public $inTraceStub; - - public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false) - { - $this->value = $frame; - $this->keepArgs = $keepArgs; - $this->inTraceStub = $inTraceStub; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/GmpCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/GmpCaster.php deleted file mode 100644 index b018cc7f8..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/GmpCaster.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts GMP objects to array representation. - * - * @author Hamza Amrouche - * @author Nicolas Grekas - * - * @final - */ -class GmpCaster -{ - public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $filter): array - { - $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ImagineCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ImagineCaster.php deleted file mode 100644 index d1289da33..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ImagineCaster.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Imagine\Image\ImageInterface; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Grégoire Pineau - */ -final class ImagineCaster -{ - public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array - { - $imgData = $c->get('png'); - if (\strlen($imgData) > 1 * 1000 * 1000) { - $a += [ - Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), - ]; - } else { - $a += [ - Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), - ]; - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ImgStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ImgStub.php deleted file mode 100644 index a16681f73..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ImgStub.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * @author Grégoire Pineau - */ -class ImgStub extends ConstStub -{ - public function __construct(string $data, string $contentType, string $size = '') - { - $this->value = ''; - $this->attr['img-data'] = $data; - $this->attr['img-size'] = $size; - $this->attr['content-type'] = $contentType; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/IntlCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/IntlCaster.php deleted file mode 100644 index a4590f4b5..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/IntlCaster.php +++ /dev/null @@ -1,187 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - * @author Jan Schädlich - * - * @final - */ -class IntlCaster -{ - /** - * @return array - */ - public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), - Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), - ]; - - return self::castError($c, $a); - } - - /** - * @return array - */ - public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), - Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), - ]; - - if ($filter & Caster::EXCLUDE_VERBOSE) { - $stub->cut += 3; - - return self::castError($c, $a); - } - - $a += [ - Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub( - [ - 'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY), - 'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED), - 'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN), - 'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS), - 'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS), - 'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS), - 'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS), - 'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS), - 'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS), - 'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER), - 'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE), - 'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE), - 'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), - 'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH), - 'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION), - 'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE), - 'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED), - 'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS), - 'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS), - 'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE), - ] - ), - Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub( - [ - 'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX), - 'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX), - 'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX), - 'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX), - 'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER), - 'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE), - 'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET), - 'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS), - ] - ), - Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub( - [ - 'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL), - 'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL), - 'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL), - 'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL), - 'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL), - 'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL), - 'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL), - 'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL), - 'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), - 'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL), - 'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL), - 'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL), - 'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL), - 'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL), - 'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL), - 'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL), - 'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL), - 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), - ] - ), - ]; - - return self::castError($c, $a); - } - - /** - * @return array - */ - public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(), - Caster::PREFIX_VIRTUAL.'id' => $c->getID(), - Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(), - ]; - - if ($c->useDaylightTime()) { - $a += [ - Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(), - ]; - } - - return self::castError($c, $a); - } - - /** - * @return array - */ - public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - Caster::PREFIX_VIRTUAL.'type' => $c->getType(), - Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(), - Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(), - Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(), - Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(), - Caster::PREFIX_VIRTUAL.'time' => $c->getTime(), - Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(), - Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(), - Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), - ]; - - return self::castError($c, $a); - } - - /** - * @return array - */ - public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), - Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), - Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(), - Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(), - Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(), - Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(), - Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(), - Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), - ]; - - return self::castError($c, $a); - } - - private static function castError(object $c, array $a): array - { - if ($errorCode = $c->getErrorCode()) { - $a += [ - Caster::PREFIX_VIRTUAL.'error_code' => $errorCode, - Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(), - ]; - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/LinkStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/LinkStub.php deleted file mode 100644 index 4930436df..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/LinkStub.php +++ /dev/null @@ -1,105 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents a file or a URL. - * - * @author Nicolas Grekas - */ -class LinkStub extends ConstStub -{ - public $inVendor = false; - - private static array $vendorRoots; - private static array $composerRoots = []; - - public function __construct(string $label, int $line = 0, ?string $href = null) - { - $this->value = $label; - - if (!\is_string($href ??= $label)) { - return; - } - if (str_starts_with($href, 'file://')) { - if ($href === $label) { - $label = substr($label, 7); - } - $href = substr($href, 7); - } elseif (str_contains($href, '://')) { - $this->attr['href'] = $href; - - return; - } - if (!is_file($href)) { - return; - } - if ($line) { - $this->attr['line'] = $line; - } - if ($label !== $this->attr['file'] = realpath($href) ?: $href) { - return; - } - if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) { - $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1; - $this->attr['ellipsis-type'] = 'path'; - $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0); - } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) { - $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2))); - $this->attr['ellipsis-type'] = 'path'; - $this->attr['ellipsis-tail'] = 1; - } - } - - private function getComposerRoot(string $file, bool &$inVendor): string|false - { - if (!isset(self::$vendorRoots)) { - self::$vendorRoots = []; - - foreach (get_declared_classes() as $class) { - if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { - $r = new \ReflectionClass($class); - $v = \dirname($r->getFileName(), 2); - if (is_file($v.'/composer/installed.json')) { - self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; - } - } - } - } - $inVendor = false; - - if (isset(self::$composerRoots[$dir = \dirname($file)])) { - return self::$composerRoots[$dir]; - } - - foreach (self::$vendorRoots as $root) { - if ($inVendor = str_starts_with($file, $root)) { - return $root; - } - } - - $parent = $dir; - while (!@is_file($parent.'/composer.json')) { - if (!@file_exists($parent)) { - // open_basedir restriction in effect - break; - } - if ($parent === \dirname($parent)) { - return self::$composerRoots[$dir] = false; - } - - $parent = \dirname($parent); - } - - return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/MemcachedCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/MemcachedCaster.php deleted file mode 100644 index 2f161e8cb..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/MemcachedCaster.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Jan Schädlich - * - * @final - */ -class MemcachedCaster -{ - private static array $optionConstants; - private static array $defaultOptions; - - /** - * @return array - */ - public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(), - Caster::PREFIX_VIRTUAL.'options' => new EnumStub( - self::getNonDefaultOptions($c) - ), - ]; - - return $a; - } - - private static function getNonDefaultOptions(\Memcached $c): array - { - self::$defaultOptions ??= self::discoverDefaultOptions(); - self::$optionConstants ??= self::getOptionConstants(); - - $nonDefaultOptions = []; - foreach (self::$optionConstants as $constantKey => $value) { - if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) { - $nonDefaultOptions[$constantKey] = $option; - } - } - - return $nonDefaultOptions; - } - - private static function discoverDefaultOptions(): array - { - $defaultMemcached = new \Memcached(); - $defaultMemcached->addServer('127.0.0.1', 11211); - - $defaultOptions = []; - self::$optionConstants ??= self::getOptionConstants(); - - foreach (self::$optionConstants as $constantKey => $value) { - $defaultOptions[$constantKey] = $defaultMemcached->getOption($value); - } - - return $defaultOptions; - } - - private static function getOptionConstants(): array - { - $reflectedMemcached = new \ReflectionClass(\Memcached::class); - - $optionConstants = []; - foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { - if (str_starts_with($constantKey, 'OPT_')) { - $optionConstants[$constantKey] = $value; - } - } - - return $optionConstants; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/MysqliCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/MysqliCaster.php deleted file mode 100644 index bfe6f0822..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/MysqliCaster.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - * - * @internal - */ -final class MysqliCaster -{ - public static function castMysqliDriver(\mysqli_driver $c, array $a, Stub $stub, bool $isNested): array - { - foreach ($a as $k => $v) { - if (isset($c->$k)) { - $a[$k] = $c->$k; - } - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/PdoCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/PdoCaster.php deleted file mode 100644 index d68eae216..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/PdoCaster.php +++ /dev/null @@ -1,128 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts PDO related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class PdoCaster -{ - private const PDO_ATTRIBUTES = [ - 'CASE' => [ - \PDO::CASE_LOWER => 'LOWER', - \PDO::CASE_NATURAL => 'NATURAL', - \PDO::CASE_UPPER => 'UPPER', - ], - 'ERRMODE' => [ - \PDO::ERRMODE_SILENT => 'SILENT', - \PDO::ERRMODE_WARNING => 'WARNING', - \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', - ], - 'TIMEOUT', - 'PREFETCH', - 'AUTOCOMMIT', - 'PERSISTENT', - 'DRIVER_NAME', - 'SERVER_INFO', - 'ORACLE_NULLS' => [ - \PDO::NULL_NATURAL => 'NATURAL', - \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', - \PDO::NULL_TO_STRING => 'TO_STRING', - ], - 'CLIENT_VERSION', - 'SERVER_VERSION', - 'STATEMENT_CLASS', - 'EMULATE_PREPARES', - 'CONNECTION_STATUS', - 'STRINGIFY_FETCHES', - 'DEFAULT_FETCH_MODE' => [ - \PDO::FETCH_ASSOC => 'ASSOC', - \PDO::FETCH_BOTH => 'BOTH', - \PDO::FETCH_LAZY => 'LAZY', - \PDO::FETCH_NUM => 'NUM', - \PDO::FETCH_OBJ => 'OBJ', - ], - ]; - - /** - * @return array - */ - public static function castPdo(\PDO $c, array $a, Stub $stub, bool $isNested) - { - $attr = []; - $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); - $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - - foreach (self::PDO_ATTRIBUTES as $k => $v) { - if (!isset($k[0])) { - $k = $v; - $v = []; - } - - try { - $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k)); - if ($v && isset($v[$attr[$k]])) { - $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]); - } - } catch (\Exception) { - } - } - if (isset($attr[$k = 'STATEMENT_CLASS'][1])) { - if ($attr[$k][1]) { - $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]); - } - $attr[$k][0] = new ClassStub($attr[$k][0]); - } - - $prefix = Caster::PREFIX_VIRTUAL; - $a += [ - $prefix.'inTransaction' => method_exists($c, 'inTransaction'), - $prefix.'errorInfo' => $c->errorInfo(), - $prefix.'attributes' => new EnumStub($attr), - ]; - - if ($a[$prefix.'inTransaction']) { - $a[$prefix.'inTransaction'] = $c->inTransaction(); - } else { - unset($a[$prefix.'inTransaction']); - } - - if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { - unset($a[$prefix.'errorInfo']); - } - - $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode); - - return $a; - } - - /** - * @return array - */ - public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - $a[$prefix.'errorInfo'] = $c->errorInfo(); - - if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { - unset($a[$prefix.'errorInfo']); - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/PgSqlCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/PgSqlCaster.php deleted file mode 100644 index 0d8b3d919..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/PgSqlCaster.php +++ /dev/null @@ -1,165 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts pqsql resources to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class PgSqlCaster -{ - private const PARAM_CODES = [ - 'server_encoding', - 'client_encoding', - 'is_superuser', - 'session_authorization', - 'DateStyle', - 'TimeZone', - 'IntervalStyle', - 'integer_datetimes', - 'application_name', - 'standard_conforming_strings', - ]; - - private const TRANSACTION_STATUS = [ - \PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', - \PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', - \PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', - \PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', - \PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', - ]; - - private const RESULT_STATUS = [ - \PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', - \PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', - \PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', - \PGSQL_COPY_OUT => 'PGSQL_COPY_OUT', - \PGSQL_COPY_IN => 'PGSQL_COPY_IN', - \PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', - \PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', - \PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', - ]; - - private const DIAG_CODES = [ - 'severity' => \PGSQL_DIAG_SEVERITY, - 'sqlstate' => \PGSQL_DIAG_SQLSTATE, - 'message' => \PGSQL_DIAG_MESSAGE_PRIMARY, - 'detail' => \PGSQL_DIAG_MESSAGE_DETAIL, - 'hint' => \PGSQL_DIAG_MESSAGE_HINT, - 'statement position' => \PGSQL_DIAG_STATEMENT_POSITION, - 'internal position' => \PGSQL_DIAG_INTERNAL_POSITION, - 'internal query' => \PGSQL_DIAG_INTERNAL_QUERY, - 'context' => \PGSQL_DIAG_CONTEXT, - 'file' => \PGSQL_DIAG_SOURCE_FILE, - 'line' => \PGSQL_DIAG_SOURCE_LINE, - 'function' => \PGSQL_DIAG_SOURCE_FUNCTION, - ]; - - /** - * @return array - */ - public static function castLargeObject($lo, array $a, Stub $stub, bool $isNested) - { - $a['seek position'] = pg_lo_tell($lo); - - return $a; - } - - /** - * @return array - */ - public static function castLink($link, array $a, Stub $stub, bool $isNested) - { - $a['status'] = pg_connection_status($link); - $a['status'] = new ConstStub(\PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']); - $a['busy'] = pg_connection_busy($link); - - $a['transaction'] = pg_transaction_status($link); - if (isset(self::TRANSACTION_STATUS[$a['transaction']])) { - $a['transaction'] = new ConstStub(self::TRANSACTION_STATUS[$a['transaction']], $a['transaction']); - } - - $a['pid'] = pg_get_pid($link); - $a['last error'] = pg_last_error($link); - $a['last notice'] = pg_last_notice($link); - $a['host'] = pg_host($link); - $a['port'] = pg_port($link); - $a['dbname'] = pg_dbname($link); - $a['options'] = pg_options($link); - $a['version'] = pg_version($link); - - foreach (self::PARAM_CODES as $v) { - if (false !== $s = pg_parameter_status($link, $v)) { - $a['param'][$v] = $s; - } - } - - $a['param']['client_encoding'] = pg_client_encoding($link); - $a['param'] = new EnumStub($a['param']); - - return $a; - } - - /** - * @return array - */ - public static function castResult($result, array $a, Stub $stub, bool $isNested) - { - $a['num rows'] = pg_num_rows($result); - $a['status'] = pg_result_status($result); - if (isset(self::RESULT_STATUS[$a['status']])) { - $a['status'] = new ConstStub(self::RESULT_STATUS[$a['status']], $a['status']); - } - $a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING); - - if (-1 === $a['num rows']) { - foreach (self::DIAG_CODES as $k => $v) { - $a['error'][$k] = pg_result_error_field($result, $v); - } - } - - $a['affected rows'] = pg_affected_rows($result); - $a['last OID'] = pg_last_oid($result); - - $fields = pg_num_fields($result); - - for ($i = 0; $i < $fields; ++$i) { - $field = [ - 'name' => pg_field_name($result, $i), - 'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), - 'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), - 'nullable' => (bool) pg_field_is_null($result, $i), - 'storage' => pg_field_size($result, $i).' bytes', - 'display' => pg_field_prtlen($result, $i).' chars', - ]; - if (' (OID: )' === $field['table']) { - $field['table'] = null; - } - if ('-1 bytes' === $field['storage']) { - $field['storage'] = 'variable size'; - } elseif ('1 bytes' === $field['storage']) { - $field['storage'] = '1 byte'; - } - if ('1 chars' === $field['display']) { - $field['display'] = '1 char'; - } - $a['fields'][] = new EnumStub($field); - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php deleted file mode 100644 index eb6c88db6..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use ProxyManager\Proxy\ProxyInterface; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - * - * @final - */ -class ProxyManagerCaster -{ - /** - * @return array - */ - public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested) - { - if ($parent = get_parent_class($c)) { - $stub->class .= ' - '.$parent; - } - $stub->class .= '@proxy'; - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/RdKafkaCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/RdKafkaCaster.php deleted file mode 100644 index fcaa1b768..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/RdKafkaCaster.php +++ /dev/null @@ -1,222 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use RdKafka\Conf; -use RdKafka\Exception as RdKafkaException; -use RdKafka\KafkaConsumer; -use RdKafka\Message; -use RdKafka\Metadata\Broker as BrokerMetadata; -use RdKafka\Metadata\Collection as CollectionMetadata; -use RdKafka\Metadata\Partition as PartitionMetadata; -use RdKafka\Metadata\Topic as TopicMetadata; -use RdKafka\Topic; -use RdKafka\TopicConf; -use RdKafka\TopicPartition; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts RdKafka related classes to array representation. - * - * @author Romain Neutron - */ -class RdKafkaCaster -{ - /** - * @return array - */ - public static function castKafkaConsumer(KafkaConsumer $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - try { - $assignment = $c->getAssignment(); - } catch (RdKafkaException) { - $assignment = []; - } - - $a += [ - $prefix.'subscription' => $c->getSubscription(), - $prefix.'assignment' => $assignment, - ]; - - $a += self::extractMetadata($c); - - return $a; - } - - /** - * @return array - */ - public static function castTopic(Topic $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'name' => $c->getName(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castTopicPartition(TopicPartition $c, array $a) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'offset' => $c->getOffset(), - $prefix.'partition' => $c->getPartition(), - $prefix.'topic' => $c->getTopic(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castMessage(Message $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'errstr' => $c->errstr(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castConf(Conf $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - foreach ($c->dump() as $key => $value) { - $a[$prefix.$key] = $value; - } - - return $a; - } - - /** - * @return array - */ - public static function castTopicConf(TopicConf $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - foreach ($c->dump() as $key => $value) { - $a[$prefix.$key] = $value; - } - - return $a; - } - - /** - * @return array - */ - public static function castRdKafka(\RdKafka $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'out_q_len' => $c->getOutQLen(), - ]; - - $a += self::extractMetadata($c); - - return $a; - } - - /** - * @return array - */ - public static function castCollectionMetadata(CollectionMetadata $c, array $a, Stub $stub, bool $isNested) - { - $a += iterator_to_array($c); - - return $a; - } - - /** - * @return array - */ - public static function castTopicMetadata(TopicMetadata $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'name' => $c->getTopic(), - $prefix.'partitions' => $c->getPartitions(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castPartitionMetadata(PartitionMetadata $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'id' => $c->getId(), - $prefix.'err' => $c->getErr(), - $prefix.'leader' => $c->getLeader(), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castBrokerMetadata(BrokerMetadata $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'id' => $c->getId(), - $prefix.'host' => $c->getHost(), - $prefix.'port' => $c->getPort(), - ]; - - return $a; - } - - /** - * @return array - */ - private static function extractMetadata(KafkaConsumer|\RdKafka $c) - { - $prefix = Caster::PREFIX_VIRTUAL; - - try { - $m = $c->getMetadata(true, null, 500); - } catch (RdKafkaException) { - return []; - } - - return [ - $prefix.'orig_broker_id' => $m->getOrigBrokerId(), - $prefix.'orig_broker_name' => $m->getOrigBrokerName(), - $prefix.'brokers' => $m->getBrokers(), - $prefix.'topics' => $m->getTopics(), - ]; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/RedisCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/RedisCaster.php deleted file mode 100644 index 6ff046754..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/RedisCaster.php +++ /dev/null @@ -1,159 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Relay\Relay; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Redis class from ext-redis to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class RedisCaster -{ - private const SERIALIZERS = [ - 0 => 'NONE', // Redis::SERIALIZER_NONE - 1 => 'PHP', // Redis::SERIALIZER_PHP - 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY - ]; - - private const MODES = [ - 0 => 'ATOMIC', // Redis::ATOMIC - 1 => 'MULTI', // Redis::MULTI - 2 => 'PIPELINE', // Redis::PIPELINE - ]; - - private const COMPRESSION_MODES = [ - 0 => 'NONE', // Redis::COMPRESSION_NONE - 1 => 'LZF', // Redis::COMPRESSION_LZF - ]; - - private const FAILOVER_OPTIONS = [ - \RedisCluster::FAILOVER_NONE => 'NONE', - \RedisCluster::FAILOVER_ERROR => 'ERROR', - \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE', - \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES', - ]; - - /** - * @return array - */ - public static function castRedis(\Redis|Relay $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if (!$connected = $c->isConnected()) { - return $a + [ - $prefix.'isConnected' => $connected, - ]; - } - - $mode = $c->getMode(); - - return $a + [ - $prefix.'isConnected' => $connected, - $prefix.'host' => $c->getHost(), - $prefix.'port' => $c->getPort(), - $prefix.'auth' => $c->getAuth(), - $prefix.'mode' => isset(self::MODES[$mode]) ? new ConstStub(self::MODES[$mode], $mode) : $mode, - $prefix.'dbNum' => $c->getDbNum(), - $prefix.'timeout' => $c->getTimeout(), - $prefix.'lastError' => $c->getLastError(), - $prefix.'persistentId' => $c->getPersistentID(), - $prefix.'options' => self::getRedisOptions($c), - ]; - } - - /** - * @return array - */ - public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - return $a + [ - $prefix.'hosts' => $c->_hosts(), - $prefix.'function' => ClassStub::wrapCallable($c->_function()), - $prefix.'lastError' => $c->getLastError(), - $prefix.'options' => self::getRedisOptions($c), - ]; - } - - /** - * @return array - */ - public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER); - - $a += [ - $prefix.'_masters' => $c->_masters(), - $prefix.'_redir' => $c->_redir(), - $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()), - $prefix.'lastError' => $c->getLastError(), - $prefix.'options' => self::getRedisOptions($c, [ - 'SLAVE_FAILOVER' => isset(self::FAILOVER_OPTIONS[$failover]) ? new ConstStub(self::FAILOVER_OPTIONS[$failover], $failover) : $failover, - ]), - ]; - - return $a; - } - - private static function getRedisOptions(\Redis|Relay|\RedisArray|\RedisCluster $redis, array $options = []): EnumStub - { - $serializer = $redis->getOption(\defined('Redis::OPT_SERIALIZER') ? \Redis::OPT_SERIALIZER : 1); - if (\is_array($serializer)) { - foreach ($serializer as &$v) { - if (isset(self::SERIALIZERS[$v])) { - $v = new ConstStub(self::SERIALIZERS[$v], $v); - } - } - } elseif (isset(self::SERIALIZERS[$serializer])) { - $serializer = new ConstStub(self::SERIALIZERS[$serializer], $serializer); - } - - $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0; - if (\is_array($compression)) { - foreach ($compression as &$v) { - if (isset(self::COMPRESSION_MODES[$v])) { - $v = new ConstStub(self::COMPRESSION_MODES[$v], $v); - } - } - } elseif (isset(self::COMPRESSION_MODES[$compression])) { - $compression = new ConstStub(self::COMPRESSION_MODES[$compression], $compression); - } - - $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0; - if (\is_array($retry)) { - foreach ($retry as &$v) { - $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v); - } - } else { - $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry); - } - - $options += [ - 'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : Relay::OPT_TCP_KEEPALIVE, - 'READ_TIMEOUT' => $redis->getOption(\defined('Redis::OPT_READ_TIMEOUT') ? \Redis::OPT_READ_TIMEOUT : Relay::OPT_READ_TIMEOUT), - 'COMPRESSION' => $compression, - 'SERIALIZER' => $serializer, - 'PREFIX' => $redis->getOption(\defined('Redis::OPT_PREFIX') ? \Redis::OPT_PREFIX : Relay::OPT_PREFIX), - 'SCAN' => $retry, - ]; - - return new EnumStub($options); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ReflectionCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ReflectionCaster.php deleted file mode 100644 index 4adb9bc9f..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ReflectionCaster.php +++ /dev/null @@ -1,485 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Reflector related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class ReflectionCaster -{ - public const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; - - private const EXTRA_MAP = [ - 'docComment' => 'getDocComment', - 'extension' => 'getExtensionName', - 'isDisabled' => 'isDisabled', - 'isDeprecated' => 'isDeprecated', - 'isInternal' => 'isInternal', - 'isUserDefined' => 'isUserDefined', - 'isGenerator' => 'isGenerator', - 'isVariadic' => 'isVariadic', - ]; - - /** - * @return array - */ - public static function castClosure(\Closure $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - $c = new \ReflectionFunction($c); - - $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); - - if (!str_contains($c->name, '{closure}')) { - $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; - unset($a[$prefix.'class']); - } - unset($a[$prefix.'extra']); - - $stub->class .= self::getSignature($a); - - if ($f = $c->getFileName()) { - $stub->attr['file'] = $f; - $stub->attr['line'] = $c->getStartLine(); - } - - unset($a[$prefix.'parameters']); - - if ($filter & Caster::EXCLUDE_VERBOSE) { - $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a); - - return []; - } - - if ($f) { - $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); - $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); - } - - return $a; - } - - /** - * @return array - */ - public static function unsetClosureFileInfo(\Closure $c, array $a) - { - unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']); - - return $a; - } - - public static function castGenerator(\Generator $c, array $a, Stub $stub, bool $isNested): array - { - // Cannot create ReflectionGenerator based on a terminated Generator - try { - $reflectionGenerator = new \ReflectionGenerator($c); - } catch (\Exception) { - $a[Caster::PREFIX_VIRTUAL.'closed'] = true; - - return $a; - } - - return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested); - } - - /** - * @return array - */ - public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if ($c instanceof \ReflectionNamedType) { - $a += [ - $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, - $prefix.'allowsNull' => $c->allowsNull(), - $prefix.'isBuiltin' => $c->isBuiltin(), - ]; - } elseif ($c instanceof \ReflectionUnionType || $c instanceof \ReflectionIntersectionType) { - $a[$prefix.'allowsNull'] = $c->allowsNull(); - self::addMap($a, $c, [ - 'types' => 'getTypes', - ]); - } else { - $a[$prefix.'allowsNull'] = $c->allowsNull(); - } - - return $a; - } - - /** - * @return array - */ - public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested) - { - self::addMap($a, $c, [ - 'name' => 'getName', - 'arguments' => 'getArguments', - ]); - - return $a; - } - - /** - * @return array - */ - public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if ($c->getThis()) { - $a[$prefix.'this'] = new CutStub($c->getThis()); - } - $function = $c->getFunction(); - $frame = [ - 'class' => $function->class ?? null, - 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, - 'function' => $function->name, - 'file' => $c->getExecutingFile(), - 'line' => $c->getExecutingLine(), - ]; - if ($trace = $c->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS)) { - $function = new \ReflectionGenerator($c->getExecutingGenerator()); - array_unshift($trace, [ - 'function' => 'yield', - 'file' => $function->getExecutingFile(), - 'line' => $function->getExecutingLine(), - ]); - $trace[] = $frame; - $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); - } else { - $function = new FrameStub($frame, false, true); - $function = ExceptionCaster::castFrameStub($function, [], $function, true); - $a[$prefix.'executing'] = $function[$prefix.'src']; - } - - $a[Caster::PREFIX_VIRTUAL.'closed'] = false; - - return $a; - } - - /** - * @return array - */ - public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if ($n = \Reflection::getModifierNames($c->getModifiers())) { - $a[$prefix.'modifiers'] = implode(' ', $n); - } - - self::addMap($a, $c, [ - 'extends' => 'getParentClass', - 'implements' => 'getInterfaceNames', - 'constants' => 'getReflectionConstants', - ]); - - foreach ($c->getProperties() as $n) { - $a[$prefix.'properties'][$n->name] = $n; - } - - foreach ($c->getMethods() as $n) { - $a[$prefix.'methods'][$n->name] = $n; - } - - self::addAttributes($a, $c, $prefix); - - if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { - self::addExtra($a, $c); - } - - return $a; - } - - /** - * @return array - */ - public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - self::addMap($a, $c, [ - 'returnsReference' => 'returnsReference', - 'returnType' => 'getReturnType', - 'class' => \PHP_VERSION_ID >= 80111 ? 'getClosureCalledClass' : 'getClosureScopeClass', - 'this' => 'getClosureThis', - ]); - - if (isset($a[$prefix.'returnType'])) { - $v = $a[$prefix.'returnType']; - $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; - $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType'] instanceof \ReflectionNamedType && $a[$prefix.'returnType']->allowsNull() && 'mixed' !== $v ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); - } - if (isset($a[$prefix.'class'])) { - $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); - } - if (isset($a[$prefix.'this'])) { - $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); - } - - foreach ($c->getParameters() as $v) { - $k = '$'.$v->name; - if ($v->isVariadic()) { - $k = '...'.$k; - } - if ($v->isPassedByReference()) { - $k = '&'.$k; - } - $a[$prefix.'parameters'][$k] = $v; - } - if (isset($a[$prefix.'parameters'])) { - $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); - } - - self::addAttributes($a, $c, $prefix); - - if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) { - foreach ($v as $k => &$v) { - if (\is_object($v)) { - $a[$prefix.'use']['$'.$k] = new CutStub($v); - } else { - $a[$prefix.'use']['$'.$k] = &$v; - } - } - unset($v); - $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']); - } - - if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { - self::addExtra($a, $c); - } - - return $a; - } - - /** - * @return array - */ - public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); - $a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue(); - - self::addAttributes($a, $c); - - return $a; - } - - /** - * @return array - */ - public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); - - return $a; - } - - /** - * @return array - */ - public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - self::addMap($a, $c, [ - 'position' => 'getPosition', - 'isVariadic' => 'isVariadic', - 'byReference' => 'isPassedByReference', - 'allowsNull' => 'allowsNull', - ]); - - self::addAttributes($a, $c, $prefix); - - if ($v = $c->getType()) { - $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; - } - - if (isset($a[$prefix.'typeHint'])) { - $v = $a[$prefix.'typeHint']; - $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); - } else { - unset($a[$prefix.'allowsNull']); - } - - if ($c->isOptional()) { - try { - $a[$prefix.'default'] = $v = $c->getDefaultValue(); - if ($c->isDefaultValueConstant() && !\is_object($v)) { - $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); - } - if (null === $v) { - unset($a[$prefix.'allowsNull']); - } - } catch (\ReflectionException) { - } - } - - return $a; - } - - /** - * @return array - */ - public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); - - self::addAttributes($a, $c); - self::addExtra($a, $c); - - return $a; - } - - /** - * @return array - */ - public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); - - return $a; - } - - /** - * @return array - */ - public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $isNested) - { - self::addMap($a, $c, [ - 'version' => 'getVersion', - 'dependencies' => 'getDependencies', - 'iniEntries' => 'getIniEntries', - 'isPersistent' => 'isPersistent', - 'isTemporary' => 'isTemporary', - 'constants' => 'getConstants', - 'functions' => 'getFunctions', - 'classes' => 'getClasses', - ]); - - return $a; - } - - /** - * @return array - */ - public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, bool $isNested) - { - self::addMap($a, $c, [ - 'version' => 'getVersion', - 'author' => 'getAuthor', - 'copyright' => 'getCopyright', - 'url' => 'getURL', - ]); - - return $a; - } - - /** - * @return string - */ - public static function getSignature(array $a) - { - $prefix = Caster::PREFIX_VIRTUAL; - $signature = ''; - - if (isset($a[$prefix.'parameters'])) { - foreach ($a[$prefix.'parameters']->value as $k => $param) { - $signature .= ', '; - if ($type = $param->getType()) { - if (!$type instanceof \ReflectionNamedType) { - $signature .= $type.' '; - } else { - if (!$param->isOptional() && $param->allowsNull() && 'mixed' !== $type->getName()) { - $signature .= '?'; - } - $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; - } - } - $signature .= $k; - - if (!$param->isDefaultValueAvailable()) { - continue; - } - $v = $param->getDefaultValue(); - $signature .= ' = '; - - if ($param->isDefaultValueConstant()) { - $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1); - } elseif (null === $v) { - $signature .= 'null'; - } elseif (\is_array($v)) { - $signature .= $v ? '[…'.\count($v).']' : '[]'; - } elseif (\is_string($v)) { - $signature .= 10 > \strlen($v) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; - } elseif (\is_bool($v)) { - $signature .= $v ? 'true' : 'false'; - } elseif (\is_object($v)) { - $signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1); - } else { - $signature .= $v; - } - } - } - $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')'; - - if (isset($a[$prefix.'returnType'])) { - $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1); - } - - return $signature; - } - - private static function addExtra(array &$a, \Reflector $c): void - { - $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; - - if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { - $x['file'] = new LinkStub($m, $c->getStartLine()); - $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); - } - - self::addMap($x, $c, self::EXTRA_MAP, ''); - - if ($x) { - $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); - } - } - - private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL): void - { - foreach ($map as $k => $m) { - if ('isDisabled' === $k) { - continue; - } - - if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { - $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; - } - } - } - - private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void - { - foreach ($c->getAttributes() as $n) { - $a[$prefix.'attributes'][] = $n; - } - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ResourceCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ResourceCaster.php deleted file mode 100644 index f3bbf3be4..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ResourceCaster.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts common resource types to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class ResourceCaster -{ - public static function castCurl(\CurlHandle $h, array $a, Stub $stub, bool $isNested): array - { - return curl_getinfo($h); - } - - /** - * @return array - */ - public static function castDba($dba, array $a, Stub $stub, bool $isNested) - { - $list = dba_list(); - $a['file'] = $list[(int) $dba]; - - return $a; - } - - /** - * @return array - */ - public static function castProcess($process, array $a, Stub $stub, bool $isNested) - { - return proc_get_status($process); - } - - public static function castStream($stream, array $a, Stub $stub, bool $isNested): array - { - $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); - if ($a['uri'] ?? false) { - $a['uri'] = new LinkStub($a['uri']); - } - - return $a; - } - - /** - * @return array - */ - public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested) - { - return @stream_context_get_params($stream) ?: $a; - } - - /** - * @return array - */ - public static function castGd($gd, array $a, Stub $stub, bool $isNested) - { - $a['size'] = imagesx($gd).'x'.imagesy($gd); - $a['trueColor'] = imageistruecolor($gd); - - return $a; - } - - /** - * @return array - */ - public static function castOpensslX509($h, array $a, Stub $stub, bool $isNested) - { - $stub->cut = -1; - $info = openssl_x509_parse($h, false); - - $pin = openssl_pkey_get_public($h); - $pin = openssl_pkey_get_details($pin)['key']; - $pin = \array_slice(explode("\n", $pin), 1, -2); - $pin = base64_decode(implode('', $pin)); - $pin = base64_encode(hash('sha256', $pin, true)); - - $a += [ - 'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])), - 'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])), - 'expiry' => new ConstStub(date(\DateTimeInterface::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']), - 'fingerprint' => new EnumStub([ - 'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)), - 'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)), - 'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)), - 'pin-sha256' => new ConstStub($pin), - ]), - ]; - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ScalarStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ScalarStub.php deleted file mode 100644 index 3bb1935b8..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/ScalarStub.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents any arbitrary value. - * - * @author Alexandre Daubois - */ -class ScalarStub extends Stub -{ - public function __construct(mixed $value) - { - $this->value = $value; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/SplCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/SplCaster.php deleted file mode 100644 index 814d824d1..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/SplCaster.php +++ /dev/null @@ -1,286 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts SPL related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class SplCaster -{ - private const SPL_FILE_OBJECT_FLAGS = [ - \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', - \SplFileObject::READ_AHEAD => 'READ_AHEAD', - \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', - \SplFileObject::READ_CSV => 'READ_CSV', - ]; - - /** - * @return array - */ - public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, bool $isNested) - { - return self::castSplArray($c, $a, $stub, $isNested); - } - - /** - * @return array - */ - public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested) - { - return self::castSplArray($c, $a, $stub, $isNested); - } - - /** - * @return array - */ - public static function castHeap(\Iterator $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), - ]; - - return $a; - } - - /** - * @return array - */ - public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - $mode = $c->getIteratorMode(); - $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); - - $a += [ - $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), - $prefix.'dllist' => iterator_to_array($c), - ]; - $c->setIteratorMode($mode); - - return $a; - } - - /** - * @return array - */ - public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, bool $isNested) - { - static $map = [ - 'path' => 'getPath', - 'filename' => 'getFilename', - 'basename' => 'getBasename', - 'pathname' => 'getPathname', - 'extension' => 'getExtension', - 'realPath' => 'getRealPath', - 'aTime' => 'getATime', - 'mTime' => 'getMTime', - 'cTime' => 'getCTime', - 'inode' => 'getInode', - 'size' => 'getSize', - 'perms' => 'getPerms', - 'owner' => 'getOwner', - 'group' => 'getGroup', - 'type' => 'getType', - 'writable' => 'isWritable', - 'readable' => 'isReadable', - 'executable' => 'isExecutable', - 'file' => 'isFile', - 'dir' => 'isDir', - 'link' => 'isLink', - 'linkTarget' => 'getLinkTarget', - ]; - - $prefix = Caster::PREFIX_VIRTUAL; - unset($a["\0SplFileInfo\0fileName"]); - unset($a["\0SplFileInfo\0pathName"]); - - try { - $c->isReadable(); - } catch (\RuntimeException $e) { - if ('Object not initialized' !== $e->getMessage()) { - throw $e; - } - - $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; - - return $a; - } catch (\Error $e) { - if ('Object not initialized' !== $e->getMessage()) { - throw $e; - } - - $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; - - return $a; - } - - foreach ($map as $key => $accessor) { - try { - $a[$prefix.$key] = $c->$accessor(); - } catch (\Exception) { - } - } - - if ($a[$prefix.'realPath'] ?? false) { - $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); - } - - if (isset($a[$prefix.'perms'])) { - $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); - } - - static $mapDate = ['aTime', 'mTime', 'cTime']; - foreach ($mapDate as $key) { - if (isset($a[$prefix.$key])) { - $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); - } - } - - return $a; - } - - /** - * @return array - */ - public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, bool $isNested) - { - static $map = [ - 'csvControl' => 'getCsvControl', - 'flags' => 'getFlags', - 'maxLineLen' => 'getMaxLineLen', - 'fstat' => 'fstat', - 'eof' => 'eof', - 'key' => 'key', - ]; - - $prefix = Caster::PREFIX_VIRTUAL; - - foreach ($map as $key => $accessor) { - try { - $a[$prefix.$key] = $c->$accessor(); - } catch (\Exception) { - } - } - - if (isset($a[$prefix.'flags'])) { - $flagsArray = []; - foreach (self::SPL_FILE_OBJECT_FLAGS as $value => $name) { - if ($a[$prefix.'flags'] & $value) { - $flagsArray[] = $name; - } - } - $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); - } - - if (isset($a[$prefix.'fstat'])) { - $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); - } - - return $a; - } - - /** - * @return array - */ - public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested) - { - $storage = []; - unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 - unset($a["\0SplObjectStorage\0storage"]); - - $clone = clone $c; - foreach ($clone as $obj) { - $storage[] = new EnumStub([ - 'object' => $obj, - 'info' => $clone->getInfo(), - ]); - } - - $a += [ - Caster::PREFIX_VIRTUAL.'storage' => $storage, - ]; - - return $a; - } - - /** - * @return array - */ - public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); - - return $a; - } - - /** - * @return array - */ - public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); - - return $a; - } - - /** - * @return array - */ - public static function castWeakMap(\WeakMap $c, array $a, Stub $stub, bool $isNested) - { - $map = []; - - foreach (clone $c as $obj => $data) { - $map[] = new EnumStub([ - 'object' => $obj, - 'data' => $data, - ]); - } - - $a += [ - Caster::PREFIX_VIRTUAL.'map' => $map, - ]; - - return $a; - } - - private static function castSplArray(\ArrayObject|\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array - { - $prefix = Caster::PREFIX_VIRTUAL; - $flags = $c->getFlags(); - - if (!($flags & \ArrayObject::STD_PROP_LIST)) { - $c->setFlags(\ArrayObject::STD_PROP_LIST); - $a = Caster::castObject($c, $c::class, method_exists($c, '__debugInfo'), $stub->class); - $c->setFlags($flags); - } - - unset($a["\0ArrayObject\0storage"], $a["\0ArrayIterator\0storage"]); - - $a += [ - $prefix.'storage' => $c->getArrayCopy(), - $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), - $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), - ]; - if ($c instanceof \ArrayObject) { - $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/StubCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/StubCaster.php deleted file mode 100644 index 4b93ff76f..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/StubCaster.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts a caster's Stub. - * - * @author Nicolas Grekas - * - * @final - */ -class StubCaster -{ - /** - * @return array - */ - public static function castStub(Stub $c, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $stub->type = $c->type; - $stub->class = $c->class; - $stub->value = $c->value; - $stub->handle = $c->handle; - $stub->cut = $c->cut; - $stub->attr = $c->attr; - - if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) { - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_BINARY; - } - - $a = []; - } - - return $a; - } - - /** - * @return array - */ - public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, bool $isNested) - { - return $isNested ? $c->preservedSubset : $a; - } - - /** - * @return array - */ - public static function cutInternals($obj, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $stub->cut += \count($a); - - return []; - } - - return $a; - } - - /** - * @return array - */ - public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $stub->class = $c->dumpKeys ? '' : null; - $stub->handle = 0; - $stub->value = null; - $stub->cut = $c->cut; - $stub->attr = $c->attr; - - $a = []; - - if ($c->value) { - foreach (array_keys($c->value) as $k) { - $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k; - } - // Preserve references with array_combine() - $a = array_combine($keys, $c->value); - } - } - - return $a; - } - - /** - * @return array - */ - public static function castScalar(ScalarStub $scalarStub, array $a, Stub $stub) - { - $stub->type = Stub::TYPE_SCALAR; - $stub->attr['value'] = $scalarStub->value; - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/SymfonyCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/SymfonyCaster.php deleted file mode 100644 index ebc00f90e..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/SymfonyCaster.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Uid\Ulid; -use Symfony\Component\Uid\Uuid; -use Symfony\Component\VarDumper\Cloner\Stub; -use Symfony\Component\VarExporter\Internal\LazyObjectState; - -/** - * @final - */ -class SymfonyCaster -{ - private const REQUEST_GETTERS = [ - 'pathInfo' => 'getPathInfo', - 'requestUri' => 'getRequestUri', - 'baseUrl' => 'getBaseUrl', - 'basePath' => 'getBasePath', - 'method' => 'getMethod', - 'format' => 'getRequestFormat', - ]; - - /** - * @return array - */ - public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested) - { - $clone = null; - - foreach (self::REQUEST_GETTERS as $prop => $getter) { - $key = Caster::PREFIX_PROTECTED.$prop; - if (\array_key_exists($key, $a) && null === $a[$key]) { - $clone ??= clone $request; - $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); - } - } - - return $a; - } - - /** - * @return array - */ - public static function castHttpClient($client, array $a, Stub $stub, bool $isNested) - { - $multiKey = sprintf("\0%s\0multi", $client::class); - if (isset($a[$multiKey])) { - $a[$multiKey] = new CutStub($a[$multiKey]); - } - - return $a; - } - - /** - * @return array - */ - public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested) - { - $stub->cut += \count($a); - $a = []; - - foreach ($response->getInfo() as $k => $v) { - $a[Caster::PREFIX_VIRTUAL.$k] = $v; - } - - return $a; - } - - /** - * @return array - */ - public static function castLazyObjectState($state, array $a, Stub $stub, bool $isNested) - { - if (!$isNested) { - return $a; - } - - $stub->cut += \count($a) - 1; - - $instance = $a['realInstance'] ?? null; - - $a = ['status' => new ConstStub(match ($a['status']) { - LazyObjectState::STATUS_INITIALIZED_FULL => 'INITIALIZED_FULL', - LazyObjectState::STATUS_INITIALIZED_PARTIAL => 'INITIALIZED_PARTIAL', - LazyObjectState::STATUS_UNINITIALIZED_FULL => 'UNINITIALIZED_FULL', - LazyObjectState::STATUS_UNINITIALIZED_PARTIAL => 'UNINITIALIZED_PARTIAL', - }, $a['status'])]; - - if ($instance) { - $a['realInstance'] = $instance; - --$stub->cut; - } - - return $a; - } - - /** - * @return array - */ - public static function castUuid(Uuid $uuid, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $uuid->toBase58(); - $a[Caster::PREFIX_VIRTUAL.'toBase32'] = $uuid->toBase32(); - - // symfony/uid >= 5.3 - if (method_exists($uuid, 'getDateTime')) { - $a[Caster::PREFIX_VIRTUAL.'time'] = $uuid->getDateTime()->format('Y-m-d H:i:s.u \U\T\C'); - } - - return $a; - } - - /** - * @return array - */ - public static function castUlid(Ulid $ulid, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $ulid->toBase58(); - $a[Caster::PREFIX_VIRTUAL.'toRfc4122'] = $ulid->toRfc4122(); - - // symfony/uid >= 5.3 - if (method_exists($ulid, 'getDateTime')) { - $a[Caster::PREFIX_VIRTUAL.'time'] = $ulid->getDateTime()->format('Y-m-d H:i:s.v \U\T\C'); - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/TraceStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/TraceStub.php deleted file mode 100644 index d215d8db0..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/TraceStub.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). - * - * @author Nicolas Grekas - */ -class TraceStub extends Stub -{ - public $keepArgs; - public $sliceOffset; - public $sliceLength; - public $numberingOffset; - - public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, ?int $sliceLength = null, int $numberingOffset = 0) - { - $this->value = $trace; - $this->keepArgs = $keepArgs; - $this->sliceOffset = $sliceOffset; - $this->sliceLength = $sliceLength; - $this->numberingOffset = $numberingOffset; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/UninitializedStub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/UninitializedStub.php deleted file mode 100644 index a9bdd9b81..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/UninitializedStub.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents an uninitialized property. - * - * @author Nicolas Grekas - */ -class UninitializedStub extends ConstStub -{ - public function __construct(\ReflectionProperty $property) - { - parent::__construct('?'.($property->hasType() ? ' '.$property->getType() : ''), 'Uninitialized property'); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/UuidCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/UuidCaster.php deleted file mode 100644 index b10277457..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/UuidCaster.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Ramsey\Uuid\UuidInterface; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Grégoire Pineau - */ -final class UuidCaster -{ - public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array - { - $a += [ - Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, - ]; - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php deleted file mode 100644 index 1cfcf4dd8..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts XmlReader class to array representation. - * - * @author Baptiste Clavié - * - * @final - */ -class XmlReaderCaster -{ - private const NODE_TYPES = [ - \XMLReader::NONE => 'NONE', - \XMLReader::ELEMENT => 'ELEMENT', - \XMLReader::ATTRIBUTE => 'ATTRIBUTE', - \XMLReader::TEXT => 'TEXT', - \XMLReader::CDATA => 'CDATA', - \XMLReader::ENTITY_REF => 'ENTITY_REF', - \XMLReader::ENTITY => 'ENTITY', - \XMLReader::PI => 'PI (Processing Instruction)', - \XMLReader::COMMENT => 'COMMENT', - \XMLReader::DOC => 'DOC', - \XMLReader::DOC_TYPE => 'DOC_TYPE', - \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', - \XMLReader::NOTATION => 'NOTATION', - \XMLReader::WHITESPACE => 'WHITESPACE', - \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', - \XMLReader::END_ELEMENT => 'END_ELEMENT', - \XMLReader::END_ENTITY => 'END_ENTITY', - \XMLReader::XML_DECLARATION => 'XML_DECLARATION', - ]; - - /** - * @return array - */ - public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested) - { - try { - $properties = [ - 'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD), - 'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS), - 'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE), - 'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES), - ]; - } catch (\Error) { - $properties = [ - 'LOADDTD' => false, - 'DEFAULTATTRS' => false, - 'VALIDATE' => false, - 'SUBST_ENTITIES' => false, - ]; - } - - $props = Caster::PREFIX_VIRTUAL.'parserProperties'; - $info = [ - 'localName' => $reader->localName, - 'prefix' => $reader->prefix, - 'nodeType' => new ConstStub(self::NODE_TYPES[$reader->nodeType], $reader->nodeType), - 'depth' => $reader->depth, - 'isDefault' => $reader->isDefault, - 'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement, - 'xmlLang' => $reader->xmlLang, - 'attributeCount' => $reader->attributeCount, - 'value' => $reader->value, - 'namespaceURI' => $reader->namespaceURI, - 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, - $props => $properties, - ]; - - if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { - $info[$props] = new EnumStub($info[$props]); - $info[$props]->cut = $count; - } - - $a = Caster::filter($a, Caster::EXCLUDE_UNINITIALIZED, [], $count); - $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); - // +2 because hasValue and hasAttributes are always filtered - $stub->cut += $count + 2; - - return $a + $info; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php b/data/web/inc/lib/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php deleted file mode 100644 index 0cf42584a..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts XML resources to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class XmlResourceCaster -{ - private const XML_ERRORS = [ - \XML_ERROR_NONE => 'XML_ERROR_NONE', - \XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', - \XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', - \XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS', - \XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN', - \XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN', - \XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR', - \XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH', - \XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE', - \XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT', - \XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF', - \XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY', - \XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF', - \XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY', - \XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF', - \XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF', - \XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', - \XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI', - \XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING', - \XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', - \XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', - \XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', - ]; - - /** - * @return array - */ - public static function castXml($h, array $a, Stub $stub, bool $isNested) - { - $a['current_byte_index'] = xml_get_current_byte_index($h); - $a['current_column_number'] = xml_get_current_column_number($h); - $a['current_line_number'] = xml_get_current_line_number($h); - $a['error_code'] = xml_get_error_code($h); - - if (isset(self::XML_ERRORS[$a['error_code']])) { - $a['error_code'] = new ConstStub(self::XML_ERRORS[$a['error_code']], $a['error_code']); - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/AbstractCloner.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/AbstractCloner.php deleted file mode 100644 index fc330bae2..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/AbstractCloner.php +++ /dev/null @@ -1,397 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -use Symfony\Component\VarDumper\Caster\Caster; -use Symfony\Component\VarDumper\Exception\ThrowingCasterException; - -/** - * AbstractCloner implements a generic caster mechanism for objects and resources. - * - * @author Nicolas Grekas - */ -abstract class AbstractCloner implements ClonerInterface -{ - public static $defaultCasters = [ - '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'], - - 'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], - 'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'], - 'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], - 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], - 'Symfony\Component\VarDumper\Caster\ScalarStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castScalar'], - - 'Fiber' => ['Symfony\Component\VarDumper\Caster\FiberCaster', 'castFiber'], - - 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], - 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], - 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], - 'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'], - 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], - 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], - 'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'], - 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], - 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], - 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], - 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], - 'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'], - 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], - 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], - - 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], - 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], - 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], - 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - - 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], - 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], - 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], - 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'], - 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], - 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], - 'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], - 'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], - 'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], - 'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], - 'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], - 'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], - 'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], - 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'], - - 'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], - - 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], - 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], - 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], - 'Symfony\Bridge\Monolog\Logger' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\HttpClient\AmpHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\Response\AmpResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], - 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], - 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], - 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], - 'Symfony\Component\Uid\Ulid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUlid'], - 'Symfony\Component\Uid\Uuid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUuid'], - 'Symfony\Component\VarExporter\Internal\LazyObjectState' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castLazyObjectState'], - 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], - 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], - 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], - 'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\ErrorHandler\Exception\FlattenException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFlattenException'], - 'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], - - 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], - - 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], - - 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], - 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - - 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], - 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], - - 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], - 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], - 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], - 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], - 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], - - 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], - 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], - 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], - 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], - 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], - 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], - 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], - 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], - 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], - 'WeakMap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakMap'], - 'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'], - - 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], - 'Relay\Relay' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], - 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], - 'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'], - - 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], - 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], - 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], - 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], - - 'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'], - - 'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'], - 'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'], - 'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'], - 'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'], - 'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'], - - 'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'], - - 'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'], - 'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'], - 'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'], - 'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'], - - 'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'], - - 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], - - ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], - ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], - - 'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], - ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], - - ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], - ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], - ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], - ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], - ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], - ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], - - 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], - ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], - - ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], - ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], - - 'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], - ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], - - 'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'], - 'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'], - 'RdKafka\KafkaConsumer' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castKafkaConsumer'], - 'RdKafka\Metadata\Broker' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castBrokerMetadata'], - 'RdKafka\Metadata\Collection' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castCollectionMetadata'], - 'RdKafka\Metadata\Partition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castPartitionMetadata'], - 'RdKafka\Metadata\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicMetadata'], - 'RdKafka\Message' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castMessage'], - 'RdKafka\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopic'], - 'RdKafka\TopicPartition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicPartition'], - 'RdKafka\TopicConf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicConf'], - - 'FFI\CData' => ['Symfony\Component\VarDumper\Caster\FFICaster', 'castCTypeOrCData'], - 'FFI\CType' => ['Symfony\Component\VarDumper\Caster\FFICaster', 'castCTypeOrCData'], - ]; - - protected $maxItems = 2500; - protected $maxString = -1; - protected $minDepth = 1; - - /** - * @var array> - */ - private array $casters = []; - - /** - * @var callable|null - */ - private $prevErrorHandler; - - private array $classInfo = []; - private int $filter = 0; - - /** - * @param callable[]|null $casters A map of casters - * - * @see addCasters - */ - public function __construct(?array $casters = null) - { - $this->addCasters($casters ?? static::$defaultCasters); - } - - /** - * Adds casters for resources and objects. - * - * Maps resources or objects types to a callback. - * Types are in the key, with a callable caster for value. - * Resource types are to be prefixed with a `:`, - * see e.g. static::$defaultCasters. - * - * @param callable[] $casters A map of casters - * - * @return void - */ - public function addCasters(array $casters) - { - foreach ($casters as $type => $callback) { - $this->casters[$type][] = $callback; - } - } - - /** - * Sets the maximum number of items to clone past the minimum depth in nested structures. - * - * @return void - */ - public function setMaxItems(int $maxItems) - { - $this->maxItems = $maxItems; - } - - /** - * Sets the maximum cloned length for strings. - * - * @return void - */ - public function setMaxString(int $maxString) - { - $this->maxString = $maxString; - } - - /** - * Sets the minimum tree depth where we are guaranteed to clone all the items. After this - * depth is reached, only setMaxItems items will be cloned. - * - * @return void - */ - public function setMinDepth(int $minDepth) - { - $this->minDepth = $minDepth; - } - - /** - * Clones a PHP variable. - * - * @param int $filter A bit field of Caster::EXCLUDE_* constants - */ - public function cloneVar(mixed $var, int $filter = 0): Data - { - $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { - if (\E_RECOVERABLE_ERROR === $type || \E_USER_ERROR === $type) { - // Cloner never dies - throw new \ErrorException($msg, 0, $type, $file, $line); - } - - if ($this->prevErrorHandler) { - return ($this->prevErrorHandler)($type, $msg, $file, $line, $context); - } - - return false; - }); - $this->filter = $filter; - - if ($gc = gc_enabled()) { - gc_disable(); - } - try { - return new Data($this->doClone($var)); - } finally { - if ($gc) { - gc_enable(); - } - restore_error_handler(); - $this->prevErrorHandler = null; - } - } - - /** - * Effectively clones the PHP variable. - */ - abstract protected function doClone(mixed $var): array; - - /** - * Casts an object to an array representation. - * - * @param bool $isNested True if the object is nested in the dumped structure - */ - protected function castObject(Stub $stub, bool $isNested): array - { - $obj = $stub->value; - $class = $stub->class; - - if (str_contains($class, "@anonymous\0")) { - $stub->class = get_debug_type($obj); - } - if (isset($this->classInfo[$class])) { - [$i, $parents, $hasDebugInfo, $fileInfo] = $this->classInfo[$class]; - } else { - $i = 2; - $parents = [$class]; - $hasDebugInfo = method_exists($class, '__debugInfo'); - - foreach (class_parents($class) as $p) { - $parents[] = $p; - ++$i; - } - foreach (class_implements($class) as $p) { - $parents[] = $p; - ++$i; - } - $parents[] = '*'; - - $r = new \ReflectionClass($class); - $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [ - 'file' => $r->getFileName(), - 'line' => $r->getStartLine(), - ]; - - $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo]; - } - - $stub->attr += $fileInfo; - $a = Caster::castObject($obj, $class, $hasDebugInfo, $stub->class); - - try { - while ($i--) { - if (!empty($this->casters[$p = $parents[$i]])) { - foreach ($this->casters[$p] as $callback) { - $a = $callback($obj, $a, $stub, $isNested, $this->filter); - } - } - } - } catch (\Exception $e) { - $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; - } - - return $a; - } - - /** - * Casts a resource to an array representation. - * - * @param bool $isNested True if the object is nested in the dumped structure - */ - protected function castResource(Stub $stub, bool $isNested): array - { - $a = []; - $res = $stub->value; - $type = $stub->class; - - try { - if (!empty($this->casters[':'.$type])) { - foreach ($this->casters[':'.$type] as $callback) { - $a = $callback($res, $a, $stub, $isNested, $this->filter); - } - } - } catch (\Exception $e) { - $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; - } - - return $a; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/ClonerInterface.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/ClonerInterface.php deleted file mode 100644 index 5a8e2e4c5..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/ClonerInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * @author Nicolas Grekas - */ -interface ClonerInterface -{ - /** - * Clones a PHP variable. - */ - public function cloneVar(mixed $var): Data; -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Cursor.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Cursor.php deleted file mode 100644 index 1fd796d67..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Cursor.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * Represents the current state of a dumper while dumping. - * - * @author Nicolas Grekas - */ -class Cursor -{ - public const HASH_INDEXED = Stub::ARRAY_INDEXED; - public const HASH_ASSOC = Stub::ARRAY_ASSOC; - public const HASH_OBJECT = Stub::TYPE_OBJECT; - public const HASH_RESOURCE = Stub::TYPE_RESOURCE; - - public $depth = 0; - public $refIndex = 0; - public $softRefTo = 0; - public $softRefCount = 0; - public $softRefHandle = 0; - public $hardRefTo = 0; - public $hardRefCount = 0; - public $hardRefHandle = 0; - public $hashType; - public $hashKey; - public $hashKeyIsBinary; - public $hashIndex = 0; - public $hashLength = 0; - public $hashCut = 0; - public $stop = false; - public $attr = []; - public $skipChildren = false; -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Data.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Data.php deleted file mode 100644 index 16f51b0c7..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Data.php +++ /dev/null @@ -1,434 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -use Symfony\Component\VarDumper\Caster\Caster; -use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; - -/** - * @author Nicolas Grekas - */ -class Data implements \ArrayAccess, \Countable, \IteratorAggregate, \Stringable -{ - private array $data; - private int $position = 0; - private int|string $key = 0; - private int $maxDepth = 20; - private int $maxItemsPerDepth = -1; - private int $useRefHandles = -1; - private array $context = []; - - /** - * @param array $data An array as returned by ClonerInterface::cloneVar() - */ - public function __construct(array $data) - { - $this->data = $data; - } - - public function getType(): ?string - { - $item = $this->data[$this->position][$this->key]; - - if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { - $item = $item->value; - } - if (!$item instanceof Stub) { - return \gettype($item); - } - if (Stub::TYPE_STRING === $item->type) { - return 'string'; - } - if (Stub::TYPE_ARRAY === $item->type) { - return 'array'; - } - if (Stub::TYPE_OBJECT === $item->type) { - return $item->class; - } - if (Stub::TYPE_RESOURCE === $item->type) { - return $item->class.' resource'; - } - - return null; - } - - /** - * Returns a native representation of the original value. - * - * @param array|bool $recursive Whether values should be resolved recursively or not - * - * @return string|int|float|bool|array|Data[]|null - */ - public function getValue(array|bool $recursive = false): string|int|float|bool|array|null - { - $item = $this->data[$this->position][$this->key]; - - if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { - $item = $item->value; - } - if (!($item = $this->getStub($item)) instanceof Stub) { - return $item; - } - if (Stub::TYPE_STRING === $item->type) { - return $item->value; - } - - $children = $item->position ? $this->data[$item->position] : []; - - foreach ($children as $k => $v) { - if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { - continue; - } - $children[$k] = clone $this; - $children[$k]->key = $k; - $children[$k]->position = $item->position; - - if ($recursive) { - if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) { - $recursive = (array) $recursive; - if (isset($recursive[$v->position])) { - continue; - } - $recursive[$v->position] = true; - } - $children[$k] = $children[$k]->getValue($recursive); - } - } - - return $children; - } - - public function count(): int - { - return \count($this->getValue()); - } - - public function getIterator(): \Traversable - { - if (!\is_array($value = $this->getValue())) { - throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, get_debug_type($value))); - } - - yield from $value; - } - - /** - * @return mixed - */ - public function __get(string $key) - { - if (null !== $data = $this->seek($key)) { - $item = $this->getStub($data->data[$data->position][$data->key]); - - return $item instanceof Stub || [] === $item ? $data : $item; - } - - return null; - } - - public function __isset(string $key): bool - { - return null !== $this->seek($key); - } - - public function offsetExists(mixed $key): bool - { - return $this->__isset($key); - } - - public function offsetGet(mixed $key): mixed - { - return $this->__get($key); - } - - public function offsetSet(mixed $key, mixed $value): void - { - throw new \BadMethodCallException(self::class.' objects are immutable.'); - } - - public function offsetUnset(mixed $key): void - { - throw new \BadMethodCallException(self::class.' objects are immutable.'); - } - - public function __toString(): string - { - $value = $this->getValue(); - - if (!\is_array($value)) { - return (string) $value; - } - - return sprintf('%s (count=%d)', $this->getType(), \count($value)); - } - - /** - * Returns a depth limited clone of $this. - */ - public function withMaxDepth(int $maxDepth): static - { - $data = clone $this; - $data->maxDepth = $maxDepth; - - return $data; - } - - /** - * Limits the number of elements per depth level. - */ - public function withMaxItemsPerDepth(int $maxItemsPerDepth): static - { - $data = clone $this; - $data->maxItemsPerDepth = $maxItemsPerDepth; - - return $data; - } - - /** - * Enables/disables objects' identifiers tracking. - * - * @param bool $useRefHandles False to hide global ref. handles - */ - public function withRefHandles(bool $useRefHandles): static - { - $data = clone $this; - $data->useRefHandles = $useRefHandles ? -1 : 0; - - return $data; - } - - public function withContext(array $context): static - { - $data = clone $this; - $data->context = $context; - - return $data; - } - - public function getContext(): array - { - return $this->context; - } - - /** - * Seeks to a specific key in nested data structures. - */ - public function seek(string|int $key): ?static - { - $item = $this->data[$this->position][$this->key]; - - if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { - $item = $item->value; - } - if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { - return null; - } - $keys = [$key]; - - switch ($item->type) { - case Stub::TYPE_OBJECT: - $keys[] = Caster::PREFIX_DYNAMIC.$key; - $keys[] = Caster::PREFIX_PROTECTED.$key; - $keys[] = Caster::PREFIX_VIRTUAL.$key; - $keys[] = "\0$item->class\0$key"; - // no break - case Stub::TYPE_ARRAY: - case Stub::TYPE_RESOURCE: - break; - default: - return null; - } - - $data = null; - $children = $this->data[$item->position]; - - foreach ($keys as $key) { - if (isset($children[$key]) || \array_key_exists($key, $children)) { - $data = clone $this; - $data->key = $key; - $data->position = $item->position; - break; - } - } - - return $data; - } - - /** - * Dumps data with a DumperInterface dumper. - * - * @return void - */ - public function dump(DumperInterface $dumper) - { - $refs = [0]; - $cursor = new Cursor(); - $cursor->hashType = -1; - $cursor->attr = $this->context[SourceContextProvider::class] ?? []; - $label = $this->context['label'] ?? ''; - - if ($cursor->attr || '' !== $label) { - $dumper->dumpScalar($cursor, 'label', $label); - } - $cursor->hashType = 0; - $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); - } - - /** - * Depth-first dumping of items. - * - * @param mixed $item A Stub object or the original value being dumped - */ - private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, mixed $item): void - { - $cursor->refIndex = 0; - $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; - $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; - $firstSeen = true; - - if (!$item instanceof Stub) { - $cursor->attr = []; - $type = \gettype($item); - if ($item && 'array' === $type) { - $item = $this->getStub($item); - } - } elseif (Stub::TYPE_REF === $item->type) { - if ($item->handle) { - if (!isset($refs[$r = $item->handle - (\PHP_INT_MAX >> 1)])) { - $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; - } else { - $firstSeen = false; - } - $cursor->hardRefTo = $refs[$r]; - $cursor->hardRefHandle = $this->useRefHandles & $item->handle; - $cursor->hardRefCount = 0 < $item->handle ? $item->refCount : 0; - } - $cursor->attr = $item->attr; - $type = $item->class ?: \gettype($item->value); - $item = $this->getStub($item->value); - } - if ($item instanceof Stub) { - if ($item->refCount) { - if (!isset($refs[$r = $item->handle])) { - $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; - } else { - $firstSeen = false; - } - $cursor->softRefTo = $refs[$r]; - } - $cursor->softRefHandle = $this->useRefHandles & $item->handle; - $cursor->softRefCount = $item->refCount; - $cursor->attr = $item->attr; - $cut = $item->cut; - - if ($item->position && $firstSeen) { - $children = $this->data[$item->position]; - - if ($cursor->stop) { - if ($cut >= 0) { - $cut += \count($children); - } - $children = []; - } - } else { - $children = []; - } - switch ($item->type) { - case Stub::TYPE_STRING: - $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut); - break; - - case Stub::TYPE_ARRAY: - $item = clone $item; - $item->type = $item->class; - $item->class = $item->value; - // no break - case Stub::TYPE_OBJECT: - case Stub::TYPE_RESOURCE: - $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth; - $dumper->enterHash($cursor, $item->type, $item->class, $withChildren); - if ($withChildren) { - if ($cursor->skipChildren) { - $withChildren = false; - $cut = -1; - } else { - $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class); - } - } elseif ($children && 0 <= $cut) { - $cut += \count($children); - } - $cursor->skipChildren = false; - $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut); - break; - - case Stub::TYPE_SCALAR: - $dumper->dumpScalar($cursor, 'default', $item->attr['value']); - break; - - default: - throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".', $item->type)); - } - } elseif ('array' === $type) { - $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); - $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); - } elseif ('string' === $type) { - $dumper->dumpString($cursor, $item, false, 0); - } else { - $dumper->dumpScalar($cursor, $type, $item); - } - } - - /** - * Dumps children of hash structures. - * - * @return int The final number of removed items - */ - private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int - { - $cursor = clone $parentCursor; - ++$cursor->depth; - $cursor->hashType = $hashType; - $cursor->hashIndex = 0; - $cursor->hashLength = \count($children); - $cursor->hashCut = $hashCut; - foreach ($children as $key => $child) { - $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key); - $cursor->hashKey = $dumpKeys ? $key : null; - $this->dumpItem($dumper, $cursor, $refs, $child); - if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) { - $parentCursor->stop = true; - - return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut; - } - } - - return $hashCut; - } - - private function getStub(mixed $item): mixed - { - if (!$item || !\is_array($item)) { - return $item; - } - - $stub = new Stub(); - $stub->type = Stub::TYPE_ARRAY; - foreach ($item as $stub->class => $stub->position) { - } - if (isset($item[0])) { - $stub->cut = $item[0]; - } - $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0); - - return $stub; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/DumperInterface.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/DumperInterface.php deleted file mode 100644 index 4c5b315b6..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/DumperInterface.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * DumperInterface used by Data objects. - * - * @author Nicolas Grekas - */ -interface DumperInterface -{ - /** - * Dumps a scalar value. - * - * @return void - */ - public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value); - - /** - * Dumps a string. - * - * @param string $str The string being dumped - * @param bool $bin Whether $str is UTF-8 or binary encoded - * @param int $cut The number of characters $str has been cut by - * - * @return void - */ - public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut); - - /** - * Dumps while entering an hash. - * - * @param int $type A Cursor::HASH_* const for the type of hash - * @param string|int|null $class The object class, resource type or array count - * @param bool $hasChild When the dump of the hash has child item - * - * @return void - */ - public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild); - - /** - * Dumps while leaving an hash. - * - * @param int $type A Cursor::HASH_* const for the type of hash - * @param string|int|null $class The object class, resource type or array count - * @param bool $hasChild When the dump of the hash has child item - * @param int $cut The number of items the hash has been cut by - * - * @return void - */ - public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut); -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Stub.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Stub.php deleted file mode 100644 index 0c2a4b9d0..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/Stub.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * Represents the main properties of a PHP variable. - * - * @author Nicolas Grekas - */ -class Stub -{ - public const TYPE_REF = 1; - public const TYPE_STRING = 2; - public const TYPE_ARRAY = 3; - public const TYPE_OBJECT = 4; - public const TYPE_RESOURCE = 5; - public const TYPE_SCALAR = 6; - - public const STRING_BINARY = 1; - public const STRING_UTF8 = 2; - - public const ARRAY_ASSOC = 1; - public const ARRAY_INDEXED = 2; - - public $type = self::TYPE_REF; - public $class = ''; - public $value; - public $cut = 0; - public $handle = 0; - public $refCount = 0; - public $position = 0; - public $attr = []; - - private static array $defaultProperties = []; - - /** - * @internal - */ - public function __sleep(): array - { - $properties = []; - - if (!isset(self::$defaultProperties[$c = static::class])) { - self::$defaultProperties[$c] = get_class_vars($c); - - foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) { - unset(self::$defaultProperties[$c][$k]); - } - } - - foreach (self::$defaultProperties[$c] as $k => $v) { - if ($this->$k !== $v) { - $properties[] = $k; - } - } - - return $properties; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/VarCloner.php b/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/VarCloner.php deleted file mode 100644 index e168d0d3b..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Cloner/VarCloner.php +++ /dev/null @@ -1,243 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * @author Nicolas Grekas - */ -class VarCloner extends AbstractCloner -{ - private static array $arrayCache = []; - - protected function doClone(mixed $var): array - { - $len = 1; // Length of $queue - $pos = 0; // Number of cloned items past the minimum depth - $refsCounter = 0; // Hard references counter - $queue = [[$var]]; // This breadth-first queue is the return value - $hardRefs = []; // Map of original zval ids to stub objects - $objRefs = []; // Map of original object handles to their stub object counterpart - $objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning - $resRefs = []; // Map of original resource handles to their stub object counterpart - $values = []; // Map of stub objects' ids to original values - $maxItems = $this->maxItems; - $maxString = $this->maxString; - $minDepth = $this->minDepth; - $currentDepth = 0; // Current tree depth - $currentDepthFinalIndex = 0; // Final $queue index for current tree depth - $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached - $cookie = (object) []; // Unique object used to detect hard references - $a = null; // Array cast for nested structures - $stub = null; // Stub capturing the main properties of an original item value - // or null if the original value is used directly - - $arrayStub = new Stub(); - $arrayStub->type = Stub::TYPE_ARRAY; - $fromObjCast = false; - - for ($i = 0; $i < $len; ++$i) { - // Detect when we move on to the next tree depth - if ($i > $currentDepthFinalIndex) { - ++$currentDepth; - $currentDepthFinalIndex = $len - 1; - if ($currentDepth >= $minDepth) { - $minimumDepthReached = true; - } - } - - $refs = $vals = $queue[$i]; - foreach ($vals as $k => $v) { - // $v is the original value or a stub object in case of hard references - - $zvalRef = ($r = \ReflectionReference::fromArrayElement($vals, $k)) ? $r->getId() : null; - - if ($zvalRef) { - $vals[$k] = &$stub; // Break hard references to make $queue completely - unset($stub); // independent from the original structure - if (null !== $vals[$k] = $hardRefs[$zvalRef] ?? null) { - $v = $vals[$k]; - if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { - ++$v->value->refCount; - } - ++$v->refCount; - continue; - } - $vals[$k] = new Stub(); - $vals[$k]->value = $v; - $vals[$k]->handle = ++$refsCounter; - $hardRefs[$zvalRef] = $vals[$k]; - } - // Create $stub when the original value $v cannot be used directly - // If $v is a nested structure, put that structure in array $a - switch (true) { - case null === $v: - case \is_bool($v): - case \is_int($v): - case \is_float($v): - continue 2; - case \is_string($v): - if ('' === $v) { - continue 2; - } - if (!preg_match('//u', $v)) { - $stub = new Stub(); - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_BINARY; - if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { - $stub->cut = $cut; - $stub->value = substr($v, 0, -$cut); - } else { - $stub->value = $v; - } - } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { - $stub = new Stub(); - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_UTF8; - $stub->cut = $cut; - $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); - } else { - continue 2; - } - $a = null; - break; - - case \is_array($v): - if (!$v) { - continue 2; - } - $stub = $arrayStub; - - $stub->class = array_is_list($v) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC; - $a = $v; - break; - - case \is_object($v): - if (empty($objRefs[$h = spl_object_id($v)])) { - $stub = new Stub(); - $stub->type = Stub::TYPE_OBJECT; - $stub->class = $v::class; - $stub->value = $v; - $stub->handle = $h; - $a = $this->castObject($stub, 0 < $i); - if ($v !== $stub->value) { - if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { - break; - } - $stub->handle = $h = spl_object_id($stub->value); - } - $stub->value = null; - if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { - $stub->cut = \count($a); - $a = null; - } - } - if (empty($objRefs[$h])) { - $objRefs[$h] = $stub; - $objects[] = $v; - } else { - $stub = $objRefs[$h]; - ++$stub->refCount; - $a = null; - } - break; - - default: // resource - if (empty($resRefs[$h = (int) $v])) { - $stub = new Stub(); - $stub->type = Stub::TYPE_RESOURCE; - if ('Unknown' === $stub->class = @get_resource_type($v)) { - $stub->class = 'Closed'; - } - $stub->value = $v; - $stub->handle = $h; - $a = $this->castResource($stub, 0 < $i); - $stub->value = null; - if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { - $stub->cut = \count($a); - $a = null; - } - } - if (empty($resRefs[$h])) { - $resRefs[$h] = $stub; - } else { - $stub = $resRefs[$h]; - ++$stub->refCount; - $a = null; - } - break; - } - - if ($a) { - if (!$minimumDepthReached || 0 > $maxItems) { - $queue[$len] = $a; - $stub->position = $len++; - } elseif ($pos < $maxItems) { - if ($maxItems < $pos += \count($a)) { - $a = \array_slice($a, 0, $maxItems - $pos, true); - if ($stub->cut >= 0) { - $stub->cut += $pos - $maxItems; - } - } - $queue[$len] = $a; - $stub->position = $len++; - } elseif ($stub->cut >= 0) { - $stub->cut += \count($a); - $stub->position = 0; - } - } - - if ($arrayStub === $stub) { - if ($arrayStub->cut) { - $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; - $arrayStub->cut = 0; - } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { - $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; - } else { - self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; - } - } - - if (!$zvalRef) { - $vals[$k] = $stub; - } else { - $hardRefs[$zvalRef]->value = $stub; - } - } - - if ($fromObjCast) { - $fromObjCast = false; - $refs = $vals; - $vals = []; - $j = -1; - foreach ($queue[$i] as $k => $v) { - foreach ([$k => true] as $gk => $gv) { - } - if ($gk !== $k) { - $vals = (object) $vals; - $vals->{$k} = $refs[++$j]; - $vals = (array) $vals; - } else { - $vals[$k] = $refs[++$j]; - } - } - } - - $queue[$i] = $vals; - } - - foreach ($values as $h => $v) { - $hardRefs[$h] = $v; - } - - return $queue; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php b/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php deleted file mode 100644 index 4450fe986..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command\Descriptor; - -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\CliDumper; - -/** - * Describe collected data clones for cli output. - * - * @author Maxime Steinhausser - * - * @final - */ -class CliDescriptor implements DumpDescriptorInterface -{ - private CliDumper $dumper; - private mixed $lastIdentifier = null; - - public function __construct(CliDumper $dumper) - { - $this->dumper = $dumper; - } - - public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void - { - $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output); - $this->dumper->setColors($output->isDecorated()); - - $rows = [['date', date('r', (int) $context['timestamp'])]]; - $lastIdentifier = $this->lastIdentifier; - $this->lastIdentifier = $clientId; - - $section = "Received from client #$clientId"; - if (isset($context['request'])) { - $request = $context['request']; - $this->lastIdentifier = $request['identifier']; - $section = sprintf('%s %s', $request['method'], $request['uri']); - if ($controller = $request['controller']) { - $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")]; - } - } elseif (isset($context['cli'])) { - $this->lastIdentifier = $context['cli']['identifier']; - $section = '$ '.$context['cli']['command_line']; - } - - if ($this->lastIdentifier !== $lastIdentifier) { - $io->section($section); - } - - if (isset($context['source'])) { - $source = $context['source']; - $sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']); - if ($fileLink = $source['file_link'] ?? null) { - $sourceInfo = sprintf('%s', $fileLink, $sourceInfo); - } - $rows[] = ['source', $sourceInfo]; - $file = $source['file_relative'] ?? $source['file']; - $rows[] = ['file', $file]; - } - - $io->table([], $rows); - - $this->dumper->dump($data); - $io->newLine(); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php b/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php deleted file mode 100644 index 267d27bfa..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command\Descriptor; - -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\VarDumper\Cloner\Data; - -/** - * @author Maxime Steinhausser - */ -interface DumpDescriptorInterface -{ - public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php b/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php deleted file mode 100644 index 98f150a5e..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command\Descriptor; - -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; - -/** - * Describe collected data clones for html output. - * - * @author Maxime Steinhausser - * - * @final - */ -class HtmlDescriptor implements DumpDescriptorInterface -{ - private HtmlDumper $dumper; - private bool $initialized = false; - - public function __construct(HtmlDumper $dumper) - { - $this->dumper = $dumper; - } - - public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void - { - if (!$this->initialized) { - $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); - $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); - $output->writeln(""); - $this->initialized = true; - } - - $title = '-'; - if (isset($context['request'])) { - $request = $context['request']; - $controller = "{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}"; - $title = sprintf('%s %s', $request['method'], $uri = $request['uri'], $uri); - $dedupIdentifier = $request['identifier']; - } elseif (isset($context['cli'])) { - $title = '$ '.$context['cli']['command_line']; - $dedupIdentifier = $context['cli']['identifier']; - } else { - $dedupIdentifier = uniqid('', true); - } - - $sourceDescription = ''; - if (isset($context['source'])) { - $source = $context['source']; - $projectDir = $source['project_dir'] ?? null; - $sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']); - if (isset($source['file_link'])) { - $sourceDescription = sprintf('%s', $source['file_link'], $sourceDescription); - } - } - - $isoDate = $this->extractDate($context, 'c'); - $tags = array_filter([ - 'controller' => $controller ?? null, - 'project dir' => $projectDir ?? null, - ]); - - $output->writeln(<< -
-
-

$title

- -
- {$this->renderTags($tags)} -
-
-

- $sourceDescription -

- {$this->dumper->dump($data, true)} -
- -HTML - ); - } - - private function extractDate(array $context, string $format = 'r'): string - { - return date($format, (int) $context['timestamp']); - } - - private function renderTags(array $tags): string - { - if (!$tags) { - return ''; - } - - $renderedTags = ''; - foreach ($tags as $key => $value) { - $renderedTags .= sprintf('
  • %s%s
  • ', $key, $value); - } - - return << -
      - $renderedTags -
    - -HTML; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Command/ServerDumpCommand.php b/data/web/inc/lib/vendor/symfony/var-dumper/Command/ServerDumpCommand.php deleted file mode 100644 index b64a884b9..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Command/ServerDumpCommand.php +++ /dev/null @@ -1,112 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command; - -use Symfony\Component\Console\Attribute\AsCommand; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Completion\CompletionInput; -use Symfony\Component\Console\Completion\CompletionSuggestions; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; -use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; -use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; -use Symfony\Component\VarDumper\Dumper\CliDumper; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; -use Symfony\Component\VarDumper\Server\DumpServer; - -/** - * Starts a dump server to collect and output dumps on a single place with multiple formats support. - * - * @author Maxime Steinhausser - * - * @final - */ -#[AsCommand(name: 'server:dump', description: 'Start a dump server that collects and displays dumps in a single place')] -class ServerDumpCommand extends Command -{ - private DumpServer $server; - - /** @var DumpDescriptorInterface[] */ - private array $descriptors; - - public function __construct(DumpServer $server, array $descriptors = []) - { - $this->server = $server; - $this->descriptors = $descriptors + [ - 'cli' => new CliDescriptor(new CliDumper()), - 'html' => new HtmlDescriptor(new HtmlDumper()), - ]; - - parent::__construct(); - } - - protected function configure(): void - { - $this - ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', implode(', ', $this->getAvailableFormats())), 'cli') - ->setHelp(<<<'EOF' -%command.name% starts a dump server that collects and displays -dumps in a single place for debugging you application: - - php %command.full_name% - -You can consult dumped data in HTML format in your browser by providing the --format=html option -and redirecting the output to a file: - - php %command.full_name% --format="html" > dump.html - -EOF - ) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - $format = $input->getOption('format'); - - if (!$descriptor = $this->descriptors[$format] ?? null) { - throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format)); - } - - $errorIo = $io->getErrorStyle(); - $errorIo->title('Symfony Var Dumper Server'); - - $this->server->start(); - - $errorIo->success(sprintf('Server listening on %s', $this->server->getHost())); - $errorIo->comment('Quit the server with CONTROL-C.'); - - $this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) { - $descriptor->describe($io, $data, $context, $clientId); - }); - - return 0; - } - - public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void - { - if ($input->mustSuggestOptionValuesFor('format')) { - $suggestions->suggestValues($this->getAvailableFormats()); - } - } - - private function getAvailableFormats(): array - { - return array_keys($this->descriptors); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/AbstractDumper.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/AbstractDumper.php deleted file mode 100644 index 53165ba64..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/AbstractDumper.php +++ /dev/null @@ -1,204 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Cloner\DumperInterface; - -/** - * Abstract mechanism for dumping a Data object. - * - * @author Nicolas Grekas - */ -abstract class AbstractDumper implements DataDumperInterface, DumperInterface -{ - public const DUMP_LIGHT_ARRAY = 1; - public const DUMP_STRING_LENGTH = 2; - public const DUMP_COMMA_SEPARATOR = 4; - public const DUMP_TRAILING_COMMA = 8; - - /** @var callable|resource|string|null */ - public static $defaultOutput = 'php://output'; - - protected $line = ''; - /** @var callable|null */ - protected $lineDumper; - /** @var resource|null */ - protected $outputStream; - protected $decimalPoint = '.'; - protected $indentPad = ' '; - protected $flags; - - private string $charset = ''; - - /** - * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput - * @param string|null $charset The default character encoding to use for non-UTF8 strings - * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation - */ - public function __construct($output = null, ?string $charset = null, int $flags = 0) - { - $this->flags = $flags; - $this->setCharset($charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8'); - $this->setOutput($output ?: static::$defaultOutput); - if (!$output && \is_string(static::$defaultOutput)) { - static::$defaultOutput = $this->outputStream; - } - } - - /** - * Sets the output destination of the dumps. - * - * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path - * - * @return callable|resource|string|null The previous output destination - */ - public function setOutput($output) - { - $prev = $this->outputStream ?? $this->lineDumper; - - if (\is_callable($output)) { - $this->outputStream = null; - $this->lineDumper = $output; - } else { - if (\is_string($output)) { - $output = fopen($output, 'w'); - } - $this->outputStream = $output; - $this->lineDumper = $this->echoLine(...); - } - - return $prev; - } - - /** - * Sets the default character encoding to use for non-UTF8 strings. - * - * @return string The previous charset - */ - public function setCharset(string $charset): string - { - $prev = $this->charset; - - $charset = strtoupper($charset); - $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; - - $this->charset = $charset; - - return $prev; - } - - /** - * Sets the indentation pad string. - * - * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level - * - * @return string The previous indent pad - */ - public function setIndentPad(string $pad): string - { - $prev = $this->indentPad; - $this->indentPad = $pad; - - return $prev; - } - - /** - * Dumps a Data object. - * - * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump - * - * @return string|null The dump as string when $output is true - */ - public function dump(Data $data, $output = null): ?string - { - if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) { - setlocale(\LC_NUMERIC, 'C'); - } - - if ($returnDump = true === $output) { - $output = fopen('php://memory', 'r+'); - } - if ($output) { - $prevOutput = $this->setOutput($output); - } - try { - $data->dump($this); - $this->dumpLine(-1); - - if ($returnDump) { - $result = stream_get_contents($output, -1, 0); - fclose($output); - - return $result; - } - } finally { - if ($output) { - $this->setOutput($prevOutput); - } - if ($locale) { - setlocale(\LC_NUMERIC, $locale); - } - } - - return null; - } - - /** - * Dumps the current line. - * - * @param int $depth The recursive depth in the dumped structure for the line being dumped, - * or -1 to signal the end-of-dump to the line dumper callable - * - * @return void - */ - protected function dumpLine(int $depth) - { - ($this->lineDumper)($this->line, $depth, $this->indentPad); - $this->line = ''; - } - - /** - * Generic line dumper callback. - * - * @return void - */ - protected function echoLine(string $line, int $depth, string $indentPad) - { - if (-1 !== $depth) { - fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); - } - } - - /** - * Converts a non-UTF-8 string to UTF-8. - */ - protected function utf8Encode(?string $s): ?string - { - if (null === $s || preg_match('//u', $s)) { - return $s; - } - - if (!\function_exists('iconv')) { - throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); - } - - if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) { - return $c; - } - if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) { - return $c; - } - - return iconv('CP850', 'UTF-8', $s); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/CliDumper.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/CliDumper.php deleted file mode 100644 index af9637072..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/CliDumper.php +++ /dev/null @@ -1,677 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Cursor; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * CliDumper dumps variables for command line output. - * - * @author Nicolas Grekas - */ -class CliDumper extends AbstractDumper -{ - public static $defaultColors; - /** @var callable|resource|string|null */ - public static $defaultOutput = 'php://stdout'; - - protected $colors; - protected $maxStringWidth = 0; - protected $styles = [ - // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics - 'default' => '0;38;5;208', - 'num' => '1;38;5;38', - 'const' => '1;38;5;208', - 'str' => '1;38;5;113', - 'note' => '38;5;38', - 'ref' => '38;5;247', - 'public' => '', - 'protected' => '', - 'private' => '', - 'meta' => '38;5;170', - 'key' => '38;5;113', - 'index' => '38;5;38', - ]; - - protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/'; - protected static $controlCharsMap = [ - "\t" => '\t', - "\n" => '\n', - "\v" => '\v', - "\f" => '\f', - "\r" => '\r', - "\033" => '\e', - ]; - protected static $unicodeCharsRx = "/[\u{00A0}\u{00AD}\u{034F}\u{061C}\u{115F}\u{1160}\u{17B4}\u{17B5}\u{180E}\u{2000}-\u{200F}\u{202F}\u{205F}\u{2060}-\u{2064}\u{206A}-\u{206F}\u{3000}\u{2800}\u{3164}\u{FEFF}\u{FFA0}\u{1D159}\u{1D173}-\u{1D17A}]/u"; - - protected $collapseNextHash = false; - protected $expandNextHash = false; - - private array $displayOptions = [ - 'fileLinkFormat' => null, - ]; - - private bool $handlesHrefGracefully; - - public function __construct($output = null, ?string $charset = null, int $flags = 0) - { - parent::__construct($output, $charset, $flags); - - if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { - // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI - $this->setStyles([ - 'default' => '31', - 'num' => '1;34', - 'const' => '1;31', - 'str' => '1;32', - 'note' => '34', - 'ref' => '1;30', - 'meta' => '35', - 'key' => '32', - 'index' => '34', - ]); - } - - $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; - } - - /** - * Enables/disables colored output. - * - * @return void - */ - public function setColors(bool $colors) - { - $this->colors = $colors; - } - - /** - * Sets the maximum number of characters per line for dumped strings. - * - * @return void - */ - public function setMaxStringWidth(int $maxStringWidth) - { - $this->maxStringWidth = $maxStringWidth; - } - - /** - * Configures styles. - * - * @param array $styles A map of style names to style definitions - * - * @return void - */ - public function setStyles(array $styles) - { - $this->styles = $styles + $this->styles; - } - - /** - * Configures display options. - * - * @param array $displayOptions A map of display options to customize the behavior - * - * @return void - */ - public function setDisplayOptions(array $displayOptions) - { - $this->displayOptions = $displayOptions + $this->displayOptions; - } - - /** - * @return void - */ - public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value) - { - $this->dumpKey($cursor); - $this->collapseNextHash = $this->expandNextHash = false; - - $style = 'const'; - $attr = $cursor->attr; - - switch ($type) { - case 'default': - $style = 'default'; - break; - - case 'label': - $this->styles += ['label' => $this->styles['default']]; - $style = 'label'; - break; - - case 'integer': - $style = 'num'; - - if (isset($this->styles['integer'])) { - $style = 'integer'; - } - - break; - - case 'double': - $style = 'num'; - - if (isset($this->styles['float'])) { - $style = 'float'; - } - - $value = match (true) { - \INF === $value => 'INF', - -\INF === $value => '-INF', - is_nan($value) => 'NAN', - default => !str_contains($value = (string) $value, $this->decimalPoint) ? $value .= $this->decimalPoint.'0' : $value, - }; - break; - - case 'NULL': - $value = 'null'; - break; - - case 'boolean': - $value = $value ? 'true' : 'false'; - break; - - default: - $attr += ['value' => $this->utf8Encode($value)]; - $value = $this->utf8Encode($type); - break; - } - - $this->line .= $this->style($style, $value, $attr); - - $this->endValue($cursor); - } - - /** - * @return void - */ - public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) - { - $this->dumpKey($cursor); - $this->collapseNextHash = $this->expandNextHash = false; - $attr = $cursor->attr; - - if ($bin) { - $str = $this->utf8Encode($str); - } - if ('' === $str) { - $this->line .= '""'; - if ($cut) { - $this->line .= '…'.$cut; - } - $this->endValue($cursor); - } else { - $attr += [ - 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, - 'binary' => $bin, - ]; - $str = $bin && str_contains($str, "\0") ? [$str] : explode("\n", $str); - if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { - unset($str[1]); - $str[0] .= "\n"; - } - $m = \count($str) - 1; - $i = $lineCut = 0; - - if (self::DUMP_STRING_LENGTH & $this->flags) { - $this->line .= '('.$attr['length'].') '; - } - if ($bin) { - $this->line .= 'b'; - } - - if ($m) { - $this->line .= '"""'; - $this->dumpLine($cursor->depth); - } else { - $this->line .= '"'; - } - - foreach ($str as $str) { - if ($i < $m) { - $str .= "\n"; - } - if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { - $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); - $lineCut = $len - $this->maxStringWidth; - } - if ($m && 0 < $cursor->depth) { - $this->line .= $this->indentPad; - } - if ('' !== $str) { - $this->line .= $this->style('str', $str, $attr); - } - if ($i++ == $m) { - if ($m) { - if ('' !== $str) { - $this->dumpLine($cursor->depth); - if (0 < $cursor->depth) { - $this->line .= $this->indentPad; - } - } - $this->line .= '"""'; - } else { - $this->line .= '"'; - } - if ($cut < 0) { - $this->line .= '…'; - $lineCut = 0; - } elseif ($cut) { - $lineCut += $cut; - } - } - if ($lineCut) { - $this->line .= '…'.$lineCut; - $lineCut = 0; - } - - if ($i > $m) { - $this->endValue($cursor); - } else { - $this->dumpLine($cursor->depth); - } - } - } - } - - /** - * @return void - */ - public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild) - { - $this->colors ??= $this->supportsColors(); - - $this->dumpKey($cursor); - $this->expandNextHash = false; - $attr = $cursor->attr; - - if ($this->collapseNextHash) { - $cursor->skipChildren = true; - $this->collapseNextHash = $hasChild = false; - } - - $class = $this->utf8Encode($class); - if (Cursor::HASH_OBJECT === $type) { - $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; - } elseif (Cursor::HASH_RESOURCE === $type) { - $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); - } else { - $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; - } - - if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { - $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); - } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { - $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); - } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { - $prefix = substr($prefix, 0, -1); - } - - $this->line .= $prefix; - - if ($hasChild) { - $this->dumpLine($cursor->depth); - } - } - - /** - * @return void - */ - public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut) - { - if (empty($cursor->attr['cut_hash'])) { - $this->dumpEllipsis($cursor, $hasChild, $cut); - $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); - } - - $this->endValue($cursor); - } - - /** - * Dumps an ellipsis for cut children. - * - * @param bool $hasChild When the dump of the hash has child item - * @param int $cut The number of items the hash has been cut by - * - * @return void - */ - protected function dumpEllipsis(Cursor $cursor, bool $hasChild, int $cut) - { - if ($cut) { - $this->line .= ' …'; - if (0 < $cut) { - $this->line .= $cut; - } - if ($hasChild) { - $this->dumpLine($cursor->depth + 1); - } - } - } - - /** - * Dumps a key in a hash structure. - * - * @return void - */ - protected function dumpKey(Cursor $cursor) - { - if (null !== $key = $cursor->hashKey) { - if ($cursor->hashKeyIsBinary) { - $key = $this->utf8Encode($key); - } - $attr = ['binary' => $cursor->hashKeyIsBinary]; - $bin = $cursor->hashKeyIsBinary ? 'b' : ''; - $style = 'key'; - switch ($cursor->hashType) { - default: - case Cursor::HASH_INDEXED: - if (self::DUMP_LIGHT_ARRAY & $this->flags) { - break; - } - $style = 'index'; - // no break - case Cursor::HASH_ASSOC: - if (\is_int($key)) { - $this->line .= $this->style($style, $key).' => '; - } else { - $this->line .= $bin.'"'.$this->style($style, $key).'" => '; - } - break; - - case Cursor::HASH_RESOURCE: - $key = "\0~\0".$key; - // no break - case Cursor::HASH_OBJECT: - if (!isset($key[0]) || "\0" !== $key[0]) { - $this->line .= '+'.$bin.$this->style('public', $key).': '; - } elseif (0 < strpos($key, "\0", 1)) { - $key = explode("\0", substr($key, 1), 2); - - switch ($key[0][0]) { - case '+': // User inserted keys - $attr['dynamic'] = true; - $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; - break 2; - case '~': - $style = 'meta'; - if (isset($key[0][1])) { - parse_str(substr($key[0], 1), $attr); - $attr += ['binary' => $cursor->hashKeyIsBinary]; - } - break; - case '*': - $style = 'protected'; - $bin = '#'.$bin; - break; - default: - $attr['class'] = $key[0]; - $style = 'private'; - $bin = '-'.$bin; - break; - } - - if (isset($attr['collapse'])) { - if ($attr['collapse']) { - $this->collapseNextHash = true; - } else { - $this->expandNextHash = true; - } - } - - $this->line .= $bin.$this->style($style, $key[1], $attr).($attr['separator'] ?? ': '); - } else { - // This case should not happen - $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; - } - break; - } - - if ($cursor->hardRefTo) { - $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; - } - } - } - - /** - * Decorates a value with some style. - * - * @param string $style The type of style being applied - * @param string $value The value being styled - * @param array $attr Optional context information - */ - protected function style(string $style, string $value, array $attr = []): string - { - $this->colors ??= $this->supportsColors(); - - $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') - && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) - && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); - - if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { - $prefix = substr($value, 0, -$attr['ellipsis']); - if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && str_starts_with($prefix, $_SERVER[$pwd])) { - $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); - } - if (!empty($attr['ellipsis-tail'])) { - $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); - $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); - } else { - $value = substr($value, -$attr['ellipsis']); - } - - $value = $this->style('default', $prefix).$this->style($style, $value); - - goto href; - } - - $map = static::$controlCharsMap; - $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; - $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; - $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { - $s = $startCchr; - $c = $c[$i = 0]; - do { - $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i])); - } while (isset($c[++$i])); - - return $s.$endCchr; - }, $value, -1, $cchrCount); - - if (!($attr['binary'] ?? false)) { - $value = preg_replace_callback(static::$unicodeCharsRx, function ($c) use (&$cchrCount, $startCchr, $endCchr) { - ++$cchrCount; - - return $startCchr.'\u{'.strtoupper(dechex(mb_ord($c[0]))).'}'.$endCchr; - }, $value); - } - - if ($this->colors && '' !== $value) { - if ($cchrCount && "\033" === $value[0]) { - $value = substr($value, \strlen($startCchr)); - } else { - $value = "\033[{$this->styles[$style]}m".$value; - } - if ($cchrCount && str_ends_with($value, $endCchr)) { - $value = substr($value, 0, -\strlen($endCchr)); - } else { - $value .= "\033[{$this->styles['default']}m"; - } - } - - href: - if ($this->colors && $this->handlesHrefGracefully) { - if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { - if ('note' === $style) { - $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; - } else { - $attr['href'] = $href; - } - } - if (isset($attr['href'])) { - if ('label' === $style) { - $value .= '^'; - } - $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; - } - } - - if ('label' === $style && '' !== $value) { - $value .= ' '; - } - - return $value; - } - - protected function supportsColors(): bool - { - if ($this->outputStream !== static::$defaultOutput) { - return $this->hasColorSupport($this->outputStream); - } - if (isset(static::$defaultColors)) { - return static::$defaultColors; - } - if (isset($_SERVER['argv'][1])) { - $colors = $_SERVER['argv']; - $i = \count($colors); - while (--$i > 0) { - if (isset($colors[$i][5])) { - switch ($colors[$i]) { - case '--ansi': - case '--color': - case '--color=yes': - case '--color=force': - case '--color=always': - case '--colors=always': - return static::$defaultColors = true; - - case '--no-ansi': - case '--color=no': - case '--color=none': - case '--color=never': - case '--colors=never': - return static::$defaultColors = false; - } - } - } - } - - $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; - $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'w') : $this->outputStream; - - return static::$defaultColors = $this->hasColorSupport($h); - } - - /** - * @return void - */ - protected function dumpLine(int $depth, bool $endOfValue = false) - { - if (null === $this->colors) { - $this->colors = $this->supportsColors(); - } - - if ($this->colors) { - $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); - } - parent::dumpLine($depth); - } - - /** - * @return void - */ - protected function endValue(Cursor $cursor) - { - if (-1 === $cursor->hashType) { - return; - } - - if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { - if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { - $this->line .= ','; - } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { - $this->line .= ','; - } - } - - $this->dumpLine($cursor->depth, true); - } - - /** - * Returns true if the stream supports colorization. - * - * Reference: Composer\XdebugHandler\Process::supportsColor - * https://github.com/composer/xdebug-handler - */ - private function hasColorSupport(mixed $stream): bool - { - if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { - return false; - } - - // Follow https://no-color.org/ - if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { - return false; - } - - if ('Hyper' === getenv('TERM_PROGRAM')) { - return true; - } - - if (\DIRECTORY_SEPARATOR === '\\') { - return (\function_exists('sapi_windows_vt100_support') - && @sapi_windows_vt100_support($stream)) - || false !== getenv('ANSICON') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM'); - } - - return stream_isatty($stream); - } - - /** - * Returns true if the Windows terminal supports true color. - * - * Note that this does not check an output stream, but relies on environment - * variables from known implementations, or a PHP and Windows version that - * supports true color. - */ - private function isWindowsTrueColor(): bool - { - $result = 183 <= getenv('ANSICON_VER') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM') - || 'Hyper' === getenv('TERM_PROGRAM'); - - if (!$result) { - $version = sprintf( - '%s.%s.%s', - PHP_WINDOWS_VERSION_MAJOR, - PHP_WINDOWS_VERSION_MINOR, - PHP_WINDOWS_VERSION_BUILD - ); - $result = $version >= '10.0.15063'; - } - - return $result; - } - - private function getSourceLink(string $file, int $line): string|false - { - if ($fmt = $this->displayOptions['fileLinkFormat']) { - return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); - } - - return false; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php deleted file mode 100644 index 38f878971..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -/** - * Tries to provide context on CLI. - * - * @author Maxime Steinhausser - */ -final class CliContextProvider implements ContextProviderInterface -{ - public function getContext(): ?array - { - if ('cli' !== \PHP_SAPI) { - return null; - } - - return [ - 'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []), - 'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']), - ]; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php deleted file mode 100644 index 532aa0f96..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -/** - * Interface to provide contextual data about dump data clones sent to a server. - * - * @author Maxime Steinhausser - */ -interface ContextProviderInterface -{ - public function getContext(): ?array; -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php deleted file mode 100644 index 69dff067b..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\VarDumper\Caster\ReflectionCaster; -use Symfony\Component\VarDumper\Cloner\VarCloner; - -/** - * Tries to provide context from a request. - * - * @author Maxime Steinhausser - */ -final class RequestContextProvider implements ContextProviderInterface -{ - private RequestStack $requestStack; - private VarCloner $cloner; - - public function __construct(RequestStack $requestStack) - { - $this->requestStack = $requestStack; - $this->cloner = new VarCloner(); - $this->cloner->setMaxItems(0); - $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); - } - - public function getContext(): ?array - { - if (null === $request = $this->requestStack->getCurrentRequest()) { - return null; - } - - $controller = $request->attributes->get('_controller'); - - return [ - 'uri' => $request->getUri(), - 'method' => $request->getMethod(), - 'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller, - 'identifier' => spl_object_hash($request), - ]; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php deleted file mode 100644 index cadddfac4..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php +++ /dev/null @@ -1,127 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter as LegacyFileLinkFormatter; -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; -use Symfony\Component\VarDumper\VarDumper; -use Twig\Template; - -/** - * Tries to provide context from sources (class name, file, line, code excerpt, ...). - * - * @author Nicolas Grekas - * @author Maxime Steinhausser - */ -final class SourceContextProvider implements ContextProviderInterface -{ - private int $limit; - private ?string $charset; - private ?string $projectDir; - private FileLinkFormatter|LegacyFileLinkFormatter|null $fileLinkFormatter; - - public function __construct(?string $charset = null, ?string $projectDir = null, FileLinkFormatter|LegacyFileLinkFormatter|null $fileLinkFormatter = null, int $limit = 9) - { - $this->charset = $charset; - $this->projectDir = $projectDir; - $this->fileLinkFormatter = $fileLinkFormatter; - $this->limit = $limit; - } - - public function getContext(): ?array - { - $trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit); - - $file = $trace[1]['file']; - $line = $trace[1]['line']; - $name = '-' === $file || 'Standard input code' === $file ? 'Standard input code' : false; - $fileExcerpt = false; - - for ($i = 2; $i < $this->limit; ++$i) { - if (isset($trace[$i]['class'], $trace[$i]['function']) - && 'dump' === $trace[$i]['function'] - && VarDumper::class === $trace[$i]['class'] - ) { - $file = $trace[$i]['file'] ?? $file; - $line = $trace[$i]['line'] ?? $line; - - while (++$i < $this->limit) { - if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && !str_starts_with($trace[$i]['function'], 'call_user_func')) { - $file = $trace[$i]['file']; - $line = $trace[$i]['line']; - - break; - } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) { - $template = $trace[$i]['object']; - $name = $template->getTemplateName(); - $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); - $info = $template->getDebugInfo(); - if (isset($info[$trace[$i - 1]['line']])) { - $line = $info[$trace[$i - 1]['line']]; - $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; - - if ($src) { - $src = explode("\n", $src); - $fileExcerpt = []; - - for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { - $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; - } - - $fileExcerpt = '
      '.implode("\n", $fileExcerpt).'
    '; - } - } - break; - } - } - break; - } - } - - if (false === $name) { - $name = str_replace('\\', '/', $file); - $name = substr($name, strrpos($name, '/') + 1); - } - - $context = ['name' => $name, 'file' => $file, 'line' => $line]; - $context['file_excerpt'] = $fileExcerpt; - - if (null !== $this->projectDir) { - $context['project_dir'] = $this->projectDir; - if (str_starts_with($file, $this->projectDir)) { - $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - } - } - - if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) { - $context['file_link'] = $fileLink; - } - - return $context; - } - - private function htmlEncode(string $s): string - { - $html = ''; - - $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset); - $dumper->setDumpHeader(''); - $dumper->setDumpBoundaries('', ''); - - $cloner = new VarCloner(); - $dumper->dump($cloner->cloneVar($s)); - - return substr(strip_tags($html), 1, -1); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php deleted file mode 100644 index 84cfb4259..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; - -/** - * @author Kévin Thérage - */ -class ContextualizedDumper implements DataDumperInterface -{ - private DataDumperInterface $wrappedDumper; - private array $contextProviders; - - /** - * @param ContextProviderInterface[] $contextProviders - */ - public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) - { - $this->wrappedDumper = $wrappedDumper; - $this->contextProviders = $contextProviders; - } - - /** - * @return string|null - */ - public function dump(Data $data) - { - $context = $data->getContext(); - foreach ($this->contextProviders as $contextProvider) { - $context[$contextProvider::class] = $contextProvider->getContext(); - } - - return $this->wrappedDumper->dump($data->withContext($context)); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php deleted file mode 100644 index df05b6af5..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; - -/** - * DataDumperInterface for dumping Data objects. - * - * @author Nicolas Grekas - */ -interface DataDumperInterface -{ - /** - * @return string|null - */ - public function dump(Data $data); -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/HtmlDumper.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/HtmlDumper.php deleted file mode 100644 index ea09e6819..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/HtmlDumper.php +++ /dev/null @@ -1,998 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Cursor; -use Symfony\Component\VarDumper\Cloner\Data; - -/** - * HtmlDumper dumps variables as HTML. - * - * @author Nicolas Grekas - */ -class HtmlDumper extends CliDumper -{ - /** @var callable|resource|string|null */ - public static $defaultOutput = 'php://output'; - - protected static $themes = [ - 'dark' => [ - 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', - 'num' => 'font-weight:bold; color:#1299DA', - 'const' => 'font-weight:bold', - 'str' => 'font-weight:bold; color:#56DB3A', - 'note' => 'color:#1299DA', - 'ref' => 'color:#A0A0A0', - 'public' => 'color:#FFFFFF', - 'protected' => 'color:#FFFFFF', - 'private' => 'color:#FFFFFF', - 'meta' => 'color:#B729D9', - 'key' => 'color:#56DB3A', - 'index' => 'color:#1299DA', - 'ellipsis' => 'color:#FF8400', - 'ns' => 'user-select:none;', - ], - 'light' => [ - 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', - 'num' => 'font-weight:bold; color:#1299DA', - 'const' => 'font-weight:bold', - 'str' => 'font-weight:bold; color:#629755;', - 'note' => 'color:#6897BB', - 'ref' => 'color:#6E6E6E', - 'public' => 'color:#262626', - 'protected' => 'color:#262626', - 'private' => 'color:#262626', - 'meta' => 'color:#B729D9', - 'key' => 'color:#789339', - 'index' => 'color:#1299DA', - 'ellipsis' => 'color:#CC7832', - 'ns' => 'user-select:none;', - ], - ]; - - protected $dumpHeader; - protected $dumpPrefix = '
    ';
    -    protected $dumpSuffix = '
    '; - protected $dumpId = 'sf-dump'; - protected $colors = true; - protected $headerIsDumped = false; - protected $lastDepth = -1; - protected $styles; - - private array $displayOptions = [ - 'maxDepth' => 1, - 'maxStringLength' => 160, - 'fileLinkFormat' => null, - ]; - private array $extraDisplayOptions = []; - - public function __construct($output = null, ?string $charset = null, int $flags = 0) - { - AbstractDumper::__construct($output, $charset, $flags); - $this->dumpId = 'sf-dump-'.mt_rand(); - $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); - $this->styles = static::$themes['dark'] ?? self::$themes['dark']; - } - - /** - * @return void - */ - public function setStyles(array $styles) - { - $this->headerIsDumped = false; - $this->styles = $styles + $this->styles; - } - - /** - * @return void - */ - public function setTheme(string $themeName) - { - if (!isset(static::$themes[$themeName])) { - throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class)); - } - - $this->setStyles(static::$themes[$themeName]); - } - - /** - * Configures display options. - * - * @param array $displayOptions A map of display options to customize the behavior - * - * @return void - */ - public function setDisplayOptions(array $displayOptions) - { - $this->headerIsDumped = false; - $this->displayOptions = $displayOptions + $this->displayOptions; - } - - /** - * Sets an HTML header that will be dumped once in the output stream. - * - * @return void - */ - public function setDumpHeader(?string $header) - { - $this->dumpHeader = $header; - } - - /** - * Sets an HTML prefix and suffix that will encapse every single dump. - * - * @return void - */ - public function setDumpBoundaries(string $prefix, string $suffix) - { - $this->dumpPrefix = $prefix; - $this->dumpSuffix = $suffix; - } - - public function dump(Data $data, $output = null, array $extraDisplayOptions = []): ?string - { - $this->extraDisplayOptions = $extraDisplayOptions; - $result = parent::dump($data, $output); - $this->dumpId = 'sf-dump-'.mt_rand(); - - return $result; - } - - /** - * Dumps the HTML header. - * - * @return string - */ - protected function getDumpHeader() - { - $this->headerIsDumped = $this->outputStream ?? $this->lineDumper; - - if (null !== $this->dumpHeader) { - return $this->dumpHeader; - } - - $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML' -'.$this->dumpHeader; - } - - /** - * @return void - */ - public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) - { - if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) { - $this->dumpKey($cursor); - $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []); - $this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' ' : ' '; - $this->endValue($cursor); - $this->line .= $this->indentPad; - $this->line .= sprintf('', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data'])); - $this->endValue($cursor); - } else { - parent::dumpString($cursor, $str, $bin, $cut); - } - } - - /** - * @return void - */ - public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild) - { - if (Cursor::HASH_OBJECT === $type) { - $cursor->attr['depth'] = $cursor->depth; - } - parent::enterHash($cursor, $type, $class, false); - - if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) { - $cursor->skipChildren = false; - $eol = ' class=sf-dump-compact>'; - } else { - $this->expandNextHash = false; - $eol = ' class=sf-dump-expanded>'; - } - - if ($hasChild) { - $this->line .= 'dumpId, $r); - } - $this->line .= $eol; - $this->dumpLine($cursor->depth); - } - } - - /** - * @return void - */ - public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut) - { - $this->dumpEllipsis($cursor, $hasChild, $cut); - if ($hasChild) { - $this->line .= ''; - } - parent::leaveHash($cursor, $type, $class, $hasChild, 0); - } - - protected function style(string $style, string $value, array $attr = []): string - { - if ('' === $value && ('label' !== $style || !isset($attr['file']) && !isset($attr['href']))) { - return ''; - } - - $v = esc($value); - - if ('ref' === $style) { - if (empty($attr['count'])) { - return sprintf('%s', $v); - } - $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); - - return sprintf('%s', $this->dumpId, $r, 1 + $attr['count'], $v); - } - - if ('const' === $style && isset($attr['value'])) { - $style .= sprintf(' title="%s"', esc(\is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value']))); - } elseif ('public' === $style) { - $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'); - } elseif ('str' === $style && 1 < $attr['length']) { - $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); - } elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) { - $style .= ' title=""'; - $attr += [ - 'ellipsis' => \strlen($value) - $c, - 'ellipsis-type' => 'note', - 'ellipsis-tail' => 1, - ]; - } elseif ('protected' === $style) { - $style .= ' title="Protected property"'; - } elseif ('meta' === $style && isset($attr['title'])) { - $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title']))); - } elseif ('private' === $style) { - $style .= sprintf(' title="Private property defined in class: `%s`"', esc($this->utf8Encode($attr['class']))); - } - - if (isset($attr['ellipsis'])) { - $class = 'sf-dump-ellipsis'; - if (isset($attr['ellipsis-type'])) { - $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']); - } - $label = esc(substr($value, -$attr['ellipsis'])); - $style = str_replace(' title="', " title=\"$v\n", $style); - $v = sprintf('%s', $class, substr($v, 0, -\strlen($label))); - - if (!empty($attr['ellipsis-tail'])) { - $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']))); - $v .= sprintf('%s%s', $class, substr($label, 0, $tail), substr($label, $tail)); - } else { - $v .= $label; - } - } - - $map = static::$controlCharsMap; - $v = "".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) { - $s = $b = ''; - }, $v).''; - - if (!($attr['binary'] ?? false)) { - $v = preg_replace_callback(static::$unicodeCharsRx, function ($c) { - return '\u{'.strtoupper(dechex(mb_ord($c[0]))).'}'; - }, $v); - } - - if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { - $attr['href'] = $href; - } - if (isset($attr['href'])) { - if ('label' === $style) { - $v .= '^'; - } - $target = isset($attr['file']) ? '' : ' target="_blank"'; - $v = sprintf('%s', esc($this->utf8Encode($attr['href'])), $target, $v); - } - if (isset($attr['lang'])) { - $v = sprintf('%s', esc($attr['lang']), $v); - } - if ('label' === $style) { - $v .= ' '; - } - - return $v; - } - - /** - * @return void - */ - protected function dumpLine(int $depth, bool $endOfValue = false) - { - if (-1 === $this->lastDepth) { - $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; - } - if ($this->headerIsDumped !== ($this->outputStream ?? $this->lineDumper)) { - $this->line = $this->getDumpHeader().$this->line; - } - - if (-1 === $depth) { - $args = ['"'.$this->dumpId.'"']; - if ($this->extraDisplayOptions) { - $args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT); - } - // Replace is for BC - $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args)); - } - $this->lastDepth = $depth; - - $this->line = mb_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'); - - if (-1 === $depth) { - AbstractDumper::dumpLine(0); - } - AbstractDumper::dumpLine($depth); - } - - private function getSourceLink(string $file, int $line): string|false - { - $options = $this->extraDisplayOptions + $this->displayOptions; - - if ($fmt = $options['fileLinkFormat']) { - return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); - } - - return false; - } -} - -function esc(string $str): string -{ - return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8'); -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ServerDumper.php b/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ServerDumper.php deleted file mode 100644 index 60fdd7ac3..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Dumper/ServerDumper.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; -use Symfony\Component\VarDumper\Server\Connection; - -/** - * ServerDumper forwards serialized Data clones to a server. - * - * @author Maxime Steinhausser - */ -class ServerDumper implements DataDumperInterface -{ - private Connection $connection; - private ?DataDumperInterface $wrappedDumper; - - /** - * @param string $host The server host - * @param DataDumperInterface|null $wrappedDumper A wrapped instance used whenever we failed contacting the server - * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name - */ - public function __construct(string $host, ?DataDumperInterface $wrappedDumper = null, array $contextProviders = []) - { - $this->connection = new Connection($host, $contextProviders); - $this->wrappedDumper = $wrappedDumper; - } - - public function getContextProviders(): array - { - return $this->connection->getContextProviders(); - } - - /** - * @return string|null - */ - public function dump(Data $data) - { - if (!$this->connection->write($data) && $this->wrappedDumper) { - return $this->wrappedDumper->dump($data); - } - - return null; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php b/data/web/inc/lib/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php deleted file mode 100644 index fd8eca9f1..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Exception; - -/** - * @author Nicolas Grekas - */ -class ThrowingCasterException extends \Exception -{ - /** - * @param \Throwable $prev The exception thrown from the caster - */ - public function __construct(\Throwable $prev) - { - parent::__construct('Unexpected '.$prev::class.' thrown from a caster: '.$prev->getMessage(), 0, $prev); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/README.md b/data/web/inc/lib/vendor/symfony/var-dumper/README.md deleted file mode 100644 index a0da8c9ab..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/README.md +++ /dev/null @@ -1,15 +0,0 @@ -VarDumper Component -=================== - -The VarDumper component provides mechanisms for walking through any arbitrary -PHP variable. It provides a better `dump()` function that you can use instead -of `var_dump()`. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/bin/var-dump-server b/data/web/inc/lib/vendor/symfony/var-dumper/Resources/bin/var-dump-server deleted file mode 100755 index f398fcef7..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/bin/var-dump-server +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env php - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -if ('cli' !== PHP_SAPI) { - throw new Exception('This script must be run from the command line.'); -} - -/** - * Starts a dump server to collect and output dumps on a single place with multiple formats support. - * - * @author Maxime Steinhausser - */ - -use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Logger\ConsoleLogger; -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\VarDumper\Command\ServerDumpCommand; -use Symfony\Component\VarDumper\Server\DumpServer; - -function includeIfExists(string $file): bool -{ - return file_exists($file) && include $file; -} - -if ( - !includeIfExists(__DIR__ . '/../../../../autoload.php') && - !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && - !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') -) { - fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); - exit(1); -} - -if (!class_exists(Application::class)) { - fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL); - exit(1); -} - -$input = new ArgvInput(); -$output = new ConsoleOutput(); -$defaultHost = '127.0.0.1:9912'; -$host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true); -$logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null; - -$app = new Application(); - -$app->getDefinition()->addOption( - new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost) -); - -$app->add($command = new ServerDumpCommand(new DumpServer($host, $logger))) - ->getApplication() - ->setDefaultCommand($command->getName(), true) - ->run($input, $output) -; diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css b/data/web/inc/lib/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css deleted file mode 100644 index 8f706d640..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css +++ /dev/null @@ -1,130 +0,0 @@ -body { - display: flex; - flex-direction: column-reverse; - justify-content: flex-end; - max-width: 1140px; - margin: auto; - padding: 15px; - word-wrap: break-word; - background-color: #F9F9F9; - color: #222; - font-family: Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.4; -} -p { - margin: 0; -} -a { - color: #218BC3; - text-decoration: none; -} -a:hover { - text-decoration: underline; -} -.text-small { - font-size: 12px !important; -} -article { - margin: 5px; - margin-bottom: 10px; -} -article > header > .row { - display: flex; - flex-direction: row; - align-items: baseline; - margin-bottom: 10px; -} -article > header > .row > .col { - flex: 1; - display: flex; - align-items: baseline; -} -article > header > .row > h2 { - font-size: 14px; - color: #222; - font-weight: normal; - font-family: "Lucida Console", monospace, sans-serif; - word-break: break-all; - margin: 20px 5px 0 0; - user-select: all; -} -article > header > .row > h2 > code { - white-space: nowrap; - user-select: none; - color: #cc2255; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; - border-radius: 3px; - margin-right: 5px; - padding: 0 3px; -} -article > header > .row > time.col { - flex: 0; - text-align: right; - white-space: nowrap; - color: #999; - font-style: italic; -} -article > header ul.tags { - list-style: none; - padding: 0; - margin: 0; - font-size: 12px; -} -article > header ul.tags > li { - user-select: all; - margin-bottom: 2px; -} -article > header ul.tags > li > span.badge { - display: inline-block; - padding: .25em .4em; - margin-right: 5px; - border-radius: 4px; - background-color: #6c757d3b; - color: #524d4d; - font-size: 12px; - text-align: center; - font-weight: 700; - line-height: 1; - white-space: nowrap; - vertical-align: baseline; - user-select: none; -} -article > section.body { - border: 1px solid #d8d8d8; - background: #FFF; - padding: 10px; - border-radius: 3px; -} -pre.sf-dump { - border-radius: 3px; - margin-bottom: 0; -} -.hidden { - display: none !important; -} -.dumped-tag > .sf-dump { - display: inline-block; - margin: 0; - padding: 1px 5px; - line-height: 1.4; - vertical-align: top; - background-color: transparent; - user-select: auto; -} -.dumped-tag > pre.sf-dump, -.dumped-tag > .sf-dump-default { - color: #CC7832; - background: none; -} -.dumped-tag > .sf-dump .sf-dump-str { color: #629755; } -.dumped-tag > .sf-dump .sf-dump-private, -.dumped-tag > .sf-dump .sf-dump-protected, -.dumped-tag > .sf-dump .sf-dump-public { color: #262626; } -.dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; } -.dumped-tag > .sf-dump .sf-dump-key { color: #789339; } -.dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; } -.dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; } -.dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; } -.dumped-tag > .sf-dump .sf-dump-ns { user-select: none; } diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/functions/dump.php b/data/web/inc/lib/vendor/symfony/var-dumper/Resources/functions/dump.php deleted file mode 100644 index f2ff74c0c..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/functions/dump.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Symfony\Component\VarDumper\Caster\ScalarStub; -use Symfony\Component\VarDumper\VarDumper; - -if (!function_exists('dump')) { - /** - * @author Nicolas Grekas - * @author Alexandre Daubois - */ - function dump(mixed ...$vars): mixed - { - if (!$vars) { - VarDumper::dump(new ScalarStub('🐛')); - - return null; - } - - if (array_key_exists(0, $vars) && 1 === count($vars)) { - VarDumper::dump($vars[0]); - $k = 0; - } else { - foreach ($vars as $k => $v) { - VarDumper::dump($v, is_int($k) ? 1 + $k : $k); - } - } - - if (1 < count($vars)) { - return $vars; - } - - return $vars[$k]; - } -} - -if (!function_exists('dd')) { - function dd(mixed ...$vars): never - { - if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) { - header('HTTP/1.1 500 Internal Server Error'); - } - - if (array_key_exists(0, $vars) && 1 === count($vars)) { - VarDumper::dump($vars[0]); - } else { - foreach ($vars as $k => $v) { - VarDumper::dump($v, is_int($k) ? 1 + $k : $k); - } - } - - exit(1); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js b/data/web/inc/lib/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js deleted file mode 100644 index 63101e57c..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js +++ /dev/null @@ -1,10 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - let prev = null; - Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) { - const dedupId = article.dataset.dedupId; - if (dedupId === prev) { - article.getElementsByTagName('header')[0].classList.add('hidden'); - } - prev = dedupId; - }); -}); diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Server/Connection.php b/data/web/inc/lib/vendor/symfony/var-dumper/Server/Connection.php deleted file mode 100644 index 4383278c9..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Server/Connection.php +++ /dev/null @@ -1,97 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Server; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; - -/** - * Forwards serialized Data clones to a server. - * - * @author Maxime Steinhausser - */ -class Connection -{ - private string $host; - private array $contextProviders; - - /** - * @var resource|null - */ - private $socket; - - /** - * @param string $host The server host - * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name - */ - public function __construct(string $host, array $contextProviders = []) - { - if (!str_contains($host, '://')) { - $host = 'tcp://'.$host; - } - - $this->host = $host; - $this->contextProviders = $contextProviders; - } - - public function getContextProviders(): array - { - return $this->contextProviders; - } - - public function write(Data $data): bool - { - $socketIsFresh = !$this->socket; - if (!$this->socket = $this->socket ?: $this->createSocket()) { - return false; - } - - $context = ['timestamp' => microtime(true)]; - foreach ($this->contextProviders as $name => $provider) { - $context[$name] = $provider->getContext(); - } - $context = array_filter($context); - $encodedPayload = base64_encode(serialize([$data, $context]))."\n"; - - set_error_handler(static fn () => null); - try { - if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { - return true; - } - if (!$socketIsFresh) { - stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR); - fclose($this->socket); - $this->socket = $this->createSocket(); - } - if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { - return true; - } - } finally { - restore_error_handler(); - } - - return false; - } - - /** - * @return resource|null - */ - private function createSocket() - { - set_error_handler(static fn () => null); - try { - return stream_socket_client($this->host, $errno, $errstr, 3) ?: null; - } finally { - restore_error_handler(); - } - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Server/DumpServer.php b/data/web/inc/lib/vendor/symfony/var-dumper/Server/DumpServer.php deleted file mode 100644 index a9228a2ef..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Server/DumpServer.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Server; - -use Psr\Log\LoggerInterface; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * A server collecting Data clones sent by a ServerDumper. - * - * @author Maxime Steinhausser - * - * @final - */ -class DumpServer -{ - private string $host; - private ?LoggerInterface $logger; - - /** - * @var resource|null - */ - private $socket; - - public function __construct(string $host, ?LoggerInterface $logger = null) - { - if (!str_contains($host, '://')) { - $host = 'tcp://'.$host; - } - - $this->host = $host; - $this->logger = $logger; - } - - public function start(): void - { - if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) { - throw new \RuntimeException(sprintf('Server start failed on "%s": ', $this->host).$errstr.' '.$errno); - } - } - - public function listen(callable $callback): void - { - if (null === $this->socket) { - $this->start(); - } - - foreach ($this->getMessages() as $clientId => $message) { - $this->logger?->info('Received a payload from client {clientId}', ['clientId' => $clientId]); - - $payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]); - - // Impossible to decode the message, give up. - if (false === $payload) { - $this->logger?->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]); - - continue; - } - - if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) { - $this->logger?->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]); - - continue; - } - - [$data, $context] = $payload; - - $callback($data, $context, $clientId); - } - } - - public function getHost(): string - { - return $this->host; - } - - private function getMessages(): iterable - { - $sockets = [(int) $this->socket => $this->socket]; - $write = []; - - while (true) { - $read = $sockets; - stream_select($read, $write, $write, null); - - foreach ($read as $stream) { - if ($this->socket === $stream) { - $stream = stream_socket_accept($this->socket); - $sockets[(int) $stream] = $stream; - } elseif (feof($stream)) { - unset($sockets[(int) $stream]); - fclose($stream); - } else { - yield (int) $stream => fgets($stream); - } - } - } - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php b/data/web/inc/lib/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php deleted file mode 100644 index 4475efd12..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Test; - -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\CliDumper; - -/** - * @author Nicolas Grekas - */ -trait VarDumperTestTrait -{ - /** - * @internal - */ - private array $varDumperConfig = [ - 'casters' => [], - 'flags' => null, - ]; - - protected function setUpVarDumper(array $casters, ?int $flags = null): void - { - $this->varDumperConfig['casters'] = $casters; - $this->varDumperConfig['flags'] = $flags; - } - - /** - * @after - */ - protected function tearDownVarDumper(): void - { - $this->varDumperConfig['casters'] = []; - $this->varDumperConfig['flags'] = null; - } - - public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = '') - { - $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); - } - - public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = '') - { - $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); - } - - protected function getDump(mixed $data, string|int|null $key = null, int $filter = 0): ?string - { - if (null === $flags = $this->varDumperConfig['flags']) { - $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0; - $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0; - $flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0; - } - - $cloner = new VarCloner(); - $cloner->addCasters($this->varDumperConfig['casters']); - $cloner->setMaxItems(-1); - $dumper = new CliDumper(null, null, $flags); - $dumper->setColors(false); - $data = $cloner->cloneVar($data, $filter)->withRefHandles(false); - if (null !== $key && null === $data = $data->seek($key)) { - return null; - } - - return rtrim($dumper->dump($data, true)); - } - - private function prepareExpectation(mixed $expected, int $filter): string - { - if (!\is_string($expected)) { - $expected = $this->getDump($expected, null, $filter); - } - - return rtrim($expected); - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/VarDumper.php b/data/web/inc/lib/vendor/symfony/var-dumper/VarDumper.php deleted file mode 100644 index e1400f150..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/VarDumper.php +++ /dev/null @@ -1,127 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper; - -use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\VarDumper\Caster\ReflectionCaster; -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\CliDumper; -use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; -use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; -use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; -use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; -use Symfony\Component\VarDumper\Dumper\ServerDumper; - -// Load the global dump() function -require_once __DIR__.'/Resources/functions/dump.php'; - -/** - * @author Nicolas Grekas - */ -class VarDumper -{ - /** - * @var callable|null - */ - private static $handler; - - /** - * @param string|null $label - * - * @return mixed - */ - public static function dump(mixed $var/* , string $label = null */) - { - $label = 2 <= \func_num_args() ? func_get_arg(1) : null; - if (null === self::$handler) { - self::register(); - } - - return (self::$handler)($var, $label); - } - - public static function setHandler(?callable $callable = null): ?callable - { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/var-dumper', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } - $prevHandler = self::$handler; - - // Prevent replacing the handler with expected format as soon as the env var was set: - if (isset($_SERVER['VAR_DUMPER_FORMAT'])) { - return $prevHandler; - } - - self::$handler = $callable; - - return $prevHandler; - } - - private static function register(): void - { - $cloner = new VarCloner(); - $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); - - $format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null; - switch (true) { - case 'html' === $format: - $dumper = new HtmlDumper(); - break; - case 'cli' === $format: - $dumper = new CliDumper(); - break; - case 'server' === $format: - case $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME): - $host = 'server' === $format ? $_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912' : $format; - $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? new CliDumper() : new HtmlDumper(); - $dumper = new ServerDumper($host, $dumper, self::getDefaultContextProviders()); - break; - default: - $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? new CliDumper() : new HtmlDumper(); - } - - if (!$dumper instanceof ServerDumper) { - $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); - } - - self::$handler = function ($var, ?string $label = null) use ($cloner, $dumper) { - $var = $cloner->cloneVar($var); - - if (null !== $label) { - $var = $var->withContext(['label' => $label]); - } - - $dumper->dump($var); - }; - } - - private static function getDefaultContextProviders(): array - { - $contextProviders = []; - - if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && class_exists(Request::class)) { - $requestStack = new RequestStack(); - $requestStack->push(Request::createFromGlobals()); - $contextProviders['request'] = new RequestContextProvider($requestStack); - } - - $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null; - - return $contextProviders + [ - 'cli' => new CliContextProvider(), - 'source' => new SourceContextProvider(null, null, $fileLinkFormatter), - ]; - } -} diff --git a/data/web/inc/lib/vendor/symfony/var-dumper/composer.json b/data/web/inc/lib/vendor/symfony/var-dumper/composer.json deleted file mode 100644 index e6166f86d..000000000 --- a/data/web/inc/lib/vendor/symfony/var-dumper/composer.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "symfony/var-dumper", - "type": "library", - "description": "Provides mechanisms for walking through any arbitrary PHP variable", - "keywords": ["dump", "debug"], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^6.3|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/uid": "^5.4|^6.0|^7.0", - "twig/twig": "^2.13|^3.0.4" - }, - "conflict": { - "symfony/console": "<5.4" - }, - "autoload": { - "files": [ "Resources/functions/dump.php" ], - "psr-4": { "Symfony\\Component\\VarDumper\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "minimum-stability": "dev" -} diff --git a/data/web/inc/lib/vendor/tightenco/collect/.github/workflows/run-tests.yml b/data/web/inc/lib/vendor/tightenco/collect/.github/workflows/run-tests.yml deleted file mode 100644 index 406c40049..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/.github/workflows/run-tests.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Run tests - -on: - push: - branches: [laravel-9-ongoing, laravel-8-ongoing] - pull_request: - -jobs: - tests: - strategy: - matrix: - os: [Ubuntu, macOS] - php: [8.0, 8.1, 8.2] - - include: - - os: Ubuntu - os-version: ubuntu-latest - - - os: macOS - os-version: macos-latest - - name: ${{ matrix.os }} - PHP ${{ matrix.php }} - - runs-on: ${{ matrix.os-version }} - - steps: - - name: Checkout code - uses: actions/checkout@v1 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: posix, dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick - coverage: none - - - name: Install dependencies - run: composer update --prefer-stable --prefer-dist --no-interaction - - - name: Run tests - run: bash upgrade.sh diff --git a/data/web/inc/lib/vendor/tightenco/collect/branch-commit-push.sh b/data/web/inc/lib/vendor/tightenco/collect/branch-commit-push.sh deleted file mode 100755 index 124e58331..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/branch-commit-push.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - - -GREEN='\033[0;32m' -RED='\033[0;31m' -WHITE='\033[0;37m' -RESET='\033[0m' - -function validateVersion() -{ - echo "" - passedVersion=$1 - echo -e "${WHITE}-- Validating tag '$passedVersion'...${RESET}" - - # Todo: validate the version here using a regex; if fail, just exit - # ... expect 8.75.0, with no v in front of it - - if [[ $passedVersion == '' ]]; then - echo -e "\n-- Invalid tag. Tags should be structured without v; e.g. 8.57.0" - exit - fi - - echo -e "${WHITE}-- Tag valid.${RESET}" - echo "" -} - -# Exit script if any command fails (e.g. phpunit) -set -e - - -# Require confirmation it's set up corrctly -echo -echo -e "${WHITE}-- This script is meant to be run after running upgrade.sh, BEFORE committing to Git.${RESET}" - -while true; do - echo -e "${GREEN}-- Is that the current state of your local project?${RESET}" - read -p "-- (y/n) " yn - case $yn in - [Yy]* ) break;; - [Nn]* ) exit;; - * ) echo "Please answer y or n.";; - esac -done - -# Get the version and exit if not valid -validateVersion $1 - -# Create official v prefaced version -version="v$1" - -# Run tests (and bail if they fail) -phpunit -echo -e "\n${WHITE}-- Tests succeeded.${RESET}" - -# Branch -echo -e "\n${WHITE}-- Creating a Git branch '$version-changes'...${RESET}\n" -git checkout -b $version-changes - -# Add and commit, with "v8.57.0 changes" as the commit name -git add -A -git commit -m "$version changes" - -echo -echo -e "${WHITE}-- Git committed.${RESET}" - -# Push -git push -u origin $version-changes diff --git a/data/web/inc/lib/vendor/tightenco/collect/composer.json b/data/web/inc/lib/vendor/tightenco/collect/composer.json deleted file mode 100644 index 949635dcd..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/composer.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "tightenco/collect", - "description": "Collect - Illuminate Collections as a separate package.", - "keywords": ["laravel", "collection"], - "license": "MIT", - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "require": { - "php": "^8.0", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^8.3", - "nesbot/carbon": "^2.23.0" - }, - "autoload": { - "files": [ - "src/Collect/Support/helpers.php", - "src/Collect/Support/alias.php" - ], - "psr-4": { - "Tightenco\\Collect\\": "src/Collect" - } - }, - "autoload-dev": { - "files": [ - "tests/files/Support/Carbon.php", - "tests/files/Support/HtmlString.php", - "tests/files/Support/HigherOrderTapProxy.php", - "tests/files/Support/Str.php", - "tests/files/Support/Stringable.php", - "tests/files/Support/ItemNotFoundException.php", - "tests/files/Support/MultipleItemsFoundException.php", - "tests/Support/Concerns/CountsEnumerations.php" - ] - }, - "scripts": { - "test": [ - "@composer install", - "phpunit" - ] - }, - "minimum-stability": "dev", - "prefer-stable": true -} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php b/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php deleted file mode 100755 index 190bb9bc3..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public function toArray(); -} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Contracts/Support/CanBeEscapedWhenCastToString.php b/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Contracts/Support/CanBeEscapedWhenCastToString.php deleted file mode 100644 index 6f3ba0025..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Contracts/Support/CanBeEscapedWhenCastToString.php +++ /dev/null @@ -1,14 +0,0 @@ - $needles - * @param bool $ignoreCase - * @return bool - */ - public static function contains($haystack, $needles, $ignoreCase = false) - { - if ($ignoreCase) { - $haystack = mb_strtolower($haystack); - } - - if (! is_iterable($needles)) { - $needles = (array) $needles; - } - - foreach ($needles as $needle) { - if ($ignoreCase) { - $needle = mb_strtolower($needle); - } - - if ($needle !== '' && str_contains($haystack, $needle)) { - return true; - } - } - - return false; - } - - /** - * Determine if a given string contains all array values. - * - * @param string $haystack - * @param iterable $needles - * @param bool $ignoreCase - * @return bool - */ - public static function containsAll($haystack, $needles, $ignoreCase = false) - { - foreach ($needles as $needle) { - if (! static::contains($haystack, $needle, $ignoreCase)) { - return false; - } - } - - return true; - } - - /** - * Determine if a given string ends with a given substring. - * - * @param string $haystack - * @param string|iterable $needles - * @return bool - */ - public static function endsWith($haystack, $needles) - { - if (! is_iterable($needles)) { - $needles = (array) $needles; - } - - foreach ($needles as $needle) { - if ((string) $needle !== '' && str_ends_with($haystack, $needle)) { - return true; - } - } - - return false; - } - - /** - * Extracts an excerpt from text that matches the first instance of a phrase. - * - * @param string $text - * @param string $phrase - * @param array $options - * @return string|null - */ - public static function excerpt($text, $phrase = '', $options = []) - { - $radius = $options['radius'] ?? 100; - $omission = $options['omission'] ?? '...'; - - preg_match('/^(.*?)('.preg_quote((string) $phrase).')(.*)$/iu', (string) $text, $matches); - - if (empty($matches)) { - return null; - } - - $start = ltrim($matches[1]); - - $start = str(mb_substr($start, max(mb_strlen($start, 'UTF-8') - $radius, 0), $radius, 'UTF-8'))->ltrim()->unless( - fn ($startWithRadius) => $startWithRadius->exactly($start), - fn ($startWithRadius) => $startWithRadius->prepend($omission), - ); - - $end = rtrim($matches[3]); - - $end = str(mb_substr($end, 0, $radius, 'UTF-8'))->rtrim()->unless( - fn ($endWithRadius) => $endWithRadius->exactly($end), - fn ($endWithRadius) => $endWithRadius->append($omission), - ); - - return $start->append($matches[2], $end)->toString(); - } - - /** - * Cap a string with a single instance of a given value. - * - * @param string $value - * @param string $cap - * @return string - */ - public static function finish($value, $cap) - { - $quoted = preg_quote($cap, '/'); - - return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap; - } - - /** - * Wrap the string with the given strings. - * - * @param string $value - * @param string $before - * @param string|null $after - * @return string - */ - public static function wrap($value, $before, $after = null) - { - return $before.$value.($after ??= $before); - } - - /** - * Determine if a given string matches a given pattern. - * - * @param string|iterable $pattern - * @param string $value - * @return bool - */ - public static function is($pattern, $value) - { - $value = (string) $value; - - if (! is_iterable($pattern)) { - $pattern = [$pattern]; - } - - foreach ($pattern as $pattern) { - $pattern = (string) $pattern; - - // If the given value is an exact match we can of course return true right - // from the beginning. Otherwise, we will translate asterisks and do an - // actual pattern match against the two strings to see if they match. - if ($pattern === $value) { - return true; - } - - $pattern = preg_quote($pattern, '#'); - - // Asterisks are translated into zero-or-more regular expression wildcards - // to make it convenient to check if the strings starts with the given - // pattern such as "library/*", making any string check convenient. - $pattern = str_replace('\*', '.*', $pattern); - - if (preg_match('#^'.$pattern.'\z#u', $value) === 1) { - return true; - } - } - - return false; - } - - /** - * Determine if a given string is 7 bit ASCII. - * - * @param string $value - * @return bool - */ - public static function isAscii($value) - { - return ASCII::is_ascii((string) $value); - } - - /** - * Determine if a given string is valid JSON. - * - * @param string $value - * @return bool - */ - public static function isJson($value) - { - if (! is_string($value)) { - return false; - } - - try { - json_decode($value, true, 512, JSON_THROW_ON_ERROR); - } catch (JsonException) { - return false; - } - - return true; - } - - /** - * Determine if a given string is a valid UUID. - * - * @param string $value - * @return bool - */ - public static function isUuid($value) - { - if (! is_string($value)) { - return false; - } - - return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0; - } - - /** - * Determine if a given string is a valid ULID. - * - * @param string $value - * @return bool - */ - public static function isUlid($value) - { - if (! is_string($value)) { - return false; - } - - return Ulid::isValid($value); - } - - /** - * Convert a string to kebab case. - * - * @param string $value - * @return string - */ - public static function kebab($value) - { - return static::snake($value, '-'); - } - - /** - * Return the length of the given string. - * - * @param string $value - * @param string|null $encoding - * @return int - */ - public static function length($value, $encoding = null) - { - if ($encoding) { - return mb_strlen($value, $encoding); - } - - return mb_strlen($value); - } - - /** - * Limit the number of characters in a string. - * - * @param string $value - * @param int $limit - * @param string $end - * @return string - */ - public static function limit($value, $limit = 100, $end = '...') - { - if (mb_strwidth($value, 'UTF-8') <= $limit) { - return $value; - } - - return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end; - } - - /** - * Convert the given string to lower-case. - * - * @param string $value - * @return string - */ - public static function lower($value) - { - return mb_strtolower($value, 'UTF-8'); - } - - /** - * Limit the number of words in a string. - * - * @param string $value - * @param int $words - * @param string $end - * @return string - */ - public static function words($value, $words = 100, $end = '...') - { - preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches); - - if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) { - return $value; - } - - return rtrim($matches[0]).$end; - } - - /** - * Converts GitHub flavored Markdown into HTML. - * - * @param string $string - * @param array $options - * @return string - */ - public static function markdown($string, array $options = []) - { - $converter = new GithubFlavoredMarkdownConverter($options); - - return (string) $converter->convert($string); - } - - /** - * Converts inline Markdown into HTML. - * - * @param string $string - * @param array $options - * @return string - */ - public static function inlineMarkdown($string, array $options = []) - { - $environment = new Environment($options); - - $environment->addExtension(new GithubFlavoredMarkdownExtension()); - $environment->addExtension(new InlinesOnlyExtension()); - - $converter = new MarkdownConverter($environment); - - return (string) $converter->convert($string); - } - - /** - * Masks a portion of a string with a repeated character. - * - * @param string $string - * @param string $character - * @param int $index - * @param int|null $length - * @param string $encoding - * @return string - */ - public static function mask($string, $character, $index, $length = null, $encoding = 'UTF-8') - { - if ($character === '') { - return $string; - } - - $segment = mb_substr($string, $index, $length, $encoding); - - if ($segment === '') { - return $string; - } - - $strlen = mb_strlen($string, $encoding); - $startIndex = $index; - - if ($index < 0) { - $startIndex = $index < -$strlen ? 0 : $strlen + $index; - } - - $start = mb_substr($string, 0, $startIndex, $encoding); - $segmentLen = mb_strlen($segment, $encoding); - $end = mb_substr($string, $startIndex + $segmentLen); - - return $start.str_repeat(mb_substr($character, 0, 1, $encoding), $segmentLen).$end; - } - - /** - * Get the string matching the given pattern. - * - * @param string $pattern - * @param string $subject - * @return string - */ - public static function match($pattern, $subject) - { - preg_match($pattern, $subject, $matches); - - if (! $matches) { - return ''; - } - - return $matches[1] ?? $matches[0]; - } - - /** - * Get the string matching the given pattern. - * - * @param string $pattern - * @param string $subject - * @return \Tightenco\Collect\Support\Collection - */ - public static function matchAll($pattern, $subject) - { - preg_match_all($pattern, $subject, $matches); - - if (empty($matches[0])) { - return collect(); - } - - return collect($matches[1] ?? $matches[0]); - } - - /** - * Pad both sides of a string with another. - * - * @param string $value - * @param int $length - * @param string $pad - * @return string - */ - public static function padBoth($value, $length, $pad = ' ') - { - $short = max(0, $length - mb_strlen($value)); - $shortLeft = floor($short / 2); - $shortRight = ceil($short / 2); - - return mb_substr(str_repeat($pad, $shortLeft), 0, $shortLeft). - $value. - mb_substr(str_repeat($pad, $shortRight), 0, $shortRight); - } - - /** - * Pad the left side of a string with another. - * - * @param string $value - * @param int $length - * @param string $pad - * @return string - */ - public static function padLeft($value, $length, $pad = ' ') - { - $short = max(0, $length - mb_strlen($value)); - - return mb_substr(str_repeat($pad, $short), 0, $short).$value; - } - - /** - * Pad the right side of a string with another. - * - * @param string $value - * @param int $length - * @param string $pad - * @return string - */ - public static function padRight($value, $length, $pad = ' ') - { - $short = max(0, $length - mb_strlen($value)); - - return $value.mb_substr(str_repeat($pad, $short), 0, $short); - } - - /** - * Parse a Class[@]method style callback into class and method. - * - * @param string $callback - * @param string|null $default - * @return array - */ - public static function parseCallback($callback, $default = null) - { - return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default]; - } - - /** - * Get the plural form of an English word. - * - * @param string $value - * @param int|array|\Countable $count - * @return string - */ - public static function plural($value, $count = 2) - { - return Pluralizer::plural($value, $count); - } - - /** - * Pluralize the last word of an English, studly caps case string. - * - * @param string $value - * @param int|array|\Countable $count - * @return string - */ - public static function pluralStudly($value, $count = 2) - { - $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE); - - $lastWord = array_pop($parts); - - return implode('', $parts).self::plural($lastWord, $count); - } - - /** - * Generate a more truly "random" alpha-numeric string. - * - * @param int $length - * @return string - */ - public static function random($length = 16) - { - return (static::$randomStringFactory ?? function ($length) { - $string = ''; - - while (($len = strlen($string)) < $length) { - $size = $length - $len; - - $bytesSize = (int) ceil($size / 3) * 3; - - $bytes = random_bytes($bytesSize); - - $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); - } - - return $string; - })($length); - } - - /** - * Set the callable that will be used to generate random strings. - * - * @param callable|null $factory - * @return void - */ - public static function createRandomStringsUsing(callable $factory = null) - { - static::$randomStringFactory = $factory; - } - - /** - * Set the sequence that will be used to generate random strings. - * - * @param array $sequence - * @param callable|null $whenMissing - * @return void - */ - public static function createRandomStringsUsingSequence(array $sequence, $whenMissing = null) - { - $next = 0; - - $whenMissing ??= function ($length) use (&$next) { - $factoryCache = static::$randomStringFactory; - - static::$randomStringFactory = null; - - $randomString = static::random($length); - - static::$randomStringFactory = $factoryCache; - - $next++; - - return $randomString; - }; - - static::createRandomStringsUsing(function ($length) use (&$next, $sequence, $whenMissing) { - if (array_key_exists($next, $sequence)) { - return $sequence[$next++]; - } - - return $whenMissing($length); - }); - } - - /** - * Indicate that random strings should be created normally and not using a custom factory. - * - * @return void - */ - public static function createRandomStringsNormally() - { - static::$randomStringFactory = null; - } - - /** - * Repeat the given string. - * - * @param string $string - * @param int $times - * @return string - */ - public static function repeat(string $string, int $times) - { - return str_repeat($string, $times); - } - - /** - * Replace a given value in the string sequentially with an array. - * - * @param string $search - * @param iterable $replace - * @param string $subject - * @return string - */ - public static function replaceArray($search, $replace, $subject) - { - if ($replace instanceof Traversable) { - $replace = collect($replace)->all(); - } - - $segments = explode($search, $subject); - - $result = array_shift($segments); - - foreach ($segments as $segment) { - $result .= (array_shift($replace) ?? $search).$segment; - } - - return $result; - } - - /** - * Replace the given value in the given string. - * - * @param string|iterable $search - * @param string|iterable $replace - * @param string|iterable $subject - * @return string - */ - public static function replace($search, $replace, $subject) - { - if ($search instanceof Traversable) { - $search = collect($search)->all(); - } - - if ($replace instanceof Traversable) { - $replace = collect($replace)->all(); - } - - if ($subject instanceof Traversable) { - $subject = collect($subject)->all(); - } - - return str_replace($search, $replace, $subject); - } - - /** - * Replace the first occurrence of a given value in the string. - * - * @param string $search - * @param string $replace - * @param string $subject - * @return string - */ - public static function replaceFirst($search, $replace, $subject) - { - $search = (string) $search; - - if ($search === '') { - return $subject; - } - - $position = strpos($subject, $search); - - if ($position !== false) { - return substr_replace($subject, $replace, $position, strlen($search)); - } - - return $subject; - } - - /** - * Replace the last occurrence of a given value in the string. - * - * @param string $search - * @param string $replace - * @param string $subject - * @return string - */ - public static function replaceLast($search, $replace, $subject) - { - if ($search === '') { - return $subject; - } - - $position = strrpos($subject, $search); - - if ($position !== false) { - return substr_replace($subject, $replace, $position, strlen($search)); - } - - return $subject; - } - - /** - * Remove any occurrence of the given string in the subject. - * - * @param string|iterable $search - * @param string $subject - * @param bool $caseSensitive - * @return string - */ - public static function remove($search, $subject, $caseSensitive = true) - { - if ($search instanceof Traversable) { - $search = collect($search)->all(); - } - - $subject = $caseSensitive - ? str_replace($search, '', $subject) - : str_ireplace($search, '', $subject); - - return $subject; - } - - /** - * Reverse the given string. - * - * @param string $value - * @return string - */ - public static function reverse(string $value) - { - return implode(array_reverse(mb_str_split($value))); - } - - /** - * Begin a string with a single instance of a given value. - * - * @param string $value - * @param string $prefix - * @return string - */ - public static function start($value, $prefix) - { - $quoted = preg_quote($prefix, '/'); - - return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value); - } - - /** - * Convert the given string to upper-case. - * - * @param string $value - * @return string - */ - public static function upper($value) - { - return mb_strtoupper($value, 'UTF-8'); - } - - /** - * Convert the given string to title case. - * - * @param string $value - * @return string - */ - public static function title($value) - { - return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8'); - } - - /** - * Convert the given string to title case for each word. - * - * @param string $value - * @return string - */ - public static function headline($value) - { - $parts = explode(' ', $value); - - $parts = count($parts) > 1 - ? array_map([static::class, 'title'], $parts) - : array_map([static::class, 'title'], static::ucsplit(implode('_', $parts))); - - $collapsed = static::replace(['-', '_', ' '], '_', implode('_', $parts)); - - return implode(' ', array_filter(explode('_', $collapsed))); - } - - /** - * Get the singular form of an English word. - * - * @param string $value - * @return string - */ - public static function singular($value) - { - return Pluralizer::singular($value); - } - - /** - * Generate a URL friendly "slug" from a given string. - * - * @param string $title - * @param string $separator - * @param string|null $language - * @param array $dictionary - * @return string - */ - public static function slug($title, $separator = '-', $language = 'en', $dictionary = ['@' => 'at']) - { - $title = $language ? static::ascii($title, $language) : $title; - - // Convert all dashes/underscores into separator - $flip = $separator === '-' ? '_' : '-'; - - $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title); - - // Replace dictionary words - foreach ($dictionary as $key => $value) { - $dictionary[$key] = $separator.$value.$separator; - } - - $title = str_replace(array_keys($dictionary), array_values($dictionary), $title); - - // Remove all characters that are not the separator, letters, numbers, or whitespace - $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', static::lower($title)); - - // Replace all separator characters and whitespace by a single separator - $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); - - return trim($title, $separator); - } - - /** - * Convert a string to snake case. - * - * @param string $value - * @param string $delimiter - * @return string - */ - public static function snake($value, $delimiter = '_') - { - $key = $value; - - if (isset(static::$snakeCache[$key][$delimiter])) { - return static::$snakeCache[$key][$delimiter]; - } - - if (! ctype_lower($value)) { - $value = preg_replace('/\s+/u', '', ucwords($value)); - - $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value)); - } - - return static::$snakeCache[$key][$delimiter] = $value; - } - - /** - * Remove all "extra" blank space from the given string. - * - * @param string $value - * @return string - */ - public static function squish($value) - { - return preg_replace('~(\s|\x{3164})+~u', ' ', preg_replace('~^[\s\x{FEFF}]+|[\s\x{FEFF}]+$~u', '', $value)); - } - - /** - * Determine if a given string starts with a given substring. - * - * @param string $haystack - * @param string|iterable $needles - * @return bool - */ - public static function startsWith($haystack, $needles) - { - if (! is_iterable($needles)) { - $needles = [$needles]; - } - - foreach ($needles as $needle) { - if ((string) $needle !== '' && str_starts_with($haystack, $needle)) { - return true; - } - } - - return false; - } - - /** - * Convert a value to studly caps case. - * - * @param string $value - * @return string - */ - public static function studly($value) - { - $key = $value; - - if (isset(static::$studlyCache[$key])) { - return static::$studlyCache[$key]; - } - - $words = explode(' ', static::replace(['-', '_'], ' ', $value)); - - $studlyWords = array_map(fn ($word) => static::ucfirst($word), $words); - - return static::$studlyCache[$key] = implode($studlyWords); - } - - /** - * Returns the portion of the string specified by the start and length parameters. - * - * @param string $string - * @param int $start - * @param int|null $length - * @param string $encoding - * @return string - */ - public static function substr($string, $start, $length = null, $encoding = 'UTF-8') - { - return mb_substr($string, $start, $length, $encoding); - } - - /** - * Returns the number of substring occurrences. - * - * @param string $haystack - * @param string $needle - * @param int $offset - * @param int|null $length - * @return int - */ - public static function substrCount($haystack, $needle, $offset = 0, $length = null) - { - if (! is_null($length)) { - return substr_count($haystack, $needle, $offset, $length); - } - - return substr_count($haystack, $needle, $offset); - } - - /** - * Replace text within a portion of a string. - * - * @param string|string[] $string - * @param string|string[] $replace - * @param int|int[] $offset - * @param int|int[]|null $length - * @return string|string[] - */ - public static function substrReplace($string, $replace, $offset = 0, $length = null) - { - if ($length === null) { - $length = strlen($string); - } - - return substr_replace($string, $replace, $offset, $length); - } - - /** - * Swap multiple keywords in a string with other keywords. - * - * @param array $map - * @param string $subject - * @return string - */ - public static function swap(array $map, $subject) - { - return strtr($subject, $map); - } - - /** - * Make a string's first character lowercase. - * - * @param string $string - * @return string - */ - public static function lcfirst($string) - { - return static::lower(static::substr($string, 0, 1)).static::substr($string, 1); - } - - /** - * Make a string's first character uppercase. - * - * @param string $string - * @return string - */ - public static function ucfirst($string) - { - return static::upper(static::substr($string, 0, 1)).static::substr($string, 1); - } - - /** - * Split a string into pieces by uppercase characters. - * - * @param string $string - * @return string[] - */ - public static function ucsplit($string) - { - return preg_split('/(?=\p{Lu})/u', $string, -1, PREG_SPLIT_NO_EMPTY); - } - - /** - * Get the number of words a string contains. - * - * @param string $string - * @param string|null $characters - * @return int - */ - public static function wordCount($string, $characters = null) - { - return str_word_count($string, 0, $characters); - } - - /** - * Generate a UUID (version 4). - * - * @return \Ramsey\Uuid\UuidInterface - */ - public static function uuid() - { - return static::$uuidFactory - ? call_user_func(static::$uuidFactory) - : Uuid::uuid4(); - } - - /** - * Generate a time-ordered UUID (version 4). - * - * @return \Ramsey\Uuid\UuidInterface - */ - public static function orderedUuid() - { - if (static::$uuidFactory) { - return call_user_func(static::$uuidFactory); - } - - $factory = new UuidFactory; - - $factory->setRandomGenerator(new CombGenerator( - $factory->getRandomGenerator(), - $factory->getNumberConverter() - )); - - $factory->setCodec(new TimestampFirstCombCodec( - $factory->getUuidBuilder() - )); - - return $factory->uuid4(); - } - - /** - * Set the callable that will be used to generate UUIDs. - * - * @param callable|null $factory - * @return void - */ - public static function createUuidsUsing(callable $factory = null) - { - static::$uuidFactory = $factory; - } - - /** - * Set the sequence that will be used to generate UUIDs. - * - * @param array $sequence - * @param callable|null $whenMissing - * @return void - */ - public static function createUuidsUsingSequence(array $sequence, $whenMissing = null) - { - $next = 0; - - $whenMissing ??= function () use (&$next) { - $factoryCache = static::$uuidFactory; - - static::$uuidFactory = null; - - $uuid = static::uuid(); - - static::$uuidFactory = $factoryCache; - - $next++; - - return $uuid; - }; - - static::createUuidsUsing(function () use (&$next, $sequence, $whenMissing) { - if (array_key_exists($next, $sequence)) { - return $sequence[$next++]; - } - - return $whenMissing(); - }); - } - - /** - * Always return the same UUID when generating new UUIDs. - * - * @param \Closure|null $callback - * @return \Ramsey\Uuid\UuidInterface - */ - public static function freezeUuids(Closure $callback = null) - { - $uuid = Str::uuid(); - - Str::createUuidsUsing(fn () => $uuid); - - if ($callback !== null) { - try { - $callback($uuid); - } finally { - Str::createUuidsNormally(); - } - } - - return $uuid; - } - - /** - * Indicate that UUIDs should be created normally and not using a custom factory. - * - * @return void - */ - public static function createUuidsNormally() - { - static::$uuidFactory = null; - } - - /** - * Generate a ULID. - * - * @return \Symfony\Component\Uid\Ulid - */ - public static function ulid() - { - return new Ulid(); - } - - /** - * Remove all strings from the casing caches. - * - * @return void - */ - public static function flushCache() - { - static::$snakeCache = []; - static::$camelCache = []; - static::$studlyCache = []; - } -} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Traits/Tappable.php b/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Traits/Tappable.php deleted file mode 100644 index 9d75d26fb..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/Traits/Tappable.php +++ /dev/null @@ -1,17 +0,0 @@ - Illuminate\Contracts\Support\Arrayable::class, - Tightenco\Collect\Contracts\Support\Jsonable::class => Illuminate\Contracts\Support\Jsonable::class, - Tightenco\Collect\Contracts\Support\Htmlable::class => Illuminate\Contracts\Support\Htmlable::class, - Tightenco\Collect\Contracts\Support\CanBeEscapedWhenCastToString::class => Illuminate\Contracts\Support\CanBeEscapedWhenCastToString::class, - Tightenco\Collect\Support\Arr::class => Illuminate\Support\Arr::class, - Tightenco\Collect\Support\Collection::class => Illuminate\Support\Collection::class, - Tightenco\Collect\Support\Enumerable::class => Illuminate\Support\Enumerable::class, - Tightenco\Collect\Support\HigherOrderCollectionProxy::class => Illuminate\Support\HigherOrderCollectionProxy::class, - Tightenco\Collect\Support\LazyCollection::class => Illuminate\Support\LazyCollection::class, - Tightenco\Collect\Support\Traits\EnumeratesValues::class => Illuminate\Support\Traits\EnumeratesValues::class, -]; - -# echo "\n\n-- Aliasing....\n---------------------------------------------\n\n"; - -foreach ($aliases as $tighten => $illuminate) { - if (! class_exists($illuminate) && ! interface_exists($illuminate) && ! trait_exists($illuminate)) { - # echo "Aliasing {$tighten} to {$illuminate}.\n"; - class_alias($tighten, $illuminate); - } -} diff --git a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/helpers.php b/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/helpers.php deleted file mode 100644 index 886a1414c..000000000 --- a/data/web/inc/lib/vendor/tightenco/collect/src/Collect/Support/helpers.php +++ /dev/null @@ -1,122 +0,0 @@ - $segment) { - unset($key[$i]); - - if (is_null($segment)) { - return $target; - } - - if ($segment === '*') { - if ($target instanceof Collection) { - $target = $target->all(); - } elseif (! is_array($target)) { - return value($default); - } - - $result = []; - - foreach ($target as $item) { - $result[] = data_get($item, $key); - } - - return in_array('*', $key) ? Arr::collapse($result) : $result; - } - - if (Arr::accessible($target) && Arr::exists($target, $segment)) { - $target = $target[$segment]; - } elseif (is_object($target) && isset($target->{$segment})) { - $target = $target->{$segment}; - } else { - return value($default); - } - } - - return $target; - } - } - - if (! function_exists('tap')) { - /** - * Call the given Closure with the given value then return the value. - * - * @param mixed $value - * @param callable|null $callback - * @return mixed - */ - function tap($value, $callback = null) - { - if (is_null($callback)) { - return new HigherOrderTapProxy($value); - } - - $callback($value); - - return $value; - } - } - - if (! function_exists('class_basename')) { - /** - * Get the class "basename" of the given object / class. - * - * @param string|object $class - * @return string - */ - function class_basename($class) - { - $class = is_object($class) ? get_class($class) : $class; - - return basename(str_replace('\\', '/', $class)); - } - } -} diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index 198e675aa..5e57a4d41 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -47,7 +47,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/CSSminifierExtended.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/array_merge_real.php'; // T/HOTP API -$qrprovider = new RobThree\Auth\Providers\Qr\QRServerProvider(); +$qrprovider = new RobThree\Auth\Providers\Qr\BaconQrCodeProvider(); $tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider); // FIDO2 diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php index 8f3192d70..ac308b687 100644 --- a/data/web/inc/sessions.inc.php +++ b/data/web/inc/sessions.inc.php @@ -140,17 +140,32 @@ function session_check() { ); return false; } - if (!empty($_POST)) { - if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) { - $_SESSION['return'][] = array( - 'type' => 'warning', - 'msg' => 'session_token' - ); - return false; + // Check if this is a POST request (form-encoded or JSON) + $is_post_request = !empty($_POST) || ( + isset($_SERVER['CONTENT_TYPE']) && + strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false + ); + + if ($is_post_request) { + // Skip CSRF check for DataTables server-side processing endpoints + // These are read-only operations (equivalent to GET) authenticated by session + $is_search_endpoint = ( + isset($_GET['query']) && + preg_match('#^search/(domain|mailbox)$#', $_GET['query']) + ); + + if (!$is_search_endpoint && !empty($_POST)) { + if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'msg' => 'session_token' + ); + return false; + } + unset($_POST['csrf_token']); + $_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32)); + $_SESSION['CSRF']['TIME'] = time(); } - unset($_POST['csrf_token']); - $_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32)); - $_SESSION['CSRF']['TIME'] = time(); } return true; } diff --git a/data/web/inc/triggers.admin.inc.php b/data/web/inc/triggers.admin.inc.php index 2a02ba511..92043190e 100644 --- a/data/web/inc/triggers.admin.inc.php +++ b/data/web/inc/triggers.admin.inc.php @@ -9,6 +9,11 @@ if (isset($_POST["verify_tfa_login"])) { unset($_SESSION['pending_mailcow_cc_role']); unset($_SESSION['pending_tfa_methods']); + // If pending actions exist, redirect to /admin to show modal + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /admin"); + die(); + } header("Location: /admin/dashboard"); die(); } @@ -42,6 +47,15 @@ if (isset($_GET["cancel_tfa_login"])) { header("Location: /admin"); } +if (isset($_GET["cancel_tfa_setup"])) { + session_regenerate_id(true); + session_unset(); + session_destroy(); + session_write_close(); + header("Location: /admin"); + exit(); +} + if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { $login_user = strtolower(trim($_POST["login_user"])); $as = check_login($login_user, $_POST["pass_user"], array("role" => "admin", "service" => "MAILCOWUI")); @@ -50,6 +64,11 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { session_regenerate_id(true); $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "admin"; + // If pending actions exist, redirect to /admin to show modal + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /admin"); + die(); + } header("Location: /admin/dashboard"); die(); } diff --git a/data/web/inc/triggers.domainadmin.inc.php b/data/web/inc/triggers.domainadmin.inc.php index 764d9009b..2eee8b993 100644 --- a/data/web/inc/triggers.domainadmin.inc.php +++ b/data/web/inc/triggers.domainadmin.inc.php @@ -20,6 +20,11 @@ if (isset($_POST["verify_tfa_login"])) { unset($_SESSION['pending_mailcow_cc_role']); unset($_SESSION['pending_tfa_methods']); + // If pending actions exist, redirect to /domainadmin to show modal + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /domainadmin"); + die(); + } header("Location: /domainadmin/mailbox"); die(); } @@ -53,6 +58,15 @@ if (isset($_GET["cancel_tfa_login"])) { header("Location: /domainadmin"); } +if (isset($_GET["cancel_tfa_setup"])) { + session_regenerate_id(true); + session_unset(); + session_destroy(); + session_write_close(); + header("Location: /domainadmin"); + exit(); +} + if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { $login_user = strtolower(trim($_POST["login_user"])); $as = check_login($login_user, $_POST["pass_user"], array("role" => "domain_admin", "service" => "MAILCOWUI")); @@ -61,6 +75,11 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { session_regenerate_id(true); $_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_role'] = "domainadmin"; + // If pending actions exist, redirect to /domainadmin to show modal + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /domainadmin"); + die(); + } header("Location: /domainadmin/mailbox"); die(); } diff --git a/data/web/inc/triggers.global.inc.php b/data/web/inc/triggers.global.inc.php index dd88fad56..1f8f92004 100644 --- a/data/web/inc/triggers.global.inc.php +++ b/data/web/inc/triggers.global.inc.php @@ -36,7 +36,26 @@ if (isset($_SESSION['mailcow_cc_role']) && (isset($_SESSION['acl']['login_as']) if (isset($_SESSION['mailcow_cc_role'])) { if (isset($_POST["set_tfa"])) { + $had_pending_tfa_setup = !empty($_SESSION['pending_tfa_setup']); set_tfa($_POST); + // After TFA setup during forced enrollment + if ($had_pending_tfa_setup && empty($_SESSION['pending_tfa_setup'])) { + if ($_SESSION['mailcow_cc_role'] === 'admin') { + header("Location: /admin/dashboard"); + } elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') { + header("Location: /domainadmin/mailbox"); + } elseif ($_SESSION['mailcow_cc_role'] === 'user') { + // Check if user should go to SOGo or /user + $user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']); + $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; + if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual && getenv('SKIP_SOGO') != "y") { + header("Location: /SOGo/so/"); + } else { + header("Location: /user"); + } + } + exit(); + } } if (isset($_POST["unset_tfa_key"])) { unset_tfa_key($_POST); diff --git a/data/web/inc/triggers.user.inc.php b/data/web/inc/triggers.user.inc.php index cc33596f9..5dcde0b18 100644 --- a/data/web/inc/triggers.user.inc.php +++ b/data/web/inc/triggers.user.inc.php @@ -76,6 +76,11 @@ if (isset($_POST["verify_tfa_login"])) { $user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']); $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; + // If pending actions exist, redirect to / to show modal + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /"); + die(); + } if (intval($user_details['attributes']['sogo_access']) == 1 && intval($user_details['attributes']['force_pw_update']) != 1 && getenv('SKIP_SOGO') != "y" && @@ -117,6 +122,15 @@ if (isset($_GET["cancel_tfa_login"])) { header("Location: /"); } +if (isset($_GET["cancel_tfa_setup"])) { + session_regenerate_id(true); + session_unset(); + session_destroy(); + session_write_close(); + header("Location: /"); + exit(); +} + if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { $login_user = strtolower(trim($_POST["login_user"])); $as = check_login($login_user, $_POST["pass_user"], array("role" => "user", "service" => "MAILCOWUI")); @@ -142,6 +156,11 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { $user_details = mailbox("get", "mailbox_details", $login_user); $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; + // If pending actions exist, redirect to / to show modal + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /"); + die(); + } if (intval($user_details['attributes']['sogo_access']) == 1 && intval($user_details['attributes']['force_pw_update']) != 1 && getenv('SKIP_SOGO') != "y" && diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index 6d1965542..2f4a4b076 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -193,6 +193,9 @@ $MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out'] = false; // Force password change on next login (only allows login to mailcow UI) $MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update'] = false; +// Force 2FA enrollment at next login +$MAILBOX_DEFAULT_ATTRIBUTES['force_tfa'] = false; + // Enable SOGo access - Users will be redirected to SOGo after login (set to false to disable redirect by default) $MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'] = true; diff --git a/data/web/index.php b/data/web/index.php index a1ff9310f..b037c14f1 100644 --- a/data/web/index.php +++ b/data/web/index.php @@ -9,22 +9,28 @@ if (isset($_SESSION['mailcow_cc_role']) && isset($_SESSION['oauth2_request'])) { exit(); } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { - $user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']); - $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; - if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual && getenv('SKIP_SOGO') != "y") { - header("Location: /SOGo/so/"); - } else { - header("Location: /user"); + if (empty($_SESSION['pending_tfa_setup']) && empty($_SESSION['pending_pw_update'])) { + $user_details = mailbox("get", "mailbox_details", $_SESSION['mailcow_cc_username']); + $is_dual = (!empty($_SESSION["dual-login"]["username"])) ? true : false; + if (intval($user_details['attributes']['sogo_access']) == 1 && !$is_dual && getenv('SKIP_SOGO') != "y") { + header("Location: /SOGo/so/"); + } else { + header("Location: /user"); + } + exit(); } - exit(); } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { - header('Location: /admin/dashboard'); - exit(); + if (empty($_SESSION['pending_tfa_setup']) && empty($_SESSION['pending_pw_update'])) { + header('Location: /admin/dashboard'); + exit(); + } } elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); + if (empty($_SESSION['pending_tfa_setup']) && empty($_SESSION['pending_pw_update'])) { + header('Location: /domainadmin/mailbox'); + exit(); + } } $host = strtolower($_SERVER['HTTP_HOST'] ?? ''); diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js index 7010077db..e8edb9940 100644 --- a/data/web/js/site/mailbox.js +++ b/data/web/js/site/mailbox.js @@ -471,8 +471,13 @@ jQuery(function($){ hideTableExpandCollapseBtn('#tab-domains', '#domain_table'); }, ajax: { - type: "GET", - url: "/api/v1/get/domain/datatables", + type: "POST", + url: "/api/v1/search/domain", + contentType: "application/json", + processData: false, + data: function(d) { + return JSON.stringify(d); + }, dataSrc: function(json){ $.each(json.data, function(i, item) { item.domain_name = escapeHtml(item.domain_name); @@ -898,8 +903,13 @@ jQuery(function($){ hideTableExpandCollapseBtn('#tab-mailboxes', '#mailbox_table'); }, ajax: { - type: "GET", - url: "/api/v1/get/mailbox/datatables", + type: "POST", + url: "/api/v1/search/mailbox", + contentType: "application/json", + processData: false, + data: function(d) { + return JSON.stringify(d); + }, dataSrc: function(json){ $.each(json.data, function (i, item) { item.quota = { diff --git a/data/web/json_api.php b/data/web/json_api.php index 5409e65d3..2d315a0b1 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -91,6 +91,11 @@ if (isset($_GET['query'])) { if ($action == 'delete') { $_POST['items'] = $request; } + + // search + if ($action == 'search') { + // placeholder for search, as the request body is already decoded and available in $requestDecoded + } } api_log($_POST); @@ -169,6 +174,8 @@ if (isset($_GET['query'])) { exit; } fido2(array("action" => "register", "registration" => $data)); + // Release pending_tfa_setup session hold + unset($_SESSION['pending_tfa_setup']); $return = new stdClass(); $return->success = true; echo json_encode($return); @@ -457,47 +464,6 @@ if (isset($_GET['query'])) { case "domain": switch ($object) { - case "datatables": - $table = ['domain', 'd']; - $primaryKey = 'domain'; - $columns = [ - ['db' => 'domain', 'dt' => 2], - ['db' => 'aliases', 'dt' => 3, 'order_subquery' => "SELECT COUNT(*) FROM `alias` WHERE (`domain`= `d`.`domain` OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = `d`.`domain`)) AND `address` NOT IN (SELECT `username` FROM `mailbox`)"], - ['db' => 'mailboxes', 'dt' => 4, 'order_subquery' => "SELECT COUNT(*) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"], - ['db' => 'quota', 'dt' => 5, 'order_subquery' => "SELECT COALESCE(SUM(`mailbox`.`quota`), 0) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"], - ['db' => 'stats', 'dt' => 6, 'dummy' => true, 'order_subquery' => "SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` IN (SELECT `username` FROM `mailbox` WHERE `domain` = `d`.`domain`)"], - ['db' => 'defquota', 'dt' => 7], - ['db' => 'maxquota', 'dt' => 8], - ['db' => 'backupmx', 'dt' => 10], - ['db' => 'tags', 'dt' => 14, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_domain` AS `td` ON `td`.`domain` = `d`.`domain`', 'where_column' => '`td`.`tag_name`']], - ['db' => 'active', 'dt' => 15], - ]; - - require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php'; - global $pdo; - if($_SESSION['mailcow_cc_role'] === 'admin') { - $data = SSP::simple($_GET, $pdo, $table, $primaryKey, $columns); - } elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') { - $data = SSP::complex($_GET, $pdo, $table, $primaryKey, $columns, - 'INNER JOIN domain_admins as da ON da.domain = d.domain', - [ - 'condition' => 'da.active = 1 and da.username = :username', - 'bindings' => ['username' => $_SESSION['mailcow_cc_username']] - ]); - } - - if (!empty($data['data'])) { - $domainsData = []; - foreach ($data['data'] as $domain) { - if ($details = mailbox('get', 'domain_details', $domain[2])) { - $domainsData[] = $details; - } - } - $data['data'] = $domainsData; - } - - process_get_return($data); - break; case "all": $tags = null; if (isset($_GET['tags']) && $_GET['tags'] != '') @@ -997,46 +963,6 @@ if (isset($_GET['query'])) { break; case "mailbox": switch ($object) { - case "datatables": - $table = ['mailbox', 'm']; - $primaryKey = 'username'; - $columns = [ - ['db' => 'username', 'dt' => 2], - ['db' => 'quota', 'dt' => 3], - ['db' => 'last_mail_login', 'dt' => 4, 'dummy' => true, 'order_subquery' => "SELECT MAX(`datetime`) FROM `sasl_log` WHERE `service` != 'SSO' AND `username` = `m`.`username`"], - ['db' => 'last_pw_change', 'dt' => 5, 'dummy' => true, 'order_subquery' => "JSON_EXTRACT(attributes, '$.passwd_update')"], - ['db' => 'in_use', 'dt' => 6, 'dummy' => true, 'order_subquery' => "(SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`) / `m`.`quota`"], - ['db' => 'name', 'dt' => 7], - ['db' => 'messages', 'dt' => 20, 'dummy' => true, 'order_subquery' => "SELECT SUM(messages) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`"], - ['db' => 'tags', 'dt' => 23, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_mailbox` AS `tm` ON `tm`.`username` = `m`.`username`', 'where_column' => '`tm`.`tag_name`']], - ['db' => 'active', 'dt' => 24], - ]; - - require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php'; - global $pdo; - if($_SESSION['mailcow_cc_role'] === 'admin') { - $data = SSP::complex($_GET, $pdo, $table, $primaryKey, $columns, null, "(`m`.`kind` = '' OR `m`.`kind` = NULL)"); - } elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') { - $data = SSP::complex($_GET, $pdo, $table, $primaryKey, $columns, - 'INNER JOIN domain_admins as da ON da.domain = m.domain', - [ - 'condition' => "(`m`.`kind` = '' OR `m`.`kind` = NULL) AND `da`.`active` = 1 AND `da`.`username` = :username", - 'bindings' => ['username' => $_SESSION['mailcow_cc_username']] - ]); - } - - if (!empty($data['data'])) { - $mailboxData = []; - foreach ($data['data'] as $mailbox) { - if ($details = mailbox('get', 'mailbox_details', $mailbox[2])) { - $mailboxData[] = $details; - } - } - $data['data'] = $mailboxData; - } - - process_get_return($data); - break; case "all": case "reduced": $tags = null; @@ -1625,6 +1551,136 @@ if (isset($_GET['query'])) { } } break; + case "search": + function process_search_return($return) { + if ($return === false) { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Cannot get item' + )); + } + else { + echo json_encode($return, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + } + } + // only allow POST requests to SEARCH API endpoints + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + http_response_code(405); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'only POST method is allowed' + )); + exit(); + } + + // Load SSP class + require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php'; + global $pdo; + + switch ($category) { + case "domain": + $table = ['domain', 'd']; + $primaryKey = 'domain'; + $columns = [ + ['db' => 'domain', 'dt' => 2], + ['db' => 'aliases', 'dt' => 3, 'order_subquery' => "SELECT COUNT(*) FROM `alias` WHERE (`domain`= `d`.`domain` OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = `d`.`domain`)) AND `address` NOT IN (SELECT `username` FROM `mailbox`)"], + ['db' => 'mailboxes', 'dt' => 4, 'order_subquery' => "SELECT COUNT(*) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"], + ['db' => 'quota', 'dt' => 5, 'order_subquery' => "SELECT COALESCE(SUM(`mailbox`.`quota`), 0) FROM `mailbox` WHERE `mailbox`.`domain` = `d`.`domain` AND (`mailbox`.`kind` = '' OR `mailbox`.`kind` = NULL)"], + ['db' => 'stats', 'dt' => 6, 'dummy' => true, 'order_subquery' => "SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` IN (SELECT `username` FROM `mailbox` WHERE `domain` = `d`.`domain`)"], + ['db' => 'defquota', 'dt' => 7], + ['db' => 'maxquota', 'dt' => 8], + ['db' => 'backupmx', 'dt' => 10], + ['db' => 'tags', 'dt' => 14, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_domain` AS `td` ON `td`.`domain` = `d`.`domain`', 'where_column' => '`td`.`tag_name`']], + ['db' => 'active', 'dt' => 15], + ]; + + if($_SESSION['mailcow_cc_role'] === 'admin') { + $data = SSP::simple($requestDecoded, $pdo, $table, $primaryKey, $columns); + } elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') { + $data = SSP::complex($requestDecoded, $pdo, $table, $primaryKey, $columns, + 'INNER JOIN domain_admins as da ON da.domain = d.domain', + [ + 'condition' => 'da.active = 1 and da.username = :username', + 'bindings' => ['username' => $_SESSION['mailcow_cc_username']] + ]); + } else { + http_response_code(403); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Insufficient permissions' + )); + exit(); + } + + if (!empty($data['data'])) { + $domainsData = []; + foreach ($data['data'] as $domain) { + if ($details = mailbox('get', 'domain_details', $domain[2])) { + $domainsData[] = $details; + } + } + $data['data'] = $domainsData; + } + + process_search_return($data); + break; + + case "mailbox": + $table = ['mailbox', 'm']; + $primaryKey = 'username'; + $columns = [ + ['db' => 'username', 'dt' => 2], + ['db' => 'quota', 'dt' => 3], + ['db' => 'last_mail_login', 'dt' => 4, 'dummy' => true, 'order_subquery' => "SELECT MAX(`datetime`) FROM `sasl_log` WHERE `service` != 'SSO' AND `username` = `m`.`username`"], + ['db' => 'last_pw_change', 'dt' => 5, 'dummy' => true, 'order_subquery' => "JSON_EXTRACT(attributes, '$.passwd_update')"], + ['db' => 'in_use', 'dt' => 6, 'dummy' => true, 'order_subquery' => "(SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`) / `m`.`quota`"], + ['db' => 'name', 'dt' => 7], + ['db' => 'messages', 'dt' => 20, 'dummy' => true, 'order_subquery' => "SELECT SUM(messages) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`"], + ['db' => 'tags', 'dt' => 23, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_mailbox` AS `tm` ON `tm`.`username` = `m`.`username`', 'where_column' => '`tm`.`tag_name`']], + ['db' => 'active', 'dt' => 24], + ]; + + if($_SESSION['mailcow_cc_role'] === 'admin') { + $data = SSP::complex($requestDecoded, $pdo, $table, $primaryKey, $columns, null, + "(`m`.`kind` = '' OR `m`.`kind` = NULL)"); + } elseif ($_SESSION['mailcow_cc_role'] === 'domainadmin') { + $data = SSP::complex($requestDecoded, $pdo, $table, $primaryKey, $columns, + 'INNER JOIN domain_admins as da ON da.domain = m.domain', + [ + 'condition' => "(`m`.`kind` = '' OR `m`.`kind` = NULL) AND `da`.`active` = 1 AND `da`.`username` = :username", + 'bindings' => ['username' => $_SESSION['mailcow_cc_username']] + ]); + } else { + http_response_code(403); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Insufficient permissions' + )); + exit(); + } + + if (!empty($data['data'])) { + $mailboxData = []; + foreach ($data['data'] as $mailbox) { + if ($details = mailbox('get', 'mailbox_details', $mailbox[2])) { + $mailboxData[] = $details; + } + } + $data['data'] = $mailboxData; + } + + process_search_return($data); + break; + + default: + http_response_code(404); + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Invalid search category' + )); + break; + } + break; case "delete": if ($_SESSION['mailcow_cc_api_access'] == 'ro' || isset($_SESSION['pending_mailcow_cc_username']) || !isset($_SESSION["mailcow_cc_username"])) { http_response_code(403); diff --git a/data/web/lang/lang.cs-cz.json b/data/web/lang/lang.cs-cz.json index b178720a9..708de8486 100644 --- a/data/web/lang/lang.cs-cz.json +++ b/data/web/lang/lang.cs-cz.json @@ -111,7 +111,8 @@ "tags": "Štítky", "dry": "Simulovat synchronizaci", "internal": "Interní", - "internal_info": "Interní aliasy jsou přístupné jen z vlastních domén nebo jejich aliasů." + "internal_info": "Interní aliasy jsou přístupné jen z vlastních domén nebo jejich aliasů.", + "sender_allowed": "Povolit jako alias pro odesílání" }, "admin": { "access": "Přístupy", @@ -304,7 +305,7 @@ "rspamd_com_settings": "Název nastavení se vygeneruje automaticky, viz ukázky nastavení níže. Více informací viz Rspamd dokumentace", "rspamd_global_filters": "Mapa globálních filtrů", "rspamd_global_filters_agree": "Budu opatrný!", - "rspamd_global_filters_info": "Mapa globálních filtrů obsahuje různé seznamy povolených a zakázaných serverů", + "rspamd_global_filters_info": "Mapa globálních filtrů obsahuje různé seznamy povolených a zakázaných serverů.", "rspamd_global_filters_regex": "Názvy stačí k vysvětlení. Položky musejí obsahovat jen platné regulární výrazy ve tvaru \"/vyraz/parametry\" (e.g. /.+@domena\\.tld/i).
    \n Každý výraz bude podroben základní kontrole, přesto je možné Rspamd 'rozbít', nebude-li syntax zcela korektní.
    \n Rspamd se pokusí po každé změně načíst mapu znovu. V případě potíží restartujte Rspamd, aby se konfigurace načetla explicitně.", "rspamd_settings_map": "Nastavení Rspamd", "sal_level": "Úroveň 'Moo'", @@ -780,7 +781,9 @@ "mta_sts_max_age_info": "Doba v sekundách, po niž poštovní servery mohou toho pravidlo držet v mezipaměti bez nutnosti obnovení.", "mta_sts_mx": "Server MX", "mta_sts_mx_info": "Dovoluje odesílání jen výslovně vypsaným poštovním serverům; odesílající server kontroluje, že server MX určený v DNS odpovídá pravidlu, a povolí doručení jen s platným certifikátem TLS (chrání přes útokem typu MITM).", - "mta_sts_mx_notice": "Lze zadat více serverů MX (oddělte čárkou)." + "mta_sts_mx_notice": "Lze zadat více serverů MX (oddělte čárkou).", + "sender_allowed": "Povolit jako alias pro odesílání", + "sender_allowed_info": "Není-li povoleno, lze alias použít jen pro příjem pošty. Lze také obejít v Sender ACL a nastavit konkrétní schránky, jež tento alias mohou používat" }, "fido2": { "confirm": "Potvrdit", diff --git a/data/web/lang/lang.de-de.json b/data/web/lang/lang.de-de.json index b86879ded..487300fe9 100644 --- a/data/web/lang/lang.de-de.json +++ b/data/web/lang/lang.de-de.json @@ -536,6 +536,7 @@ "temp_error": "Temporärer Fehler", "text_empty": "Text darf nicht leer sein", "tfa_token_invalid": "TFA-Token ungültig", + "tfa_removal_blocked": "Zwei-Faktor-Authentifizierung kann nicht entfernt werden, da sie für Ihr Konto erforderlich ist.", "tls_policy_map_dest_invalid": "Ziel ist ungültig", "tls_policy_map_entry_exists": "Eine TLS-Richtlinie \"%s\" existiert bereits", "tls_policy_map_parameter_invalid": "Parameter ist ungültig", @@ -1233,7 +1234,12 @@ "webauthn": "WebAuthn-Authentifizierung", "waiting_usb_auth": "Warte auf USB-Gerät...

    Bitte jetzt den vorgesehenen Taster des USB-Gerätes berühren.", "waiting_usb_register": "Warte auf USB-Gerät...

    Bitte zuerst das obere Passwortfeld ausfüllen und erst dann den vorgesehenen Taster des USB-Gerätes berühren.", - "yubi_otp": "Yubico OTP-Authentifizierung" + "yubi_otp": "Yubico OTP-Authentifizierung", + "force_tfa": "Zwei-Faktor-Authentifizierung beim Login erzwingen", + "force_tfa_info": "Der Benutzer muss Zwei-Faktor-Authentifizierung einrichten, bevor er auf den Bereich zugreifen kann.", + "setup_title": "Zwei-Faktor-Authentifizierung erforderlich", + "setup_required": "Ihr Konto erfordert Zwei-Faktor-Authentifizierung. Bitte richten Sie eine 2FA-Methode ein, um fortzufahren.", + "cancel_setup": "Abbrechen und abmelden" }, "user": { "action": "Aktion", @@ -1312,6 +1318,7 @@ "never": "Niemals", "new_password": "Neues Passwort", "new_password_repeat": "Neues Passwort (Wiederholung)", + "pw_update_required": "Ihr Konto erfordert eine Passwortänderung. Bitte setzen Sie ein neues Passwort, um fortzufahren.", "no_active_filter": "Kein aktiver Filter vorhanden", "no_last_login": "Keine letzte UI-Anmeldung gespeichert", "no_record": "Kein Eintrag", diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index 79175e578..eb26e3fcf 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -416,6 +416,7 @@ }, "danger": { "access_denied": "Access denied or invalid form data", + "tfa_removal_blocked": "Two-factor authentication cannot be removed because it is required for your account.", "alias_domain_invalid": "Alias domain %s is invalid", "alias_empty": "Alias address must not be empty", "alias_goto_identical": "Alias and goto address must not be identical", @@ -1221,6 +1222,11 @@ "confirm_totp_token": "Please confirm your changes by entering the generated token", "delete_tfa": "Disable TFA", "disable_tfa": "Disable TFA until next successful login", + "force_tfa": "Force 2FA enrollment at login", + "force_tfa_info": "The user will be required to set up two-factor authentication before accessing the panel.", + "setup_title": "Two-Factor Authentication Required", + "setup_required": "Your account requires two-factor authentication. Please set up a 2FA method to continue.", + "cancel_setup": "Cancel and log out", "enter_qr_code": "Your TOTP code if your device cannot scan QR codes", "error_code": "Error code", "init_webauthn": "Initializing, please wait...", @@ -1327,6 +1333,7 @@ "overview": "Overview", "password": "Password", "password_now": "Current password (confirm changes)", + "pw_update_required": "Your account requires a password change. Please set a new password to continue.", "password_repeat": "Password (repeat)", "password_reset_info": "If no email for password recovery is provided, this function cannot be used.", "protocols": "Protocols", diff --git a/data/web/lang/lang.lv-lv.json b/data/web/lang/lang.lv-lv.json index 86eb91a66..fd29e594a 100644 --- a/data/web/lang/lang.lv-lv.json +++ b/data/web/lang/lang.lv-lv.json @@ -38,7 +38,7 @@ "alias_domain": "Aizstājdomēni", "alias_domain_info": "Tikai derīgi domēna vārdi (komatu atdalīti).", "automap": "Mēģiniet automatizēt mapes (\"Nosūtītie vienumi\", \"Nosūtītie\" => \"Nosūtītie\" etc.)", - "backup_mx_options": "Dublējuma MX iespējas", + "backup_mx_options": "Releja iespējas", "delete1": "Izdzēst no avota pēc pabeigšanas", "delete2": "Dzēsiet ziņojumus galamērķī, kas nav avotā", "delete2duplicates": "Izdzēst atkārtojošos vienumus galamērķī", @@ -65,8 +65,8 @@ "relay_all": "Pārsūtīt visus saņēmējus", "relay_all_info": "↪ Ja izvēlies nepārsūtīt visus saņēmējus, tad Tev būs nepieciešams pievienot (\"aklo\") pastkasti katram saņēmējam, kas būtu jāpārsūta.", "relay_domain": "Pārsūtīt šo domēnu", - "select": "Lūdzu izvēlaties...", - "select_domain": "Lūdzu sākumā izvēlaties domēnu", + "select": "Lūgums atlasīt…", + "select_domain": "Lūgums vispirms atlasīt domēnu", "sieve_desc": "Īss apraksts", "sieve_type": "Filtra tips", "skipcrossduplicates": "Izlaist dublētus ziņojumus pa mapēm (pirmais nāk, pirmais kalpo)", @@ -153,7 +153,7 @@ "r_info": "Pelēkie/atspējotie vienumi spēkā esošo ierobežojumu sarakstā mailcow nav zināmi kā derīgi ierobežojumi, un tos nevar pārvietot. Nezināmi ierobežojumi jebkurā gadījumā parādīšanas secībā.
    Jaunus vienumus var pievienot inc/vars.local.inc.php, lai varētu tos pārslēgt.", "recipients": "Adresāts", "refresh": "Atsvaidzināt", - "regen_api_key": "Reģenerēt API atslēgu", + "regen_api_key": "Atkārtoti izveidot API atslēgu", "relay_from": "\"No:\" adrese", "relay_run": "Palaist testu", "relayhosts_hint": "Norādīt no sūtītāja atkarīgas piegādes, lai varētu tos atlasīt domēnu konfigurācijas uzvednē.
    \n Piegādes pakalpojums vienmēr ir \"smtp\", tādējādi tiks mēģināts TLS, kad piedāvāts. Iekļautais TLS (SMTPS) netiek atbalstīts. Tiek ņemts vērā lietotāja atsevišķais izejošā TLS nosacījuma iestatījums.
    \n Ietekmē atlasītos domēnus, tajā skaitā aizstājdomēnus.", @@ -185,7 +185,10 @@ "login_time": "Pieteikšanās laiks", "iam_version": "Versija", "quarantine_max_age": "Lielākais pieļaujamais vecums dienās
    Vērtībai jābūt vienādai ar vai lielākai par 1 dienu.", - "quarantine_max_score": "Atmest paziņojumu, ja e-pasta ziņojuma mēstuļu novērtējums ir augstāks par šo vērtību:
    Noklusējums ir 9999.0" + "quarantine_max_score": "Atmest paziņojumu, ja e-pasta ziņojuma mēstuļu novērtējums ir augstāks par šo vērtību:
    Noklusējums ir 9999.0", + "options": "Iespējas", + "password_reset_settings": "Paroļu atkopes iestatījumi", + "password_settings": "Paroļu iestatījumi" }, "danger": { "access_denied": "Piekļuve liegta, vai nepareizi dati", @@ -257,7 +260,7 @@ "active": "Aktīvs", "alias": "Labot aizstājvārdu", "automap": "Mēģiniet automatizēt mapes (\"Nosūtītie vienumi\", \"Nosūtītie\" => \"Nosūtītie\" utt.)", - "backup_mx_options": "Dublēt MX iespējas", + "backup_mx_options": "Retranslācijas iespējams", "delete1": "Izdzēst no avota pēc pabeigšanas", "delete2": "Dzēsiet ziņojumus galamērķī, kas nav avotā", "delete2duplicates": "Izdzēst atkārtojošos vienumus galamērķī", @@ -558,7 +561,7 @@ "key_id_totp": "Identifikators Jūsu atslēgai", "none": "Deaktivizēt", "scan_qr_code": "Lūdzu, skenējiet šo kodu ar savu autentifikācijas lietojumprogrammu vai ievadiet kodu manuāli.", - "select": "Lūdzu izvēlaties", + "select": "Lūgums atlasīt", "set_tfa": "Uzstādīt difi faktoru autentifik;acijas metodi", "tfa": "Divpakāpju pieteikšanās", "totp": "Uz laiku bāzēta vienreizēja parole (Google Autentifikātors utt.)", diff --git a/data/web/lang/lang.nl-nl.json b/data/web/lang/lang.nl-nl.json index 71ec97209..7b341cecf 100644 --- a/data/web/lang/lang.nl-nl.json +++ b/data/web/lang/lang.nl-nl.json @@ -26,7 +26,8 @@ "syncjobs": "Sync jobs", "tls_policy": "Versleutelingsbeleid", "unlimited_quota": "Onbeperkte quota voor mailboxen", - "domain_desc": "Wijzig domeinbeschrijving" + "domain_desc": "Wijzig domeinbeschrijving", + "pw_reset": "Toegang om mailcow gebruikers wachtwoord te resetten" }, "add": { "activate_filter_warn": "Alle andere filters worden gedeactiveerd zolang deze geactiveerd is.", @@ -104,7 +105,11 @@ "validate": "Verifieer", "validation_success": "Succesvol geverifieerd", "tags": "Tags", - "bcc_dest_format": "BCC-bestemming moet één geldig e-mailadres zijn.
    Als u een kopie naar meerdere adressen wilt sturen, maak dan een alias aan en gebruik die hier." + "bcc_dest_format": "BCC-bestemming moet één geldig e-mailadres zijn.
    Als u een kopie naar meerdere adressen wilt sturen, maak dan een alias aan en gebruik die hier.", + "dry": "Synchronisatie simuleren", + "internal": "Intern", + "internal_info": "Interne aliassen zijn alleen toegankelijk vanuit het eigen (alias)domein.", + "sender_allowed": "Toestaan om te verzenden als deze alias" }, "admin": { "access": "Toegang", @@ -138,7 +143,7 @@ "arrival_time": "Aankomsttijd", "authed_user": "Geauthenticeerde gebruiker", "ays": "Weet je zeker dat je deze actie wilt uitvoeren?", - "ban_list_info": "Bekijk de lijst met verbannen IP-adressen hieronder: netwerk (resterende tijd) - [acties].
    Rode labels geven een permanente verbanning aan.
    Het kan enkele seconden duren voordat wijzigingen hieronder zichtbaar zijn.", + "ban_list_info": "Bekijk de lijst met verbannen IP-adressen hieronder: netwerk (resterende tijd) - [acties].
    IP-adressen die in de wachtrij staan om te worden gedeblokkeerd, worden binnen enkele seconden uit de actieve banlijst verwijderd.
    \n
    Rode labels geven actieve permanente blokkades door denylisting aan.", "change_logo": "Logo", "configuration": "Instellingen", "convert_html_to_text": "Converteer HTML naar plaintext", @@ -171,7 +176,7 @@ "excludes": "Exclusief", "f2b_ban_time": "Verbanningstijd (s)", "f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning", - "f2b_blacklist": "Netwerken/hosts op de blacklist", + "f2b_blacklist": "Netwerken/hosts op de denylist", "f2b_filter": "Regex-filters", "f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.", "f2b_max_attempts": "Maximaal aantal pogingen", @@ -206,7 +211,7 @@ "link": "Link", "loading": "Even geduld aub...", "logo_info": "De afbeelding zal worden geschaald naar een hoogte van 40px voor de navigatiebar, en naar een breedte van 250px voor de startpagina.", - "lookup_mx": "Match bestemming aan MX (gebruik .outlook.com om alle mail gericht aan MX *.outlook.com over deze hop te laten gaan)", + "lookup_mx": "Bestemming is een reguliere expressie die wordt gematcht met de MX-naam (.*\\.google\\.com om alle e-mail die gericht is aan een MX die eindigt op google.com via deze hop te routeren)", "main_name": "\"Mailcow\"", "merged_vars_hint": "Grijze rijen zijn samengevoegd van vars.(local.)inc.php en kunnen niet worden gewijzigd.", "message": "Bericht", @@ -275,7 +280,7 @@ "rspamd_com_settings": "Een beschrijving voor deze instelling zal automatisch worden gegenereerd, gebruik de onderstaande presets als voorbeeld. Raadpleeg de Rspamd-documentatie voor meer informatie.", "rspamd_global_filters": "Globale filters", "rspamd_global_filters_agree": "Ik ben me ervan bewust dat aanpassingen desastreuze gevolgen kunnen hebben", - "rspamd_global_filters_info": "Ieder globaal filter heeft zijn eigen functie, zie de namen.", + "rspamd_global_filters_info": "Globale filtermaps bevatten verschillende soorten globale deny- en allowlists.", "rspamd_global_filters_regex": "De velden kunnen uitsluitend regular expressions bevatten met het formaat \"/pattern/options\", bijvoorbeeld /.+@domain\\.tld/i.
    Ondanks dat alle invoer wordt gecontroleerd op fouten, is het toch mogelijk dat Rspamd onbruikbaar wordt als deze de invoer niet kan lezen.
    Als je problemen ervaart, herstart Rspamd dan om de filters opnieuw te laten lezen.
    Elementen op de blacklist zijn uitgesloten van de quarantaine.", "rspamd_settings_map": "Rspamd", "sal_level": "Moo-level", @@ -337,7 +342,71 @@ "admins_ldap": "LDAP administrators", "api_read_only": "Alleen-lezen toegang", "api_read_write": "Lees en schrijf toegang", - "login_time": "Login tijd" + "login_time": "Login tijd", + "admin_quicklink": "Snelle link naar de admin-loginpagina verbergen", + "allowed_methods": "Toegestane toegangsmethoden", + "app_hide": "Verberg voor login", + "copy_to_clipboard": "Tekst gekopieerd naar het klembord!", + "login_page": "Loginpagina", + "domainadmin_quicklink": "Snelle link naar de domeinbeheerder-loginpagina verbergen", + "f2b_manage_external": "Fail2Ban extern beheren", + "f2b_manage_external_info": "Fail2Ban zal nog steeds de banlijst bijhouden, maar zal niet actief regels instellen om verkeer te blokkeren. Gebruik de onderstaande gegenereerde banlijst om het verkeer extern te blokkeren.", + "filter": "Filter", + "force_sso_text": "Als een externe OIDC-provider is geconfigureerd, verbergt deze optie de standaard Mailcow-loginformulieren en wordt alleen de Single SignOn-knop weergegeven.", + "force_sso": "Mailcow-login uitschakelen en alleen Single SignOn tonen", + "iam": "Identiteitsprovider", + "iam_attribute_field": "Attribuutveld", + "iam_authorize_url": "Autorisatie endpoint", + "iam_auth_flow": "Autorisatie flow", + "iam_auth_flow_info": "Naast de Authorization Code Flow (Standard Flow in Keycloak), die wordt gebruikt voor Single SignOn-login, ondersteunt Mailcow ook een authenticatiestroom met directe inloggegevens. De Mailpassword Flow probeert de gebruikersgegevens te valideren door gebruik te maken van de Keycloak Admin REST API. Mailcow haalt het gehashte wachtwoord op uit het attribuut mailcow_password, dat in Keycloak is gekoppeld.", + "iam_client_id": "Client ID", + "iam_client_secret": "Client Secret", + "iam_client_scopes": "Clientscopes", + "iam_default_template": "Standaardsjabloon", + "iam_default_template_description": "Als er geen sjabloon aan een gebruiker is toegewezen, wordt het standaardsjabloon gebruikt voor het aanmaken van de mailbox, maar niet voor het bijwerken van de mailbox.", + "iam_description": "Configureer een externe provider voor authenticatie. De mailboxen van gebruikers worden automatisch aangemaakt bij hun eerste login, mits er een attribuutkoppeling is ingesteld.", + "iam_extra_permission": "Om de volgende instellingen te laten werken, heeft de mailcow-client in Keycloak een service account nodig en de toestemming view-users.", + "iam_host": "Hostname", + "iam_host_info": "Voer één of meerdere LDAP-hosts in, gescheiden door komma’s.", + "iam_import_users": "Importeer gebruikers", + "iam_login_provisioning": "Gebruikers automatisch aanmaken bij het inloggen", + "iam_mapping": "Attribuutkoppeling", + "iam_bindpass": "Bind-wachtwoord", + "iam_periodic_full_sync": "Periodieke volledige synchronisatie", + "iam_port": "Poort", + "iam_realm": "Realm", + "iam_redirect_url": "Redirect-URL", + "iam_rest_flow": "Mailwachtwoord-flow", + "iam_server_url": "Server Url", + "iam_sso": "Single SignOn", + "iam_sync_interval": "Synchronisatie-/importinterval (min)", + "iam_test_connection": "Verbinding testen", + "iam_token_url": "Token endpoint", + "iam_userinfo_url": "Gebruikersinfo endpoint", + "iam_username_field": "Gebruikersnaam veld", + "iam_use_ssl": "Gebruik SSL", + "iam_use_ssl_info": "Als SSL wordt ingeschakeld en de poort is ingesteld op 389, wordt deze automatisch aangepast naar 636.", + "iam_use_tls": "Gebruik StartTLS", + "iam_use_tls_info": "Als TLS wordt ingeschakeld, moet je de standaardpoort voor je LDAP-server (389) gebruiken. SSL-poorten kunnen niet worden gebruikt.", + "iam_version": "Versie", + "ignore_ssl_error": "Negeer SSL foutmeldingen", + "ip_check_opt_in": "Kies ervoor om de externe diensten ipv4.mailcow.email en ipv6.mailcow.email te gebruiken om externe IP-adressen te bepalen.", + "needs_restart": "vereist een herstart", + "no": "✕", + "password_reset_info": "Als er geen herstel-e-mailadres is opgegeven, kan deze functie niet worden gebruikt.", + "password_reset_settings": "Instellingen voor wachtwoordherstel", + "password_reset_tmpl_html": "HTML sjabloon", + "password_reset_tmpl_text": "Tekst sjabloon", + "password_settings": "Wachtwoordinstellingen", + "quicklink_text": "Toon of verberg snelle links naar andere inlogpagina’s onder het inlogformulier", + "queue_unban": "deblokkeren", + "reset_password_vars": "{{link}} De gegenereerde link om het wachtwoord te resetten
    {{username}} De mailboxnaam van de gebruiker die het wachtwoordherstel heeft aangevraagd
    {{username2}} De naam van de herstelmailbox
    {{date}} De datum waarop het verzoek tot wachtwoordherstel is gedaan
    {{token_lifetime}} De geldigheidsduur van de token in minuten
    {{hostname}} De mailcow-hostnaam", + "restore_template": "Leeg laten om het standaardsjabloon te herstellen.", + "task": "Taak", + "transport_test_rcpt_info": "• Gebruik null@hosted.mailcow.de om relaying naar een externe bestemming te testen.", + "user_link": "Gebruikerslink", + "user_quicklink": "Snelle link naar de gebruikers-loginpagina verbergen", + "yes": "✓" }, "danger": { "access_denied": "Toegang geweigerd of ongeldige gegevens", @@ -463,7 +532,27 @@ "demo_mode_enabled": "Demo modus is ingeschakeld", "template_exists": "Sjabloon %s bestaat al", "template_id_invalid": "Sjabloon ID %s ongeldig", - "template_name_invalid": "Sjabloon naam ongeldig" + "template_name_invalid": "Sjabloon naam ongeldig", + "tfa_removal_blocked": "Twee-factor-authenticatie kan niet worden verwijderd omdat dit vereist is voor uw account.", + "authsource_in_use": "De identiteitsprovider kan niet worden gewijzigd of verwijderd omdat deze momenteel door één of meerdere gebruikers wordt gebruikt.", + "cors_invalid_origin": "Ongeldige Allow-Origin opgegeven", + "extended_sender_acl_denied": "Ontbrekende ACL om externe afzenderadressen in te stellen", + "generic_server_error": "Er is een onverwachte serverfout opgetreden. Neem contact op met uw beheerder.", + "iam_test_connection": "Verbinding mislukt", + "img_dimensions_exceeded": "De afbeelding overschrijdt de maximale afbeeldingsgrootte", + "img_size_exceeded": "De afbeelding overschrijdt de maximale afbeeldingsgrootte", + "invalid_reset_token": "Ongeldige reset-token", + "max_age_invalid": "Maximale leeftijd %s is ongeldig", + "mode_invalid": "Mode %s is ongeldig", + "mx_invalid": "MX-record %s is ongeldig", + "password_reset_invalid_user": "Mailbox niet gevonden of er is geen herstel-e-mailadres ingesteld", + "password_reset_na": "Wachtwoordherstel is momenteel niet beschikbaar. Neem contact op met uw beheerder.", + "recovery_email_failed": "Kon geen herstel-e-mail verzenden. Neem contact op met uw beheerder.", + "required_data_missing": "Vereiste gegevens %s ontbreken", + "reset_token_limit_exceeded": "De limiet voor reset-tokens is overschreden. Probeer het later opnieuw.", + "to_invalid": "Ontvanger mag niet leeg zijn", + "webauthn_username_failed": "De geselecteerde authenticator behoort tot een ander account", + "version_invalid": "Versie %s is ongeldig" }, "debug": { "chart_this_server": "Grafiek (deze server)", @@ -520,7 +609,7 @@ "alias": "Wijzig alias", "allow_from_smtp": "Sta enkel de volgende IP-adressen toe voor SMTP", "allow_from_smtp_info": "Laat leeg om alle afzenders toe te staan.
    IPv4/IPv6-adressen en netwerken.", - "allowed_protocols": "Toegestane protocollen", + "allowed_protocols": "Toegestane protocollen voor directe gebruikerstoegang (heeft geen invloed op protocollen voor app-wachtwoorden).", "app_name": "Naam van app", "app_passwd": "Appwachtwoord", "automap": "Probeer mappen automatisch te koppelen (\"Verzonden items\", \"Verzonden\" => \"Verzonden\" etc.)", @@ -605,7 +694,7 @@ "sogo_visible_info": "Wanneer verborgen zal een alias niet worden weergegeven als een selecteerbaar verzendadres. Deze optie beïnvloedt uitsluitend objecten die kunnen worden weergegeven in SOGo (gedeelde of niet-gedeelde aliasadressen die naar minstens één mailbox verwijzen).", "spam_alias": "Maak een nieuw tijdelijk alias aan, of pas deze aan", "spam_filter": "Spamfilter", - "spam_policy": "Voeg items toe of verwijder items van de white- of blacklist", + "spam_policy": "Voeg items toe of verwijder items van de deny- en allowlists", "spam_score": "Stel een aangepaste spamscore in", "subfolder2": "Synchroniseer in submap op bestemming
    (leeg = gebruik geen submappen)", "syncjob": "Wijzig sync job", @@ -624,7 +713,47 @@ "acl": "ACL (Toestemming)", "domain_footer": "Domeinbreede footer", "domain_footer_html": "HTML footer", - "mailbox_relayhost_info": "Wordt alleen toegepast op de mailbox en directe aliassen, maar heft een domein relayhost op." + "mailbox_relayhost_info": "Wordt alleen toegepast op de mailbox en directe aliassen, maar heft een domein relayhost op.", + "custom_attributes": "Aangepaste attributen", + "domain_footer_info_vars": { + "auth_user": "{= auth_user =} - Geauthenticeerde gebruikersnaam opgegeven door een MTA", + "from_user": "{= from_user =} - Afzendergedeelte van het e-mailadres, bijvoorbeeld bij \"moo@mailcow.tld\" wordt \"moo\" teruggegeven", + "from_name": "{= from_name =} - Naam van de afzender, bijvoorbeeld bij \"Mailcow \" wordt \"Mailcow\" teruggegeven", + "from_addr": "{= from_addr =} - Adres van de afzender", + "from_domain": "{= from_domain =} - Domein van de afzender", + "custom": "{= foo =} - Als de mailbox het aangepaste attribuut \"foo\" met de waarde \"bar\" heeft, wordt \"bar\" teruggegeven." + }, + "domain_footer_plain": "PLAIN-voettekst", + "domain_footer_skip_replies": "Voettekst negeren bij antwoord-e-mails", + "footer_exclude": "Uitsluiten van voettekst", + "internal": "Intern", + "internal_info": "Interne aliassen zijn alleen toegankelijk vanuit het eigen (alias)domein.", + "sender_allowed": "Toestaan om als deze alias te verzenden", + "sender_allowed_info": "Als dit is uitgeschakeld, kan deze alias alleen e-mail ontvangen. Gebruik de afzender-ACL om dit te overschrijven en specifieke mailboxen toestemming te geven om te verzenden.", + "lookup_mx": "Bestemming is een reguliere expressie die wordt gematcht met de MX-naam (.*.google.com om alle e-mail die gericht is aan een MX die eindigt op google.com via deze hop te routeren)", + "mailbox_rename": "Hernoem mailbox", + "mailbox_rename_agree": "Ik heb een backup gemaakt.", + "mailbox_rename_warning": "BELANGRIJK! Maak een back-up voordat u de mailbox hernoemt.", + "mailbox_rename_alias": "Alias automatisch aanmaken.", + "mailbox_rename_title": "Nieuwe naam van de lokale mailbox", + "mta_sts": "MTA-STS", + "mta_sts_info": "MTA-STS is een standaard die afdwingt dat e-mailbezorging tussen mailservers TLS met geldige certificaten gebruikt.
    \nDeze wordt gebruikt wanneer DANE niet mogelijk is vanwege ontbrekende of niet-ondersteunde DNSSEC.
    \nOpmerking: Als het ontvangende domein DANE met DNSSEC ondersteunt, heeft DANE altijd de voorkeur – MTA-STS fungeert alleen als een fallback.", + "mta_sts_version": "Versie", + "mta_sts_version_info": "Definieert de versie van de MTA-STS-standaard – momenteel is alleen STSv1 geldig.", + "mta_sts_mode": "Modus", + "mta_sts_mode_info": "Er zijn drie modus om uit te kiezen:
    • testing – het beleid wordt alleen gemonitord, overtredingen hebben geen impact.
    • enforce – het beleid wordt strikt afgedwongen, verbindingen zonder geldige TLS worden geweigerd.
    • none – het beleid wordt gepubliceerd maar niet toegepast.
    ", + "mta_sts_max_age": "Maximale leeftijd", + "mta_sts_max_age_info": "Tijd in seconden dat ontvangende mailservers dit beleid mogen cachen voordat het opnieuw wordt opgehaald.", + "mta_sts_mx": "MX server", + "mta_sts_mx_info": "Staat verzending alleen toe naar expliciet vermelde mailserver-hostnamen; de verzendende MTA controleert of de DNS MX-hostnaam overeenkomt met de beleidslijst en staat levering alleen toe met een geldig TLS-certificaat (beschermt tegen MITM).", + "mta_sts_mx_notice": "Meerdere MX-servers kunnen worden opgegeven (gescheiden door komma’s).", + "none_inherit": "Geen / Erven", + "password_recovery_email": "E-mail voor wachtwoordherstel", + "pushover": "Pushover", + "quota_warning_bcc": "Quotum waarschuwing BCC", + "quota_warning_bcc_info": "Waarschuwingen zullen in afzonderlijke exemplaren naar de volgende ontvangers worden gestuurd. Het onderwerp zal worden gevolgd door de bijbehorende gebruikersnaam tussen haakjes, bijvoorbeeld: Contingentwaarschuwing (user@example.com).", + "sogo_access": "Verleen directe logintoegang tot SOGo", + "sogo_access_info": "Na het inloggen wordt de gebruiker automatisch doorgestuurd naar SOGo" }, "footer": { "cancel": "Annuleren", @@ -637,18 +766,21 @@ "restart_container": "Herstart container", "restart_container_info": "Belangrijk: Een herstart kan enige tijd in beslag nemen, wacht aub totdat dit proces voltooid is.
    Deze pagina zal zichzelf verversen zodra het proces voltooid is.", "restart_now": "Nu herstarten", - "restarting_container": "Container wordt herstart, even geduld aub..." + "restarting_container": "Container wordt herstart, even geduld aub...", + "hibp_check": "Controleer tegen haveibeenpwned.com", + "nothing_selected": "Niets geselecteerd" }, "header": { "administration": "Configuratie & details", "apps": "Apps", - "debug": "Systeeminformatie", + "debug": "Informatie", "email": "E-Mail", "mailcow_config": "Beheer", "quarantine": "Quarantaine", "restart_netfilter": "Herstart netfilter", "restart_sogo": "Herstart SOGo", - "user_settings": "Gebruikersinstellingen" + "user_settings": "Gebruikersinstellingen", + "mailcow_system": "Systeem" }, "info": { "awaiting_tfa_confirmation": "In afwachting van tweefactorauthenticatie...", @@ -662,7 +794,22 @@ "mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.", "other_logins": "of aanmelden met", "password": "Wachtwoord", - "username": "Gebruikersnaam" + "username": "Gebruikersnaam", + "back_to_mailcow": "Terug naar mailcow", + "forgot_password": "> Wachtwoord vergeten?", + "invalid_pass_reset_token": "De reset-wachtwoordtoken is ongeldig of verlopen.
    Vraag een nieuwe link voor het resetten van het wachtwoord aan.", + "login_linkstext": "Niet de juiste login?", + "login_usertext": "Log in als gebruiker", + "login_domainadmintext": "Login in als domeinbeheerder", + "login_admintext": "Log in als administrator", + "login_user": "Gebruikerslogin", + "login_dadmin": "Domeinbeheerder login", + "login_admin": "Administrator login", + "new_password": "Nieuw wachtwoord", + "new_password_confirm": "Bevestig nieuw wachtwoord", + "reset_password": "Herstel wachtwoord", + "request_reset_password": "Verzoek wachtwoord wijziging", + "email": "E-mailadres" }, "mailbox": { "action": "Handeling", @@ -782,7 +929,7 @@ "sieve_preset_5": "Autoreply (vakantie)", "sieve_preset_6": "Weiger mail met antwoord", "sieve_preset_7": "Forward en behoud/verwijder", - "sieve_preset_8": "Verwijder mail verstuurd naar een aliasadres van de afzender", + "sieve_preset_8": "E-mail van een specifieke afzender doorsturen, als gelezen markeren en in een submap plaatsen", "sieve_preset_header": "Zie de onderstaande voorbeelden. Raadpleeg Wikipedia voor meer informatie.", "sogo_visible": "Alias tonen in SOGo", "sogo_visible_n": "Verberg alias in SOGo", @@ -809,7 +956,37 @@ "toggle_all": "Selecteer alles", "username": "Gebruikersnaam", "waiting": "Wachten", - "weekly": "Wekelijks" + "weekly": "Wekelijks", + "add_alias_expand": "Alias uitbreiden over alias-domeinen", + "add_template": "Sjabloon toevoegen", + "all_domains": "Alle domeinen", + "catch_all": "Catch-All", + "created_on": "Aangemaakt op", + "domain_templates": "Domein sjablonen", + "domain_quota_total": "Totale domein-opslagruimte", + "goto_ham": "Leren als Ham", + "goto_spam": "Leren als spam", + "iam": "Identiteitsprovider", + "internal": "Intern", + "last_pw_change": "Laatste wachtwoordwijziging", + "mailbox_templates": "Mailbox sjablonen", + "no": "✕", + "open_logs": "Open logs", + "recipient": "Ontvanger", + "relay_unknown": "Onbekende mailboxen relayen", + "sender": "Afzender", + "syncjob_check_log": "Controleer log", + "syncjob_last_run_result": "Laatste uitvoeringsresultaat", + "syncjob_EX_OK": "Succes", + "syncjob_EXIT_CONNECTION_FAILURE": "Verbindingsprobleem", + "syncjob_EXIT_TLS_FAILURE": "Probleem met versleutelde verbinding", + "syncjob_EXIT_AUTHENTICATION_FAILURE": "Authenticatieprobleem", + "syncjob_EXIT_OVERQUOTA": "Doel mailbox is over quota", + "syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kan geen verbinding maken met de externe server", + "syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Verkeerde gebruikersnaam of wachtwoord", + "templates": "Sjablonen", + "template": "Sjabloon", + "yes": "✓" }, "oauth2": { "access_denied": "Log in als een mailboxgebruiker om toegang via OAuth te verlenen", @@ -842,7 +1019,7 @@ "notified": "Verwittigd", "qhandler_success": "Aanvraag succesvol verzonden naar het systeem. Je kunt nu het venster sluiten.", "qid": "Rspamd QID", - "qinfo": "Het quarantainesysteem slaat een kopie van zowel geweigerde mail (voor de afzender zal het lijken alsof de mail niet afgeleverd is), als mail die afgeleverd is de spamfolder, op in de database.\r\n
    \"Markeer als spam en verwijder\" traint het systeem om soortgelijke mails in de toekomst opnieuw als spam te markeren.\r\n
    Wanneer er meerdere berichten tegelijkertijd worden behandeld kan het mogelijk enige tijd duren.
    Elementen op de blacklist zijn uitgesloten van de quarantaine.", + "qinfo": "Het quarantainesysteem slaat een kopie van zowel geweigerde mail (voor de afzender zal het lijken alsof de mail niet afgeleverd is), als mail die afgeleverd is de spamfolder, op in de database.\n
    \"Markeer als spam en verwijder\" traint het systeem om soortgelijke mails in de toekomst opnieuw als spam te markeren.\n
    Wanneer er meerdere berichten tegelijkertijd worden behandeld kan het mogelijk enige tijd duren.
    Elementen op de denylist zijn uitgesloten van de quarantaine.", "qitem": "Quarantaine-item", "quarantine": "Quarantaine", "quick_actions": "Handelingen", @@ -874,7 +1051,19 @@ "toggle_all": "Selecteer alles" }, "queue": { - "queue_manager": "Queue manager" + "queue_manager": "Queue manager", + "delete": "Verwijder alles", + "flush": "Wachtrij leegmaken", + "info": "De mailwachtrij bevat alle e-mails die wachten op aflevering. Als een e-mail lange tijd in de mailwachtrij blijft staan, wordt deze automatisch door het systeem verwijderd.
    De foutmelding van de betreffende e-mail geeft informatie over waarom de e-mail niet kon worden afgeleverd.", + "legend": "Functies voor acties in de mailwachtrij:", + "ays": "Bevestig dat u alle items uit de huidige wachtrij wilt verwijderen.", + "deliver_mail": "Afleveren", + "deliver_mail_legend": "Probeert de geselecteerde e-mails opnieuw te bezorgen.", + "hold_mail": "Vasthouden", + "hold_mail_legend": "Houdt de geselecteerde e-mails vast. (Voorkomt verdere afleverpogingen)", + "show_message": "Bericht weergeven", + "unban": "Wachtrij deblokkeren", + "unhold_mail": "Vrijgeven" }, "start": { "help": "Toon/verberg hulppaneel", @@ -994,7 +1183,8 @@ "start_fido2_validation": "Start FIDO2-validatie", "fido2_auth": "Aanmelden met FIDO2", "fido2_success": "Apparaat succesvol geregistreerd", - "fido2_validation_failed": "Validatie mislukt" + "fido2_validation_failed": "Validatie mislukt", + "set_fido2_touchid": "Registreer Touch ID op Apple M1" }, "user": { "action": "Handeling", @@ -1161,6 +1351,7 @@ "loadingRecords": "Laden...", "processing": "Wachten alstublieft..", "search": "Zoeken:", - "zeroRecords": "Geen overeenkomsten gevonden" + "zeroRecords": "Geen overeenkomsten gevonden", + "infoFiltered": "(gefilterd uit _MAX_ totale vermeldingen)" } } diff --git a/data/web/lang/lang.ru-ru.json b/data/web/lang/lang.ru-ru.json index e7d825639..58353fb5c 100644 --- a/data/web/lang/lang.ru-ru.json +++ b/data/web/lang/lang.ru-ru.json @@ -111,7 +111,8 @@ "validate": "Проверить", "validation_success": "Проверка прошла успешно", "internal": "Внутренний", - "internal_info": "Внутренние псевдонимы доступны только из самого домена или доменов-псевдонимов." + "internal_info": "Внутренние псевдонимы доступны только из самого домена или доменов-псевдонимов.", + "sender_allowed": "Разрешить отправку с этим псевдонимом" }, "admin": { "access": "Настройки доступа", @@ -781,7 +782,9 @@ "mta_sts_max_age_info": "Время в секундах, в течение которого принимающие почтовые серверы могут кэшировать эту политику перед повторной загрузкой.", "mta_sts_mx": "Сервер MX", "mta_sts_mx_info": "Разрешает отправку только на явно указанные имена хостов почтовых серверов; отправляющий MTA проверяет, соответствует ли DNS-имя MX-хоста списку политик, и разрешает доставку только с подлинным TLS-сертификатом (защита от MITM).", - "mta_sts_mx_notice": "Можно указать несколько MX-серверов (через запятую)." + "mta_sts_mx_notice": "Можно указать несколько MX-серверов (через запятую).", + "sender_allowed": "Разрешить отправку с этим псевдонимом", + "sender_allowed_info": "Если отключено, этот псевдоним может только принимать почту. Используйте ACL отправителя, чтобы переопределить и предоставить определенным почтовым ящикам разрешение на отправку." }, "fido2": { "confirm": "Подтвердить", diff --git a/data/web/lang/lang.si-si.json b/data/web/lang/lang.si-si.json index 918fb8b1a..5b7b85745 100644 --- a/data/web/lang/lang.si-si.json +++ b/data/web/lang/lang.si-si.json @@ -111,7 +111,8 @@ "timeout2": "Časovna omejitev za povezavo do lokalnega gostitelja", "dry": "Simuliraj sinhronizacijo", "internal": "Notranje", - "internal_info": "Notranji vzdevki so dostopni samo iz lastne domene ali vzdevkov domen." + "internal_info": "Notranji vzdevki so dostopni samo iz lastne domene ali vzdevkov domen.", + "sender_allowed": "Dovoli pošiljanje kot ta vzdevek" }, "admin": { "access": "Dostop", @@ -556,7 +557,8 @@ "max_age_invalid": "Najvišja starost %s je neveljavna", "mode_invalid": "Način %s ni veljaven", "mx_invalid": "Zapis MX %s je neveljaven", - "version_invalid": "Različica %s je neveljavna" + "version_invalid": "Različica %s je neveljavna", + "tfa_removal_blocked": "Dvofaktorske avtentikacije ni mogoče odstraniti, ker je obvezna za vaš račun." }, "debug": { "containers_info": "Informacije o zabojniku", @@ -780,7 +782,9 @@ "mta_sts_mx_info": "Omogoča pošiljanje samo na izrecno navedena imena gostiteljskih strežnikov poštnih strežnikov; pošiljajoči MTA preveri, ali se ime gostitelja DNS MX ujema s seznamom pravilnikov, in dovoljuje dostavo le z veljavnim potrdilom TLS (zaščita pred MITM).", "mta_sts_mx_notice": "Določiti je mogoče več strežnikov MX (ločenih z vejicami).", "internal": "Notranje", - "internal_info": "Notranji vzdevki so dostopni samo iz lastne domene ali vzdevkov domen." + "internal_info": "Notranji vzdevki so dostopni samo iz lastne domene ali vzdevkov domen.", + "sender_allowed": "Dovoli pošiljanje kot ta vzdevek", + "sender_allowed_info": "Če je onemogočeno, lahko ta vzdevek samo prejema pošto. Za preglasitev in dodelitev dovoljenja za pošiljanje določenim poštnim predalom uporabite seznam za nadzor dostopa pošiljatelja." }, "footer": { "restart_container_info": "Pomembno: Eleganten ponovni zagon lahko traja nekaj časa, zato počakajte, da se konča.", @@ -1229,7 +1233,12 @@ "u2f_deprecated_important": "Prosimo, registrirajte svoj ključ v skrbniški plošči z novo metodo WebAuthn.", "waiting_usb_auth": "Čakanje na napravo USB ...

    Zdaj se dotaknite gumba na napravi USB.", "waiting_usb_register": "Čakanje na napravo USB ...

    Vnesite svoje geslo zgoraj in potrdite registracijo tako, da tapnete gumb na napravi USB.", - "yubi_otp": "Avtentikacija z enkratnim geslom Yubico" + "yubi_otp": "Avtentikacija z enkratnim geslom Yubico", + "force_tfa": "Vsiljena registracija 2FA ob prijavi", + "force_tfa_info": "Uporabnik bo moral pred dostopom do nadzorne plošče nastaviti dvofaktorsko overjanje.", + "setup_title": "Zahtevana je dvofaktorska avtentikacija", + "setup_required": "Vaš račun zahteva dvofaktorsko overjanje. Za nadaljevanje nastavite metodo dvofaktorske overitve.", + "cancel_setup": "Prekliči in se odjavi" }, "ratelimit": { "disabled": "Onemogočeno", @@ -1410,7 +1419,8 @@ "syncjob_EX_OK": "Uspeh", "expire_never": "Nikoli ne poteče", "forever": "Za vedno", - "spam_aliases_info": "Vzdevek za neželeno pošto je začasni e-poštni naslov, ki ga je mogoče uporabiti za zaščito pravih e-poštnih naslovov.
    Po želji je mogoče nastaviti čas poteka veljavnosti, tako da se vzdevek po določenem obdobju samodejno deaktivira, s čimer se učinkovito znebite zlorabljenih ali razkritih naslovov." + "spam_aliases_info": "Vzdevek za neželeno pošto je začasni e-poštni naslov, ki ga je mogoče uporabiti za zaščito pravih e-poštnih naslovov.
    Po želji je mogoče nastaviti čas poteka veljavnosti, tako da se vzdevek po določenem obdobju samodejno deaktivira, s čimer se učinkovito znebite zlorabljenih ali razkritih naslovov.", + "pw_update_required": "Za vaš račun je potrebna sprememba gesla. Za nadaljevanje nastavite novo geslo." }, "warning": { "cannot_delete_self": "Prijavljenega uporabnika ni mogoče izbrisati", diff --git a/data/web/lang/lang.vi-vn.json b/data/web/lang/lang.vi-vn.json index 0e9e6a2c4..523b5dca9 100644 --- a/data/web/lang/lang.vi-vn.json +++ b/data/web/lang/lang.vi-vn.json @@ -111,7 +111,8 @@ "timeout2": "Thời gian chờ kết nối đến máy chủ cục bộ", "username": "Tên người dùng", "validate": "Xác thực", - "validation_success": "Xác thực thành công" + "validation_success": "Xác thực thành công", + "sender_allowed": "Cho phép gửi dưới bí danh này" }, "admin": { "access": "Truy cập", diff --git a/data/web/lang/lang.zh-tw.json b/data/web/lang/lang.zh-tw.json index 592efa809..e7310be7a 100644 --- a/data/web/lang/lang.zh-tw.json +++ b/data/web/lang/lang.zh-tw.json @@ -24,7 +24,7 @@ "sogo_access": "管理 SOGo 存取權", "sogo_profile_reset": "重設 SOGo 個人資料", "spam_alias": "臨時別名", - "spam_policy": "黑名單/白名單", + "spam_policy": "封鎖名單/允許名單", "spam_score": "垃圾郵件分數", "syncjobs": "同步任務", "tls_policy": "TLS 規則", @@ -1180,7 +1180,7 @@ "no_last_login": "沒有最後 UI 登入訊息", "no_record": "沒有紀錄", "open_logs": "開啟日誌", - "open_webmail_sso": "网络邮件", + "open_webmail_sso": "網頁信箱", "password": "密碼", "password_now": "目前密碼 (確認更改)", "password_repeat": "密碼 (再次輸入)", diff --git a/data/web/sogo-auth.php b/data/web/sogo-auth.php index 2da28d4d4..07456a7d1 100644 --- a/data/web/sogo-auth.php +++ b/data/web/sogo-auth.php @@ -50,6 +50,11 @@ elseif (isset($_GET['login'])) { (($_SESSION['acl']['login_as'] == "1" && $ALLOW_ADMIN_EMAIL_LOGIN !== 0) || ($is_dual === false && $login == $_SESSION['mailcow_cc_username']))) { if (filter_var($login, FILTER_VALIDATE_EMAIL)) { if (user_get_alias_details($login) !== false) { + // Block SOGo access if pending actions (2FA setup, password update) + if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) { + header("Location: /"); + exit; + } // register username in session $_SESSION[$session_var_user_allowed][] = $login; // set dual login @@ -94,7 +99,8 @@ elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HT filter_var($email, FILTER_VALIDATE_EMAIL) && is_array($_SESSION[$session_var_user_allowed]) && in_array($email, $_SESSION[$session_var_user_allowed]) && - !$_SESSION['pending_pw_update'] + !$_SESSION['pending_pw_update'] && + !$_SESSION['pending_tfa_setup'] ) { $username = $email; $password = file_get_contents("/etc/sogo-sso/sogo-sso.pass"); diff --git a/data/web/templates/base.twig b/data/web/templates/base.twig index 98fdd86e4..4290edaef 100644 --- a/data/web/templates/base.twig +++ b/data/web/templates/base.twig @@ -377,6 +377,112 @@ function recursiveBase64StrToArrayBuffer(obj) { }); {% endif %} + {% if pending_tfa_setup %} + var setupTFAModal = new bootstrap.Modal(document.getElementById("SetupTFAModal"), { + backdrop: 'static', + keyboard: false + }); + setupTFAModal.show(); + + // Load QR code for TOTP setup in SetupTFAModal + var setupTotpSecret = $('#setup-tfa-qr-img').data('totp-secret'); + if (setupTotpSecret) { + $.ajax({ + type: "GET", + url: "/inc/ajax/qr_gen.php?token=" + encodeURIComponent(setupTotpSecret), + success: function(data) { + $('#setup-tfa-qr-img').attr('src', data); + } + }); + } + + // WebAuthn registration for SetupTFAModal + $('#start_setup_webauthn_register').click(function() { + if (!window.fetch || !navigator.credentials || !navigator.credentials.create) { + window.alert('Browser not supported.'); + return; + } + var keyId = $('#setup_webauthn_reg_form input[name=key_id]').val(); + if (!keyId) { + $('#setup_webauthn_return_code').show().text('Please fill in the key ID first.'); + return; + } + window.fetch('/api/v1/get/webauthn-tfa-registration', {method: 'GET', cache: 'no-cache'}).then(function(response) { + return response.json(); + }).then(function(json) { + if (json.success === false) throw new Error(json.error || 'Registration failed'); + recursiveBase64StrToArrayBuffer(json); + return navigator.credentials.create(json); + }).then(function(cred) { + return { + id: cred.id, + rawId: arrayBufferToBase64(cred.rawId), + response: { + attestationObject: arrayBufferToBase64(cred.response.attestationObject), + clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON) + }, + type: cred.type + }; + }).then(function(credData) { + $('#setup_webauthn_register_data').val(JSON.stringify(credData)); + $('#setup_webauthn_reg_form input[name=set_tfa]').val('1'); + $('#setup_webauthn_reg_form').submit(); + }).catch(function(err) { + $('#setup_webauthn_return_code').show().text(err.message || 'Registration failed'); + }); + }); + {% endif %} + + {% if pending_pw_update_modal and not pending_tfa_setup and not pending_tfa_methods %} + var changePWModal = new bootstrap.Modal(document.getElementById("ChangePWModal"), { + backdrop: 'static', + keyboard: false + }); + changePWModal.show(); + + $('#changePWModalForm').on('submit', function(e) { + e.preventDefault(); + var newPw = $('#changePWNew').val(); + var newPw2 = $('#changePWNew2').val(); + var role = '{{ mailcow_cc_role }}'; + var username = '{{ mailcow_cc_username }}'; + + var url, attrPayload, itemsPayload; + if (role === 'admin') { + url = '/api/v1/edit/admin'; + attrPayload = {password: newPw, password2: newPw2}; + itemsPayload = [username]; + } else { + url = '/api/v1/edit/self'; + attrPayload = {user_new_pass: newPw, user_new_pass2: newPw2}; + itemsPayload = null; + } + + $('#changePWAlert').hide(); + $.ajax({ + type: 'POST', + url: url, + data: { + attr: JSON.stringify(attrPayload), + items: JSON.stringify(itemsPayload), + csrf_token: '{{ csrf_token }}' + }, + dataType: 'json', + success: function(data) { + if (data && data[0] && data[0].type === 'success') { + window.location.reload(); + } else { + var msg = (data && data[0] && data[0].msg) ? data[0].msg : 'Password change failed.'; + $('#changePWAlert').show().text(msg); + } + }, + error: function() { + $('#changePWAlert').show().text('Request failed. Please try again.'); + } + }); + }); + {% endif %} + // Validate FIDO2 $("#fido2-login").click(function(){ diff --git a/data/web/templates/edit/admin.twig b/data/web/templates/edit/admin.twig index e2c6f66ed..d9cf4aecb 100644 --- a/data/web/templates/edit/admin.twig +++ b/data/web/templates/edit/admin.twig @@ -39,6 +39,24 @@ +
    +
    +
    + + + {{ lang.tfa.force_tfa_info }} +
    +
    +
    +
    +
    +
    + + + {{ lang.edit.force_pw_update_info|format(ui_texts.main_name) }} +
    +
    +
    diff --git a/data/web/templates/edit/domainadmin.twig b/data/web/templates/edit/domainadmin.twig index 2c40faaa3..b3e0198c8 100644 --- a/data/web/templates/edit/domainadmin.twig +++ b/data/web/templates/edit/domainadmin.twig @@ -52,6 +52,24 @@
    +
    +
    +
    + + + {{ lang.tfa.force_tfa_info }} +
    +
    +
    +
    +
    +
    + + + {{ lang.edit.force_pw_update_info|format(ui_texts.main_name) }} +
    +
    +
    diff --git a/data/web/templates/edit/mailbox.twig b/data/web/templates/edit/mailbox.twig index abf0c45ba..34e869ce1 100644 --- a/data/web/templates/edit/mailbox.twig +++ b/data/web/templates/edit/mailbox.twig @@ -24,6 +24,7 @@
    +
    @@ -317,6 +318,14 @@
    +
    +
    +
    + + {{ lang.tfa.force_tfa_info }} +
    +
    +
    {% if not skip_sogo %}
    diff --git a/data/web/templates/mailbox/tab-mailboxes.twig b/data/web/templates/mailbox/tab-mailboxes.twig index fce7d6276..6a176cda2 100644 --- a/data/web/templates/mailbox/tab-mailboxes.twig +++ b/data/web/templates/mailbox/tab-mailboxes.twig @@ -58,6 +58,14 @@
  • {{ lang.mailbox.activate }}
  • {{ lang.mailbox.deactivate }}
  • +
  • + +
  • {{ lang.mailbox.activate }}
  • +
  • {{ lang.mailbox.deactivate }}
  • +
  • + +
  • {{ lang.mailbox.activate }}
  • +
  • {{ lang.mailbox.deactivate }}
  • {{ lang.mailbox.add_mailbox }}
    @@ -104,9 +112,18 @@
  • {{ lang.mailbox.activate }}
  • {{ lang.mailbox.deactivate }}
  • +
  • {{ lang.mailbox.activate }}
  • {{ lang.mailbox.deactivate }}
  • +
  • + +
  • {{ lang.mailbox.activate }}
  • +
  • {{ lang.mailbox.deactivate }}
  • +
  • + +
  • {{ lang.mailbox.activate }}
  • +
  • {{ lang.mailbox.deactivate }}
  • diff --git a/data/web/templates/modals/footer.twig b/data/web/templates/modals/footer.twig index 8ff112d5d..b39544a63 100644 --- a/data/web/templates/modals/footer.twig +++ b/data/web/templates/modals/footer.twig @@ -311,6 +311,135 @@
    {% endif %} +{% if pending_tfa_setup %} + +{% endif %} +{% if pending_pw_update_modal and not pending_tfa_methods %} + +{% endif %} {% if mailcow_cc_role == 'admin' %} +
    +
    +
    + + {{ lang.tfa.force_tfa_info }} +
    +
    +
    {% if not skip_sogo %}
    @@ -237,6 +246,7 @@ + @@ -394,6 +404,14 @@
    +
    +
    +
    + + {{ lang.tfa.force_tfa_info }} +
    +
    +
    {% if not skip_sogo %}
    diff --git a/data/web/user.php b/data/web/user.php index 7c34ba953..cadedf4a7 100644 --- a/data/web/user.php +++ b/data/web/user.php @@ -2,90 +2,78 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.user.inc.php'; -if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') { +/* +/ USER +*/ - /* - / USER - */ +// Protect route: user only +protect_route(['user']); - require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; - $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; - $username = $_SESSION['mailcow_cc_username']; - $mailboxdata = mailbox('get', 'mailbox_details', $username); - $pushover_data = pushover('get', $username); - $tfa_data = get_tfa(); - $fido2_data = fido2(array("action" => "get_friendly_names")); +require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php'; +$_SESSION['return_to'] = $_SERVER['REQUEST_URI']; +$username = $_SESSION['mailcow_cc_username']; +$mailboxdata = mailbox('get', 'mailbox_details', $username); +$pushover_data = pushover('get', $username); +$tfa_data = get_tfa(); +$fido2_data = fido2(array("action" => "get_friendly_names")); - $clientconfigstr = "host=" . urlencode($mailcow_hostname) . "&email=" . urlencode($username) . "&name=" . urlencode($mailboxdata['name']) . "&ui=" . urlencode(strtok($_SERVER['HTTP_HOST'], ':')) . "&port=" . urlencode($autodiscover_config['caldav']['port']); - if ($autodiscover_config['useEASforOutlook'] == 'yes') - $clientconfigstr .= "&outlookEAS=1"; - if (file_exists('thunderbird-plugins/version.csv')) { - $fh = fopen('thunderbird-plugins/version.csv', 'r'); - if ($fh) { - while (($row = fgetcsv($fh, 1000, ';')) !== FALSE) { - if ($row[0] == 'sogo-connector@inverse.ca') { - $clientconfigstr .= "&connector=" . urlencode($row[1]); - } +$clientconfigstr = "host=" . urlencode($mailcow_hostname) . "&email=" . urlencode($username) . "&name=" . urlencode($mailboxdata['name']) . "&ui=" . urlencode(strtok($_SERVER['HTTP_HOST'], ':')) . "&port=" . urlencode($autodiscover_config['caldav']['port']); +if ($autodiscover_config['useEASforOutlook'] == 'yes') +$clientconfigstr .= "&outlookEAS=1"; +if (file_exists('thunderbird-plugins/version.csv')) { + $fh = fopen('thunderbird-plugins/version.csv', 'r'); + if ($fh) { + while (($row = fgetcsv($fh, 1000, ';')) !== FALSE) { + if ($row[0] == 'sogo-connector@inverse.ca') { + $clientconfigstr .= "&connector=" . urlencode($row[1]); } - fclose($fh); } + fclose($fh); } +} - // Get user information about aliases - $user_get_alias_details = user_get_alias_details($username); - $user_get_alias_details['direct_aliases'] = array_filter($user_get_alias_details['direct_aliases']); - $user_get_alias_details['shared_aliases'] = array_filter($user_get_alias_details['shared_aliases']); - $user_domains[] = mailbox('get', 'mailbox_details', $username)['domain']; - $user_alias_domains = $user_get_alias_details['alias_domains']; - if (!empty($user_alias_domains)) { - $user_domains = array_merge($user_domains, $user_alias_domains); - } +// Get user information about aliases +$user_get_alias_details = user_get_alias_details($username); +$user_get_alias_details['direct_aliases'] = array_filter($user_get_alias_details['direct_aliases']); +$user_get_alias_details['shared_aliases'] = array_filter($user_get_alias_details['shared_aliases']); +$user_domains[] = mailbox('get', 'mailbox_details', $username)['domain']; +$user_alias_domains = $user_get_alias_details['alias_domains']; +if (!empty($user_alias_domains)) { + $user_domains = array_merge($user_domains, $user_alias_domains); +} - // get number of app passwords - $number_of_app_passwords = 0; - foreach (app_passwd("get") as $app_password) - { - $app_password = app_passwd("details", $app_password['id']); - if ($app_password['active']) - { - $number_of_app_passwords++; - } - } +// get number of app passwords +$number_of_app_passwords = 0; +foreach (app_passwd("get") as $app_password) +{ + $app_password = app_passwd("details", $app_password['id']); + if ($app_password['active']) + { + $number_of_app_passwords++; + } +} - $template = 'user.twig'; - $template_data = [ - 'acl' => $_SESSION['acl'], - 'acl_json' => json_encode($_SESSION['acl']), - 'user_spam_score' => mailbox('get', 'spam_score', $username), - 'tfa_data' => $tfa_data, - 'tfa_id' => @$_SESSION['tfa_id'], - 'fido2_data' => $fido2_data, - 'mailboxdata' => $mailboxdata, - 'clientconfigstr' => $clientconfigstr, - 'user_get_alias_details' => $user_get_alias_details, - 'get_tagging_options' => mailbox('get', 'delimiter_action', $username), - 'get_tls_policy' => mailbox('get', 'tls_policy', $username), - 'quarantine_notification' => mailbox('get', 'quarantine_notification', $username), - 'quarantine_category' => mailbox('get', 'quarantine_category', $username), - 'user_domains' => $user_domains, - 'pushover_data' => $pushover_data, - 'lang_user' => json_encode($lang['user']), - 'number_of_app_passwords' => $number_of_app_passwords, - 'lang_datatables' => json_encode($lang['datatables']), - ]; -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin') { - header('Location: /admin/dashboard'); - exit(); -} -elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') { - header('Location: /domainadmin/mailbox'); - exit(); -} -else { - header('Location: /'); - exit(); -} +$template = 'user.twig'; +$template_data = [ + 'acl' => $_SESSION['acl'], + 'acl_json' => json_encode($_SESSION['acl']), + 'user_spam_score' => mailbox('get', 'spam_score', $username), + 'tfa_data' => $tfa_data, + 'tfa_id' => @$_SESSION['tfa_id'], + 'fido2_data' => $fido2_data, + 'mailboxdata' => $mailboxdata, + 'clientconfigstr' => $clientconfigstr, + 'user_get_alias_details' => $user_get_alias_details, + 'get_tagging_options' => mailbox('get', 'delimiter_action', $username), + 'get_tls_policy' => mailbox('get', 'tls_policy', $username), + 'quarantine_notification' => mailbox('get', 'quarantine_notification', $username), + 'quarantine_category' => mailbox('get', 'quarantine_category', $username), + 'user_domains' => $user_domains, + 'pushover_data' => $pushover_data, + 'lang_user' => json_encode($lang['user']), + 'number_of_app_passwords' => $number_of_app_passwords, + 'lang_datatables' => json_encode($lang['datatables']), +]; $js_minifier->add('/web/js/site/user.js'); $js_minifier->add('/web/js/site/pwgen.js'); diff --git a/docker-compose.yml b/docker-compose.yml index 75cca872d..fbf1125fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: unbound-mailcow: - image: ghcr.io/mailcow/unbound:1.24 + image: ghcr.io/mailcow/unbound:1.25 environment: - TZ=${TZ} - SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n} @@ -84,7 +84,7 @@ services: - clamd rspamd-mailcow: - image: ghcr.io/mailcow/rspamd:3.14.2 + image: ghcr.io/mailcow/rspamd:3.14.3-1 stop_grace_period: 30s depends_on: - dovecot-mailcow @@ -117,7 +117,7 @@ services: - rspamd php-fpm-mailcow: - image: ghcr.io/mailcow/phpfpm:8.2.29-1 + image: ghcr.io/mailcow/phpfpm:8.2.29-2 command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" depends_on: - redis-mailcow @@ -200,7 +200,7 @@ services: - phpfpm sogo-mailcow: - image: ghcr.io/mailcow/sogo:5.12.4-1 + image: ghcr.io/mailcow/sogo:5.12.5-1 environment: - DBNAME=${DBNAME} - DBUSER=${DBUSER} @@ -225,12 +225,12 @@ services: - ./data/hooks/sogo:/hooks:Z - ./data/conf/sogo/:/etc/sogo/:z - ./data/web/inc/init_db.inc.php:/init_db.inc.php:z - - ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z - - ./data/conf/sogo/custom-shortlogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-compact.svg:z - - ./data/conf/sogo/custom-fulllogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg:z - - ./data/conf/sogo/custom-fulllogo.png:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-logo.png:z - - ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z - - ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z + - ./data/conf/sogo/custom-favicon.ico:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z + - ./data/conf/sogo/custom-shortlogo.svg:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo-compact.svg:z + - ./data/conf/sogo/custom-fulllogo.svg:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg:z + - ./data/conf/sogo/custom-fulllogo.png:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo-logo.png:z + - ./data/conf/sogo/custom-theme.js:/usr/local/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z + - ./data/conf/sogo/custom-sogo.js:/usr/local/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z - mysql-socket-vol-1:/var/run/mysqld/:z - sogo-web-vol-1:/sogo_web - sogo-userdata-backup-vol-1:/sogo_backup @@ -339,7 +339,7 @@ services: - dovecot postfix-mailcow: - image: ghcr.io/mailcow/postfix:3.7.11-1 + image: ghcr.io/mailcow/postfix:3.7.11-2 depends_on: mysql-mailcow: condition: service_started @@ -382,7 +382,7 @@ services: - postfix postfix-tlspol-mailcow: - image: ghcr.io/mailcow/postfix-tlspol:1.8.22 + image: ghcr.io/mailcow/postfix-tlspol:1.8.23 depends_on: unbound-mailcow: condition: service_healthy @@ -419,7 +419,7 @@ services: - php-fpm-mailcow - sogo-mailcow - rspamd-mailcow - image: ghcr.io/mailcow/nginx:1.05 + image: ghcr.io/mailcow/nginx:1.06 dns: - ${IPV4_NETWORK:-172.22.1}.254 environment: @@ -449,7 +449,7 @@ services: - ./data/web/inc/functions.inc.php:/mailcowauth/functions.inc.php:z - ./data/web/inc/functions.auth.inc.php:/mailcowauth/functions.auth.inc.php:z - ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z - - sogo-web-vol-1:/usr/lib/GNUstep/SOGo/ + - sogo-web-vol-1:/usr/local/lib/GNUstep/SOGo/ ports: - "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}" - "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}" @@ -465,7 +465,7 @@ services: condition: service_started unbound-mailcow: condition: service_healthy - image: ghcr.io/mailcow/acme:1.95 + image: ghcr.io/mailcow/acme:1.96 dns: - ${IPV4_NETWORK:-172.22.1}.254 environment: @@ -490,11 +490,15 @@ services: - REDISPASS=${REDISPASS} - SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n} - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} + - ACME_DNS_CHALLENGE=${ACME_DNS_CHALLENGE:-n} + - ACME_DNS_PROVIDER=${ACME_DNS_PROVIDER:-dns_xxx} + - ACME_ACCOUNT_EMAIL=${ACME_ACCOUNT_EMAIL:-me@example.com} volumes: - ./data/web/.well-known/acme-challenge:/var/www/acme:z - ./data/assets/ssl:/var/lib/acme/:z - ./data/assets/ssl-example:/var/lib/ssl-example/:ro,Z - mysql-socket-vol-1:/var/run/mysqld/:z + - ./data/conf/acme:/etc/acme/:z restart: always networks: mailcow-network: @@ -502,7 +506,7 @@ services: - acme netfilter-mailcow: - image: ghcr.io/mailcow/netfilter:1.63 + image: ghcr.io/mailcow/netfilter:1.64 stop_grace_period: 30s restart: always privileged: true @@ -522,7 +526,7 @@ services: - /lib/modules:/lib/modules:ro watchdog-mailcow: - image: ghcr.io/mailcow/watchdog:2.09 + image: ghcr.io/mailcow/watchdog:2.10 dns: - ${IPV4_NETWORK:-172.22.1}.254 tmpfs: @@ -597,7 +601,7 @@ services: - watchdog dockerapi-mailcow: - image: ghcr.io/mailcow/dockerapi:2.11 + image: ghcr.io/mailcow/dockerapi:2.12 security_opt: - label=disable restart: always diff --git a/generate_config.sh b/generate_config.sh index de15eac17..420854eac 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -293,6 +293,21 @@ ADDITIONAL_SERVER_NAMES= # Skip running ACME (acme-mailcow, Let's Encrypt certs) - y/n SKIP_LETS_ENCRYPT=n +# Enable DNS-01 challenge for ACME (acme-mailcow) - y/n +# This requires you to set ACME_DNS_PROVIDER and ACME_ACCOUNT_EMAIL below +ACME_DNS_CHALLENGE=n +ACME_DNS_PROVIDER=dns_xxx +ACME_ACCOUNT_EMAIL=me@example.com +# You will need to pass provider-specific environment variables to the acme-mailcow container. +# See the dns-01 provider documentation for more information. +# for example for Azure DNS: +#AZUREDNS_SUBSCRIPTIONID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +#AZUREDNS_TENANTID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +#AZUREDNS_APPID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +#AZUREDNS_CLIENTSECRET=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +#AZUREDNS_RESOURCEGROUP="your-resource-group" +#AZUREDNS_ZONE="your-zone-name" + # Create separate certificates for all domains - y/n # this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames # see https://doc.dovecot.org/admin_manual/ssl/sni_support diff --git a/helper-scripts/dev_tests/view_autodiscover.sh b/helper-scripts/dev_tests/view_autodiscover.sh new file mode 100755 index 000000000..a203370de --- /dev/null +++ b/helper-scripts/dev_tests/view_autodiscover.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# Autodiscover XML Debug Script +# Usage: ./view_autodiscover.sh [OPTIONS] [email@domain.com] + +# Function to display help +show_help() { + cat << EOF +Autodiscover XML Debug Script + +Usage: $0 [OPTIONS] [email@domain.com] + +OPTIONS: + -h, --help Show this help message + -d, --domain FQDN Override autodiscover domain (default: autodiscover.DOMAIN) + Example: -d mail.example.com + +EXAMPLES: + $0 user@example.com + Test autodiscover for user@example.com using autodiscover.example.com + + $0 -d mail.example.com user@example.com + Test autodiscover for user@example.com using mail.example.com + + $0 -d localhost:8443 user@example.com + Test autodiscover using localhost:8443 (useful for development) + +EOF + exit 0 +} + +# Initialize variables +EMAIL="" +DOMAIN_OVERRIDE="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + ;; + -d|--domain) + DOMAIN_OVERRIDE="$2" + shift 2 + ;; + -*) + echo "Error: Unknown option $1" + echo "Use -h or --help for usage information" + exit 1 + ;; + *) + EMAIL="$1" + shift + ;; + esac +done + +# Check if xmllint is available +if ! command -v xmllint &> /dev/null; then + echo "WARNING: xmllint not found. Output will not be formatted." + echo "Install with: apt install libxml2-utils (Debian/Ubuntu) or yum install libxml2 (CentOS/RHEL)" + echo "" + USE_XMLLINT=false +else + USE_XMLLINT=true +fi + +# Get email address from user input if not provided +if [ -z "$EMAIL" ]; then + read -p "Enter email address to test: " EMAIL +fi + +# Validate email format +if [[ ! "$EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + echo "Error: Invalid email address format" + exit 1 +fi + +# Extract domain from email +EMAIL_DOMAIN="${EMAIL#*@}" + +# Determine autodiscover URL +if [ -n "$DOMAIN_OVERRIDE" ]; then + AUTODISCOVER_URL="https://${DOMAIN_OVERRIDE}/Autodiscover/Autodiscover.xml" + echo "Testing Autodiscover for: $EMAIL" + echo "Override domain: $DOMAIN_OVERRIDE" +else + AUTODISCOVER_URL="https://autodiscover.${EMAIL_DOMAIN}/Autodiscover/Autodiscover.xml" + echo "Testing Autodiscover for: $EMAIL" +fi + +echo "URL: $AUTODISCOVER_URL" +echo "============================================" +echo "" + +# Make the request +RESPONSE=$(curl -k -s -X POST "$AUTODISCOVER_URL" \ + -H "Content-Type: text/xml" \ + -d " + + + $EMAIL + http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a + +") + +# Check if response is empty +if [ -z "$RESPONSE" ]; then + echo "Error: No response received from server" + exit 1 +fi + +# Format and display output +if [ "$USE_XMLLINT" = true ]; then + echo "$RESPONSE" | xmllint --format - 2>&1 +else + echo "$RESPONSE" +fi + +echo "" +echo "============================================" +echo "Response length: ${#RESPONSE} bytes" diff --git a/helper-scripts/mailcow-reset-admin.sh b/helper-scripts/mailcow-reset-admin.sh index ea8a4a43c..823391f85 100755 --- a/helper-scripts/mailcow-reset-admin.sh +++ b/helper-scripts/mailcow-reset-admin.sh @@ -7,6 +7,12 @@ if [[ -z ${DBUSER} ]] || [[ -z ${DBPASS} ]] || [[ -z ${DBNAME} ]]; then exit 1 fi +SKIP_CONFIRM=false +if [[ "${1:-}" == "-y" || "${1:-}" == "--yes" ]]; then + SKIP_CONFIRM=true + shift # prevent $1 from bleeding into head -c${1:-16} below +fi + echo -n "Checking MySQL service... " if [[ -z $(docker ps -qf name=mysql-mailcow) ]]; then echo "failed" @@ -15,8 +21,12 @@ if [[ -z $(docker ps -qf name=mysql-mailcow) ]]; then fi echo "OK" -read -r -p "Are you sure you want to reset the mailcow administrator account? [y/N] " response -response=${response,,} # tolower +if [[ "$SKIP_CONFIRM" == "true" ]]; then + response="yes" +else + read -r -p "Are you sure you want to reset the mailcow administrator account? [y/N] " response + response=${response,,} +fi if [[ "$response" =~ ^(yes|y)$ ]]; then echo -e "\nWorking, please wait..." random=$( /dev/null | head -c${1:-16}) diff --git a/update.sh b/update.sh index 74eb8ff2c..3e53d2134 100755 --- a/update.sh +++ b/update.sh @@ -19,30 +19,48 @@ if [ ! -f "${PWD}/mailcow.conf" ]; then fi BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)" +# Check for --dev flag early to skip _modules update +for arg in "$@"; do + if [[ "$arg" == "--dev" || "$arg" == "-d" ]]; then + echo -e "\e[32mRunning in Developer mode...\e[0m" + DEV=y + break + fi +done + MODULE_DIR="${SCRIPT_DIR}/_modules" -# Calculate hash before fetch -if [[ -d "${MODULE_DIR}" && -n "$(ls -A "${MODULE_DIR}" 2>/dev/null)" ]]; then - MODULES_HASH_BEFORE=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') + +if [ ! "$DEV" ]; then + # Calculate hash before fetch + if [[ -d "${MODULE_DIR}" && -n "$(ls -A "${MODULE_DIR}" 2>/dev/null)" ]]; then + MODULES_HASH_BEFORE=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') + else + MODULES_HASH_BEFORE="EMPTY" + fi + + echo -e "\e[33mFetching latest _modules from origin/${BRANCH}…\e[0m" + git fetch origin "${BRANCH}" + git checkout "origin/${BRANCH}" -- _modules + + if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then + echo -e "\e[31mError: _modules is still missing or empty after fetch!\e[0m" + exit 2 + fi + + # Calculate hash after fetch + MODULES_HASH_AFTER=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') + + # Check if modules changed + if [[ "${MODULES_HASH_BEFORE}" != "${MODULES_HASH_AFTER}" ]]; then + echo -e "\e[33m_modules have been updated. Please restart the update script.\e[0m" + exit 2 + fi else - MODULES_HASH_BEFORE="EMPTY" -fi - -echo -e "\e[33mFetching latest _modules from origin/${BRANCH}…\e[0m" -git fetch origin "${BRANCH}" -git checkout "origin/${BRANCH}" -- _modules - -if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then - echo -e "\e[31mError: _modules is still missing or empty after fetch!\e[0m" - exit 2 -fi - -# Calculate hash after fetch -MODULES_HASH_AFTER=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') - -# Check if modules changed -if [[ "${MODULES_HASH_BEFORE}" != "${MODULES_HASH_AFTER}" ]]; then - echo -e "\e[33m_modules have been updated. Please restart the update script.\e[0m" - exit 2 + echo -e "\e[33mDeveloper mode: Skipping _modules update from git\e[0m" + if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then + echo -e "\e[31mError: _modules directory is missing or empty!\e[0m" + exit 2 + fi fi source _modules/scripts/core.sh @@ -151,8 +169,7 @@ while (($#)); do FORCE=y ;; -d|--dev) - echo -e "\e[32mRunning in Developer mode...\e[0m" - DEV=y + # Already handled at the top of the script before _modules update ;; --legacy) CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"