diff --git a/.env b/.env
index 0c8c7f5..90ba3b4 100644
--- a/.env
+++ b/.env
@@ -3,3 +3,6 @@ PM_APP_URL=http://localhost
PM_APP_PORT=8080
PM_BROADCASTER_PORT=6004
PM_DOCKER_SOCK=/var/run/docker.sock
+
+CI_PROJECT=processmaker
+CI_PACKAGE_BRANCH=updates-for-ci
\ No newline at end of file
diff --git a/.env-cicd b/.env-cicd
new file mode 100644
index 0000000..0e8e277
--- /dev/null
+++ b/.env-cicd
@@ -0,0 +1,23 @@
+PM_VERSION=local
+CODEBASE_PATH=/code/pm4
+PACKAGES_PATH=/code/pm4-packages
+PM_CI=true
+PM_APP_URL=http://localhost
+PM_APP_PORT=8080
+PM_BROADCASTER_PORT=6004
+PROCESSMAKER_SCRIPTS_DOCKER=/usr/local/bin/docker
+LARAVEL_ECHO_SERVER_AUTH_HOST=http://localhost
+SESSION_SECURE_COOKIE=false
+CACHE_DRIVER=array
+SESSION_DOMAIN=null
+GOOGLE_API_TOKEN=test
+SLACK_OAUTH_ACCESS_TOKEN=test
+DB_HOSTNAME=mysql
+DB_PORT=3306
+DB_USERNAME=root
+DB_PASSWORD=password
+REDIS_HOST=redis
+TELESCOPE_ENABLED=false
+BUILD_JAVASCRIPT=true
+
+# COMPOSER_MIRROR_PATH_REPOS=1
diff --git a/.gitignore b/.gitignore
index 6a01034..080fa4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
-bind-mount/composer/cache
-bind-mount/npm
\ No newline at end of file
+pmrepo
+cache
+.DS_Store
diff --git a/Dockerfile.base b/Dockerfile.base
index cfe43b7..a573868 100644
--- a/Dockerfile.base
+++ b/Dockerfile.base
@@ -5,13 +5,13 @@ ENV TZ="America/Los_Angeles"
RUN apt update
# In ubuntu 20.04, installing php without specifying a version installs 7.4 :)
-RUN apt install -y php php-cli php-fpm php-json php-common php-mysql php-zip php-gd php-mbstring php-curl php-xml php-pear php-bcmath php-imagick php-dom php-sqlite3 \
-nginx vim curl unzip wget supervisor cron mysql-client build-essential
+RUN apt install -y php php-cli php-fpm php-json php-common php-mysql php-zip php-gd php-mbstring php-curl php-xml php-pear php-bcmath php-imagick php-dom php-sqlite3 php-imap \
+nginx vim curl git unzip wget supervisor cron mysql-client build-essential
#
# node
#
-RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
+RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
RUN apt -y install nodejs
#
@@ -51,7 +51,16 @@ COPY build-files/nginx.conf /etc/nginx/nginx.conf
#
COPY build-files/services.conf /etc/supervisor/conf.d/services.conf
+#
+# laravel echo server
+#
+RUN mkdir -p /code
+COPY build-files/laravel-echo-server.json /code
+
+# populate $_ENV with environment variables
+RUN echo "variables_order = \"EGPCS\"" > /etc/php/7.4/cli/conf.d/30-env.ini
+
+# Use the correct version of mysqldump
+COPY --from=mysql:5.7 /usr/bin/mysqldump /usr/bin/mysqldump
-RUN mkdir -p /code/pm4
-WORKDIR /code/pm4
EXPOSE 80 443 6001
\ No newline at end of file
diff --git a/Dockerfile.cicd b/Dockerfile.cicd
new file mode 100644
index 0000000..b4e7ee5
--- /dev/null
+++ b/Dockerfile.cicd
@@ -0,0 +1,15 @@
+ARG BASE_VERSION
+FROM processmaker/pm4-base:${BASE_VERSION}
+
+RUN npm config set cache /cache/npm --global
+ENV COMPOSER_HOME=/cache/composer
+
+COPY ./pm4-tools /code/pm4-tools
+WORKDIR /code/pm4-tools
+RUN composer install --no-interaction
+
+WORKDIR /code
+COPY build-files/cicd-build-run.sh .
+COPY build-files/cicd-run-app.sh .
+COPY build-files/cicd-run-phpunit.sh .
+COPY packages-to-install.php .
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile.core-app
similarity index 89%
rename from Dockerfile
rename to Dockerfile.core-app
index 999dac4..61b1c82 100644
--- a/Dockerfile
+++ b/Dockerfile.core-app
@@ -13,4 +13,4 @@ RUN npm install --unsafe-perm=true && npm run dev
COPY build-files/laravel-echo-server.json .
COPY build-files/init.sh .
-CMD bash init.sh && supervisord --nodaemon
+CMD bash core-build-run.sh && supervisord --nodaemon
\ No newline at end of file
diff --git a/Dockerfile.php81.base b/Dockerfile.php81.base
new file mode 100644
index 0000000..e0ac28e
--- /dev/null
+++ b/Dockerfile.php81.base
@@ -0,0 +1,77 @@
+FROM ubuntu:20.04
+ENV DEBIAN_FRONTEND=noninteractive
+ENV TZ="America/Los_Angeles"
+
+RUN apt update
+RUN apt install -y software-properties-common
+RUN add-apt-repository ppa:ondrej/php
+
+RUN apt install -y php8.1 php8.1-cli php8.1-fpm php8.1-common
+RUN apt install -y php8.1-mysql php8.1-zip php8.1-gd php8.1-mbstring php8.1-curl php8.1-xml php8.1-bcmath php8.1-imagick php8.1-dom php8.1-sqlite3 php8.1-imap
+RUN apt install -y nginx vim curl git unzip wget supervisor cron mysql-client build-essential
+
+#
+# node
+#
+# RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
+# RUN apt -y install nodejs
+
+ENV NVM_DIR /root/.nvm
+ENV NODE_VERSION 16.18.1
+RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \
+ . "$NVM_DIR/nvm.sh" && \
+ nvm install $NODE_VERSION && \
+ nvm alias default $NODE_VERSION && \
+ nvm use default
+ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules
+ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
+
+RUN npm install -g npm@8.19
+
+#
+# install composer
+#
+RUN wget -O composer-setup.php https://getcomposer.org/installer
+RUN php composer-setup.php --install-dir=/usr/local/bin --filename=composer
+RUN composer self-update
+
+#
+# cron
+#
+COPY build-files/laravel-cron /etc/cron.d/laravel-cron
+RUN chmod 0644 /etc/cron.d/laravel-cron && crontab /etc/cron.d/laravel-cron
+
+#
+# docker client
+#
+ENV DOCKERVERSION=20.10.5
+RUN curl -fsSLO https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKERVERSION}.tgz \
+ && tar xzvf docker-${DOCKERVERSION}.tgz --strip 1 \
+ -C /usr/local/bin docker/docker \
+ && rm docker-${DOCKERVERSION}.tgz
+
+#
+# configure php-fpm to run as root
+#
+RUN sed -i 's/www-data/root/g' /etc/php/8.1/fpm/pool.d/www.conf
+
+#
+# nginx
+#
+COPY build-files/nginx.conf /etc/nginx/nginx.conf
+
+#
+# supervisord
+#
+COPY build-files/services.conf /etc/supervisor/conf.d/services.conf
+
+#
+# laravel echo server
+#
+RUN mkdir -p /code
+COPY build-files/laravel-echo-server.json /code
+
+# populate $_ENV with environment variables
+RUN echo "variables_order = \"EGPCS\"" > /etc/php/8.1/cli/conf.d/30-env.ini
+
+EXPOSE 80 443 6001
\ No newline at end of file
diff --git a/README.md b/README.md
index ce46120..3ac3a97 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,37 @@ This allows for smaller images and better performance than using dind (docker in
See [this post](http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/) for more info.
The host socket file is usually at /var/run/docker.sock but can be changed in the .env file
+# ProcessMaker CI/CD Build
+This repository includes our setup for internal CI/CD builds run by Github Actions. This includes all enterprise packages.
+## Running locally
+1. Create a github personal access token with access to all enterprise packages and set it as an environment variable called GITHUB_TOKEN
+ ```
+ export GITHUB_TOKEN=ghp_........
+ ```
+2. Source set_context_example.sh to set an example GITHUB_CONTEXT variable with the PR body from pr_body_example.txt
+ ```
+ source set_context_example.sh
+ ```
+3. (Optional) Build the base image (otherwise it will get pulled from dockerhub)
+ ```
+ docker-compose build base
+ ```
+4. Build the local CI/CD image
+ ```
+ docker-compose build cicd
+ ```
+5. Run the image to build the app and execute phpunit tests
+ ```
+ docker-compose run cicd
+ ```
+6. To debug, commit the container to an image and run bash. To find the container ID run `docker ps -a` and look for the name pm4core-docker_builder_run...
+ ```
+ docker commit {container id} debug:latest
+ docker run --rm -it debug bash
+ ```
+ You can then CD into /code/pm4 and run individual phpunit tests
+
+
## Todo
### Automated builds pushed to dockerhub
diff --git a/build-docker-image.sh b/build-docker-image.sh
new file mode 100755
index 0000000..4e102ce
--- /dev/null
+++ b/build-docker-image.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+docker commit $(./container.sh) processmaker/pm4app:"$IMAGE_TAG"
+echo "$DOCKERHUB_TOKEN" | docker login -u processmaker --password-stdin
+docker push processmaker/pm4app:"$IMAGE_TAG"
diff --git a/build-files/cicd-build-run.sh b/build-files/cicd-build-run.sh
new file mode 100644
index 0000000..1d932cc
--- /dev/null
+++ b/build-files/cicd-build-run.sh
@@ -0,0 +1,19 @@
+set -ex
+
+while ! mysqladmin ping -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT --silent; do
+ echo "Waiting for mysql"
+ sleep 1
+done
+
+composer config --global github-oauth.github.com $GITHUB_TOKEN
+
+/code/pm4-tools/pm build-ci
+
+mv /code/laravel-echo-server.json /code/pm4/
+
+/code/pm4-tools/pm build-javascript-ci
+
+DB_DATABASE=processmaker \
+ /code/pm4-tools/pm install-ci
+
+cd /code/pm4
diff --git a/build-files/cicd-run-app.sh b/build-files/cicd-run-app.sh
new file mode 100644
index 0000000..1bb8bc7
--- /dev/null
+++ b/build-files/cicd-run-app.sh
@@ -0,0 +1,15 @@
+set -ex
+
+while ! mysqladmin ping -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT --silent; do
+ echo "Waiting for mysql"
+ sleep 1
+done
+
+mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "DROP DATABASE IF EXISTS $DB_DATABASE;"
+mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "CREATE DATABASE $DB_DATABASE;"
+mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT $DB_DATABASE < database.sql
+
+cd /code/pm4
+PM_CI=false php artisan docker-executor-php:install
+
+supervisord --nodaemon
\ No newline at end of file
diff --git a/build-files/cicd-run-phpunit.sh b/build-files/cicd-run-phpunit.sh
new file mode 100644
index 0000000..07ee10e
--- /dev/null
+++ b/build-files/cicd-run-phpunit.sh
@@ -0,0 +1,14 @@
+set -ex
+
+while ! mysqladmin ping -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT --silent; do
+ echo "Waiting for mysql"
+ sleep 1
+done
+
+mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "DROP DATABASE IF EXISTS test;"
+mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "CREATE DATABASE test;"
+# mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT test < database.sql
+
+cd /code/pm4
+# vendor/bin/paratest -p $PARALLEL_TEST_PROCESSES
+vendor/bin/phpunit --testdox
\ No newline at end of file
diff --git a/build-files/init.sh b/build-files/core-build-run.sh
similarity index 74%
rename from build-files/init.sh
rename to build-files/core-build-run.sh
index 4c6b682..50c000a 100644
--- a/build-files/init.sh
+++ b/build-files/core-build-run.sh
@@ -1,18 +1,5 @@
set -ex
-# # Wait for mysql to bec available
-# while ! mysqladmin ping -u pm -ppass -h mysql --silent; do
-# echo "Waiting for mysql"
-# sleep 1
-# done
-
-# # Check if database has been initialized
-# NUM_TABLES=$(mysql -u pm -p'pass' -h mysql -N -B -e 'show tables;' processmaker | wc -l)
-
-# if [ $NUM_TABLES -eq 0 ]; then
-# mysql -u pm -p'pass' -h mysql processmaker < mysqldump.sql
-# fi
-
if [ ! -f ".env" ]; then
while ! mysqladmin ping -u pm -ppass -h mysql --silent; do
@@ -52,4 +39,4 @@ if [ ! -f ".env" ]; then
echo "PROCESSMAKER_SCRIPTS_DOCKER_MODE=copying" >> .env
echo "LARAVEL_ECHO_SERVER_AUTH_HOST=http://localhost" >> .env
echo "SESSION_SECURE_COOKIE=false" >> .env
-fi
+fi
\ No newline at end of file
diff --git a/build-files/services.conf b/build-files/services.conf
index 5445350..370770f 100644
--- a/build-files/services.conf
+++ b/build-files/services.conf
@@ -15,6 +15,7 @@ stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
+autorestart=true
[program:laravel-echo-server]
directory=/code/pm4
diff --git a/container.sh b/container.sh
new file mode 100755
index 0000000..074d1e1
--- /dev/null
+++ b/container.sh
@@ -0,0 +1 @@
+docker ps -aqlf "name=cicd_run"
diff --git a/deploy-to-qa.sh b/deploy-to-qa.sh
new file mode 100644
index 0000000..47bbc21
--- /dev/null
+++ b/deploy-to-qa.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+git clone https://github.com/ProcessMaker/pm4core-docker.git --branch v2 --depth 1
+
+sed -i "s#\${CI_PROJECT}#$CI_PROJECT#g" pm4core-docker/docker-compose.yml
+sed -i "s#\${CI_PACKAGE_BRANCH}#$CI_PACKAGE_BRANCH#g" pm4core-docker/docker-compose.yml
+sed -i "s#\${PM_APP_PORT}#$PM_APP_PORT#g" pm4core-docker/docker-compose.yml
+sed -i "s#\${PM_BROADCASTER_PORT}#$PM_BROADCASTER_PORT#g" pm4core-docker/docker-compose.yml
+sed -i "s#\${PM_DOCKER_SOCK}#/var/run/docker.sock#g" pm4core-docker/docker-compose.yml
+sed -i "s#\${IMAGE_TAG}#$IMAGE_TAG#g" pm4core-docker/docker-compose.yml
+
+ssh ssh-qapackages-pm4 " sudo rm -rf /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH/*"
+ssh ssh-qapackages-pm4 " sudo mkdir -p /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH"
+ssh ssh-qapackages-pm4 " sudo chmod 777 /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH"
+scp -r -o StrictHostKeyChecking=no -o ProxyCommand='ssh -o StrictHostKeyChecking=no buildbot@buildbot.processmaker.net -A -W %h:%p' pm4core-docker buildbot@qa-pm4packages:/home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH/
+ssh ssh-qapackages-pm4 " sudo docker login -u=processmaker -p=$DOCKERHUB_TOKEN"
+ssh ssh-qapackages-pm4 " sudo docker-compose -p $CI_PROJECT-$CI_PACKAGE_BRANCH -f /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH/pm4core-docker/docker-compose.yml up web >/dev/null 2>&1 &"
diff --git a/docker-compose.yml b/docker-compose.yml
index 8127f6d..f9449be 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,35 +1,101 @@
version: "3.8"
services:
+ cicd:
+ image: cicd:latest
+ build:
+ context: .
+ dockerfile: Dockerfile.cicd
+ args:
+ - BASE_VERSION=${BASE_VERSION:-2.1.7}
+ command: sh cicd-build-run.sh
+ environment:
+ - GITHUB_TOKEN
+ - CI_PR_BODY
+ - CI_PROJECT
+ - CI_PACKAGE_BRANCH
+ - BASE_VERSION
+ env_file:
+ - .env-cicd
+ links:
+ - redis
+ - mysql
+ depends_on:
+ - mysql
+ - redis
+ volumes:
+ - ./cache:/cache
+
+ base-old:
+ image: processmaker/pm4-base:2.0.0
+ build:
+ context: .
+ dockerfile: Dockerfile.base
+
+ base-php81:
+ image: processmaker/pm4-base:${BASE_VERSION:-2.1.7}
+ build:
+ context: .
+ dockerfile: Dockerfile.php81.base
+
+ redis:
+ image: redis
+ volumes:
+ - redis:/data
+
+ debug:
+ image: processmaker/pm4app:${IMAGE_TAG}
+ command: bash
+ env_file:
+ - .env-cicd
+ links:
+ - mysql
+ depends_on:
+ - mysql
+
+ phpunit:
+ image: processmaker/pm4app:${IMAGE_TAG}
+ command: sh cicd-run-phpunit.sh
+ env_file:
+ - .env-cicd
+ links:
+ - mysql
+ - redis
+ depends_on:
+ - mysql
+ - redis
+
web:
- image: processmaker/pm4-core:${PM_VERSION}
+ image: processmaker/pm4app:${IMAGE_TAG}
+ command: sh cicd-run-app.sh
ports:
- ${PM_APP_PORT}:80
- ${PM_BROADCASTER_PORT}:6001
+ - 9001:9001
environment:
- PM_APP_URL
- PM_APP_PORT
- PM_BROADCASTER_PORT
+ - DB_DATABASE=processmaker
+ - PM_CI=false
+ - CACHE_DRIVER=redis
volumes:
- ${PM_DOCKER_SOCK}:/var/run/docker.sock
- storage:/code/pm4/storage
links:
- redis
- - mysql
+ - mysql
depends_on:
- mysql
- - redis
- redis:
- image: redis
+ - redis
+
mysql:
- image: mysql:5.7
+ image: mysql:8
restart: always
environment:
- MYSQL_ROOT_PASSWORD: password
+ MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: processmaker
- MYSQL_USER: pm
- MYSQL_PASSWORD: pass
- volumes:
- - database:/var/lib/mysql
+ tmpfs: /var/lib/mysql
+
volumes:
- database:
- storage:
\ No newline at end of file
+ redis:
+ storage:
diff --git a/packages-to-install.php b/packages-to-install.php
new file mode 100644
index 0000000..842b939
--- /dev/null
+++ b/packages-to-install.php
@@ -0,0 +1,19 @@
+ [ 'tests' => false ],
+ 'package-comments' => [ 'tests' => false ],
+ 'package-googleplaces' => [ 'tests' => false ],
+ 'package-process-documenter' => [ 'tests' => false ],
+ 'package-process-optimization' => [ 'tests' => false ],
+ 'package-sentry' => [ 'tests' => false ],
+ 'package-signature' => [ 'tests' => false ],
+ 'packages' => [ 'tests' => false ],
+
+ 'package-versions' => [ 'tests' => true ],
+ 'package-translations' => [ 'tests' => true ],
+
+ // Add new packages here or enable tests only after unit test fixes are merged into develop
+ 'connector-send-email' => [ 'tests' => false ],
+ 'package-collections' => [ 'tests' => false ],
+ 'package-savedsearch' => [ 'tests' => false ],
+];
diff --git a/pm4-tools/cli/ProcessMaker/CommandLine.php b/pm4-tools/cli/ProcessMaker/CommandLine.php
new file mode 100644
index 0000000..a092229
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/CommandLine.php
@@ -0,0 +1,196 @@
+time = microtime(true);
+ }
+
+ /**
+ * Returns the timing (in seconds) since the
+ * CommandLine class was instantiated
+ *
+ * @return string
+ */
+ public function timing(): string
+ {
+ $seconds = round(abs($this->time - microtime(true)), 2);
+ $minutes = round($seconds / 60, 2);
+ $hours = round($minutes / 60, 2);
+
+ if ($hours >= 1.00) {
+ return "$hours hours";
+ }
+
+ if ($minutes >= 1.00) {
+ return "$minutes minutes";
+ }
+
+ return "$seconds seconds";
+ }
+
+ /**
+ * Simple global function to run commands.
+ *
+ * @param string $command
+ *
+ * @return void
+ */
+ public function quietly(string $command)
+ {
+ $this->runCommand($command.' > /dev/null 2>&1');
+ }
+
+ /**
+ * Simple global function to run commands.
+ *
+ * @param string $command
+ *
+ * @return void
+ */
+ public function quietlyAsUser(string $command)
+ {
+ $this->quietly('sudo -u "'.user().'" '.$command.' > /dev/null 2>&1');
+ }
+
+ public function transformCommandToRunAsUser(string $command, string $path = null): string
+ {
+ return ($path ? 'cd '.$path.' && ' : '').'sudo -u '.user().' '.$command;
+ }
+
+ /**
+ * Pass the command to the command line and display the output.
+ *
+ * @param string $command
+ *
+ * @return void
+ */
+ public function passthru(string $command)
+ {
+ passthru($command);
+ }
+
+ /**
+ * Create a ProgressBar bound to this class instance
+ *
+ * @param int $count
+ * @param string $type
+ */
+ public function createProgressBar(int $count, string $type = 'minimal'): void
+ {
+ $this->progress = new ProgressBar(new ConsoleOutput, $count);
+
+ ProgressBar::setFormatDefinition('message', '%message% (%percent%%)');
+ ProgressBar::setFormatDefinition('minimal', 'Progress: %percent%%');
+
+ $this->progress->setFormat($type);
+ $this->progress->setRedrawFrequency(25);
+ $this->progress->minSecondsBetweenRedraws(0.025);
+ $this->progress->maxSecondsBetweenRedraws(0.05);
+ }
+
+ /**
+ * @param int|null $count
+ *
+ * @return \Symfony\Component\Console\Helper\ProgressBar
+ */
+ public function getProgress(int $count = null): ProgressBar
+ {
+ if (!$this->progress instanceof ProgressBar) {
+ $this->createProgressBar($count);
+ }
+
+ return $this->progress;
+ }
+
+ /**
+ * Run the given command as the non-root user.
+ *
+ * @param string $command
+ * @param callable|null $onError
+ * @param string|null $workingDir
+ *
+ * @return string
+ */
+ public function run(string $command, callable $onError = null, string $workingDir = null): string
+ {
+ return $this->runCommand($command, $onError, $workingDir);
+ }
+
+ /**
+ * Run the given command.
+ *
+ * @param string $command
+ * @param callable|null $onError
+ * @param string|null $workingDir
+ *
+ * @return string
+ */
+ public function runAsUser(string $command, callable $onError = null, string $workingDir = null): string
+ {
+ return $this->runCommand('sudo -u "'.user().'" '.$command, $onError, $workingDir);
+ }
+
+ /**
+ * Run the given command.
+ *
+ * @param string $command
+ * @param callable|null $onError
+ * @param string|null $workingDir
+ *
+ * @return string
+ */
+ public function runCommand(string $command, callable $onError = null, string $workingDir = null): string
+ {
+ info("Running Command: $command in $workingDir");
+ $onError = $onError ? : function () {};
+
+ if (method_exists(Process::class, 'fromShellCommandline')) {
+ $process = Process::fromShellCommandline($command, null, $_ENV);
+ } else {
+ $process = new Process($command, null, $_ENV);
+ }
+
+ if ($workingDir) {
+ if (is_dir($workingDir) && !is_file($workingDir)) {
+ $process->setWorkingDirectory($workingDir);
+ }
+ }
+
+ $processOutput = '';
+
+ // $process->setTimeout(null)->run(function ($type, $line) use (&$processOutput) {
+ // $processOutput .= $line;
+ // });
+ $process->setTimeout(null);
+ $process->start();
+ foreach ($process as $type => $data) {
+ info($data);
+ $processOutput .= $data;
+ }
+
+ if ($process->getExitCode() > 0) {
+ $onError($process->getExitCode(), $processOutput);
+ }
+
+ return $processOutput;
+ }
+
+ public function mustRun($command, $path = null)
+ {
+ return $this->runCommand($command, function($code, $output) {
+ throw new \Exception($output);
+ }, $path);
+ }
+}
diff --git a/pm4-tools/cli/ProcessMaker/Composer.php b/pm4-tools/cli/ProcessMaker/Composer.php
new file mode 100644
index 0000000..08c1aae
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/Composer.php
@@ -0,0 +1,55 @@
+env('PACKAGES_PATH');
+ if (!$path) {
+ $path = Install::read()['packages_path'];
+ }
+ $path = $this->handleTrailingSlash($path, $package);
+ return $path;
+ }
+
+ public function codebasePath()
+ {
+ $path = $this->env('CODEBASE_PATH');
+ if (!$path) {
+ $path = Install::read()['codebase_path'];
+ }
+ return $path;
+ }
+
+ public function cachePath()
+ {
+ return $this->env('CACHE_PATH');
+ }
+
+ public function cacheManifestPath()
+ {
+ return $this->cachePath() . '/' . 'manifest.json';
+ }
+
+ public function codebaseEnv()
+ {
+ return Dotenv::createArrayBacked($this->codebasePath())->load();
+ }
+
+ public function env($var, $suppressError = false)
+ {
+ if (!isset($_ENV[$var])) {
+ if (!$suppressError) {
+ throw new \Exception("$var environment variable not found");
+ }
+ return "";
+ }
+ return $_ENV[$var];
+ }
+
+ public function handleTrailingSlash($path, $package)
+ {
+ if ($package) {
+ // if package is specified, add a trailing slash if it's missing
+ $path = rtrim($path, '/') . '/';
+ $path = $path . $package;
+ } else {
+ // otherwise, make sure there is no trailing slash
+ $path = rtrim($path, '/');
+ }
+ return $path;
+ }
+}
\ No newline at end of file
diff --git a/pm4-tools/cli/ProcessMaker/FileSystem.php b/pm4-tools/cli/ProcessMaker/FileSystem.php
new file mode 100644
index 0000000..62f0a54
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/FileSystem.php
@@ -0,0 +1,387 @@
+isDir($path)) {
+ return false;
+ }
+
+ $success = true;
+
+ CommandLineFacade::runAsUser("rm -rf $path",
+ function ($error, $output) use (&$success, $path) {
+ $success = false;
+ });
+
+ return $success;
+ }
+
+ /**
+ * Create a directory.
+ *
+ * @param string $path
+ * @param string|null $owner
+ * @param int $mode
+ *
+ * @return void
+ */
+ public function mkdir(string $path, string $owner = null, int $mode = 0755): void
+ {
+ if (!mkdir($path, $mode, true) && ! is_dir($path)) {
+ throw new RuntimeException(sprintf('Directory "%s" was not created', $path));
+ }
+
+ if ($owner) {
+ $this->chown($path, $owner);
+ }
+ }
+
+ /**
+ * Ensure that the given directory exists.
+ *
+ * @param string $path
+ * @param string|null $owner
+ * @param int $mode
+ *
+ * @return void
+ */
+ public function ensureDirExists(string $path, string $owner = null, int $mode = 0755)
+ {
+ if (!$this->isDir($path)) {
+ $this->mkdir($path, $owner, $mode);
+ }
+ }
+
+ /**
+ * Create a directory as the non-root user.
+ *
+ * @param string $path
+ * @param int $mode
+ *
+ * @return void
+ */
+ public function mkdirAsUser(string $path, int $mode = 0755)
+ {
+ $this->mkdir($path, user(), $mode);
+ }
+
+ /**
+ * Touch the given path.
+ *
+ * @param string $path
+ * @param string|null $owner
+ *
+ * @return string
+ */
+ public function touch(string $path, string $owner = null): string
+ {
+ touch($path);
+
+ if ($owner) {
+ $this->chown($path, $owner);
+ }
+
+ return $path;
+ }
+
+ /**
+ * Touch the given path as the non-root user.
+ *
+ * @param string $path
+ *
+ * @return string
+ */
+ public function touchAsUser(string $path): string
+ {
+ return $this->touch($path, user());
+ }
+
+ /**
+ * Determine if the given file exists.
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ public function exists(string $path): bool
+ {
+ return file_exists($path);
+ }
+
+ /**
+ * Read the contents of the given file.
+ *
+ * @param string $path
+ *
+ * @return string
+ */
+ public function get(string $path): string
+ {
+ return file_get_contents($path);
+ }
+
+ /**
+ * Write to the given file.
+ *
+ * @param string $path
+ * @param string $contents
+ * @param string|null $owner
+ *
+ * @return void
+ */
+ public function put(string $path, string $contents, string $owner = null)
+ {
+ file_put_contents($path, $contents);
+
+ if ($owner) {
+ $this->chown($path, $owner);
+ }
+ }
+
+ /**
+ * Write to the given file as the non-root user.
+ *
+ * @param string $path
+ * @param string $contents
+ *
+ * @return void
+ */
+ public function putAsUser(string $path, string $contents)
+ {
+ $this->put($path, $contents, user());
+ }
+
+ /**
+ * Append the contents to the given file.
+ *
+ * @param string $path
+ * @param string $contents
+ * @param string|null $owner
+ *
+ * @return void
+ */
+ public function append(string $path, string $contents, string $owner = null)
+ {
+ file_put_contents($path, $contents, FILE_APPEND);
+
+ if ($owner) {
+ $this->chown($path, $owner);
+ }
+ }
+
+ /**
+ * Append the contents to the given file as the non-root user.
+ *
+ * @param string $path
+ * @param string $contents
+ *
+ * @return void
+ */
+ public function appendAsUser(string $path, string $contents)
+ {
+ $this->append($path, $contents, user());
+ }
+
+ /**
+ * Copy the given file to a new location.
+ *
+ * @param string $from
+ * @param string $to
+ *
+ * @return void
+ */
+ public function copy(string $from, string $to)
+ {
+ copy($from, $to);
+ }
+
+ /**
+ * Copy the given file to a new location for the non-root user.
+ *
+ * @param string $from
+ * @param string $to
+ *
+ * @return void
+ */
+ public function copyAsUser(string $from, string $to)
+ {
+ copy($from, $to);
+
+ $this->chown($to, user());
+ }
+
+ /**
+ * Create a symlink to the given target.
+ *
+ * @param string $target
+ * @param string $link
+ *
+ * @return void
+ */
+ public function symlink(string $target, string $link)
+ {
+ if ($this->exists($link)) {
+ $this->unlink($link);
+ }
+
+ symlink($target, $link);
+ }
+
+ /**
+ * Create a symlink to the given target for the non-root user.
+ *
+ * This uses the command line as PHP can't change symlink permissions.
+ *
+ * @param string $target
+ * @param string $link
+ * @return void
+ */
+ public function symlinkAsUser($target, $link)
+ {
+ if ($this->exists($link)) {
+ $this->unlink($link);
+ }
+
+ CommandLineFacade::runAsUser('ln -s '.escapeshellarg($target).' '.escapeshellarg($link));
+ }
+
+ /**
+ * Delete the file at the given path.
+ *
+ * @param string $path
+ *
+ * @return void
+ */
+ public function unlink(string $path)
+ {
+ if (file_exists($path) || is_link($path)) {
+ @unlink($path);
+ }
+ }
+
+ /**
+ * Change the owner of the given path.
+ *
+ * @param string $path
+ * @param string $user
+ */
+ public function chown(string $path, string $user)
+ {
+ chown($path, $user);
+ }
+
+ /**
+ * Change the group of the given path.
+ *
+ * @param string $path
+ * @param string $group
+ */
+ public function chgrp(string $path, string $group)
+ {
+ chgrp($path, $group);
+ }
+
+ /**
+ * Resolve the given path.
+ *
+ * @param string $path
+ *
+ * @return string
+ */
+ public function realpath(string $path): string
+ {
+ return realpath($path);
+ }
+
+ /**
+ * Determine if the given path is a symbolic link.
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ public function isLink(string $path): bool
+ {
+ return is_link($path);
+ }
+
+ /**
+ * Resolve the given symbolic link.
+ *
+ * @param string $path
+ *
+ * @return string
+ */
+ public function readLink(string $path): string
+ {
+ return readlink($path);
+ }
+
+ /**
+ * Remove all of the broken symbolic links at the given path.
+ *
+ * @param string $path
+ *
+ * @return void
+ */
+ public function removeBrokenLinksAt(string $path)
+ {
+ collect($this->scandir($path))->filter(function ($file) use ($path) {
+ return $this->isBrokenLink($path.'/'.$file);
+ })->each(function ($file) use ($path) {
+ $this->unlink($path.'/'.$file);
+ });
+ }
+
+ /**
+ * Determine if the given path is a broken symbolic link.
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ public function isBrokenLink(string $path): bool
+ {
+ return is_link($path) && ! file_exists($path);
+ }
+
+ /**
+ * Scan the given directory path.
+ *
+ * @param string $path
+ *
+ * @return array
+ */
+ public function scandir(string $path): array
+ {
+ return collect(scandir($path))->reject(function ($file) {
+ return in_array($file, ['.', '..']);
+ })->values()->all();
+ }
+}
diff --git a/pm4-tools/cli/ProcessMaker/Git.php b/pm4-tools/cli/ProcessMaker/Git.php
new file mode 100644
index 0000000..f9b5c94
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/Git.php
@@ -0,0 +1,110 @@
+validateGitRepository($path_to_repo);
+
+ $output = CommandLineFacade::runAsUser('git rev-parse --abbrev-ref HEAD', function ($e, $o) {
+ throw new RuntimeException('Error trying to retrieve current git branch name');
+ }, $path_to_repo);
+
+ return Str::replace([PHP_EOL, "\n"], '', $output);
+ }
+
+ /**
+ * @param string $path_to_repo
+ *
+ * @return string
+ */
+ public function getCurrentCommitHash(string $path_to_repo): string
+ {
+ $this->validateGitRepository($path_to_repo);
+
+ $output = CommandLineFacade::runAsUser('git rev-parse --short HEAD', function ($e, $o) {
+ throw new RuntimeException('Error trying to retrieve current git commit hash.');
+ }, $path_to_repo);
+
+ return Str::replace([PHP_EOL, "\n"], '', $output);
+ }
+
+ public function getDefaultBranch(string $path): string
+ {
+ $this->validateGitRepository($path);
+
+ $command = "git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'";
+
+ $branch = CommandLineFacade::runAsUser($command, function ($e, $o) {
+ warning('Could not find default git branch.');
+ output($o);
+ }, $path);
+
+ return Str::replace([PHP_EOL, "\n"], '', $branch);
+ }
+
+ /**
+ * @param string $branchName
+ * @param string $path
+ * @param bool $force
+ *
+ * @return string
+ */
+ public function switchBranch(string $branchName, string $path, bool $force = false): string
+ {
+ $this->validateGitRepository($path);
+
+ if ($force) {
+ CommandLineFacade::runAsUser('git reset --hard', null, $path);
+ CommandLineFacade::runAsUser('git clean -d -f .', null, $path);
+ }
+
+ $switched = CommandLineFacade::runAsUser("git checkout $branchName", function ($e, $o) {
+ throw new RuntimeException('Failed to switch branch');
+ }, $path);
+
+ return Str::replace([PHP_EOL, "\n"], '', $switched);
+ }
+
+ public function shallowClone(string $package, string $branch = null, string $path, string $folderName = null)
+ {
+ $branchArg = '';
+ if ($branch) {
+ $branchArg = "--branch $branch ";
+ }
+
+ $token = ConfigFacade::env('GITHUB_TOKEN');
+ CommandLineFacade::mustRun("mkdir -p $path");
+ CommandLineFacade::mustRun("ls -A1 | xargs rm -rf", $path);
+ $cmd = "git clone --depth 1 ${branchArg}https://${token}@github.com/processmaker/${package} ${path}";
+ CommandLineFacade::mustRun($cmd);
+ }
+}
diff --git a/pm4-tools/cli/ProcessMaker/Install.php b/pm4-tools/cli/ProcessMaker/Install.php
new file mode 100644
index 0000000..b2e72fb
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/Install.php
@@ -0,0 +1,153 @@
+files = $files;
+ }
+
+ /**
+ * Create the "sudoers.d" entry for running Valet.
+ *
+ * @return void
+ */
+ public function createSudoersEntry(): void
+ {
+ $this->files->ensureDirExists('/etc/sudoers.d');
+
+ $this->files->put('/etc/sudoers.d/pm', 'Cmnd_Alias PM = '.BREW_PREFIX.'/bin/pm *
+%admin ALL=(root) NOPASSWD:SETENV: PM'.PHP_EOL);
+ }
+
+ /**
+ * Remove the "sudoers.d" entry for running Valet.
+ *
+ * @return void
+ */
+ public function removeSudoersEntry()
+ {
+ CommandLineFacade::quietly('rm /etc/sudoers.d/pm');
+ }
+
+ public function symlinkToUsersBin(): void
+ {
+ $this->unlinkFromUsersBin();
+
+ CommandLineFacade::runAsUser('ln -s "'.realpath(__DIR__.'/../../pm').'" '.$this->bin);
+ }
+
+ /**
+ * Remove the symlink from the user's local bin.
+ *
+ * @return void
+ */
+ public function unlinkFromUsersBin(): void
+ {
+ CommandLineFacade::quietlyAsUser('rm '.$this->bin);
+ }
+
+ /**
+ * Install the Valet configuration file.
+ *
+ * @param string $codebase_path
+ * @param string $packages_path
+ *
+ * @return void
+ */
+ public function install(string $codebase_path, string $packages_path): void
+ {
+ $this->unlinkFromUsersBin();
+
+ $this->symlinkToUsersBin();
+
+ $this->createSudoersEntry();
+
+ $this->createConfigurationDirectory();
+
+ $this->write([
+ 'codebase_path' => $codebase_path,
+ 'packages_path' => $packages_path
+ ]);
+
+ $this->files->chown($this->path(), user());
+ }
+
+ /**
+ * Forcefully delete the Valet home configuration directory and contents.
+ *
+ * @return void
+ */
+ public function uninstall(): void
+ {
+ $this->files->unlink(PM_HOME_PATH);
+ }
+
+ /**
+ * Create the Valet configuration directory.
+ *
+ * @return void
+ */
+ public function createConfigurationDirectory(): void
+ {
+ $this->files->ensureDirExists(PM_HOME_PATH, user());
+ }
+
+ /**
+ * Read the configuration file as JSON.
+ *
+ * @return array
+ */
+ public function read(): array
+ {
+ return json_decode($this->files->get($this->path()), true);
+ }
+
+ /**
+ * Update a specific key in the configuration file.
+ *
+ * @param string $key
+ * @param mixed $value
+ *
+ * @return array
+ */
+ public function updateKey(string $key, $value): array
+ {
+ return tap($this->read(), function (&$config) use ($key, $value) {
+ $config[$key] = $value;
+ $this->write($config);
+ });
+ }
+
+ /**
+ * Write the given configuration to disk.
+ *
+ * @param array $config
+ *
+ * @return void
+ */
+ public function write(array $config): void
+ {
+ $this->files->putAsUser($this->path(), json_encode(
+ $config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES).PHP_EOL
+ );
+ }
+
+ /**
+ * Get the configuration file path.
+ *
+ * @return string
+ */
+ public function path(): string
+ {
+ return PM_HOME_PATH.'/config.json';
+ }
+}
diff --git a/pm4-tools/cli/ProcessMaker/Packages.php b/pm4-tools/cli/ProcessMaker/Packages.php
new file mode 100644
index 0000000..4bf34c4
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/Packages.php
@@ -0,0 +1,547 @@
+getPackages())) {
+ throw new LogicException("Package with name \"$name\" does not exist locally.");
+ }
+
+ return $this->getPackages()[$name];
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return string
+ */
+ public function getPackagePath(string $name): string
+ {
+ return $this->getPackage($name)['path'];
+ }
+
+ /**
+ * @param bool $enterpriseOnly
+ *
+ * @return array
+ */
+ public function getSupportedPackages(bool $enterpriseOnly = false): array
+ {
+ if (!$this->packageExists('packages')) {
+ $this->clonePackage('packages');
+ }
+
+ // We need the packages meta-package to get the
+ // list of supported enterprise packages that
+ // ProcessMaker 4 is compatible with
+ $packages_package = $this->getPackage('packages');
+ $packages_package_path = $packages_package['path'];
+
+ // Make sure we're on the right branch
+ $defaultBranch = GitFacade::getDefaultBranch($packages_package_path);
+ $branchSwitchResult = GitFacade::switchBranch($defaultBranch, $packages_package_path);
+
+ // Find and decode composer.json
+ $composer_json = ComposerFacade::getComposerJson($packages_package_path);
+
+ try {
+ // We want just the package names for now
+ $supported_packages = array_keys(get_object_vars($composer_json->extra->processmaker->enterprise));
+ } catch (Exception $exception) {
+ throw new LogicException('Enterprise packages not found in processmaker/packages composer.json');
+ }
+
+ if (!$enterpriseOnly) {
+ // Merge the supported enterprise package names with
+ // the handful of other packages required for the
+ // primary (processmaker/processmaker) app to function
+ $supported_packages = array_merge($supported_packages ?? [], self::$additionalPackages);
+ }
+
+ // Sort it and and remove two packages so they can be
+ // prepended as other packages rely on them if the order
+ // returned is the order installed
+ $supported_packages = collect($supported_packages)->values()->sort()->reject(static function ($package) {
+ return $package === 'docker-executor-node-ssr'
+ || $package === 'connector-send-email'
+ || $package === 'packages';
+ });
+
+ // Prepend the removed packages so they're installed
+ // first, assuming the returned order is relied on
+ // for installation
+ return $supported_packages->prepend('connector-send-email')
+ ->prepend('docker-executor-node-ssr')
+ ->prepend('packages')
+ ->toArray();
+ }
+
+ /**
+ * @param string $name
+ * @param bool $force
+ *
+ * @return bool
+ */
+ public function clonePackage(string $name, bool $force = false): bool
+ {
+ $name = Str::replace('processmaker/', '', $name);
+
+ if (!$force && $this->packageExists($name)) {
+ throw new LogicException("Package already exists: processmaker/$name");
+ }
+
+ if ($force) {
+ FileSystemFacade::rmdir(ConfigFacade::packagesPath()."/$name");
+ }
+
+ $command = "git clone https://github.com/ProcessMaker/$name";
+
+ $output = CommandLineFacade::runAsUser($command, function ($code, $out) use ($name) {
+ throw new RuntimeException("Failed to clone $name: ".PHP_EOL.$out);
+ }, ConfigFacade::packagesPath());
+
+ return $this->packageExists($name);
+ }
+
+ /**
+ * Clones all supported PM4 packages to the local package directory
+ *
+ * @param bool $force
+ *
+ * @return array
+ */
+ public function cloneAllPackages(bool $force = false): array
+ {
+ // Clear the ProcessMaker packages directory before
+ // we start cloning the new ones down
+ if ($force) {
+ foreach ($this->getPackages() as $package) {
+ FileSystemFacade::rmdir($package['path']);
+ }
+ }
+
+ // Clone down the processmaker/packages meta-package to
+ // to make sure we can reference all official supported
+ // ProcessMaker 4 enterprise packages
+ if (!$this->packageExists('packages')) {
+ $this->clonePackage('packages');
+ }
+
+ return $this->getSupportedPackages();
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function packageExists(string $name): bool
+ {
+ if (Str::contains($name, 'processmaker/')) {
+ $name = Str::replace('processmaker/', '', $name);
+ }
+
+ return in_array($name, $this->getPackagesListFromDirectory(), true);
+ }
+
+ /**
+ * Get the names of the local composer packages
+ *
+ * @param string|null $package_directory
+ *
+ * @return array
+ */
+ public function getPackagesListFromDirectory(string $package_directory = null): array
+ {
+ if (!is_string($package_directory)) {
+ $package_directory = ConfigFacade::packagesPath();
+ }
+
+ return array_filter(FileSystemFacade::scandir($package_directory), function ($dir) use($package_directory) {
+
+ // Set the absolute path to the file or directory
+ $dir = $package_directory.'/'.$dir;
+
+ // Filter out any non-directory files
+ return FileSystemFacade::isDir($dir) && !is_file($dir);
+ });
+ }
+
+ /**
+ * Returns an multi-dimensional array with
+ * each package name and path
+ *
+ * @return array
+ */
+ public function getPackages(): array
+ {
+ $packages = array_map(function ($package_name) {
+ return [
+ 'name' => $package_name,
+ 'path' => ConfigFacade::packagesPath() . "/$package_name"
+ ];
+ }, $this->getPackagesListFromDirectory());
+
+ return collect($packages)->keyBy('name')->toArray();
+ }
+
+ /**
+ * Get the package version number for a package
+ *
+ * @param string $package_directory
+ *
+ * @return string
+ */
+ public function getPackageVersion(string $package_directory): string
+ {
+ $composer_json = ComposerFacade::getComposerJson($package_directory) ?? new class {};
+
+ if (!property_exists($composer_json, 'version')) {
+ return '...';
+ }
+
+ return $composer_json->version;
+ }
+
+ /**
+ * @param string $path
+ *
+ * @return string
+ */
+ public function getCurrentGitBranchName(string $path): string
+ {
+ if (!FileSystemFacade::isDir($path)) {
+ return '...';
+ }
+
+ // Run this command and get the current git branch
+ $branch = CommandLineFacade::runAsUser('git rev-parse --abbrev-ref HEAD', null, $path);
+
+ // Remove unnecessary end of line character(s)
+ return Str::replace(["\n", PHP_EOL], "", $branch);
+ }
+
+ /**
+ * @param bool $verbose
+ *
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
+ */
+ public function pull41(bool $verbose = false)
+ {
+ $this->pull($verbose, '4.1-develop');
+ }
+
+ /**
+ * Stores basic package metadata on this class instance. Used to store metadata
+ * prior to running the pull() method for comparisons afterwards.
+ *
+ * @param bool $updated
+ * @param array $metadata Any package metadata to start with
+ *
+ * @return array
+ */
+ public function takePackagesSnapshot(bool $updated = false, array $metadata = []): array
+ {
+ foreach ($this->getPackages() as $package) {
+
+ $path = $package['path'];
+ $version_key = $updated ? 'updated_version' : 'version';
+ $branch_key = $updated ? 'updated_branch' : 'branch';
+ $hash_key = $updated ? 'updated_commit_hash' : 'commit_hash';
+
+ $metadata[$package['name']] = [
+ 'name' => $package['name'],
+ $version_key => $this->getPackageVersion($path),
+ $branch_key => $this->getCurrentGitBranchName($path),
+ $hash_key => GitFacade::getCurrentCommitHash($path)
+ ];
+ }
+
+ return $metadata;
+ }
+
+ /**
+ * @param string $branch
+ * @param array $commands
+ *
+ * @return array
+ */
+ public function buildPullCommands(string $branch, array $commands = []): array
+ {
+ foreach ($this->getPackages() as $package) {
+ $package_commands = [
+ 'git reset --hard',
+ 'git clean -d -f .',
+ "git checkout $branch",
+ 'git fetch --all',
+ 'git pull --force',
+ ];
+
+ $commands[$package['name']] = array_map(function ($command) use ($package) {
+ return CommandLineFacade::transformCommandToRunAsUser($command, $package['path']);
+ }, $package_commands);
+ }
+
+ return $commands;
+ }
+
+ /**
+ * @param bool $verbose
+ * @param string|null $branch
+ *
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
+ */
+ public function pull(bool $verbose = false, string $branch = null): void
+ {
+ // A quick command (thanks Nolan!) to grab the default branch
+ $get_default_git_branch = "$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')";
+
+ // Build the commands for each package (keyed by package name)
+ $commands = $this->buildPullCommands($branch ?? $get_default_git_branch);
+
+ // Store the pre-pull metadata for each package
+ $metadata = $this->takePackagesSnapshot();
+
+ // Create a new ProcessManagerFacade instance to run the
+ // git commands in parallel where possible
+ $processManager = resolve(ProcessManager::class);
+
+ // Set verbosity for output to stdout
+ $processManager->setVerbosity($verbose);
+
+ // Set a closure to be called when the final process exits
+ $processManager->setFinalCallback(function () use ($metadata) {
+ $this->outputPullResults($metadata);
+ });
+
+ // Build the process queue and run
+ $processManager->buildProcessesBundleAndStart($commands);
+ }
+
+ /**
+ * @return array
+ */
+ public function getPackagesTableData(): array
+ {
+ $table = [];
+
+ // Build the table rows by merging the compare-with
+ // package metadata with a recent snapshot
+ foreach ($this->takePackagesSnapshot() as $package => $updated) {
+ $table[$package] = $updated;
+ }
+
+ // Sort the columns in a more sensible way
+ foreach ($table as $key => $row) {
+ $table[$key] = [
+ 'name' => ''.$row['name'].'>',
+ 'version' => $row['version'],
+ 'branch' => $row['branch'],
+ 'commit_hash' => $row['commit_hash'],
+ ];
+ }
+
+ return $table;
+ }
+
+ /**
+ * @param array $pre_pull_package_metadata
+ */
+ public function outputPullResults(array $pre_pull_package_metadata)
+ {
+ $table = [];
+
+ // Build the table rows
+ foreach ($this->takePackagesSnapshot(true) as $package => $updated) {
+ $table[$package] = array_merge($pre_pull_package_metadata[$package], $updated);
+ }
+
+ // Sort the columns in a more sensible way
+ foreach ($table as $key => $row) {
+ $table[$key] = [
+ 'name' => $row['name'],
+ 'version' => $row['version'],
+ 'updated_version' => $row['updated_version'],
+ 'branch' => $row['branch'],
+ 'updated_branch' => $row['updated_branch'],
+ 'commit_hash' => $row['commit_hash'],
+ 'updated_commit_hash' => $row['updated_commit_hash']
+ ];
+ }
+
+ // Add console styling
+ foreach ($table as $key => $row) {
+
+ // Highlight the package name
+ $table[$key]['name'] = ''.$row['name'].'>';
+
+ // If the versions are the same, no updated occurred.
+ // If they are different, let's make it easier to see.
+ if ($row['version'] !== $row['updated_version']) {
+ $table[$key]['updated_version'] = ''.$row['updated_version'].'';
+ }
+
+ // Do the same thing with branches, since we may
+ // have switch to 4.1 or 4.2 during the pull, which
+ // is set by the user by adding a flag to the command
+ if ($row['branch'] !== $row['updated_branch']) {
+ $table[$key]['updated_branch'] = ''.$row['updated_branch'].'';
+ }
+
+ // One more time to see if the commit hash has changed
+ if ($row['commit_hash'] !== $row['updated_commit_hash']) {
+ $table[$key]['updated_commit_hash'] = ''.$row['updated_commit_hash'].'';
+ }
+ }
+
+ // Add a new line for space above the table
+ output(PHP_EOL);
+
+ // Format our results in an easy-to-ready table
+ table(['Name', 'Version ->', '-> Version', 'Branch ->', '-> Branch', 'Hash ->', '-> Hash'], $table);
+ }
+
+ /**
+ * Build the stack of commands to composer require and
+ * install each enterprise ProcessMaker 4 package
+ *
+ * @param bool $for_41_develop
+ * @param bool $force
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function buildPackageInstallCommands(bool $for_41_develop = false, bool $force = false): Collection
+ {
+ if (!FileSystemFacade::isDir(ConfigFacade::codebasePath())) {
+ throw new LogicException('Could not find ProcessMaker codebase: '. ConfigFacade::codebasePath());
+ }
+
+ // Find out which branch to switch to in the local
+ // processmaker/processmaker codebase
+ $branch = $for_41_develop ? '4.1-develop' : 'develop';
+
+ // Find out which branch we're on
+ $current_branch = GitFacade::getCurrentBranchName(ConfigFacade::codebasePath());
+
+ // Make sure we're on the right branch
+ if ($current_branch !== $branch && ! $force) {
+ throw new DomainException("Core codebase branch should be \"$branch\" but \"$current_branch\" was found.");
+ }
+
+ // Grab the list of supported enterprise packages
+ $enterprise_packages = new Collection($this->getSupportedPackages(true));
+
+ if ($for_41_develop) {
+ // Filter out any packages not on the 4.1-develop
+ // branch which aren't compatible with 4.1
+ $enterprise_packages = $enterprise_packages->reject(function ($package) {
+ if ($package === 'docker-executor-node-ssr') {
+ return false;
+ }
+
+ return '4.1-develop' !== GitFacade::getCurrentBranchName($this->getPackagePath($package));
+ });
+ }
+
+ // Key by package name
+ $enterprise_packages = $enterprise_packages->keyBy(static function ($package, $index) {
+ return $package;
+ });
+
+ // Build the stack of commands to run
+ return $enterprise_packages->transform(static function (string $package) {
+ return new Collection([
+ "composer require processmaker/$package --no-interaction",
+ PHP_BINARY." artisan $package:install --no-interaction",
+ PHP_BINARY." artisan vendor:publish --tag=$package --no-interaction"
+ ]);
+ });
+ }
+
+ public function getEnterprisePackages($tests = false)
+ {
+ // TODO: Bring this part back when all unit test fixes are merged
+ $composer = FileSystemFacade::get(ConfigFacade::codebasePath() . '/composer.json');
+ $composer = json_decode($composer, true);
+ $list = Arr::get($composer, 'extra.processmaker.enterprise', []);
+
+ return collect(array_keys($list));
+
+ // $list = collect(require(__DIR__ . '/../../../packages-to-install.php'));
+ // if ($tests) {
+ // $list = $list->filter(fn($p) => $p['tests'] === true);
+ // }
+
+ // $list = $list->keys();
+
+ // // Always install the current package
+ // if (PackagesCi::repoName() !== 'processmaker') {
+ // $list->push(PackagesCi::repoName());
+ // }
+
+ // // Always install packages listed in the pr body
+ // foreach(PackagesCi::getBranches() as $key => $branch) {
+ // if ($key !== 'processmaker') {
+ // $list->push($key);
+ // }
+ // }
+
+ // return $list;
+ }
+
+ public function getJavascriptPackages($path)
+ {
+ $composer = FileSystemFacade::get($path . '/package.json');
+ $composer = json_decode($composer, true);
+ $list = Arr::get($composer, 'dependencies', []);
+ return collect(array_keys($list))->filter(function($package) {
+ return Str::startsWith($package, '@processmaker/');
+ })->map(function($package) {
+ return str_replace('@processmaker/', '', $package);
+ })->filter(function($package) {
+ return PackagesCi::getBranch($package);
+ });
+ }
+}
diff --git a/pm4-tools/cli/ProcessMaker/PackagesCi.php b/pm4-tools/cli/ProcessMaker/PackagesCi.php
new file mode 100644
index 0000000..13972fb
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/PackagesCi.php
@@ -0,0 +1,423 @@
+ 'npm run build-bundle',
+ "screen-builder" => 'npm run build-bundle',
+ "vue-form-elements" => 'npm run build-bundle',
+ ];
+
+ private $cacheManifest;
+ private $remoteShas = [];
+
+ public function build()
+ {
+ $this->buildCore();
+ $this->buildPackages();
+ }
+
+ public function buildJavascript()
+ {
+ $javascriptPackages = Packages::getJavascriptPackages(Config::codebasePath());
+ $this->installJavascriptPackages($javascriptPackages);
+ $this->npmRunDev();
+ }
+
+ public function install()
+ {
+ $this->installCore();
+ $list = Packages::getEnterprisePackages();
+
+ // Our packages-to-install.php is ordered
+ $list = $this->orderList($list);
+
+ foreach ($list as $package) {
+ info("Artisan installing $package");
+ $this->artisanInstall($package);
+ }
+
+ $this->artisanCommand('optimize:clear');
+ $this->artisanCommand('horizon:terminate');
+
+ info("Modifying phpunit.xml to add package tests");
+ PhpUnit::addTests(PhpUnit::configFile());
+ // $this->exportDatabase();
+
+ $this->cleanUp();
+
+ info("Done");
+ }
+
+ public function cleanUp()
+ {
+ CommandLine::mustRun('rm -rf node_modules', Config::codebasePath());
+ }
+
+ public function shouldUseCache($package)
+ {
+ info("Checking cache for package $package");
+ return $this->remoteSha($package) === $this->cachedSha($package);
+ }
+
+ public function fetchBranchSha($package, $branch = null)
+ {
+ if (!$branch) {
+ $branch = 'HEAD';
+ }
+
+ $token = Config::env('GITHUB_TOKEN');
+ $sha = CommandLine::mustRun("git ls-remote https://${token}@github.com/processmaker/$package $branch | awk '{ print $1}'");
+ return trim($sha);
+ }
+
+ public function setCache($package)
+ {
+ if ($package === 'processmaker') {
+ $from = Config::codebasePath();
+ } else {
+ $from = Config::packagesPath($package);
+ }
+ $to = Config::cachePath() . '/' . $package;
+ $this->copy($from, $to);
+ $this->updateManifest($package);
+ }
+
+ public function restoreCache($package)
+ {
+ if ($package === 'processmaker') {
+ $to = Config::codebasePath();
+ } else {
+ $to = Config::packagesPath($package);
+ }
+ $from = Config::cachePath() . '/' . $package;
+ $this->copy($from, $to);
+ }
+
+ public function copy($from, $to)
+ {
+ CommandLine::mustRun("mkdir -p $to");
+ CommandLine::mustRun("ls -A1 | xargs rm -rf", $to);
+ CommandLine::mustRun("rsync -r --exclude='/node_modules' --exclude='/vendor' ${from}/ ${to}");
+ }
+
+ private function cachedSha($package)
+ {
+ $manifest = $this->getCacheManifest();
+ if (isset($manifest[$package])) {
+ return $manifest[$package];
+ }
+ return null;
+ }
+
+ private function remoteSha($package)
+ {
+ if (isset($this->remoteShas[$package])) {
+ $sha = $this->remoteShas[$package];
+ } else {
+ $sha = $this->fetchBranchSha($package, $this->getBranch($package));
+ $this->remoteShas[$package] = $sha;
+ }
+ return $sha;
+ }
+
+ private function getCacheManifest()
+ {
+ if ($this->cacheManifest) {
+ return $this->cacheManifest;
+ }
+ $manifest = [];
+ if (FileSystem::exists(Config::cacheManifestPath())) {
+ $manifest = json_decode(FileSystem::get(Config::cacheManifestPath()), true);
+ }
+ $this->cacheManifest = $manifest;
+ return $manifest;
+ }
+
+ private function updateManifest($package)
+ {
+ $manifest = $this->getCacheManifest();
+ $manifest[$package] = $this->remoteSha($package);
+ $this->cacheManifest = $manifest;
+ $manifest = json_encode($manifest);
+ FileSystem::put(Config::cacheManifestPath(), $manifest);
+ }
+
+ private function composerRequireList($list)
+ {
+ return $list->map(function ($package) {
+ return "processmaker/$package";
+ })->join(" ");
+ }
+
+ private function artisanInstall($package)
+ {
+ return $this->artisanCommand("${package}:install");
+ }
+
+ private function artisanCommand($cmd)
+ {
+ return CommandLine::runCommand("php artisan ${cmd}", function ($code, $output) {
+ throw new \Exception($output);
+ }, Config::codebasePath());
+ }
+
+ public function getBranch($package)
+ {
+ if (!$this->branches) {
+ $this->setBranches();
+ }
+ if (isset($this->branches[$package])) {
+ return $this->branches[$package];
+ }
+
+ return null;
+ }
+
+ private function setBranches()
+ {
+ $result = preg_match_all('/ci:(.+?):(.+?)(\s|$)/', $this->pullRequestBody(), $matches);
+ if ($result && $result > 0) {
+ $this->branches = array_combine($matches[1], $matches[2]);
+ } else {
+ $this->branches = [];
+ }
+ $this->branches[$this->repoName()] = $this->pullRequestBranch();
+ }
+
+ public function getBranches()
+ {
+ if (!$this->branches) {
+ $this->setBranches();
+ }
+ return $this->branches;
+ }
+
+ private function pullRequestBody()
+ {
+ return Config::env('CI_PR_BODY');
+ }
+
+ private function pullRequestBranch()
+ {
+ return Config::env('CI_PACKAGE_BRANCH');
+ }
+
+ public function repoName()
+ {
+ return Config::env('CI_PROJECT');
+ }
+
+ private function installCommand()
+ {
+ extract($_ENV);
+ if ($PM_APP_PORT !== "80") {
+ $portWithPrefix = ":${PM_APP_PORT}";
+ }
+
+ return <<cloneCore();
+ CommandLine::mustRun('composer install --no-interaction', Config::codebasePath());
+ }
+
+ private function buildPackages()
+ {
+ info("Installing enterprise packages for CI");
+ $list = Packages::getEnterprisePackages();
+
+ info("Cloning " . $list->count() . " packages");
+ foreach ($list as $package) {
+ $branch = $this->getBranch($package);
+ $info = "Cloning $package";
+ if ($branch) {
+ $info .= " with branch $branch";
+ }
+ info($info);
+ Git::shallowClone($package, $branch, Config::packagesPath($package));
+ }
+
+ foreach ($list as $package) {
+ info("Registering local repository path for $package");
+ Composer::addRepositoryPath($package);
+ }
+
+ info("Composer requiring packages");
+ $listString = $this->composerRequireList($list);
+ Composer::require($listString);
+ }
+
+ private function installCore()
+ {
+ $this->createDatabase(Config::env('DB_DATABASE'));
+ CommandLine::mustRun($this->installCommand(), Config::codebasePath());
+ FileSystem::append(Config::codebasePath() . '/.env', $this->additionalEnv());
+ }
+
+ private function cloneCore()
+ {
+ $pm = 'processmaker';
+ info("Cloning processmaker core");
+ Git::shallowClone($pm, $this->getBranch($pm), Config::codebasePath());
+ }
+
+ public function databaseConnectionParams()
+ {
+ extract($_ENV);
+ return "-h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p'${DB_PASSWORD}'";
+ }
+
+ public function createDatabase($name)
+ {
+ $connection = $this->databaseConnectionParams();
+ CommandLine::mustRun("mysql $connection -e 'DROP DATABASE IF EXISTS `$name`;'");
+ CommandLine::mustRun("mysql $connection -e 'CREATE DATABASE `$name`;'");
+ }
+
+ public function exportDatabase()
+ {
+ $from = Config::env('DB_DATABASE');
+ $to = 'test';
+ $file = 'database.sql';
+ $connection = $this->databaseConnectionParams();
+ CommandLine::mustRun("mysqldump $connection $from > $file");
+ $this->createDatabase($to);
+ CommandLine::mustRun("mysql $connection $to < $file");
+ }
+
+ private function clonePackage($package)
+ {
+ Git::shallowClone($package, $this->getBranch($package), Config::packagesPath($package));
+ }
+
+ private function installJavascriptPackages($packages)
+ {
+ $packagesToBuild = [];
+ foreach ($packages as $package) {
+ // Only clone/build/link if specified in the PR
+ // Otherwise, just use core's package.json
+ if (!$this->getBranch($package)) {
+ continue;
+ }
+
+ $path = Config::packagesPath($package);
+ $this->clonePackage($package);
+
+ // unsafe-perm is needed to run postinstall scripts as root
+ CommandLine::mustRun("npm install --unsafe-perm", $path);
+ $packagesToBuild[] = $package;
+ }
+
+ foreach ($packagesToBuild as $package) {
+ $this->linkDependentPackages(Config::packagesPath($package));
+ $this->buildJavascriptPackage($package);
+ }
+
+ return count($packagesToBuild);
+ }
+
+ private function linkDependentPackages($path)
+ {
+ $dependentPackages = Packages::getJavascriptPackages($path);
+ foreach ($dependentPackages as $dependentPackage) {
+ CommandLine::mustRun("mkdir -p node_modules/@processmaker", $path);
+ $packagePath = Config::packagesPath($dependentPackage);
+ $nodeModulesPackagePath = "node_modules/@processmaker/${dependentPackage}";
+ CommandLine::mustRun("rm -rf $nodeModulesPackagePath", $path);
+ CommandLine::mustRun("ln -s $packagePath $nodeModulesPackagePath", $path);
+ }
+ }
+
+ private function buildJavascriptPackage($package)
+ {
+ if (isset($this->javascriptPackageBuildCommands[$package])) {
+ $cmd = $this->javascriptPackageBuildCommands[$package];
+ CommandLine::mustRun($cmd, Config::packagesPath($package));
+ }
+ CommandLine::mustRun('rm -rf node_modules', Config::packagesPath($package));
+ }
+
+ private function npmRunDev()
+ {
+ // unsafe-perm is needed to run postinstall scripts as root
+ info("Starting at " . date('c'));
+ CommandLine::mustRun('npm ci --unsafe-perm --omit=dev', Config::codebasePath());
+ $this->linkDependentPackages(Config::codebasePath());
+ CommandLine::mustRun('npm install cross-env', Config::codebasePath());
+ CommandLine::mustRun('npm run dev', Config::codebasePath());
+ info("Finished at " . date('c'));
+ }
+
+ private function orderList($list)
+ {
+ return $list->sortBy(function ($package) {
+ switch($package) {
+ // Must be installed first
+ case 'package-data-vocabularies':
+ return 0;
+
+ // Must be installed last
+ case 'connector-docusign':
+ return 2;
+
+ default:
+ return 1;
+ }
+ });
+ }
+}
diff --git a/pm4-tools/cli/ProcessMaker/PhpUnit.php b/pm4-tools/cli/ProcessMaker/PhpUnit.php
new file mode 100644
index 0000000..e16bb2c
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/PhpUnit.php
@@ -0,0 +1,45 @@
+preserveWhiteSpace = false;
+ $dom->formatOutput = true;
+ $dom->load($path);
+
+ $xpath = new DOMXpath($dom);
+ $directories = $xpath->query('//testsuite[@name="Features"]')[0];
+
+ foreach ($list as $package)
+ {
+ $testsDirectory = Config::packagesPath($package) . "/tests";
+ if (FileSystem::exists($testsDirectory)) {
+ info("Phpunit - Adding $testsDirectory");
+ $directory = $dom->createElement('directory', $testsDirectory);
+ $directories->appendChild($directory);
+ }
+ }
+
+ return $dom->save($path);
+ }
+
+ public function configFile()
+ {
+ return Config::codebasePath() . '/phpunit.xml';
+ }
+
+}
\ No newline at end of file
diff --git a/pm4-tools/cli/ProcessMaker/ProcessManager.php b/pm4-tools/cli/ProcessMaker/ProcessManager.php
new file mode 100644
index 0000000..fb2895c
--- /dev/null
+++ b/pm4-tools/cli/ProcessMaker/ProcessManager.php
@@ -0,0 +1,292 @@
+cli = $cli;
+ $this->processCollections = $processCollections;
+ $this->outputCollection = $outputCollection;
+ }
+
+ public function setVerbosity(bool $verbose)
+ {
+ $this->verbose = $verbose;
+ }
+
+ public function getProcessOutput(string $key = null): Collection
+ {
+ return $key ? $this->outputCollection->get($key) : $this->outputCollection;
+ }
+
+ public function addProcessOutput(string $key, $output)
+ {
+ if (!$this->getProcessOutput()->has($key)) {
+ $this->getProcessOutput()->put($key, new Collection);
+ }
+
+ $this->getProcessOutput($key)->push($output);
+ }
+
+ public function findProcessExitCode(string $key): int
+ {
+ if (!$output = $this->getProcessOutput($key)) {
+ return 1;
+ }
+
+ // Search through the output for the array
+ // containing the exit code value
+ $exitCode = $output->reject(function ($line) {
+ return ! is_array($line) || ! array_key_exists('exit_code', $line);
+ });
+
+ // If we can't find it, assume the process
+ // exited with a general error
+ if ($exitCode->isNotEmpty()) {
+ return $exitCode->flatten()->first() ?? 1;
+ }
+
+ return 1;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return Collection
+ */
+ public function getProcessCollections(string $key): Collection
+ {
+ if (!$this->processCollections->get($key) instanceof Collection) {
+ $this->processCollections->put($key, new Collection());
+ }
+
+ return $this->processCollections->get($key);
+ }
+
+ /**
+ * @param array $commands
+ */
+ public function buildProcessesBundleAndStart(array $commands)
+ {
+ $this->startProcessesBundle($this->buildProcessesBundle($commands));
+ }
+
+ /**
+ * @param array $commands
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ public function buildProcessesBundle(array $commands): Collection
+ {
+ $commands = array_filter($commands, function ($command) {
+ return !is_string($command);
+ });
+
+ if (blank($commands)) {
+ throw new LogicException('Commands array cannot be empty');
+ }
+
+ $bundles = collect($commands)->transform(function (array $set) {
+ return collect(array_map(function ($command) {
+ return new Process($command);
+ }, $set));
+ });
+
+ return $this->setExitListeners($bundles);
+ }
+
+ /**
+ * @param \Illuminate\Support\Collection $bundles
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ private function setExitListeners(Collection $bundles): Collection
+ {
+ // Set each Process to start the next process
+ // in the bundle when it exists
+ $bundles->each(function (Collection $bundle) {
+ return $bundle->transform(function (Process $process, $index) use (&$bundle) {
+
+ $process->on('exit', function ($exitCode, $termSignal) use (&$bundle, $process, $index) {
+
+ if (!$this->verbose) {
+ $this->cli->getProgress()->advance();
+ }
+
+ // Add to the "exited" process collection
+ $this->getProcessCollections('exited')->push($process);
+
+ // Get the info we need to output to stdout
+ $pid = $process->getPid();
+ $command = $process->getCommand();
+
+ if ($this->verbose) {
+ if ($exitCode === 0) {
+ output("$pid>: $command");
+ } else {
+ output("$pid>: $command>");
+ }
+ }
+
+ // Add to the process collections
+ if ($exitCode === 0) {
+ $this->getProcessCollections('successful')->push($process);
+ } else {
+ $this->getProcessCollections('errors')->push($process);
+ }
+
+ // Add to the processOutput property for reading later
+ $this->addProcessOutput($process->getCommand(), ['exit_code' => $exitCode]);
+
+ // Find the next process to run
+ $next_process = $bundle->get($index + 1);
+
+ // If one exists, run it
+ if ($next_process instanceof Process) {
+ $this->startProcessAndPipeOutput($next_process);
+ }
+
+ $queued = $this->getProcessCollections('queued')->count();
+ $exited = $this->getProcessCollections('exited')->count();
+
+ // All processes are finished
+ if ($queued === $exited) {
+ // Keeps the stdout clean during verbose mode
+ if (!$this->verbose) {
+ $this->cli->getProgress()->finish();
+ }
+
+ // Last but not least, run the bound callback
+ $this->getFinalCallback();
+ }
+ });
+
+ return $process;
+ });
+ });
+
+ return $bundles;
+ }
+
+ public function getFinalCallback()
+ {
+ if (is_callable($this->finalCallback)) {
+ return call_user_func($this->finalCallback);
+ }
+ }
+
+ public function setFinalCallback(callable $callback)
+ {
+ $this->finalCallback = $callback;
+ }
+
+ /**
+ * @param \Illuminate\Support\Collection $bundle
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ private function validateBundle(Collection $bundle): Collection
+ {
+ return $bundle->reject(function ($process) {
+ return ! $process instanceof Process;
+ });
+ }
+
+ /**
+ * @param \Illuminate\Support\Collection $bundles
+ */
+ public function startProcessesBundle(Collection $bundles): void
+ {
+ $bundles->transform(function (Collection $bundle) {
+ return $this->validateBundle($bundle);
+ });
+
+ if ($bundles->isEmpty()) {
+ throw new LogicException('No bundles of Processes found');
+ }
+
+ $this->setProcessIndexes($bundles);
+
+ $this->getStartProcesses($bundles)->each(function (Process $process) {
+ $this->startProcessAndPipeOutput($process);
+ });
+ }
+
+ /**
+ * @param \Illuminate\Support\Collection $bundles
+ *
+ * @return \Illuminate\Support\Collection
+ */
+ private function getStartProcesses(Collection $bundles): Collection
+ {
+ return $this->validateBundle(
+ $bundles->map(function (Collection $bundle) {
+ return $bundle->first();
+ })
+ );
+ }
+
+ /**
+ * @param \Illuminate\Support\Collection $bundles
+ *
+ * @return void
+ */
+ private function setProcessIndexes(Collection $bundles): void
+ {
+ $index = 0;
+ $total_processes = 0;
+
+ // Count up all of the processes
+ $bundles->each(function ($bundle) use (&$total_processes) {
+ $total_processes += $bundle->count();
+ });
+
+ // Set the "process_index" property for each
+ // process among each bundle
+ $bundles->each(function ($bundle) use (&$index) {
+ $bundle->transform(function (Process $process) use (&$index) {
+ $process->index = $index++;
+
+ $this->getProcessCollections('queued')->push($process);
+
+ return $process;
+ });
+ });
+
+ if (!$this->verbose) {
+ $this->cli->getProgress($total_processes);
+ }
+ }
+
+ /**
+ * @param \React\ChildProcess\Process $process
+ */
+ private function startProcessAndPipeOutput(Process $process): void
+ {
+ if ($process->isRunning()) {
+ return;
+ }
+
+ $process->start();
+
+ $process->stdout->on('data', function ($output) use (&$process) {
+ $this->addProcessOutput($process->getCommand(), $output);
+ });
+
+ $this->getProcessCollections('started')->push($process);
+ }
+}
diff --git a/pm4-tools/cli/pm.php b/pm4-tools/cli/pm.php
new file mode 100644
index 0000000..2dd9b8d
--- /dev/null
+++ b/pm4-tools/cli/pm.php
@@ -0,0 +1,294 @@
+#!/usr/bin/env php
+load();
+}
+
+Container::setInstance(new Container);
+
+$app = new Application('ProcessMaker CLI Tool', '0.5.0');
+
+if (!FileSystem::isDir(PM_HOME_PATH)) {
+ /*
+ * -------------------------------------------------+
+ * | |
+ * | Command: Install Cli |
+ * | |
+ * -------------------------------------------------+
+ */
+ $app->command('install-cli', function (InputInterface $input, OutputInterface $output) {
+
+ // First thing we want to do is ask the user to
+ // tell us the absolute path to the core codebase
+ $helper = $this->getHelperSet()->get('question');
+
+ // Callback to autocomplete the directories available
+ // as the user is typing
+ $callback = function (string $userInput): array {
+
+ $inputPath = preg_replace('%(/|^)[^/]*$%', '$1', $userInput);
+ $inputPath = '' === $inputPath ? '.' : $inputPath;
+ $foundFilesAndDirs = @scandir($inputPath) ?: [];
+
+ return array_map(static function ($dirOrFile) use ($inputPath) {
+ return $inputPath.$dirOrFile;
+ }, $foundFilesAndDirs);
+ };
+
+ $question = "Please enter the absolute path to the local copy of the processmaker/processmaker codebase:".PHP_EOL;
+ $question = new Question($question);
+ $question->setAutocompleterCallback($callback);
+ $codebase_path = $helper->ask($input, $output, $question);
+
+ // Make sure they entered something
+ if (null === $codebase_path) {
+ warningThenExit('You must enter a valid absolute path to continue the installation. Please try again.');
+ }
+
+ // Check for composer.json
+ if (!FileSystem::exists("$codebase_path/composer.json")) {
+ warningThenExit("Could not the composer.json for processmaker/processmaker in: $codebase_path");
+ }
+
+ // Next we need to know where all of the local copies
+ // of the processmaker/* packages will be stored
+ $question = "Please enter the absolute path to the directory where local copies of the ProcessMaker packages will be stored::".PHP_EOL;
+ $question = new Question($question);
+ $question->setAutocompleterCallback($callback);
+ $packages_path = $helper->ask($input, $output, $question);
+
+ // Make sure they entered something
+ if (null === $packages_path) {
+ warningThenExit('You must enter a valid absolute path to continue the installation. Please try again.');
+ }
+
+ // Creates the sudoers entry and the base config file/directory
+ Install::install($codebase_path, $packages_path);
+
+ info('Installation complete!');
+
+ })->descriptions('Runs the installation process for this tool. Necessary before other commands will appear.');
+
+} else {
+
+ /*
+ * -------------------------------------------------+
+ * | |
+ * | Command: Install Packages |
+ * | |
+ * -------------------------------------------------+
+ */
+ $app->command('install-packages [-4|--for_41_develop]', function (InputInterface $input, OutputInterface $output) {
+
+ // Indicates if we should install the 4.1-develop
+ // versions of each package or the 4.2
+ $for_41_develop = $input->getOption('for_41_develop');
+
+ // Should the output be verbose or not
+ $verbose = $input->getOption('verbose');
+
+ // Use an anonymous function to we can easily re-run if
+ // we decide to force the installation of the packages
+ $build_install_commands = static function ($force = false) use ($for_41_develop) {
+ return Packages::buildPackageInstallCommands($for_41_develop, $force);
+ };
+
+ // Builds an array of commands to run in the local
+ // processmaker/processmaker codebase to require
+ // each supported package, then install it and
+ // publish it's vendor assets (if any are available)
+ try {
+ $install_commands = $build_install_commands();
+ } catch (DomainException $exception) {
+
+ // Show the user the incompatible branch information
+ warning($exception->getMessage());
+
+ // Ask the user if they want to force the install anyway
+ $helper = $this->getHelperSet()->get('question');
+ $question = new ConfirmationQuestion('Force install the packages? "No" to abort or "Yes" to proceed: ', false);
+
+ // Bail out if they user doesn't want to force the install
+ if (false === $helper->ask($input, $output, $question)) {
+ warningThenExit('Packages install aborted.');
+ }
+
+ // Re-run the install but add the force argument to
+ // prevent the incompatible exception from being thrown
+ $install_commands = $build_install_commands(true);
+ }
+
+ // Grab an instance of the CommandLine class
+ $cli = resolve(\ProcessMaker\Cli\CommandLine::class);
+
+ // Create a progress bar and start it
+ $cli->createProgressBar($install_commands->flatten()->count(), 'message');
+
+ // Set the initial message and start up the progress bar
+ $cli->getProgress()->setMessage('Starting install...');
+ $cli->getProgress()->start();
+
+ // Iterate through the collection of commands
+ foreach ($install_commands as $package => $command_collection) {
+
+ // Iterate through each command and attempt to run it
+ foreach ($command_collection as $command) {
+
+ // Update the progress bar
+ $cli->getProgress()->setMessage("Installing $package...");
+ $cli->getProgress()->advance();
+
+ try {
+ $command_output = $cli->runAsUser($command, static function ($exitCode, $out) {
+ throw new RuntimeException($out);
+ }, Config::codebasePath());
+ } catch (RuntimeException $exception) {
+
+ $cli->getProgress()->clear();
+
+ output("Command Failed:> $command");
+ output($exception->getMessage());
+
+ $cli->getProgress()->display();
+
+ continue;
+ }
+
+ if (!$verbose) {
+ continue;
+ }
+
+ // If the user wants verbose output, then show the
+ // stdout for all of the successful commands as well
+ // (in addition to the ones which failed)
+ $cli->getProgress()->clear();
+
+ output("Command Success: $command");
+ output($command_output);
+
+ $cli->getProgress()->display();
+ }
+ }
+
+ // Clean up the progress bar
+ $cli->getProgress()->finish();
+ $cli->getProgress()->clear();
+
+ // See how long it took to run everything
+ $timing = $cli->timing();
+
+ // Output and we're done!
+ output(PHP_EOL."Finished in $timing");
+
+ })->descriptions('Installs all enterprise packages in the local ProcessMaker core (processmaker/processmaker) codebase.', [
+ '--for_41_develop' => 'Uses 4.1 version of the supported packages'
+ ]);
+
+ /*
+ * -------------------------------------------------+
+ * | |
+ * | Command: Packages |
+ * | |
+ * -------------------------------------------------+
+ */
+ $app->command('packages', function () {
+
+ table(['Name', 'Version', 'Branch', 'Commit Hash'], Packages::getPackagesTableData());
+
+ })->descriptions('Display the current version, branch, and names of known local packages');
+
+ /*
+ * -------------------------------------------------+
+ * | |
+ * | Command: Pull |
+ * | |
+ * -------------------------------------------------+
+ */
+ $app->command('pull [-4|--for_41_develop]', function (InputInterface $input, OutputInterface $output) {
+
+ // Updates to 4.1-branch of packages (or not)
+ $for_41_develop = $input->getOption('for_41_develop');
+
+ // Set verbosity level of output
+ $verbose = $input->getOption('verbose');
+
+ // Put everything together and run it
+ $for_41_develop ? Packages::pull41($verbose) : Packages::pull($verbose);
+
+ })->descriptions('Cycles through each local store of supported ProcessMaker 4 packages.',
+ ['--for_41_develop' => 'Change each package to the correct version for the 4.1 version of processmaker/processmaker']
+ );
+
+ /*
+ * -------------------------------------------------+
+ * | |
+ * | Command: Clone All |
+ * | |
+ * -------------------------------------------------+
+ */
+ $app->command('clone-all [-f|--force]', function ($force = null) {
+ foreach (Packages::getSupportedPackages() as $index => $package) {
+ try {
+ if (Packages::clonePackage($package)) {
+ info("Package $package cloned successfully!");
+ }
+ } catch (Exception $exception) {
+ warning($exception->getMessage());
+ }
+ }
+ })->descriptions('Clone all supported ProcessMaker 4 packages to a local directory', [
+ '--force' => 'Delete the package locally if it exists already'
+ ]);
+
+}
+
+/*
+* -------------------------------------------------+
+* | |
+* | Command: Install Packages CI |
+* | |
+* -------------------------------------------------+
+*/
+$app->command('build-ci', function() {
+ PackagesCi::build();
+});
+
+$app->command('build-javascript-ci', function() {
+ PackagesCi::buildJavascript();
+});
+
+$app->command('install-ci', function() {
+ PackagesCi::install($this);
+});
+
+$app->run();
diff --git a/pm4-tools/composer.json b/pm4-tools/composer.json
new file mode 100644
index 0000000..b8567fa
--- /dev/null
+++ b/pm4-tools/composer.json
@@ -0,0 +1,41 @@
+{
+ "name": "processmaker/pm4-tools",
+ "description": "A lightweight cli tool to aid local develop of ProcessMaker software",
+ "type": "project",
+ "license": "MIT",
+ "bin": ["pm"],
+ "version": "0.5.0",
+ "authors": [
+ {
+ "name": "Alex Runyan",
+ "email": "alex@runyan.co"
+ }
+ ],
+ "require": {
+ "php": ">=7.2",
+ "ext-json": "*",
+ "ext-posix": "*",
+ "illuminate/support": "^8.73",
+ "illuminate/container": "^8.73",
+ "mnapoli/silly": "^1.7",
+ "react/child-process": "^0.6.4",
+ "symfony/process": "^5.3",
+ "symfony/console": "^5.3",
+ "mockery/mockery": "^1.4",
+ "phpunit/phpunit": "^9.5",
+ "vlucas/phpdotenv": "^5.4",
+ "brianium/paratest": "^6.4"
+ },
+ "require-dev": {
+ "beyondcode/laravel-dump-server": "^1.7"
+ },
+ "autoload": {
+ "files": [
+ "includes/facades.php",
+ "includes/helpers.php"
+ ],
+ "psr-4": {
+ "ProcessMaker\\Cli\\": "cli/ProcessMaker/"
+ }
+ }
+}
diff --git a/pm4-tools/composer.lock b/pm4-tools/composer.lock
new file mode 100644
index 0000000..c26eb77
--- /dev/null
+++ b/pm4-tools/composer.lock
@@ -0,0 +1,5762 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "ca1dfce4af07399a8852edf970504a2e",
+ "packages": [
+ {
+ "name": "brianium/paratest",
+ "version": "v6.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paratestphp/paratest.git",
+ "reference": "c32a5c4fc2ff339202437d25d19a5f496f880d61"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paratestphp/paratest/zipball/c32a5c4fc2ff339202437d25d19a5f496f880d61",
+ "reference": "c32a5c4fc2ff339202437d25d19a5f496f880d61",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-simplexml": "*",
+ "php": "^7.3 || ^8.0",
+ "phpunit/php-code-coverage": "^9.2.9",
+ "phpunit/php-file-iterator": "^3.0.5",
+ "phpunit/php-timer": "^5.0.3",
+ "phpunit/phpunit": "^9.5.10",
+ "sebastian/environment": "^5.1.3",
+ "symfony/console": "^5.4.0 || ^6.0.0",
+ "symfony/process": "^5.4.0 || ^6.0.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9.0.0",
+ "ekino/phpstan-banned-code": "^0.5.0",
+ "ergebnis/phpstan-rules": "^0.15.3",
+ "ext-posix": "*",
+ "infection/infection": "^0.25.3",
+ "malukenho/mcbumpface": "^1.1.5",
+ "phpstan/phpstan": "^0.12.99",
+ "phpstan/phpstan-deprecation-rules": "^0.12.6",
+ "phpstan/phpstan-phpunit": "^0.12.22",
+ "phpstan/phpstan-strict-rules": "^0.12.11",
+ "squizlabs/php_codesniffer": "^3.6.1",
+ "symfony/filesystem": "^v5.4.0",
+ "thecodingmachine/phpstan-strict-rules": "^v0.12.2",
+ "vimeo/psalm": "^4.13.1"
+ },
+ "bin": [
+ "bin/paratest"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ParaTest\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Scaturro",
+ "email": "scaturrob@gmail.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Filippo Tessarotto",
+ "email": "zoeslam@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "Parallel testing for PHP",
+ "homepage": "https://github.com/paratestphp/paratest",
+ "keywords": [
+ "concurrent",
+ "parallel",
+ "phpunit",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/paratestphp/paratest/issues",
+ "source": "https://github.com/paratestphp/paratest/tree/v6.4.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/Slamdunk",
+ "type": "github"
+ },
+ {
+ "url": "https://paypal.me/filippotessarotto",
+ "type": "paypal"
+ }
+ ],
+ "time": "2021-12-02T09:12:23+00:00"
+ },
+ {
+ "name": "doctrine/inflector",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89",
+ "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^8.2",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpstan/phpstan-strict-rules": "^0.12",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "vimeo/psalm": "^4.10"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-10-22T20:16:43+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
+ "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^8.0",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-10T18:47:58+00:00"
+ },
+ {
+ "name": "evenement/evenement",
+ "version": "v3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/igorw/evenement.git",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Evenement": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Igor Wiedler",
+ "email": "igor@wiedler.ch"
+ }
+ ],
+ "description": "Événement is a very simple event dispatching library for PHP",
+ "keywords": [
+ "event-dispatcher",
+ "event-emitter"
+ ],
+ "support": {
+ "issues": "https://github.com/igorw/evenement/issues",
+ "source": "https://github.com/igorw/evenement/tree/master"
+ },
+ "time": "2017-07-23T21:35:13+00:00"
+ },
+ {
+ "name": "graham-campbell/result-type",
+ "version": "v1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GrahamCampbell/Result-Type.git",
+ "reference": "0690bde05318336c7221785f2a932467f98b64ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca",
+ "reference": "0690bde05318336c7221785f2a932467f98b64ca",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "phpoption/phpoption": "^1.8"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GrahamCampbell\\ResultType\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "An Implementation Of The Result Type",
+ "keywords": [
+ "Graham Campbell",
+ "GrahamCampbell",
+ "Result Type",
+ "Result-Type",
+ "result"
+ ],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-21T21:41:47+00:00"
+ },
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3|^7.0|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ },
+ "time": "2020-07-09T08:09:16+00:00"
+ },
+ {
+ "name": "illuminate/collections",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/collections.git",
+ "reference": "5a018387352afa2af30fd2be0a78c31e93295720"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/collections/zipball/5a018387352afa2af30fd2be0a78c31e93295720",
+ "reference": "5a018387352afa2af30fd2be0a78c31e93295720",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/contracts": "^8.0",
+ "illuminate/macroable": "^8.0",
+ "php": "^7.3|^8.0"
+ },
+ "suggest": {
+ "symfony/var-dumper": "Required to use the dump method (^5.4)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ },
+ "files": [
+ "helpers.php"
+ ]
+ },
+ "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": "2021-12-07T14:48:29+00:00"
+ },
+ {
+ "name": "illuminate/container",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/container.git",
+ "reference": "6ac391bb27391706c5f921b85060aa2c4ca03fae"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/container/zipball/6ac391bb27391706c5f921b85060aa2c4ca03fae",
+ "reference": "6ac391bb27391706c5f921b85060aa2c4ca03fae",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/contracts": "^8.0",
+ "php": "^7.3|^8.0",
+ "psr/container": "^1.0"
+ },
+ "provide": {
+ "psr/container-implementation": "1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Container\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Container package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-11-17T15:04:30+00:00"
+ },
+ {
+ "name": "illuminate/contracts",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/contracts.git",
+ "reference": "b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f",
+ "reference": "b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3|^8.0",
+ "psr/container": "^1.0",
+ "psr/simple-cache": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Contracts\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Contracts package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-12-07T08:18:44+00:00"
+ },
+ {
+ "name": "illuminate/macroable",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/macroable.git",
+ "reference": "aed81891a6e046fdee72edd497f822190f61c162"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/macroable/zipball/aed81891a6e046fdee72edd497f822190f61c162",
+ "reference": "aed81891a6e046fdee72edd497f822190f61c162",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3|^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.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": "2021-11-16T13:57:03+00:00"
+ },
+ {
+ "name": "illuminate/support",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/support.git",
+ "reference": "f542bb78a8a1013ab33fbe14974c45b2a88993d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/support/zipball/f542bb78a8a1013ab33fbe14974c45b2a88993d0",
+ "reference": "f542bb78a8a1013ab33fbe14974c45b2a88993d0",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/inflector": "^1.4|^2.0",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "illuminate/collections": "^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/macroable": "^8.0",
+ "nesbot/carbon": "^2.53.1",
+ "php": "^7.3|^8.0",
+ "voku/portable-ascii": "^1.4.8"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "suggest": {
+ "illuminate/filesystem": "Required to use the composer class (^8.0).",
+ "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^1.3|^2.0.2).",
+ "ramsey/uuid": "Required to use Str::uuid() (^4.2.2).",
+ "symfony/process": "Required to use the composer class (^5.4).",
+ "symfony/var-dumper": "Required to use the dd function (^5.4).",
+ "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.2)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Support\\": ""
+ },
+ "files": [
+ "helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Support package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-12-03T15:33:45+00:00"
+ },
+ {
+ "name": "mnapoli/silly",
+ "version": "1.7.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mnapoli/silly.git",
+ "reference": "66807f87abd2ab8e5708754d70b4b601f5614c32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mnapoli/silly/zipball/66807f87abd2ab8e5708754d70b4b601f5614c32",
+ "reference": "66807f87abd2ab8e5708754d70b4b601f5614c32",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0",
+ "php-di/invoker": "~2.0",
+ "psr/container": "^1.0",
+ "symfony/console": "~3.0|~4.0|~5.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.12",
+ "mnapoli/phpunit-easymock": "~1.0",
+ "phpunit/phpunit": "~6.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Silly\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Silly CLI micro-framework based on Symfony Console",
+ "keywords": [
+ "PSR-11",
+ "cli",
+ "console",
+ "framework",
+ "micro-framework",
+ "silly"
+ ],
+ "support": {
+ "issues": "https://github.com/mnapoli/silly/issues",
+ "source": "https://github.com/mnapoli/silly/tree/1.7.2"
+ },
+ "time": "2019-11-26T20:07:27+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/e01123a0e847d52d186c5eb4b9bf58b0c6d00346",
+ "reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": "^7.3 || ^8.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5 || ^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Mockery": "library/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "http://blog.astrumfutura.com"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "http://davedevelopment.co.uk"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/mockery/mockery/issues",
+ "source": "https://github.com/mockery/mockery/tree/1.4.4"
+ },
+ "time": "2021-09-13T15:28:59+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.10.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
+ "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "replace": {
+ "myclabs/deep-copy": "self.version"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.0",
+ "doctrine/common": "^2.6",
+ "phpunit/phpunit": "^7.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ },
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-13T09:40:50+00:00"
+ },
+ {
+ "name": "nesbot/carbon",
+ "version": "2.55.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/briannesbitt/Carbon.git",
+ "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8c2a18ce3e67c34efc1b29f64fe61304368259a2",
+ "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.1.8 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^2.0 || ^3.0",
+ "doctrine/orm": "^2.7",
+ "friendsofphp/php-cs-fixer": "^3.0",
+ "kylekatarnls/multi-tester": "^2.0",
+ "phpmd/phpmd": "^2.9",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^0.12.54",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.14",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "bin": [
+ "bin/carbon"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-3.x": "3.x-dev",
+ "dev-master": "2.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Carbon\\Laravel\\ServiceProvider"
+ ]
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Carbon\\": "src/Carbon/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brian Nesbitt",
+ "email": "brian@nesbot.com",
+ "homepage": "https://markido.com"
+ },
+ {
+ "name": "kylekatarnls",
+ "homepage": "https://github.com/kylekatarnls"
+ }
+ ],
+ "description": "An API extension for DateTime that supports 281 different languages.",
+ "homepage": "https://carbon.nesbot.com",
+ "keywords": [
+ "date",
+ "datetime",
+ "time"
+ ],
+ "support": {
+ "docs": "https://carbon.nesbot.com/docs",
+ "issues": "https://github.com/briannesbitt/Carbon/issues",
+ "source": "https://github.com/briannesbitt/Carbon"
+ },
+ "funding": [
+ {
+ "url": "https://opencollective.com/Carbon",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-03T14:59:52+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v4.13.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
+ },
+ "time": "2021-11-30T19:35:32+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+ },
+ "time": "2021-07-20T11:28:43+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "bae7c545bef187884426f042434e561ab1ddb182"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
+ "reference": "bae7c545bef187884426f042434e561ab1ddb182",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.1.0"
+ },
+ "time": "2021-02-23T14:00:09+00:00"
+ },
+ {
+ "name": "php-di/invoker",
+ "version": "2.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHP-DI/Invoker.git",
+ "reference": "5214cbe5aad066022cd845dbf313f0e47aed928f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/5214cbe5aad066022cd845dbf313f0e47aed928f",
+ "reference": "5214cbe5aad066022cd845dbf313f0e47aed928f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "psr/container": "^1.0|^2.0"
+ },
+ "require-dev": {
+ "athletic/athletic": "~0.1.8",
+ "mnapoli/hard-mode": "~0.3.0",
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Invoker\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Generic and extensible callable invoker",
+ "homepage": "https://github.com/PHP-DI/Invoker",
+ "keywords": [
+ "callable",
+ "dependency",
+ "dependency-injection",
+ "injection",
+ "invoke",
+ "invoker"
+ ],
+ "support": {
+ "issues": "https://github.com/PHP-DI/Invoker/issues",
+ "source": "https://github.com/PHP-DI/Invoker/tree/2.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-30T15:05:32+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "5.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.3",
+ "webmozart/assert": "^1.9.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.2",
+ "psalm/phar": "^4.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ },
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "account@ijaap.nl"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+ },
+ "time": "2021-10-19T17:43:47+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae",
+ "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "psalm/phar": "^4.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1"
+ },
+ "time": "2021-10-02T14:08:47+00:00"
+ },
+ {
+ "name": "phpoption/phpoption",
+ "version": "1.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15",
+ "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpOption\\": "src/PhpOption/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com",
+ "homepage": "https://github.com/schmittjoh"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "Option Type for PHP",
+ "keywords": [
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.8.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-12-04T23:24:31+00:00"
+ },
+ {
+ "name": "phpspec/prophecy",
+ "version": "1.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy.git",
+ "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
+ "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.2",
+ "php": "^7.2 || ~8.0, <8.2",
+ "phpdocumentor/reflection-docblock": "^5.2",
+ "sebastian/comparator": "^3.0 || ^4.0",
+ "sebastian/recursion-context": "^3.0 || ^4.0"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "^6.0 || ^7.0",
+ "phpunit/phpunit": "^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Prophecy\\": "src/Prophecy"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Konstantin Kudryashov",
+ "email": "ever.zet@gmail.com",
+ "homepage": "http://everzet.com"
+ },
+ {
+ "name": "Marcello Duarte",
+ "email": "marcello.duarte@gmail.com"
+ }
+ ],
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
+ "homepage": "https://github.com/phpspec/prophecy",
+ "keywords": [
+ "Double",
+ "Dummy",
+ "fake",
+ "mock",
+ "spy",
+ "stub"
+ ],
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy/issues",
+ "source": "https://github.com/phpspec/prophecy/tree/1.14.0"
+ },
+ "time": "2021-09-10T09:02:12+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "9.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687",
+ "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.13.0",
+ "php": ">=7.3",
+ "phpunit/php-file-iterator": "^3.0.3",
+ "phpunit/php-text-template": "^2.0.2",
+ "sebastian/code-unit-reverse-lookup": "^2.0.2",
+ "sebastian/complexity": "^2.0",
+ "sebastian/environment": "^5.1.2",
+ "sebastian/lines-of-code": "^1.0.3",
+ "sebastian/version": "^3.0.1",
+ "theseer/tokenizer": "^1.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcov": "*",
+ "ext-xdebug": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-05T09:12:13+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "3.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-12-02T12:48:52+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "3.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:58:55+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T05:33:50+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:16:10+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "9.5.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
+ "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.3.1",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.10.1",
+ "phar-io/manifest": "^2.0.3",
+ "phar-io/version": "^3.0.2",
+ "php": ">=7.3",
+ "phpspec/prophecy": "^1.12.1",
+ "phpunit/php-code-coverage": "^9.2.7",
+ "phpunit/php-file-iterator": "^3.0.5",
+ "phpunit/php-invoker": "^3.1.1",
+ "phpunit/php-text-template": "^2.0.3",
+ "phpunit/php-timer": "^5.0.2",
+ "sebastian/cli-parser": "^1.0.1",
+ "sebastian/code-unit": "^1.0.6",
+ "sebastian/comparator": "^4.0.5",
+ "sebastian/diff": "^4.0.3",
+ "sebastian/environment": "^5.1.3",
+ "sebastian/exporter": "^4.0.3",
+ "sebastian/global-state": "^5.0.1",
+ "sebastian/object-enumerator": "^4.0.3",
+ "sebastian/resource-operations": "^3.0.3",
+ "sebastian/type": "^2.3.4",
+ "sebastian/version": "^3.0.2"
+ },
+ "require-dev": {
+ "ext-pdo": "*",
+ "phpspec/prophecy-phpunit": "^2.0.1"
+ },
+ "suggest": {
+ "ext-soap": "*",
+ "ext-xdebug": "*"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.5-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ],
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/donate.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-09-25T07:38:51+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
+ },
+ "time": "2021-11-05T16:50:12+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/master"
+ },
+ "time": "2017-10-23T01:57:42+00:00"
+ },
+ {
+ "name": "react/child-process",
+ "version": "v0.6.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/child-process.git",
+ "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/child-process/zipball/a778f3fb828d68caf8a9ab6567fd8342a86f12fe",
+ "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.0",
+ "react/event-loop": "^1.2",
+ "react/stream": "^1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
+ "react/socket": "^1.8",
+ "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\ChildProcess\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven library for executing child processes with ReactPHP.",
+ "keywords": [
+ "event-driven",
+ "process",
+ "reactphp"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/child-process/issues",
+ "source": "https://github.com/reactphp/child-process/tree/v0.6.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-10-12T10:37:07+00:00"
+ },
+ {
+ "name": "react/event-loop",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/event-loop.git",
+ "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2",
+ "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
+ },
+ "suggest": {
+ "ext-event": "~1.0 for ExtEventLoop",
+ "ext-pcntl": "For signal handling support when using the StreamSelectLoop",
+ "ext-uv": "* for ExtUvLoop"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\EventLoop\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
+ "keywords": [
+ "asynchronous",
+ "event-loop"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/event-loop/issues",
+ "source": "https://github.com/reactphp/event-loop/tree/v1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-11T12:31:24+00:00"
+ },
+ {
+ "name": "react/stream",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/stream.git",
+ "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9",
+ "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9",
+ "shasum": ""
+ },
+ "require": {
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "php": ">=5.3.8",
+ "react/event-loop": "^1.2"
+ },
+ "require-dev": {
+ "clue/stream-filter": "~1.2",
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Stream\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering",
+ "homepage": "https://clue.engineering/"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "reactphp@ceesjankiewiet.nl",
+ "homepage": "https://wyrihaximus.net/"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com",
+ "homepage": "https://sorgalla.com/"
+ },
+ {
+ "name": "Chris Boden",
+ "email": "cboden@gmail.com",
+ "homepage": "https://cboden.dev/"
+ }
+ ],
+ "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
+ "keywords": [
+ "event-driven",
+ "io",
+ "non-blocking",
+ "pipe",
+ "reactphp",
+ "readable",
+ "stream",
+ "writable"
+ ],
+ "support": {
+ "issues": "https://github.com/reactphp/stream/issues",
+ "source": "https://github.com/reactphp/stream/tree/v1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/WyriHaximus",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/clue",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-11T12:37:55+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:08:49+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:08:54+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:30:19+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "4.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "55f4261989e546dc112258c7a75935a81a7ce382"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
+ "reference": "55f4261989e546dc112258c7a75935a81a7ce382",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/diff": "^4.0",
+ "sebastian/exporter": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T15:49:45+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.7",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T15:52:27+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:10:38+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "5.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "388b6ced16caa751030f6a69e588299fa09200ac"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
+ "reference": "388b6ced16caa751030f6a69e588299fa09200ac",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T05:52:38+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-mbstring": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-11-11T14:18:36+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "5.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49",
+ "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^9.3"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-06-11T13:31:12+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.6",
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-28T06:42:11+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3",
+ "sebastian/object-reflector": "^2.0",
+ "sebastian/recursion-context": "^4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:12:34+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:14:26+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-10-26T13:17:30+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:45:17+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "2.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914",
+ "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.3-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/2.3.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2021-06-15T12:49:02+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2020-09-28T06:39:44+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "ec3661faca1d110d6c307e124b44f99ac54179e3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/ec3661faca1d110d6c307e124b44f99ac54179e3",
+ "reference": "ec3661faca1d110d6c307e124b44f99ac54179e3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php73": "^1.8",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.1|^6.0"
+ },
+ "conflict": {
+ "psr/log": ">=3",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/dotenv": "<5.1",
+ "symfony/event-dispatcher": "<4.4",
+ "symfony/lock": "<4.4",
+ "symfony/process": "<4.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/event-dispatcher": "^4.4|^5.0|^6.0",
+ "symfony/lock": "^4.4|^5.0|^6.0",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "psr/log": "For using the console logger",
+ "symfony/event-dispatcher": "",
+ "symfony/lock": "",
+ "symfony/process": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v5.4.0"
+ },
+ "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": "2021-11-29T15:30:56+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
+ "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "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": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
+ },
+ "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": "2021-07-12T14:48:14+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
+ "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0"
+ },
+ "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": "2021-02-19T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "16880ba9c5ebe3642d1995ab866db29270b36535"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535",
+ "reference": "16880ba9c5ebe3642d1995ab866db29270b36535",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "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": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1"
+ },
+ "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": "2021-05-27T12:26:48+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
+ "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "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": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0"
+ },
+ "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": "2021-02-19T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6",
+ "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "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": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1"
+ },
+ "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": "2021-05-27T12:26:48+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php73",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php73.git",
+ "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
+ "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php73\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0"
+ },
+ "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": "2021-02-19T12:13:01+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.23.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
+ "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ],
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
+ },
+ "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": "2021-07-28T13:41:28+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "5be20b3830f726e019162b26223110c8f47cf274"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/5be20b3830f726e019162b26223110c8f47cf274",
+ "reference": "5be20b3830f726e019162b26223110c8f47cf274",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v5.4.0"
+ },
+ "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": "2021-11-28T15:25:38+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
+ "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.1",
+ "symfony/deprecation-contracts": "^2.1"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ }
+ },
+ "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": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
+ },
+ "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": "2021-11-04T16:48:04+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d",
+ "reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "~1.15"
+ },
+ "conflict": {
+ "symfony/translation-contracts": ">=3.0"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/http-client": "^4.4|^5.0|^6.0",
+ "symfony/translation-contracts": "^1.1|^2",
+ "symfony/var-exporter": "^4.4|^5.0|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "files": [
+ "Resources/functions.php"
+ ],
+ "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 an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v5.4.0"
+ },
+ "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": "2021-11-24T10:02:00+00:00"
+ },
+ {
+ "name": "symfony/translation",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation.git",
+ "reference": "6fe32b10e912a518805bc9eafc2a87145773cf13"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/6fe32b10e912a518805bc9eafc2a87145773cf13",
+ "reference": "6fe32b10e912a518805bc9eafc2a87145773cf13",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "^1.16",
+ "symfony/translation-contracts": "^2.3"
+ },
+ "conflict": {
+ "symfony/config": "<4.4",
+ "symfony/console": "<5.3",
+ "symfony/dependency-injection": "<5.0",
+ "symfony/http-kernel": "<5.0",
+ "symfony/twig-bundle": "<5.0",
+ "symfony/yaml": "<4.4"
+ },
+ "provide": {
+ "symfony/translation-implementation": "2.3"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.0|^6.0",
+ "symfony/finder": "^4.4|^5.0|^6.0",
+ "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
+ "symfony/http-kernel": "^5.0|^6.0",
+ "symfony/intl": "^4.4|^5.0|^6.0",
+ "symfony/polyfill-intl-icu": "^1.21",
+ "symfony/service-contracts": "^1.1.2|^2|^3",
+ "symfony/yaml": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "psr/log-implementation": "To use logging capability in translator",
+ "symfony/config": "",
+ "symfony/yaml": ""
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to internationalize your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/translation/tree/v5.4.0"
+ },
+ "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": "2021-11-29T15:30:56+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e",
+ "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5"
+ },
+ "suggest": {
+ "symfony/translation-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ }
+ },
+ "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": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0"
+ },
+ "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": "2021-08-17T14:20:01+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2021-07-28T10:34:58+00:00"
+ },
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "d4394d044ed69a8f244f3445bcedf8a0d7fe2403"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/d4394d044ed69a8f244f3445bcedf8a0d7fe2403",
+ "reference": "d4394d044ed69a8f244f3445bcedf8a0d7fe2403",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "graham-campbell/result-type": "^1.0.2",
+ "php": "^7.1.3 || ^8.0",
+ "phpoption/phpoption": "^1.8",
+ "symfony/polyfill-ctype": "^1.23",
+ "symfony/polyfill-mbstring": "^1.23.1",
+ "symfony/polyfill-php80": "^1.23.1"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4.1",
+ "ext-filter": "*",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10"
+ },
+ "suggest": {
+ "ext-filter": "Required to use the boolean validator."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk"
+ },
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `Config::env()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-11-10T01:08:39+00:00"
+ },
+ {
+ "name": "voku/portable-ascii",
+ "version": "1.5.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/voku/portable-ascii.git",
+ "reference": "80953678b19901e5165c56752d087fc11526017c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c",
+ "reference": "80953678b19901e5165c56752d087fc11526017c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
+ },
+ "suggest": {
+ "ext-intl": "Use Intl for transliterator_transliterate() support"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "voku\\": "src/voku/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lars Moelleken",
+ "homepage": "http://www.moelleken.org/"
+ }
+ ],
+ "description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
+ "homepage": "https://github.com/voku/portable-ascii",
+ "keywords": [
+ "ascii",
+ "clean",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/voku/portable-ascii/issues",
+ "source": "https://github.com/voku/portable-ascii/tree/1.5.6"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/moelleken",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/voku",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/portable-ascii",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://www.patreon.com/voku",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-12T00:07:28+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
+ "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.10.0"
+ },
+ "time": "2021-03-09T10:59:23+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "beyondcode/laravel-dump-server",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/beyondcode/laravel-dump-server.git",
+ "reference": "e27c7b942ab62f6ac7168359393d328ec5215b89"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/beyondcode/laravel-dump-server/zipball/e27c7b942ab62f6ac7168359393d328ec5215b89",
+ "reference": "e27c7b942ab62f6ac7168359393d328ec5215b89",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/console": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0",
+ "illuminate/http": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0",
+ "illuminate/support": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0",
+ "php": ">=7.2.5",
+ "symfony/var-dumper": "^5.0"
+ },
+ "require-dev": {
+ "larapack/dd": "^1.0",
+ "phpunit/phpunit": "^7.0|^9.3"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "BeyondCode\\DumpServer\\DumpServerServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "BeyondCode\\DumpServer\\": "src"
+ },
+ "files": [
+ "helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marcel Pociot",
+ "email": "marcel@beyondco.de",
+ "homepage": "https://beyondco.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Symfony Var-Dump Server for Laravel",
+ "homepage": "https://github.com/beyondcode/laravel-dump-server",
+ "keywords": [
+ "beyondcode",
+ "laravel-dump-server"
+ ],
+ "support": {
+ "issues": "https://github.com/beyondcode/laravel-dump-server/issues",
+ "source": "https://github.com/beyondcode/laravel-dump-server/tree/1.7.0"
+ },
+ "time": "2020-12-15T10:57:43+00:00"
+ },
+ {
+ "name": "illuminate/console",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/console.git",
+ "reference": "1f952710c671b8ca90bb10be585ebcc94f72166a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/console/zipball/1f952710c671b8ca90bb10be585ebcc94f72166a",
+ "reference": "1f952710c671b8ca90bb10be585ebcc94f72166a",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/collections": "^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/macroable": "^8.0",
+ "illuminate/support": "^8.0",
+ "php": "^7.3|^8.0",
+ "symfony/console": "^5.4",
+ "symfony/process": "^5.4"
+ },
+ "suggest": {
+ "dragonmantank/cron-expression": "Required to use scheduler (^3.0.2).",
+ "guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.5.5|^7.0.1).",
+ "illuminate/bus": "Required to use the scheduled job dispatcher (^8.0).",
+ "illuminate/container": "Required to use the scheduler (^8.0).",
+ "illuminate/filesystem": "Required to use the generator command (^8.0).",
+ "illuminate/queue": "Required to use closures for scheduled jobs (^8.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Console\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Console package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-11-30T14:13:40+00:00"
+ },
+ {
+ "name": "illuminate/filesystem",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/filesystem.git",
+ "reference": "b8f4b43a65d0e93312d8599bc4e86e21f68f7ccf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/filesystem/zipball/b8f4b43a65d0e93312d8599bc4e86e21f68f7ccf",
+ "reference": "b8f4b43a65d0e93312d8599bc4e86e21f68f7ccf",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/collections": "^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/macroable": "^8.0",
+ "illuminate/support": "^8.0",
+ "php": "^7.3|^8.0",
+ "symfony/finder": "^5.4"
+ },
+ "suggest": {
+ "ext-ftp": "Required to use the Flysystem FTP driver.",
+ "illuminate/http": "Required for handling uploaded files (^7.0).",
+ "league/flysystem": "Required to use the Flysystem local and FTP drivers (^1.1).",
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).",
+ "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).",
+ "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
+ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+ "symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).",
+ "symfony/mime": "Required to enable support for guessing extensions (^5.4)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Filesystem\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Filesystem package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-11-30T14:13:40+00:00"
+ },
+ {
+ "name": "illuminate/http",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/http.git",
+ "reference": "9750b34b7df8f5a3ef66328dc10a7a51e5d13f6c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/http/zipball/9750b34b7df8f5a3ef66328dc10a7a51e5d13f6c",
+ "reference": "9750b34b7df8f5a3ef66328dc10a7a51e5d13f6c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/collections": "^8.0",
+ "illuminate/macroable": "^8.0",
+ "illuminate/session": "^8.0",
+ "illuminate/support": "^8.0",
+ "php": "^7.3|^8.0",
+ "symfony/http-foundation": "^5.4",
+ "symfony/http-kernel": "^5.4",
+ "symfony/mime": "^5.4"
+ },
+ "suggest": {
+ "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+ "guzzlehttp/guzzle": "Required to use the HTTP Client (^6.5.5|^7.0.1)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Http\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Http package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-11-30T21:48:40+00:00"
+ },
+ {
+ "name": "illuminate/session",
+ "version": "v8.75.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/illuminate/session.git",
+ "reference": "c5964a2348354f266fb18aac60eed0b81355c258"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/illuminate/session/zipball/c5964a2348354f266fb18aac60eed0b81355c258",
+ "reference": "c5964a2348354f266fb18aac60eed0b81355c258",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/collections": "^8.0",
+ "illuminate/contracts": "^8.0",
+ "illuminate/filesystem": "^8.0",
+ "illuminate/support": "^8.0",
+ "php": "^7.3|^8.0",
+ "symfony/finder": "^5.4",
+ "symfony/http-foundation": "^5.4"
+ },
+ "suggest": {
+ "illuminate/console": "Required to use the session:table command (^8.0)."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Session\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "The Illuminate Session package.",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "time": "2021-11-30T14:13:40+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
+ },
+ "time": "2021-05-03T11:20:27+00:00"
+ },
+ {
+ "name": "symfony/error-handler",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/error-handler.git",
+ "reference": "8433fa3145ac78df88b87a4a539118e950828126"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/8433fa3145ac78df88b87a4a539118e950828126",
+ "reference": "8433fa3145ac78df88b87a4a539118e950828126",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/log": "^1|^2|^3",
+ "symfony/var-dumper": "^4.4|^5.0|^6.0"
+ },
+ "require-dev": {
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/http-kernel": "^4.4|^5.0|^6.0",
+ "symfony/serializer": "^4.4|^5.0|^6.0"
+ },
+ "bin": [
+ "Resources/bin/patch-type-declarations"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\ErrorHandler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools to manage errors and ease debugging PHP code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v5.4.0"
+ },
+ "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": "2021-11-29T15:30:56+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/27d39ae126352b9fa3be5e196ccf4617897be3eb",
+ "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/event-dispatcher-contracts": "^2|^3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<4.4"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/http-foundation": "^4.4|^5.0|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/stopwatch": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.0"
+ },
+ "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": "2021-11-23T10:19:22+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v2.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a",
+ "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/event-dispatcher": "^1"
+ },
+ "suggest": {
+ "symfony/event-dispatcher-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "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": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0"
+ },
+ "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": "2021-07-12T14:48:14+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/d2f29dac98e96a98be467627bd49c2efb1bc2590",
+ "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v5.4.0"
+ },
+ "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": "2021-11-28T15:25:38+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "5ef86ac7927d2de08dc1e26eb91325f9ccbe6309"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5ef86ac7927d2de08dc1e26eb91325f9ccbe6309",
+ "reference": "5ef86ac7927d2de08dc1e26eb91325f9ccbe6309",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.1",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "predis/predis": "~1.0",
+ "symfony/cache": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/mime": "^4.4|^5.0|^6.0"
+ },
+ "suggest": {
+ "symfony/mime": "To use the file extension guesser"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v5.4.0"
+ },
+ "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": "2021-11-28T15:25:38+00:00"
+ },
+ {
+ "name": "symfony/http-kernel",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-kernel.git",
+ "reference": "e012f16688bcb151e965473a70d8ebaa8b1d15ea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e012f16688bcb151e965473a70d8ebaa8b1d15ea",
+ "reference": "e012f16688bcb151e965473a70d8ebaa8b1d15ea",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/log": "^1|^2",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/error-handler": "^4.4|^5.0|^6.0",
+ "symfony/event-dispatcher": "^5.0|^6.0",
+ "symfony/http-foundation": "^5.3.7|^6.0",
+ "symfony/polyfill-ctype": "^1.8",
+ "symfony/polyfill-php73": "^1.9",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<5.4",
+ "symfony/cache": "<5.0",
+ "symfony/config": "<5.0",
+ "symfony/console": "<4.4",
+ "symfony/dependency-injection": "<5.3",
+ "symfony/doctrine-bridge": "<5.0",
+ "symfony/form": "<5.0",
+ "symfony/http-client": "<5.0",
+ "symfony/mailer": "<5.0",
+ "symfony/messenger": "<5.0",
+ "symfony/translation": "<5.0",
+ "symfony/twig-bridge": "<5.0",
+ "symfony/validator": "<5.0",
+ "twig/twig": "<2.13"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "symfony/browser-kit": "^5.4|^6.0",
+ "symfony/config": "^5.0|^6.0",
+ "symfony/console": "^4.4|^5.0|^6.0",
+ "symfony/css-selector": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^5.3|^6.0",
+ "symfony/dom-crawler": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/finder": "^4.4|^5.0|^6.0",
+ "symfony/http-client-contracts": "^1.1|^2|^3",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/routing": "^4.4|^5.0|^6.0",
+ "symfony/stopwatch": "^4.4|^5.0|^6.0",
+ "symfony/translation": "^4.4|^5.0|^6.0",
+ "symfony/translation-contracts": "^1.1|^2|^3",
+ "twig/twig": "^2.13|^3.0.4"
+ },
+ "suggest": {
+ "symfony/browser-kit": "",
+ "symfony/config": "",
+ "symfony/console": "",
+ "symfony/dependency-injection": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpKernel\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides a structured process for converting a Request into a Response",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v5.4.0"
+ },
+ "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": "2021-11-29T16:56:53+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "d4365000217b67c01acff407573906ff91bcfb34"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/d4365000217b67c01acff407573906ff91bcfb34",
+ "reference": "d4365000217b67c01acff407573906ff91bcfb34",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/mailer": "<4.4"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/property-access": "^4.4|^5.1|^6.0",
+ "symfony/property-info": "^4.4|^5.1|^6.0",
+ "symfony/serializer": "^5.2|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "mime",
+ "mime-type"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v5.4.0"
+ },
+ "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": "2021-11-23T10:19:22+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/65bd267525e82759e7d8c4e8ceea44f398838e65",
+ "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "symfony/polyfill-intl-normalizer": "^1.10",
+ "symfony/polyfill-php72": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.23.0"
+ },
+ "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": "2021-05-27T09:27:20+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.23.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
+ "reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.23-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "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": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0"
+ },
+ "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": "2021-05-27T09:17:38+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v5.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "89ab66eaef230c9cd1992de2e9a1b26652b127b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/89ab66eaef230c9cd1992de2e9a1b26652b127b9",
+ "reference": "89ab66eaef230c9cd1992de2e9a1b26652b127b9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<5.4.3",
+ "symfony/console": "<4.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^4.4|^5.0|^6.0",
+ "symfony/process": "^4.4|^5.0|^6.0",
+ "symfony/uid": "^5.1|^6.0",
+ "twig/twig": "^2.13|^3.0.4"
+ },
+ "suggest": {
+ "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+ "ext-intl": "To show region name in time zone dump",
+ "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+ },
+ "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/v5.4.0"
+ },
+ "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": "2021-11-29T15:30:56+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=7.2",
+ "ext-json": "*",
+ "ext-posix": "*"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.0.0"
+}
diff --git a/pm4-tools/includes/facades.php b/pm4-tools/includes/facades.php
new file mode 100644
index 0000000..f42eed7
--- /dev/null
+++ b/pm4-tools/includes/facades.php
@@ -0,0 +1,83 @@
+make(static::containerKey());
+
+ return call_user_func_array([$resolvedInstance, $method], $parameters);
+ }
+}
+
+/**
+ * @see \ProcessMaker\Cli\CommandLine
+ */
+class CommandLine extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\FileSystem
+ */
+class FileSystem extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\Packages
+ */
+class Packages extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\Install
+ */
+class Install extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\ProcessManager
+ */
+class ProcessManager extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\Composer
+ */
+class Composer extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\Git
+ */
+class Git extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\PackagesCi
+ */
+class PackagesCi extends Facade {}
+
+/**
+ * @see \ProcessMaker\Cli\Config
+ */
+class Config extends Facade {}
+
+
+/**
+ * @see \ProcessMaker\Cli\Config
+ */
+class PhpUnit extends Facade {}
\ No newline at end of file
diff --git a/pm4-tools/includes/helpers.php b/pm4-tools/includes/helpers.php
new file mode 100644
index 0000000..eb9bb88
--- /dev/null
+++ b/pm4-tools/includes/helpers.php
@@ -0,0 +1,132 @@
+runAsUser('printf $(brew --prefix)'));
+// }
+
+if (!defined('PM_HOME_PATH')) {
+ define('PM_HOME_PATH', $_SERVER['HOME'] . '/.config/pm');
+}
+
+if (!defined('USER_HOME')) {
+ define('USER_HOME', getenv('HOME'));
+}
+
+/**
+ * @param string $output
+ * @param int $exitCode
+ *
+ * @return string
+ */
+function warningThenExit(string $output, int $exitCode = 0): string {
+ return warning($output) . exit($exitCode);
+}
+
+/**
+ * Resolve the given class from the container.
+ *
+ * @param string $class
+ *
+ * @return mixed
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
+ */
+function resolve(string $class) {
+ return Container::getInstance()->make($class);
+}
+
+/**
+ * Swap the given class implementation in the container.
+ *
+ * @param string $class
+ * @param mixed $instance
+ *
+ * @return void
+ */
+function swap(string $class, $instance) {
+ Container::getInstance()->instance($class, $instance);
+}
+
+/**
+ * @return mixed
+ */
+function user() {
+ if (! isset($_SERVER['SUDO_USER'])) {
+ return $_SERVER['USER'];
+ }
+
+ return $_SERVER['SUDO_USER'];
+}
+
+/**
+ * Verify that the script is currently running as "sudo".
+ *
+ * @return void
+ * @throws \Exception
+ */
+function should_be_sudo() {
+ if (!isset($_SERVER['SUDO_USER'])) {
+ throw new Exception('This command must be run with sudo.');
+ }
+}
+
+/**
+ * Output the given text to the console.
+ *
+ * @param string $output
+ *
+ * @return void
+ */
+function info(string $output) {
+ $secrets = [
+ Config::env('GITHUB_TOKEN', true)
+ ];
+ $output = str_replace($secrets, '****', $output);
+ output(''.$output.'');
+}
+
+/**
+ * Output the given text to the console.
+ *
+ * @param string $output
+ *
+ * @return void
+ */
+function warning(string $output) {
+ output('' . $output . '>');
+}
+
+/**
+ * Output a table to the console.
+ *
+ * @param array $headers
+ * @param array $rows
+ * @return void
+ */
+function table(array $headers = [], array $rows = []) {
+ $table = new Table(new ConsoleOutput);
+ $table->setHeaders($headers)->setRows($rows);
+ $table->render();
+}
+
+/**
+ * Output the given text to the console.
+ *
+ * @param string $output
+ *
+ * @return void
+ */
+function output(string $output) {
+ if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') {
+ return;
+ }
+
+ (new ConsoleOutput())->writeln($output);
+}
diff --git a/pm4-tools/phpunit.xml b/pm4-tools/phpunit.xml
new file mode 100644
index 0000000..1c9c237
--- /dev/null
+++ b/pm4-tools/phpunit.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ ./tests
+
+
+
+
+
+
diff --git a/pm4-tools/pm b/pm4-tools/pm
new file mode 100755
index 0000000..370125d
--- /dev/null
+++ b/pm4-tools/pm
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+SOURCE="${BASH_SOURCE[0]}"
+
+# If the current source is a symbolic link, we need to resolve it to an
+# actual directory name. We'll use PHP to do this easier than we can
+# do it in pure Bash. So, we'll call into PHP CLI here to resolve.
+if [[ -L "$SOURCE" ]]; then
+ DIR=$(php -r "echo dirname(realpath('$SOURCE'));")
+else
+ DIR="$( cd "$( dirname "$SOURCE" )" && pwd )"
+fi
+
+# If we are in the global Composer "bin" directory, we need to bump our
+# current directory up two, so that we will correctly proxy into the
+# Valet CLI script which is written in PHP. Will use PHP to do it.
+if [ ! -f "$DIR/cli/pm.php" ]; then
+ DIR=$(php -r "echo realpath('$DIR/../runyan-co/pm');")
+fi
+
+if [[ "$EUID" -ne 0 && "$PM_CI" -ne "true" ]]; then
+ sudo USER="$USER" --preserve-env "$SOURCE" "$@"
+ exit
+fi
+
+php "$DIR/cli/pm.php" "$@"
diff --git a/pr_body_example.txt b/pr_body_example.txt
new file mode 100644
index 0000000..061aab4
--- /dev/null
+++ b/pr_body_example.txt
@@ -0,0 +1,8 @@
+## Test PR
+
+Branches needed:
+
+ci:connector-send-email:updates-for-ci
+
+Done!
+
diff --git a/run-endtest-tests.sh b/run-endtest-tests.sh
new file mode 100755
index 0000000..4f34aa7
--- /dev/null
+++ b/run-endtest-tests.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+## TODO: Add here the test suites that we want to execute. Ex.
+# testSuites=(350178)
+# testSuites=(245814 245815)
+testSuites=(357469)
+
+declare -A testsStatus
+declare -A testsHashes
+declare -A testsRequest
+
+# Default Values
+AppID=${ENDTEST_APP_ID:=""}
+AppCode=${ENDTEST_APP_CODE:=""}
+SERVER_URL=${SERVER_URL:=""}
+PM_USER="admin"
+PM_PASSWORD="admin"
+
+echo "Sending tests to EndTest platform"
+timeElapsed=0
+exitCode=0
+for test in "${testSuites[@]}"
+do
+ testsStatus[$test]=""
+ testsHashes[$test]=""
+ testsRequest[$test]="https://endtest.io/api.php?action=runWeb&appId=${AppID}&appCode=${AppCode}&suite=${test}&platform=windows&os=windows10&browser=chrome&resolution=1920x1200&geolocation=sanfrancisco&cases=all¬es=TestFromEndtest.cloud.processmaker.io&username=${PM_USER}&pwd=${PM_PASSWORD}&URL=${SERVER_URL}"
+
+ echo "Get Hash for execution test suite id: $test"
+ testsHashes[$test]=$(curl -s -X GET --header "Accept: */*" "${testsRequest[$test]}")
+ echo "Hash: ${testsHashes[$test]}"
+ # TODO: if not_allowed then show error
+ pendingTest=true
+ echo "Test in progress..."
+ testTimeElapsed=0
+ while $pendingTest; do
+ sleep 60
+ timeElapsed=$((timeElapsed + 1))
+ testTimeElapsed=$((testTimeElapsed + 1))
+ result=$(curl -s -X GET --header "Accept: */*" "https://endtest.io/api.php?action=getResults&appId=${AppID}&appCode=${AppCode}&hash=${testsHashes[$test]}&format=json")
+ if [ "$result" == "Test is still running." ]
+ then
+ testsStatus[$test]=$result
+ echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes."
+ pendingTest=true
+ elif [ "$result" == "Processing video recording." ]
+ then
+ testsStatus[$test]=$result
+ echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes."
+ pendingTest=true
+ elif [ "$result" == "Stopping." ]
+ then
+ testsStatus[$test]=$result
+ echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes."
+ pendingTest=true
+ elif [ "$result" == "Erred." ]
+ then
+ testsStatus[$test]=$result
+ echo "TestSuite failed for ID: $test, Status: $result"
+ echo "Time Elapsed of this test: $testTimeElapsed minutes."
+ echo "Please check this link for detailed info: https://endtest.io/results?hash=${testsHashes[$test]}"
+ pendingTest=false
+ exitCode=1
+ elif [ "$result" == "" ]
+ then
+ testsStatus[$test]=$result
+ echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes."
+ else
+ # TODO - Nice to have: Upload results to CI
+ echo "$result" > "$test".json
+ pendingTest=false
+ testsStatus[$test]="Completed"
+ echo "Test completed for ID: $test. Time Elapsed of this test: $testTimeElapsed minutes."
+ fi
+ done
+done
+
+if [ $exitCode -ne 0 ]; then
+ echo "TestSuites finished with Errors, please see logs above."
+else
+ echo "TestSuites finished successfully."
+fi
+
+echo "Elapsed time of all tests: $timeElapsed"
+exit $exitCode
diff --git a/set_context_example.sh b/set_context_example.sh
new file mode 100644
index 0000000..8fa5a7a
--- /dev/null
+++ b/set_context_example.sh
@@ -0,0 +1,7 @@
+# Sets variables normally set by circleci but for local testing
+# Edit pr_body_example.txt to set the PR's body
+# run with source ./set_context_example.sh
+export CI_PR_BODY="$(cat pr_body_example.txt)"
+export CI_PACKAGE_BRANCH="updates-for-ci"
+export CI_PROJECT="processmaker"
+export IMAGE_TAG="processmaker-updates-for-ci"