├── .circleci
└── config.yml
├── .gitignore
├── Dockerfile
├── README.md
├── bin
├── container-start.sh
└── wait.sh
├── composer.json
├── composer.lock
├── install
└── composer.sh
├── phpunit.xml
├── rebuild.sh
├── test
└── AppTest.php
└── web
├── health.php
└── index.php
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Build file for CD demo
2 | #
3 | # No real need for layer caching, this will be really quick to build
4 |
5 | # Some useful repeated stanzas
6 |
7 | docker-sign-in: &docker-sign-in
8 | name: Sign into Docker registry
9 | command: |
10 | docker login \
11 | -u ${PROJECT_DOCKER_REGISTRY_USER} \
12 | -p ${GITLAB_CDDEMO_REGISTRY_TOKEN} \
13 | ${PROJECT_DOCKER_REGISTRY}
14 |
15 | only-deploy-tags: &only-deploy-tags
16 | filters:
17 | tags:
18 | only: /^deploy-.*/
19 | branches:
20 | ignore: /.*/
21 |
22 | common-env-vars: &common-env-vars
23 | PROJECT_DOCKER_REGISTRY: registry.gitlab.com
24 | PROJECT_DOCKER_REGISTRY_USER: halfercode
25 | PROJECT_DOCKER_IMAGE_URL: registry.gitlab.com/halfercode
26 | PROJECT_DOCKER_SERVICE_NAME: cd-demo
27 | PROJECT_DEPLOY_HOST: agnes.jondh.me.uk
28 | PROJECT_DEPLOY_USER: root
29 |
30 | # Here are the jobs
31 |
32 | version: 2
33 | jobs:
34 | build:
35 | working_directory: /app
36 | environment: *common-env-vars
37 | docker:
38 | - image: docker:17.05.0-ce-git
39 | steps:
40 | - checkout
41 | # This gets the local Docker service started
42 | - setup_remote_docker
43 | - run: *docker-sign-in
44 | - run:
45 | name: Build application Docker image
46 | command: |
47 | docker build \
48 | --tag ${CIRCLE_PROJECT_REPONAME} \
49 | .
50 | - run:
51 | name: Install test software
52 | command: |
53 | apk update
54 | apk --update add php7 openssl
55 | # Composer requirements
56 | apk --update add php7-openssl php7-json php7-mbstring php7-zlib php7-phar
57 | # PHPUnit requirements
58 | apk --update add php7-dom php7-mbstring
59 | # Give the PHP binary a nice name
60 | ln -s /usr/bin/php7 /usr/bin/php
61 | cd /app && sh /app/install/composer.sh
62 | php /app/composer.phar install
63 | - run:
64 | name: Start Docker container
65 | command: |
66 | docker run \
67 | --detach \
68 | --name ${CIRCLE_PROJECT_REPONAME} \
69 | ${CIRCLE_PROJECT_REPONAME}
70 | docker ps
71 | - run:
72 | # Thanks to https://unix.stackexchange.com/a/5279. CircleCI's Docker
73 | # system does not allow external connections in a DinD set-up, so I am
74 | # reaching into the container to do that.
75 | name: Wait for container to be ready
76 | command: |
77 | docker exec -it ${CIRCLE_PROJECT_REPONAME} /app/bin/wait.sh
78 | - run:
79 | name: Run tests
80 | command: |
81 | /app/vendor/bin/phpunit
82 | - run:
83 | name: Stop Docker container
84 | command: |
85 | docker stop ${CIRCLE_PROJECT_REPONAME}
86 | - run:
87 | name: Push Docker image if a deploy tag is detected
88 | command: |
89 | case $CIRCLE_TAG in
90 | deploy*)
91 | docker tag ${CIRCLE_PROJECT_REPONAME} ${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG}
92 | docker push ${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG}
93 | ;;
94 | *)
95 | echo "No deploy tag, so no need to push"
96 | ;;
97 | esac
98 |
99 | deploy:
100 | working_directory: /app
101 | environment: *common-env-vars
102 | docker:
103 | - image: docker:17.05.0-ce-git
104 | steps:
105 | # Adds the keys from the UI so we can access a remote service
106 | - add_ssh_keys
107 | # This gets the local Docker service started
108 | - setup_remote_docker
109 | - run:
110 | name: Add the keys to the known hosts
111 | command: |
112 | ssh-keyscan -H ${PROJECT_DEPLOY_HOST} >> /root/.ssh/known_hosts
113 | - run:
114 | name: Pull Docker image onto deploy box
115 | command: |
116 | ssh ${PROJECT_DEPLOY_USER}@${PROJECT_DEPLOY_HOST} "docker pull ${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG}"
117 | - run:
118 | name: Do a rolling service update
119 | command: |
120 | # This should already be scaled to two instances
121 | # The 'uniq' suppresses a long stream of ncurses updates
122 | # (which the CI console doesn't cope with very well)
123 | ssh ${PROJECT_DEPLOY_USER}@${PROJECT_DEPLOY_HOST} \
124 | "docker service update \
125 | --env-add CD_DEMO_VERSION=${CIRCLE_TAG} \
126 | --image=${PROJECT_DOCKER_IMAGE_URL}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG} \
127 | --with-registry-auth \
128 | ${PROJECT_DOCKER_SERVICE_NAME} \
129 | | uniq \
130 | "
131 | workflows:
132 | version: 2
133 | # This runs on non-tag pushes
134 | untagged-build:
135 | jobs:
136 | - build
137 | # This only runs on deploy tags and not branches
138 | tagged-build:
139 | jobs:
140 | - build: *only-deploy-tags
141 | - deploy:
142 | <<: *only-deploy-tags
143 | requires:
144 | - build
145 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore IDE
2 | nbproject/
3 |
4 | # Ignore dependencies
5 | vendor/
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Docker build script for continuous deployment demo container
2 |
3 | FROM alpine:3.8
4 |
5 | # Install software
6 | RUN apk update
7 | RUN apk add php-cli php-apache2
8 |
9 | # Prep Apache
10 | RUN mkdir -p /run/apache2
11 | RUN echo "ServerName localhost" > /etc/apache2/conf.d/server-name.conf
12 |
13 | # Add dumb init to improve sig handling (stop time in CircleCI of 10sec is too slow)
14 | RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64
15 | RUN chmod +x /usr/local/bin/dumb-init
16 |
17 | # Copy shell scripts
18 | COPY bin /app/bin
19 | RUN chmod -R +x /app/bin
20 |
21 | # Copy contents of a web dir
22 | RUN rm /var/www/localhost/htdocs/*
23 | COPY web/* /var/www/localhost/htdocs/
24 |
25 | EXPOSE 80
26 |
27 | # The healthcheck is used by the Routing Mesh, during a rolling update, to understand
28 | # when to avoid a container that is not ready to receive HTTP traffic.
29 | HEALTHCHECK --interval=5s --timeout=5s --start-period=2s --retries=5 \
30 | CMD wget -qO- http://localhost/health.php > /dev/null || exit 1
31 |
32 | # Start the web server
33 | ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
34 | CMD ["sh", "/app/bin/container-start.sh"]
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Continuous integration/deployment demo
2 | ===
3 |
4 | [](https://circleci.com/gh/halfer/cd-demo-container)
5 |
6 | Introduction
7 | ---
8 |
9 | This is a simple demonstration repository to show how to use continuous integration and
10 | continuous deployment to deploy a web service with zero downtime. To do that, I am using
11 | some features in Docker Swarm to run multiple containers, such that during the short outage
12 | during a container upgrade, traffic is automatically routed to a healthy running instance.
13 |
14 | I am using these things:
15 |
16 | * PHP for scripting
17 | * The Apache web-server
18 | * GitHub for version control (obviously `;-)`)
19 | * Docker to containerise the app
20 | * Alpine Linux as a lightweight container OS
21 | * Docker Swarm to run multiple containers in a service
22 | * HTTP health-checks to help Docker maintain availability
23 | * CircleCI for integration
24 | * GitLab as a Docker image registry
25 | * Vultr as a cloud Linux host
26 | * Git tags as an auto-deployment trigger
27 |
28 | If you're wanting to get this project working, but want to change individual things, go
29 | right ahead. For example, you could use a different Git host or Docker registry
30 | without much bother. The only thing that would need much more work to swap is CircleCI,
31 | since the configuration file is specific to their system.
32 |
33 | How it works
34 | ---
35 |
36 | The heart of this project is a CircleCI configuration containing two jobs: one (`build`)
37 | for building and testing a Docker image, and the other (`deploy`) for deploying it
38 | automatically. When pushing code, the first job will run, and it will build the
39 | Dockerfile. If this works, it will try to start it and run a test on it.
40 |
41 | If the developer pushes a deploy tag (i.e. with a prefix of `deploy-`) then the first
42 | job will additionally tag the Docker image with the Git tag and will push it to
43 | the Docker image registry. If this is all successful, the second job will SSH to the
44 | remote machine, pull the image from the registry, and start a rolling Swarm update.
45 |
46 | In this example, the Swarm service is _always_ run using two replicas, so there
47 | is always one operational container to service incoming web requests. Docker Swarm
48 | uses a networking system called [Routing Mesh](https://docs.docker.com/engine/swarm/ingress/#using-the-routing-mesh)
49 | together with the custom health-checks to determine how to update internal networking
50 | rules during a rolling update.
51 |
52 | Target audience
53 | ---
54 |
55 | This material is not suitable for beginners. A basic understanding of the Linux
56 | command line, containers and general hosting concepts are pretty much essential.
57 |
58 | Preparation
59 | ---
60 |
61 | To follow this tutorial, sign up for the following:
62 |
63 | * GitHub
64 | * Pretty much any VPS host
65 | * GitLab
66 | * CircleCI
67 |
68 | The VPS host will generally cost some money. If you can go for a cloud provider they
69 | will charge very accurately, and as long as you destroy the machine when you are
70 | finished, you'll incur very low costs (less than one US dollar). I use Vultr, but
71 | there's also Digital Ocean, Linode and many others available.
72 |
73 | General steps
74 | ---
75 |
76 | Experienced users may wish to just follow these guideline steps. They are expanded
77 | upon later if extra guidance is required.
78 |
79 | * Fork this repo
80 | * Create a small server in the VPS host
81 | * Create an empty project in GitLab (using the same name as the GitHub project)
82 | * Connect CircleCI to GitHub and start building the project
83 | * Customise the env vars in `config.yml` and push
84 | * Install Docker in the remote server
85 | * Create a registry token for CircleCI to do the image push
86 | * Login to the Docker registry in the remote server
87 | * Generate deployment keys
88 | * Initialise a Docker swarm (`docker swarm init`)
89 | * Start a Docker service in the remote
90 | * Add a Git deployment tag and push that tag to kick off an automatic deployment
91 |
92 | Optional steps:
93 |
94 | * Obtain a domain/sub-domain and point it to the server IP
95 | * Add other Docker machines into the Swarm (`docker swarm join`)
96 |
97 | Expanded steps
98 | ---
99 |
100 | ### Fork the repo
101 |
102 | The project name can be anything, but it may be easier to stick with
103 | `cd-demo-container`.
104 |
105 | To use CircleCI, you _have_ to use either GitHub or BitBucket, at the time of writing.
106 | Although you can checkout from any Git repo, I don't think CircleCI will monitor code
107 | pushes on anything other than these two.
108 |
109 | ### Create a small server
110 |
111 | I use a machine with 1G of RAM but you might get away with less than that. I use Ubuntu
112 | as it is popular and widely supported, but if you prefer something else (Fedora, Centos,
113 | Debian, etc) then go for it.
114 |
115 | If you're using Ubuntu, use the most recent stable Long Term Support version.
116 |
117 | ### Create an empty project in GitLab
118 |
119 | Your project URL will be `https://gitlab.com/username/cd-demo-container`, with the
120 | appropriate swap for the username.
121 |
122 | ### Customise the env vars
123 |
124 | These vars will need to be changed in `config.yml`:
125 |
126 | PROJECT_DOCKER_REGISTRY_USER: halfercode
127 | PROJECT_DOCKER_IMAGE_URL: registry.gitlab.com/halfercode
128 | PROJECT_DEPLOY_HOST: agnes.jondh.me.uk
129 |
130 | You can probably leave these be (but change them if you know what you are doing):
131 |
132 | PROJECT_DOCKER_REGISTRY: registry.gitlab.com
133 | PROJECT_DOCKER_SERVICE_NAME: cd-demo
134 | PROJECT_DEPLOY_USER: root
135 |
136 | ### Install Docker in the remote server
137 |
138 | For Ubuntu, I like to use the [Docker repository](https://download.docker.com/linux/ubuntu),
139 | since it is more up to date than the standard OS version, but use what works for you. Here
140 | are [the installation notes](https://docs.docker.com/install/linux/docker-ce/ubuntu/).
141 |
142 | ### Create a registry token for CircleCI to do the image push
143 |
144 | Generate an [access token in GitLab](https://gitlab.com/profile/personal_access_tokens)
145 | with the "api" permission. I believe that's the only permission with write access,
146 | and unfortunately is not per-project. You can choose the name for the token, I suggest
147 | "cd-demo-write". If you have high-value projects in GitLab, consider using a shorter
148 | expiry period, or perhaps even sign up for a separate account.
149 |
150 | Then store the generated token somewhere safe, such as a password keeper.
151 |
152 | Go to your project in CircleCI, click the cog icon, go to the "Environment Variables"
153 | section, and create a new key called `GITLAB_CDDEMO_REGISTRY_TOKEN`. Paste in the new
154 | token as the value of this variable.
155 |
156 | This will be used by the `docker login` command in the CircleCI config to obtain
157 | write access to your registry.
158 |
159 | ### Login to the Docker registry in the remote server
160 |
161 | Generate an [access token in GitLab](https://gitlab.com/profile/personal_access_tokens)
162 | with the "read_registry" permission. This is a read-only permission and will be used
163 | to pull images on your remote server. You can choose the name for the token, I suggest
164 | "cd-demo-read-only". It's worth noting that while you can reuse your read/write
165 | token here, it is better from a security perspective to only give the permissions
166 | that are actually needed.
167 |
168 | As before, it is a good idea to store the generated token somewhere safe, such as a
169 | password keeper.
170 |
171 | Finally, get a remote shell on the deployment target and enter the token in response to
172 | a login operation:
173 |
174 | docker login --username halfercode registry.gitlab.com
175 |
176 | A hash of your credentials will be stored in `~/.docker` and thus will survive
177 | a system reboot.
178 |
179 | ### Generate deployment keys
180 |
181 | * Generate a SSH key pair
182 | * Add the private key to CircleCI
183 | * Add the public key to the deploy target using `ssh-copy-id`
184 |
185 | I have more details about this [in this blog post](https://blog.jondh.me.uk/2018/08/creating-a-vps-deploy-key-for-a-circleci-build/).
186 |
187 | ### Start a Docker service in the remote
188 |
189 | After a Swarm has been initialised, run this on the manager node (of course, with
190 | suitable replacements for the repo URL and container name etc):
191 |
192 | # Get latest tag from the Git repo
193 | # (this lists available tags, removes the non-numeric prefix, sorts the
194 | # numbers mathematically, gets the largest one, then adds the prefix back)
195 | DEPLOY_VERSION=`git ls-remote --tags --refs \
196 | git@github.com:halfer/cd-demo-container.git \
197 | | grep deploy- \
198 | | cut -f 2 \
199 | | sed "s/refs\/tags\/deploy-v//" \
200 | | sort --general-numeric-sort --reverse \
201 | | head -n 1`
202 | DEPLOY_TAG=deploy-v${DEPLOY_VERSION}
203 |
204 | # Start service (if the image is not already pulled, this will do it for you)
205 | docker service create \
206 | --name cd-demo \
207 | --env CD_DEMO_VERSION=${DEPLOY_TAG} \
208 | --replicas 2 \
209 | --with-registry-auth \
210 | --publish 80:80 \
211 | registry.gitlab.com/halfercode/cd-demo-container:${DEPLOY_TAG}
212 |
213 | Docker Swarm will restart your service(s) automatically if you reboot the host. Try that
214 | now, to make sure it works for you.
215 |
216 | ### Add a Git deployment tag
217 |
218 | The configuration is designed to run the `build` job on an ordinary code push, and
219 | to run both the `build` and `deploy` jobs on a tag push.
220 |
221 | To add a tag, you can use:
222 |
223 | git tag -a deploy-v1
224 |
225 | To push both commits and tags, you can use:
226 |
227 | git push --follow-tags
228 |
229 | Running instance
230 | ---
231 |
232 | I have a [running instance](https://cd-demo.jondh.me.uk/) of this service. This one
233 | is a bit different because, obviously, it runs via a secure site. This is achieved
234 | by adding a [Traefik](https://traefik.io/) front-end proxy in front of the demo
235 | container.
236 |
237 | Other resources
238 | ---
239 |
240 | To achieve zero-downtime continuous deployment, I used the technique shown in
241 | [this video](https://www.youtube.com/watch?v=dLBGoaMz7dQ); I recommend watching it,
242 | as it is very well presented.
243 |
244 | License
245 | ---
246 |
247 | This material is licensed under the [MIT License](https://opensource.org/licenses/MIT),
248 | which means you can pretty much do anything you like with it.
249 |
--------------------------------------------------------------------------------
/bin/container-start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # This script is the default CMD in the Dockerfile
4 |
5 | # Generate a GUID for this server:
6 | php -r "echo hash('sha256', uniqid());" > /var/www/localhost/htdocs/guid.txt
7 | chown apache /var/www/localhost/htdocs/guid.txt
8 |
9 | # Start web server
10 | /usr/sbin/httpd -DFOREGROUND
11 |
--------------------------------------------------------------------------------
/bin/wait.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | timeout -t 5 sh -c 'while ! echo exit | nc localhost 80; do sleep 0.5; done'
4 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require-dev": {
3 | "phpunit/phpunit": "^6"
4 | }
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "2bf7d4a2da3e1a4acc9973c9fe028094",
8 | "packages": [],
9 | "packages-dev": [
10 | {
11 | "name": "doctrine/instantiator",
12 | "version": "1.0.5",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/doctrine/instantiator.git",
16 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
21 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "php": ">=5.3,<8.0-DEV"
26 | },
27 | "require-dev": {
28 | "athletic/athletic": "~0.1.8",
29 | "ext-pdo": "*",
30 | "ext-phar": "*",
31 | "phpunit/phpunit": "~4.0",
32 | "squizlabs/php_codesniffer": "~2.0"
33 | },
34 | "type": "library",
35 | "extra": {
36 | "branch-alias": {
37 | "dev-master": "1.0.x-dev"
38 | }
39 | },
40 | "autoload": {
41 | "psr-4": {
42 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
43 | }
44 | },
45 | "notification-url": "https://packagist.org/downloads/",
46 | "license": [
47 | "MIT"
48 | ],
49 | "authors": [
50 | {
51 | "name": "Marco Pivetta",
52 | "email": "ocramius@gmail.com",
53 | "homepage": "http://ocramius.github.com/"
54 | }
55 | ],
56 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
57 | "homepage": "https://github.com/doctrine/instantiator",
58 | "keywords": [
59 | "constructor",
60 | "instantiate"
61 | ],
62 | "time": "2015-06-14T21:17:01+00:00"
63 | },
64 | {
65 | "name": "myclabs/deep-copy",
66 | "version": "1.7.0",
67 | "source": {
68 | "type": "git",
69 | "url": "https://github.com/myclabs/DeepCopy.git",
70 | "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
71 | },
72 | "dist": {
73 | "type": "zip",
74 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
75 | "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
76 | "shasum": ""
77 | },
78 | "require": {
79 | "php": "^5.6 || ^7.0"
80 | },
81 | "require-dev": {
82 | "doctrine/collections": "^1.0",
83 | "doctrine/common": "^2.6",
84 | "phpunit/phpunit": "^4.1"
85 | },
86 | "type": "library",
87 | "autoload": {
88 | "psr-4": {
89 | "DeepCopy\\": "src/DeepCopy/"
90 | },
91 | "files": [
92 | "src/DeepCopy/deep_copy.php"
93 | ]
94 | },
95 | "notification-url": "https://packagist.org/downloads/",
96 | "license": [
97 | "MIT"
98 | ],
99 | "description": "Create deep copies (clones) of your objects",
100 | "keywords": [
101 | "clone",
102 | "copy",
103 | "duplicate",
104 | "object",
105 | "object graph"
106 | ],
107 | "time": "2017-10-19T19:58:43+00:00"
108 | },
109 | {
110 | "name": "phar-io/manifest",
111 | "version": "1.0.1",
112 | "source": {
113 | "type": "git",
114 | "url": "https://github.com/phar-io/manifest.git",
115 | "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
116 | },
117 | "dist": {
118 | "type": "zip",
119 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
120 | "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
121 | "shasum": ""
122 | },
123 | "require": {
124 | "ext-dom": "*",
125 | "ext-phar": "*",
126 | "phar-io/version": "^1.0.1",
127 | "php": "^5.6 || ^7.0"
128 | },
129 | "type": "library",
130 | "extra": {
131 | "branch-alias": {
132 | "dev-master": "1.0.x-dev"
133 | }
134 | },
135 | "autoload": {
136 | "classmap": [
137 | "src/"
138 | ]
139 | },
140 | "notification-url": "https://packagist.org/downloads/",
141 | "license": [
142 | "BSD-3-Clause"
143 | ],
144 | "authors": [
145 | {
146 | "name": "Arne Blankerts",
147 | "email": "arne@blankerts.de",
148 | "role": "Developer"
149 | },
150 | {
151 | "name": "Sebastian Heuer",
152 | "email": "sebastian@phpeople.de",
153 | "role": "Developer"
154 | },
155 | {
156 | "name": "Sebastian Bergmann",
157 | "email": "sebastian@phpunit.de",
158 | "role": "Developer"
159 | }
160 | ],
161 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
162 | "time": "2017-03-05T18:14:27+00:00"
163 | },
164 | {
165 | "name": "phar-io/version",
166 | "version": "1.0.1",
167 | "source": {
168 | "type": "git",
169 | "url": "https://github.com/phar-io/version.git",
170 | "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
171 | },
172 | "dist": {
173 | "type": "zip",
174 | "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
175 | "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
176 | "shasum": ""
177 | },
178 | "require": {
179 | "php": "^5.6 || ^7.0"
180 | },
181 | "type": "library",
182 | "autoload": {
183 | "classmap": [
184 | "src/"
185 | ]
186 | },
187 | "notification-url": "https://packagist.org/downloads/",
188 | "license": [
189 | "BSD-3-Clause"
190 | ],
191 | "authors": [
192 | {
193 | "name": "Arne Blankerts",
194 | "email": "arne@blankerts.de",
195 | "role": "Developer"
196 | },
197 | {
198 | "name": "Sebastian Heuer",
199 | "email": "sebastian@phpeople.de",
200 | "role": "Developer"
201 | },
202 | {
203 | "name": "Sebastian Bergmann",
204 | "email": "sebastian@phpunit.de",
205 | "role": "Developer"
206 | }
207 | ],
208 | "description": "Library for handling version information and constraints",
209 | "time": "2017-03-05T17:38:23+00:00"
210 | },
211 | {
212 | "name": "phpdocumentor/reflection-common",
213 | "version": "1.0.1",
214 | "source": {
215 | "type": "git",
216 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
217 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
218 | },
219 | "dist": {
220 | "type": "zip",
221 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
222 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
223 | "shasum": ""
224 | },
225 | "require": {
226 | "php": ">=5.5"
227 | },
228 | "require-dev": {
229 | "phpunit/phpunit": "^4.6"
230 | },
231 | "type": "library",
232 | "extra": {
233 | "branch-alias": {
234 | "dev-master": "1.0.x-dev"
235 | }
236 | },
237 | "autoload": {
238 | "psr-4": {
239 | "phpDocumentor\\Reflection\\": [
240 | "src"
241 | ]
242 | }
243 | },
244 | "notification-url": "https://packagist.org/downloads/",
245 | "license": [
246 | "MIT"
247 | ],
248 | "authors": [
249 | {
250 | "name": "Jaap van Otterdijk",
251 | "email": "opensource@ijaap.nl"
252 | }
253 | ],
254 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
255 | "homepage": "http://www.phpdoc.org",
256 | "keywords": [
257 | "FQSEN",
258 | "phpDocumentor",
259 | "phpdoc",
260 | "reflection",
261 | "static analysis"
262 | ],
263 | "time": "2017-09-11T18:02:19+00:00"
264 | },
265 | {
266 | "name": "phpdocumentor/reflection-docblock",
267 | "version": "4.3.0",
268 | "source": {
269 | "type": "git",
270 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
271 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
272 | },
273 | "dist": {
274 | "type": "zip",
275 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
276 | "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
277 | "shasum": ""
278 | },
279 | "require": {
280 | "php": "^7.0",
281 | "phpdocumentor/reflection-common": "^1.0.0",
282 | "phpdocumentor/type-resolver": "^0.4.0",
283 | "webmozart/assert": "^1.0"
284 | },
285 | "require-dev": {
286 | "doctrine/instantiator": "~1.0.5",
287 | "mockery/mockery": "^1.0",
288 | "phpunit/phpunit": "^6.4"
289 | },
290 | "type": "library",
291 | "extra": {
292 | "branch-alias": {
293 | "dev-master": "4.x-dev"
294 | }
295 | },
296 | "autoload": {
297 | "psr-4": {
298 | "phpDocumentor\\Reflection\\": [
299 | "src/"
300 | ]
301 | }
302 | },
303 | "notification-url": "https://packagist.org/downloads/",
304 | "license": [
305 | "MIT"
306 | ],
307 | "authors": [
308 | {
309 | "name": "Mike van Riel",
310 | "email": "me@mikevanriel.com"
311 | }
312 | ],
313 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
314 | "time": "2017-11-30T07:14:17+00:00"
315 | },
316 | {
317 | "name": "phpdocumentor/type-resolver",
318 | "version": "0.4.0",
319 | "source": {
320 | "type": "git",
321 | "url": "https://github.com/phpDocumentor/TypeResolver.git",
322 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
323 | },
324 | "dist": {
325 | "type": "zip",
326 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
327 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
328 | "shasum": ""
329 | },
330 | "require": {
331 | "php": "^5.5 || ^7.0",
332 | "phpdocumentor/reflection-common": "^1.0"
333 | },
334 | "require-dev": {
335 | "mockery/mockery": "^0.9.4",
336 | "phpunit/phpunit": "^5.2||^4.8.24"
337 | },
338 | "type": "library",
339 | "extra": {
340 | "branch-alias": {
341 | "dev-master": "1.0.x-dev"
342 | }
343 | },
344 | "autoload": {
345 | "psr-4": {
346 | "phpDocumentor\\Reflection\\": [
347 | "src/"
348 | ]
349 | }
350 | },
351 | "notification-url": "https://packagist.org/downloads/",
352 | "license": [
353 | "MIT"
354 | ],
355 | "authors": [
356 | {
357 | "name": "Mike van Riel",
358 | "email": "me@mikevanriel.com"
359 | }
360 | ],
361 | "time": "2017-07-14T14:27:02+00:00"
362 | },
363 | {
364 | "name": "phpspec/prophecy",
365 | "version": "1.8.0",
366 | "source": {
367 | "type": "git",
368 | "url": "https://github.com/phpspec/prophecy.git",
369 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
370 | },
371 | "dist": {
372 | "type": "zip",
373 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
374 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
375 | "shasum": ""
376 | },
377 | "require": {
378 | "doctrine/instantiator": "^1.0.2",
379 | "php": "^5.3|^7.0",
380 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
381 | "sebastian/comparator": "^1.1|^2.0|^3.0",
382 | "sebastian/recursion-context": "^1.0|^2.0|^3.0"
383 | },
384 | "require-dev": {
385 | "phpspec/phpspec": "^2.5|^3.2",
386 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
387 | },
388 | "type": "library",
389 | "extra": {
390 | "branch-alias": {
391 | "dev-master": "1.8.x-dev"
392 | }
393 | },
394 | "autoload": {
395 | "psr-0": {
396 | "Prophecy\\": "src/"
397 | }
398 | },
399 | "notification-url": "https://packagist.org/downloads/",
400 | "license": [
401 | "MIT"
402 | ],
403 | "authors": [
404 | {
405 | "name": "Konstantin Kudryashov",
406 | "email": "ever.zet@gmail.com",
407 | "homepage": "http://everzet.com"
408 | },
409 | {
410 | "name": "Marcello Duarte",
411 | "email": "marcello.duarte@gmail.com"
412 | }
413 | ],
414 | "description": "Highly opinionated mocking framework for PHP 5.3+",
415 | "homepage": "https://github.com/phpspec/prophecy",
416 | "keywords": [
417 | "Double",
418 | "Dummy",
419 | "fake",
420 | "mock",
421 | "spy",
422 | "stub"
423 | ],
424 | "time": "2018-08-05T17:53:17+00:00"
425 | },
426 | {
427 | "name": "phpunit/php-code-coverage",
428 | "version": "5.3.2",
429 | "source": {
430 | "type": "git",
431 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
432 | "reference": "c89677919c5dd6d3b3852f230a663118762218ac"
433 | },
434 | "dist": {
435 | "type": "zip",
436 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac",
437 | "reference": "c89677919c5dd6d3b3852f230a663118762218ac",
438 | "shasum": ""
439 | },
440 | "require": {
441 | "ext-dom": "*",
442 | "ext-xmlwriter": "*",
443 | "php": "^7.0",
444 | "phpunit/php-file-iterator": "^1.4.2",
445 | "phpunit/php-text-template": "^1.2.1",
446 | "phpunit/php-token-stream": "^2.0.1",
447 | "sebastian/code-unit-reverse-lookup": "^1.0.1",
448 | "sebastian/environment": "^3.0",
449 | "sebastian/version": "^2.0.1",
450 | "theseer/tokenizer": "^1.1"
451 | },
452 | "require-dev": {
453 | "phpunit/phpunit": "^6.0"
454 | },
455 | "suggest": {
456 | "ext-xdebug": "^2.5.5"
457 | },
458 | "type": "library",
459 | "extra": {
460 | "branch-alias": {
461 | "dev-master": "5.3.x-dev"
462 | }
463 | },
464 | "autoload": {
465 | "classmap": [
466 | "src/"
467 | ]
468 | },
469 | "notification-url": "https://packagist.org/downloads/",
470 | "license": [
471 | "BSD-3-Clause"
472 | ],
473 | "authors": [
474 | {
475 | "name": "Sebastian Bergmann",
476 | "email": "sebastian@phpunit.de",
477 | "role": "lead"
478 | }
479 | ],
480 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
481 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
482 | "keywords": [
483 | "coverage",
484 | "testing",
485 | "xunit"
486 | ],
487 | "time": "2018-04-06T15:36:58+00:00"
488 | },
489 | {
490 | "name": "phpunit/php-file-iterator",
491 | "version": "1.4.5",
492 | "source": {
493 | "type": "git",
494 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
495 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
496 | },
497 | "dist": {
498 | "type": "zip",
499 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
500 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
501 | "shasum": ""
502 | },
503 | "require": {
504 | "php": ">=5.3.3"
505 | },
506 | "type": "library",
507 | "extra": {
508 | "branch-alias": {
509 | "dev-master": "1.4.x-dev"
510 | }
511 | },
512 | "autoload": {
513 | "classmap": [
514 | "src/"
515 | ]
516 | },
517 | "notification-url": "https://packagist.org/downloads/",
518 | "license": [
519 | "BSD-3-Clause"
520 | ],
521 | "authors": [
522 | {
523 | "name": "Sebastian Bergmann",
524 | "email": "sb@sebastian-bergmann.de",
525 | "role": "lead"
526 | }
527 | ],
528 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
529 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
530 | "keywords": [
531 | "filesystem",
532 | "iterator"
533 | ],
534 | "time": "2017-11-27T13:52:08+00:00"
535 | },
536 | {
537 | "name": "phpunit/php-text-template",
538 | "version": "1.2.1",
539 | "source": {
540 | "type": "git",
541 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
542 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
543 | },
544 | "dist": {
545 | "type": "zip",
546 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
547 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
548 | "shasum": ""
549 | },
550 | "require": {
551 | "php": ">=5.3.3"
552 | },
553 | "type": "library",
554 | "autoload": {
555 | "classmap": [
556 | "src/"
557 | ]
558 | },
559 | "notification-url": "https://packagist.org/downloads/",
560 | "license": [
561 | "BSD-3-Clause"
562 | ],
563 | "authors": [
564 | {
565 | "name": "Sebastian Bergmann",
566 | "email": "sebastian@phpunit.de",
567 | "role": "lead"
568 | }
569 | ],
570 | "description": "Simple template engine.",
571 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
572 | "keywords": [
573 | "template"
574 | ],
575 | "time": "2015-06-21T13:50:34+00:00"
576 | },
577 | {
578 | "name": "phpunit/php-timer",
579 | "version": "1.0.9",
580 | "source": {
581 | "type": "git",
582 | "url": "https://github.com/sebastianbergmann/php-timer.git",
583 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
584 | },
585 | "dist": {
586 | "type": "zip",
587 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
588 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
589 | "shasum": ""
590 | },
591 | "require": {
592 | "php": "^5.3.3 || ^7.0"
593 | },
594 | "require-dev": {
595 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
596 | },
597 | "type": "library",
598 | "extra": {
599 | "branch-alias": {
600 | "dev-master": "1.0-dev"
601 | }
602 | },
603 | "autoload": {
604 | "classmap": [
605 | "src/"
606 | ]
607 | },
608 | "notification-url": "https://packagist.org/downloads/",
609 | "license": [
610 | "BSD-3-Clause"
611 | ],
612 | "authors": [
613 | {
614 | "name": "Sebastian Bergmann",
615 | "email": "sb@sebastian-bergmann.de",
616 | "role": "lead"
617 | }
618 | ],
619 | "description": "Utility class for timing",
620 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
621 | "keywords": [
622 | "timer"
623 | ],
624 | "time": "2017-02-26T11:10:40+00:00"
625 | },
626 | {
627 | "name": "phpunit/php-token-stream",
628 | "version": "2.0.2",
629 | "source": {
630 | "type": "git",
631 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
632 | "reference": "791198a2c6254db10131eecfe8c06670700904db"
633 | },
634 | "dist": {
635 | "type": "zip",
636 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
637 | "reference": "791198a2c6254db10131eecfe8c06670700904db",
638 | "shasum": ""
639 | },
640 | "require": {
641 | "ext-tokenizer": "*",
642 | "php": "^7.0"
643 | },
644 | "require-dev": {
645 | "phpunit/phpunit": "^6.2.4"
646 | },
647 | "type": "library",
648 | "extra": {
649 | "branch-alias": {
650 | "dev-master": "2.0-dev"
651 | }
652 | },
653 | "autoload": {
654 | "classmap": [
655 | "src/"
656 | ]
657 | },
658 | "notification-url": "https://packagist.org/downloads/",
659 | "license": [
660 | "BSD-3-Clause"
661 | ],
662 | "authors": [
663 | {
664 | "name": "Sebastian Bergmann",
665 | "email": "sebastian@phpunit.de"
666 | }
667 | ],
668 | "description": "Wrapper around PHP's tokenizer extension.",
669 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
670 | "keywords": [
671 | "tokenizer"
672 | ],
673 | "time": "2017-11-27T05:48:46+00:00"
674 | },
675 | {
676 | "name": "phpunit/phpunit",
677 | "version": "6.5.12",
678 | "source": {
679 | "type": "git",
680 | "url": "https://github.com/sebastianbergmann/phpunit.git",
681 | "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e"
682 | },
683 | "dist": {
684 | "type": "zip",
685 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/24da433d7384824d65ea93fbb462e2f31bbb494e",
686 | "reference": "24da433d7384824d65ea93fbb462e2f31bbb494e",
687 | "shasum": ""
688 | },
689 | "require": {
690 | "ext-dom": "*",
691 | "ext-json": "*",
692 | "ext-libxml": "*",
693 | "ext-mbstring": "*",
694 | "ext-xml": "*",
695 | "myclabs/deep-copy": "^1.6.1",
696 | "phar-io/manifest": "^1.0.1",
697 | "phar-io/version": "^1.0",
698 | "php": "^7.0",
699 | "phpspec/prophecy": "^1.7",
700 | "phpunit/php-code-coverage": "^5.3",
701 | "phpunit/php-file-iterator": "^1.4.3",
702 | "phpunit/php-text-template": "^1.2.1",
703 | "phpunit/php-timer": "^1.0.9",
704 | "phpunit/phpunit-mock-objects": "^5.0.9",
705 | "sebastian/comparator": "^2.1",
706 | "sebastian/diff": "^2.0",
707 | "sebastian/environment": "^3.1",
708 | "sebastian/exporter": "^3.1",
709 | "sebastian/global-state": "^2.0",
710 | "sebastian/object-enumerator": "^3.0.3",
711 | "sebastian/resource-operations": "^1.0",
712 | "sebastian/version": "^2.0.1"
713 | },
714 | "conflict": {
715 | "phpdocumentor/reflection-docblock": "3.0.2",
716 | "phpunit/dbunit": "<3.0"
717 | },
718 | "require-dev": {
719 | "ext-pdo": "*"
720 | },
721 | "suggest": {
722 | "ext-xdebug": "*",
723 | "phpunit/php-invoker": "^1.1"
724 | },
725 | "bin": [
726 | "phpunit"
727 | ],
728 | "type": "library",
729 | "extra": {
730 | "branch-alias": {
731 | "dev-master": "6.5.x-dev"
732 | }
733 | },
734 | "autoload": {
735 | "classmap": [
736 | "src/"
737 | ]
738 | },
739 | "notification-url": "https://packagist.org/downloads/",
740 | "license": [
741 | "BSD-3-Clause"
742 | ],
743 | "authors": [
744 | {
745 | "name": "Sebastian Bergmann",
746 | "email": "sebastian@phpunit.de",
747 | "role": "lead"
748 | }
749 | ],
750 | "description": "The PHP Unit Testing framework.",
751 | "homepage": "https://phpunit.de/",
752 | "keywords": [
753 | "phpunit",
754 | "testing",
755 | "xunit"
756 | ],
757 | "time": "2018-08-22T06:32:48+00:00"
758 | },
759 | {
760 | "name": "phpunit/phpunit-mock-objects",
761 | "version": "5.0.10",
762 | "source": {
763 | "type": "git",
764 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
765 | "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f"
766 | },
767 | "dist": {
768 | "type": "zip",
769 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f",
770 | "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f",
771 | "shasum": ""
772 | },
773 | "require": {
774 | "doctrine/instantiator": "^1.0.5",
775 | "php": "^7.0",
776 | "phpunit/php-text-template": "^1.2.1",
777 | "sebastian/exporter": "^3.1"
778 | },
779 | "conflict": {
780 | "phpunit/phpunit": "<6.0"
781 | },
782 | "require-dev": {
783 | "phpunit/phpunit": "^6.5.11"
784 | },
785 | "suggest": {
786 | "ext-soap": "*"
787 | },
788 | "type": "library",
789 | "extra": {
790 | "branch-alias": {
791 | "dev-master": "5.0.x-dev"
792 | }
793 | },
794 | "autoload": {
795 | "classmap": [
796 | "src/"
797 | ]
798 | },
799 | "notification-url": "https://packagist.org/downloads/",
800 | "license": [
801 | "BSD-3-Clause"
802 | ],
803 | "authors": [
804 | {
805 | "name": "Sebastian Bergmann",
806 | "email": "sebastian@phpunit.de",
807 | "role": "lead"
808 | }
809 | ],
810 | "description": "Mock Object library for PHPUnit",
811 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
812 | "keywords": [
813 | "mock",
814 | "xunit"
815 | ],
816 | "time": "2018-08-09T05:50:03+00:00"
817 | },
818 | {
819 | "name": "sebastian/code-unit-reverse-lookup",
820 | "version": "1.0.1",
821 | "source": {
822 | "type": "git",
823 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
824 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
825 | },
826 | "dist": {
827 | "type": "zip",
828 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
829 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
830 | "shasum": ""
831 | },
832 | "require": {
833 | "php": "^5.6 || ^7.0"
834 | },
835 | "require-dev": {
836 | "phpunit/phpunit": "^5.7 || ^6.0"
837 | },
838 | "type": "library",
839 | "extra": {
840 | "branch-alias": {
841 | "dev-master": "1.0.x-dev"
842 | }
843 | },
844 | "autoload": {
845 | "classmap": [
846 | "src/"
847 | ]
848 | },
849 | "notification-url": "https://packagist.org/downloads/",
850 | "license": [
851 | "BSD-3-Clause"
852 | ],
853 | "authors": [
854 | {
855 | "name": "Sebastian Bergmann",
856 | "email": "sebastian@phpunit.de"
857 | }
858 | ],
859 | "description": "Looks up which function or method a line of code belongs to",
860 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
861 | "time": "2017-03-04T06:30:41+00:00"
862 | },
863 | {
864 | "name": "sebastian/comparator",
865 | "version": "2.1.3",
866 | "source": {
867 | "type": "git",
868 | "url": "https://github.com/sebastianbergmann/comparator.git",
869 | "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9"
870 | },
871 | "dist": {
872 | "type": "zip",
873 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9",
874 | "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9",
875 | "shasum": ""
876 | },
877 | "require": {
878 | "php": "^7.0",
879 | "sebastian/diff": "^2.0 || ^3.0",
880 | "sebastian/exporter": "^3.1"
881 | },
882 | "require-dev": {
883 | "phpunit/phpunit": "^6.4"
884 | },
885 | "type": "library",
886 | "extra": {
887 | "branch-alias": {
888 | "dev-master": "2.1.x-dev"
889 | }
890 | },
891 | "autoload": {
892 | "classmap": [
893 | "src/"
894 | ]
895 | },
896 | "notification-url": "https://packagist.org/downloads/",
897 | "license": [
898 | "BSD-3-Clause"
899 | ],
900 | "authors": [
901 | {
902 | "name": "Jeff Welch",
903 | "email": "whatthejeff@gmail.com"
904 | },
905 | {
906 | "name": "Volker Dusch",
907 | "email": "github@wallbash.com"
908 | },
909 | {
910 | "name": "Bernhard Schussek",
911 | "email": "bschussek@2bepublished.at"
912 | },
913 | {
914 | "name": "Sebastian Bergmann",
915 | "email": "sebastian@phpunit.de"
916 | }
917 | ],
918 | "description": "Provides the functionality to compare PHP values for equality",
919 | "homepage": "https://github.com/sebastianbergmann/comparator",
920 | "keywords": [
921 | "comparator",
922 | "compare",
923 | "equality"
924 | ],
925 | "time": "2018-02-01T13:46:46+00:00"
926 | },
927 | {
928 | "name": "sebastian/diff",
929 | "version": "2.0.1",
930 | "source": {
931 | "type": "git",
932 | "url": "https://github.com/sebastianbergmann/diff.git",
933 | "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd"
934 | },
935 | "dist": {
936 | "type": "zip",
937 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
938 | "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
939 | "shasum": ""
940 | },
941 | "require": {
942 | "php": "^7.0"
943 | },
944 | "require-dev": {
945 | "phpunit/phpunit": "^6.2"
946 | },
947 | "type": "library",
948 | "extra": {
949 | "branch-alias": {
950 | "dev-master": "2.0-dev"
951 | }
952 | },
953 | "autoload": {
954 | "classmap": [
955 | "src/"
956 | ]
957 | },
958 | "notification-url": "https://packagist.org/downloads/",
959 | "license": [
960 | "BSD-3-Clause"
961 | ],
962 | "authors": [
963 | {
964 | "name": "Kore Nordmann",
965 | "email": "mail@kore-nordmann.de"
966 | },
967 | {
968 | "name": "Sebastian Bergmann",
969 | "email": "sebastian@phpunit.de"
970 | }
971 | ],
972 | "description": "Diff implementation",
973 | "homepage": "https://github.com/sebastianbergmann/diff",
974 | "keywords": [
975 | "diff"
976 | ],
977 | "time": "2017-08-03T08:09:46+00:00"
978 | },
979 | {
980 | "name": "sebastian/environment",
981 | "version": "3.1.0",
982 | "source": {
983 | "type": "git",
984 | "url": "https://github.com/sebastianbergmann/environment.git",
985 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5"
986 | },
987 | "dist": {
988 | "type": "zip",
989 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
990 | "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
991 | "shasum": ""
992 | },
993 | "require": {
994 | "php": "^7.0"
995 | },
996 | "require-dev": {
997 | "phpunit/phpunit": "^6.1"
998 | },
999 | "type": "library",
1000 | "extra": {
1001 | "branch-alias": {
1002 | "dev-master": "3.1.x-dev"
1003 | }
1004 | },
1005 | "autoload": {
1006 | "classmap": [
1007 | "src/"
1008 | ]
1009 | },
1010 | "notification-url": "https://packagist.org/downloads/",
1011 | "license": [
1012 | "BSD-3-Clause"
1013 | ],
1014 | "authors": [
1015 | {
1016 | "name": "Sebastian Bergmann",
1017 | "email": "sebastian@phpunit.de"
1018 | }
1019 | ],
1020 | "description": "Provides functionality to handle HHVM/PHP environments",
1021 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1022 | "keywords": [
1023 | "Xdebug",
1024 | "environment",
1025 | "hhvm"
1026 | ],
1027 | "time": "2017-07-01T08:51:00+00:00"
1028 | },
1029 | {
1030 | "name": "sebastian/exporter",
1031 | "version": "3.1.0",
1032 | "source": {
1033 | "type": "git",
1034 | "url": "https://github.com/sebastianbergmann/exporter.git",
1035 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
1036 | },
1037 | "dist": {
1038 | "type": "zip",
1039 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
1040 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
1041 | "shasum": ""
1042 | },
1043 | "require": {
1044 | "php": "^7.0",
1045 | "sebastian/recursion-context": "^3.0"
1046 | },
1047 | "require-dev": {
1048 | "ext-mbstring": "*",
1049 | "phpunit/phpunit": "^6.0"
1050 | },
1051 | "type": "library",
1052 | "extra": {
1053 | "branch-alias": {
1054 | "dev-master": "3.1.x-dev"
1055 | }
1056 | },
1057 | "autoload": {
1058 | "classmap": [
1059 | "src/"
1060 | ]
1061 | },
1062 | "notification-url": "https://packagist.org/downloads/",
1063 | "license": [
1064 | "BSD-3-Clause"
1065 | ],
1066 | "authors": [
1067 | {
1068 | "name": "Jeff Welch",
1069 | "email": "whatthejeff@gmail.com"
1070 | },
1071 | {
1072 | "name": "Volker Dusch",
1073 | "email": "github@wallbash.com"
1074 | },
1075 | {
1076 | "name": "Bernhard Schussek",
1077 | "email": "bschussek@2bepublished.at"
1078 | },
1079 | {
1080 | "name": "Sebastian Bergmann",
1081 | "email": "sebastian@phpunit.de"
1082 | },
1083 | {
1084 | "name": "Adam Harvey",
1085 | "email": "aharvey@php.net"
1086 | }
1087 | ],
1088 | "description": "Provides the functionality to export PHP variables for visualization",
1089 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
1090 | "keywords": [
1091 | "export",
1092 | "exporter"
1093 | ],
1094 | "time": "2017-04-03T13:19:02+00:00"
1095 | },
1096 | {
1097 | "name": "sebastian/global-state",
1098 | "version": "2.0.0",
1099 | "source": {
1100 | "type": "git",
1101 | "url": "https://github.com/sebastianbergmann/global-state.git",
1102 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
1103 | },
1104 | "dist": {
1105 | "type": "zip",
1106 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
1107 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
1108 | "shasum": ""
1109 | },
1110 | "require": {
1111 | "php": "^7.0"
1112 | },
1113 | "require-dev": {
1114 | "phpunit/phpunit": "^6.0"
1115 | },
1116 | "suggest": {
1117 | "ext-uopz": "*"
1118 | },
1119 | "type": "library",
1120 | "extra": {
1121 | "branch-alias": {
1122 | "dev-master": "2.0-dev"
1123 | }
1124 | },
1125 | "autoload": {
1126 | "classmap": [
1127 | "src/"
1128 | ]
1129 | },
1130 | "notification-url": "https://packagist.org/downloads/",
1131 | "license": [
1132 | "BSD-3-Clause"
1133 | ],
1134 | "authors": [
1135 | {
1136 | "name": "Sebastian Bergmann",
1137 | "email": "sebastian@phpunit.de"
1138 | }
1139 | ],
1140 | "description": "Snapshotting of global state",
1141 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1142 | "keywords": [
1143 | "global state"
1144 | ],
1145 | "time": "2017-04-27T15:39:26+00:00"
1146 | },
1147 | {
1148 | "name": "sebastian/object-enumerator",
1149 | "version": "3.0.3",
1150 | "source": {
1151 | "type": "git",
1152 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1153 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
1154 | },
1155 | "dist": {
1156 | "type": "zip",
1157 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1158 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
1159 | "shasum": ""
1160 | },
1161 | "require": {
1162 | "php": "^7.0",
1163 | "sebastian/object-reflector": "^1.1.1",
1164 | "sebastian/recursion-context": "^3.0"
1165 | },
1166 | "require-dev": {
1167 | "phpunit/phpunit": "^6.0"
1168 | },
1169 | "type": "library",
1170 | "extra": {
1171 | "branch-alias": {
1172 | "dev-master": "3.0.x-dev"
1173 | }
1174 | },
1175 | "autoload": {
1176 | "classmap": [
1177 | "src/"
1178 | ]
1179 | },
1180 | "notification-url": "https://packagist.org/downloads/",
1181 | "license": [
1182 | "BSD-3-Clause"
1183 | ],
1184 | "authors": [
1185 | {
1186 | "name": "Sebastian Bergmann",
1187 | "email": "sebastian@phpunit.de"
1188 | }
1189 | ],
1190 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1191 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1192 | "time": "2017-08-03T12:35:26+00:00"
1193 | },
1194 | {
1195 | "name": "sebastian/object-reflector",
1196 | "version": "1.1.1",
1197 | "source": {
1198 | "type": "git",
1199 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1200 | "reference": "773f97c67f28de00d397be301821b06708fca0be"
1201 | },
1202 | "dist": {
1203 | "type": "zip",
1204 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
1205 | "reference": "773f97c67f28de00d397be301821b06708fca0be",
1206 | "shasum": ""
1207 | },
1208 | "require": {
1209 | "php": "^7.0"
1210 | },
1211 | "require-dev": {
1212 | "phpunit/phpunit": "^6.0"
1213 | },
1214 | "type": "library",
1215 | "extra": {
1216 | "branch-alias": {
1217 | "dev-master": "1.1-dev"
1218 | }
1219 | },
1220 | "autoload": {
1221 | "classmap": [
1222 | "src/"
1223 | ]
1224 | },
1225 | "notification-url": "https://packagist.org/downloads/",
1226 | "license": [
1227 | "BSD-3-Clause"
1228 | ],
1229 | "authors": [
1230 | {
1231 | "name": "Sebastian Bergmann",
1232 | "email": "sebastian@phpunit.de"
1233 | }
1234 | ],
1235 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1236 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1237 | "time": "2017-03-29T09:07:27+00:00"
1238 | },
1239 | {
1240 | "name": "sebastian/recursion-context",
1241 | "version": "3.0.0",
1242 | "source": {
1243 | "type": "git",
1244 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1245 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
1246 | },
1247 | "dist": {
1248 | "type": "zip",
1249 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1250 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
1251 | "shasum": ""
1252 | },
1253 | "require": {
1254 | "php": "^7.0"
1255 | },
1256 | "require-dev": {
1257 | "phpunit/phpunit": "^6.0"
1258 | },
1259 | "type": "library",
1260 | "extra": {
1261 | "branch-alias": {
1262 | "dev-master": "3.0.x-dev"
1263 | }
1264 | },
1265 | "autoload": {
1266 | "classmap": [
1267 | "src/"
1268 | ]
1269 | },
1270 | "notification-url": "https://packagist.org/downloads/",
1271 | "license": [
1272 | "BSD-3-Clause"
1273 | ],
1274 | "authors": [
1275 | {
1276 | "name": "Jeff Welch",
1277 | "email": "whatthejeff@gmail.com"
1278 | },
1279 | {
1280 | "name": "Sebastian Bergmann",
1281 | "email": "sebastian@phpunit.de"
1282 | },
1283 | {
1284 | "name": "Adam Harvey",
1285 | "email": "aharvey@php.net"
1286 | }
1287 | ],
1288 | "description": "Provides functionality to recursively process PHP variables",
1289 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1290 | "time": "2017-03-03T06:23:57+00:00"
1291 | },
1292 | {
1293 | "name": "sebastian/resource-operations",
1294 | "version": "1.0.0",
1295 | "source": {
1296 | "type": "git",
1297 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1298 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
1299 | },
1300 | "dist": {
1301 | "type": "zip",
1302 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1303 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1304 | "shasum": ""
1305 | },
1306 | "require": {
1307 | "php": ">=5.6.0"
1308 | },
1309 | "type": "library",
1310 | "extra": {
1311 | "branch-alias": {
1312 | "dev-master": "1.0.x-dev"
1313 | }
1314 | },
1315 | "autoload": {
1316 | "classmap": [
1317 | "src/"
1318 | ]
1319 | },
1320 | "notification-url": "https://packagist.org/downloads/",
1321 | "license": [
1322 | "BSD-3-Clause"
1323 | ],
1324 | "authors": [
1325 | {
1326 | "name": "Sebastian Bergmann",
1327 | "email": "sebastian@phpunit.de"
1328 | }
1329 | ],
1330 | "description": "Provides a list of PHP built-in functions that operate on resources",
1331 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1332 | "time": "2015-07-28T20:34:47+00:00"
1333 | },
1334 | {
1335 | "name": "sebastian/version",
1336 | "version": "2.0.1",
1337 | "source": {
1338 | "type": "git",
1339 | "url": "https://github.com/sebastianbergmann/version.git",
1340 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1341 | },
1342 | "dist": {
1343 | "type": "zip",
1344 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1345 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1346 | "shasum": ""
1347 | },
1348 | "require": {
1349 | "php": ">=5.6"
1350 | },
1351 | "type": "library",
1352 | "extra": {
1353 | "branch-alias": {
1354 | "dev-master": "2.0.x-dev"
1355 | }
1356 | },
1357 | "autoload": {
1358 | "classmap": [
1359 | "src/"
1360 | ]
1361 | },
1362 | "notification-url": "https://packagist.org/downloads/",
1363 | "license": [
1364 | "BSD-3-Clause"
1365 | ],
1366 | "authors": [
1367 | {
1368 | "name": "Sebastian Bergmann",
1369 | "email": "sebastian@phpunit.de",
1370 | "role": "lead"
1371 | }
1372 | ],
1373 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1374 | "homepage": "https://github.com/sebastianbergmann/version",
1375 | "time": "2016-10-03T07:35:21+00:00"
1376 | },
1377 | {
1378 | "name": "theseer/tokenizer",
1379 | "version": "1.1.0",
1380 | "source": {
1381 | "type": "git",
1382 | "url": "https://github.com/theseer/tokenizer.git",
1383 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b"
1384 | },
1385 | "dist": {
1386 | "type": "zip",
1387 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b",
1388 | "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b",
1389 | "shasum": ""
1390 | },
1391 | "require": {
1392 | "ext-dom": "*",
1393 | "ext-tokenizer": "*",
1394 | "ext-xmlwriter": "*",
1395 | "php": "^7.0"
1396 | },
1397 | "type": "library",
1398 | "autoload": {
1399 | "classmap": [
1400 | "src/"
1401 | ]
1402 | },
1403 | "notification-url": "https://packagist.org/downloads/",
1404 | "license": [
1405 | "BSD-3-Clause"
1406 | ],
1407 | "authors": [
1408 | {
1409 | "name": "Arne Blankerts",
1410 | "email": "arne@blankerts.de",
1411 | "role": "Developer"
1412 | }
1413 | ],
1414 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
1415 | "time": "2017-04-07T12:08:54+00:00"
1416 | },
1417 | {
1418 | "name": "webmozart/assert",
1419 | "version": "1.3.0",
1420 | "source": {
1421 | "type": "git",
1422 | "url": "https://github.com/webmozart/assert.git",
1423 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a"
1424 | },
1425 | "dist": {
1426 | "type": "zip",
1427 | "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
1428 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a",
1429 | "shasum": ""
1430 | },
1431 | "require": {
1432 | "php": "^5.3.3 || ^7.0"
1433 | },
1434 | "require-dev": {
1435 | "phpunit/phpunit": "^4.6",
1436 | "sebastian/version": "^1.0.1"
1437 | },
1438 | "type": "library",
1439 | "extra": {
1440 | "branch-alias": {
1441 | "dev-master": "1.3-dev"
1442 | }
1443 | },
1444 | "autoload": {
1445 | "psr-4": {
1446 | "Webmozart\\Assert\\": "src/"
1447 | }
1448 | },
1449 | "notification-url": "https://packagist.org/downloads/",
1450 | "license": [
1451 | "MIT"
1452 | ],
1453 | "authors": [
1454 | {
1455 | "name": "Bernhard Schussek",
1456 | "email": "bschussek@gmail.com"
1457 | }
1458 | ],
1459 | "description": "Assertions to validate method input/output with nice error messages.",
1460 | "keywords": [
1461 | "assert",
1462 | "check",
1463 | "validate"
1464 | ],
1465 | "time": "2018-01-29T19:49:41+00:00"
1466 | }
1467 | ],
1468 | "aliases": [],
1469 | "minimum-stability": "stable",
1470 | "stability-flags": [],
1471 | "prefer-stable": false,
1472 | "prefer-lowest": false,
1473 | "platform": [],
1474 | "platform-dev": []
1475 | }
1476 |
--------------------------------------------------------------------------------
/install/composer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
4 | php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
5 | ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
6 |
7 | if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
8 | then
9 | >&2 echo 'ERROR: Invalid installer signature'
10 | rm composer-setup.php
11 | exit 1
12 | fi
13 |
14 | php composer-setup.php --quiet
15 | RESULT=$?
16 | rm composer-setup.php
17 | exit $RESULT
18 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 | Version: 12 |
13 |14 | GUID: 15 |
16 | 17 | 18 | 19 | --------------------------------------------------------------------------------