Compare commits

...

100 Commits

Author SHA1 Message Date
Michal Kleiner
9881c03c22 Merge branch '2.7' into 2 2023-05-19 22:22:54 +12:00
Guy Sartorelli
e8517ebfb0
Merge pull request #90 from silverstripe/85-implode-args-order
Fix arguments order in SolrIndexCheck
2023-05-19 20:43:40 +12:00
Michal Kleiner
28ca036345
Fix arguments order in SolrIndexCheck 2023-05-19 14:53:43 +12:00
Guy Sartorelli
16caad30b2
Merge branch '2.7' into 2 2023-04-26 12:48:24 +12:00
Maxime Rainville
6f6c30bdcc
Merge pull request #87 from creative-commoners/pulls/2.7/fix-constraints
MNT Revert erroneous dependency changes
2023-03-28 17:15:30 +13:00
Guy Sartorelli
f918209e35
MNT Revert erroneous dependency changes 2023-03-28 14:55:27 +13:00
Maxime Rainville
714de64252
Merge pull request #86 from creative-commoners/pulls/2/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:10:44 +13:00
Steve Boyd
d9d245a0da MNT Use gha-dispatch-ci 2023-03-21 12:24:56 +13:00
Guy Sartorelli
9d5f8fce7d
MNT Update development dependencies 2023-03-10 16:36:51 +13:00
Guy Sartorelli
f12698f47f
MNT Update release dependencies 2023-03-10 16:36:47 +13:00
Guy Sartorelli
6f0d1c84f2
MNT Update development dependencies 2023-03-10 12:21:31 +13:00
Guy Sartorelli
15a8bca241
Merge pull request #81 from creative-commoners/pulls/2/depr-messages
API Update deprecations
2022-11-21 09:50:03 +13:00
Steve Boyd
94ff5b621c API Update deprecations 2022-11-16 11:31:42 +13:00
Will Rossiter
0e4867f736
DOC add note about FileAccessibilityAndValidationCheck 2022-10-07 09:09:09 +13:00
Steve Boyd
7e59b7e88b Merge branch '2.5' into 2 2022-08-02 18:49:26 +12:00
Steve Boyd
e4b8934148 Merge branch '2.4' into 2.5 2022-08-02 18:49:22 +12:00
Guy Sartorelli
737ac0d8ed
Merge pull request #80 from creative-commoners/pulls/2.4/standardise-modules
MNT Standardise modules
2022-08-02 15:09:01 +12:00
Steve Boyd
3378dc7a99 MNT Standardise modules 2022-08-01 16:21:54 +12:00
Steve Boyd
58caa67c96 Merge branch '2.5' into 2 2022-07-25 11:25:08 +12:00
Steve Boyd
7be4c0dbf3 Merge branch '2.4' into 2.5 2022-07-25 11:25:04 +12:00
Guy Sartorelli
7ba4acc4b2
Merge pull request #79 from creative-commoners/pulls/2.4/module-standards
MNT Use GitHub Actions CI
2022-07-15 17:30:58 +12:00
Steve Boyd
a76c3b89ea MNT Use GitHub Actions CI 2022-07-05 18:55:00 +12:00
Guy Sartorelli
7f6c03b0cb
Merge pull request #78 from wilr/pr/public-privacy
fix: public health/check should not include full details
2022-07-05 17:10:32 +12:00
Will Rossiter
3e3907d14c
fix: public health/check should not include full details 2022-07-05 09:15:03 +12:00
Guy Sartorelli
d3d20e5539
Merge pull request #76 from creative-commoners/pulls/2/php81
ENH PHP 8.1 compatibility
2022-04-26 17:58:17 +12:00
Steve Boyd
b7554a5fc9 ENH PHP 8.1 compatibility 2022-04-13 10:29:17 +12:00
Daniel Hensby
cb13f4ec12
Merge pull request #75 from creative-commoners/pulls/2/php74
DEP Set PHP 7.4 as the minimum version
2022-02-10 11:58:45 +00:00
Steve Boyd
3902a1d688 DEP Set PHP 7.4 as the minimum version 2022-02-10 16:16:07 +13:00
Steve Boyd
aa1311fa01
Merge pull request #72 from creative-commoners/pulls/2/bumpguzzle-requirements
MNT Bump guzzle requirements
2021-11-09 13:13:10 +13:00
Steve Boyd
47b184df5d MNT Fix unit test 2021-11-09 11:35:24 +13:00
Maxime Rainville
94fa4ee1f0 MNT Bump guzzle requirements 2021-11-08 10:32:34 +13:00
Maxime Rainville
3f2fbdaea0
Merge pull request #71 from creative-commoners/pulls/2/sapphire-test-nine
API phpunit 9 support
2021-11-01 21:25:32 +13:00
Steve Boyd
05dd7bc0a7 API phpunit 9 support 2021-10-27 18:06:29 +13:00
Steve Boyd
f6e001bf55 Merge branch '2.3' into 2 2021-05-21 13:50:33 +12:00
Maxime Rainville
e5b77ff325 MNT Remove obsolete branch-alias 2021-05-05 11:17:25 +12:00
Steve Boyd
029078d157
Update build status badge 2021-01-21 16:34:03 +13:00
Steve Boyd
f14fe7c40d Merge branch '2.2' into 2 2021-01-02 20:06:19 +13:00
Maxime Rainville
585b6564b2
Merge pull request #70 from creative-commoners/pulls/2.2/travis-shared
MNT Travis shared config
2020-12-21 15:52:49 +13:00
Maxime Rainville
dd037c8f50
DEP Remove explicit dev requirement for sminnee/phpunit-mock-objects 2020-12-21 14:51:00 +13:00
Steve Boyd
d29a062562 MNT Travis shared config, use sminnee/phpunit 2020-12-01 16:29:13 +13:00
Steve Boyd
2c4d32af6b Merge branch '2.2' into 2 2020-11-12 14:45:10 +13:00
Robbie Averill
7c0b7b0ca7
Merge pull request #68 from creative-commoners/pulls/2.2/travis
Travis 2.2
2020-06-23 10:15:38 -07:00
Steve Boyd
b765ec87c1 Travis 2.2 2020-06-23 12:54:42 +12:00
Steve Boyd
56dab108c9 Require phpunit-mock-objects, bump recipe versions 2020-06-18 13:54:59 +12:00
Steve Boyd
b01567767c Merge branch '2.2' into 2 2020-06-18 11:06:23 +12:00
Maxime Rainville
e19e476cf4
Merge pull request #66 from creative-commoners/2.2
Update for 2.2
2020-06-15 18:32:48 +12:00
Maxime Rainville
dfd9c15d56 Add PHP 7.4 test 2020-06-15 15:52:29 +12:00
Maxime Rainville
23104b77aa Add PHP 7.4 test 2020-06-15 15:48:00 +12:00
Steve Boyd
1aa18e7ced Update for 2.2 2020-06-15 14:53:33 +12:00
Maxime Rainville
08409bb9ca Merge remote-tracking branch 'origin/2.1' into 2 2020-06-12 14:12:14 +12:00
Damian Mooyman
b2f6e06795
Merge pull request #65 from creative-commoners/pulls/2.1/missing-envtypecheck-alias
FIX Add missing aliases for EnvironmentChecks
2020-01-13 11:49:46 +13:00
Garion Herman
0685dfad75 FIX Add missing aliases for EnvironmentChecks 2020-01-06 10:08:08 +13:00
Garion Herman
5f2591a5c1 Merge branch '2.1' into 2 2019-11-18 20:42:46 +13:00
Garion Herman
2bbebc321e Merge branch '2.0' into 2.1 2019-11-18 20:42:15 +13:00
Sam Minnée
9829476dae
Merge pull request #63 from creative-commoners/pulls/2.0/xenial
FIX Update Travis config to Xenial
2019-11-18 19:16:38 +13:00
Garion Herman
49d7d5f2a3 FIX Update Travis config to Xenial 2019-11-18 18:01:51 +13:00
Robbie Averill
f347d1d5c0 Merge branch '2.1' 2019-06-12 10:19:45 +12:00
Serge Latyntcev
c490c4e5a5 ADD ConfirmationMiddleware exceptions for the dev routes 2019-06-12 09:37:05 +12:00
Aaron Carlino
d8452c681a
Merge pull request #1 from silverstripe-security/pulls/master/ss-2018-022
[SS-2018-022] ADD ConfirmationMiddleware exceptions for the dev routes
2019-06-12 09:25:59 +12:00
Serge Latyntcev
e757c9477c ADD ConfirmationMiddleware exceptions for the dev routes 2019-06-12 09:24:52 +12:00
Damian Mooyman
6fe0b1889e
Update master alias to 2.2 2019-04-05 16:02:38 +13:00
Damian Mooyman
40050e9ab4
Update changelog 2019-04-05 16:01:54 +13:00
Robbie Averill
efd996d618
Merge pull request #61 from tractorcow/pulls/2.1/basicauth
BUG Only present basic auth challenge if configured to use it
2019-03-29 11:29:29 +13:00
Robbie Averill
922d810a3a Reduce line length for PSR-2 compatibility 2019-03-29 10:03:02 +13:00
Damian Mooyman
a54400681f
BUG Only present basic auth challenge if configured to use it 2019-03-28 17:21:16 +13:00
Dylan Wagstaff
c5783a2450
Merge pull request #60 from silverstripe-terraformers/feature/extra-checks
Extra checks; cache headers, and session cookies
2019-03-20 11:51:11 +13:00
Frank Mullenger
da9eddc0e1
Merge branch 'master' into feature/extra-checks 2019-03-20 10:23:03 +13:00
Frank Mullenger
8f7f8570f2 NEW: Refactor to provide unit tests for cache headers check. 2019-03-20 10:21:00 +13:00
Frank Mullenger
578c73e6ce NEW: Refactor to provide unit tests for session check. 2019-03-20 09:38:58 +13:00
Robbie Averill
128ddb56ed
Merge pull request #58 from silverstripe-terraformers/feature/env-type-check
NEW: Environment type check.
2019-03-11 13:20:52 +13:00
Frank Mullenger
ace6c51aae FIX: Missed a reference to project code. 2019-03-11 13:03:48 +13:00
Frank Mullenger
540710ab0f NEW: Adding unit test. 2019-03-11 12:44:24 +13:00
Frank Mullenger
2674beb94a FIX: Line length. 2019-02-15 13:58:08 +13:00
Frank Mullenger
90f0556ca4 FIX: Removal of optional param. 2019-02-15 13:52:58 +13:00
Frank Mullenger
27994e51fa FIX: Removing return type declarations. 2019-02-15 13:42:41 +13:00
Guy Marriott
986bc370b9
Update scrutinizer config 2019-02-15 13:10:32 +13:00
Frank Mullenger
c1bfd5640d NEW: Cache headers check. 2019-02-15 11:57:14 +13:00
Frank Mullenger
df59195634 NEW: Session check. 2019-02-15 11:56:32 +13:00
Guy Marriott
f74c371e16
Merge branch '2.0' 2019-02-15 11:43:42 +13:00
Guy Marriott
41763c6042
Merge pull request #59 from creative-commoners/pulls/2.0/update-travis
Update travis configuration
2019-02-15 11:43:12 +13:00
Guy Marriott
f8d35639a0
Update travis configuration 2019-02-15 11:37:55 +13:00
Frank Mullenger
e323674adf NEW: Adding Guzzle dependency. 2019-02-15 11:30:51 +13:00
Frank Mullenger
da6a8a8ee4 MINOR: Adding new check to documentation. 2019-02-15 11:02:13 +13:00
Frank Mullenger
9940c5e8ab FIX: Removing PHP7 return type declaration. 2019-02-15 10:32:13 +13:00
Frank Mullenger
1771a6ef11 NEW: Environment type check. 2019-02-15 10:07:05 +13:00
Robbie Averill
05ac221c2f
Merge pull request #56 from creative-commoners/pulls/master/add-supported-module-badge
Add supported module badge to readme
2018-06-19 09:23:42 +12:00
Dylan Wagstaff
ed61c6bb3a Add supported module badge to readme 2018-06-15 17:34:56 +12:00
Robbie Averill
014c23804f Merge branch '2.0' 2018-05-25 14:55:32 +12:00
Robbie Averill
f6652c2b37 Remove obsolete branch alias 2018-05-25 14:55:17 +12:00
Robbie Averill
161f0aefaf Merge branch '1' 2018-05-03 16:01:24 +12:00
Robbie Averill
a788b37de5 Merge branch '1.2' into 1 2018-05-03 16:00:34 +12:00
Robbie Averill
449a265301 Merge branch '1.1' into 1.2 2018-05-03 16:00:19 +12:00
Dylan Wagstaff
7ad2334f49
Merge pull request #55 from creative-commoners/pulls/1.1/update-travis
Update Travis matrix, Scrutinizer config and readme badges. Use codecov.io
2018-05-03 14:53:59 +12:00
Robbie Averill
250c8d2e00 Use test database in database check test 2018-05-03 11:01:18 +12:00
Robbie Averill
e5651c429c Update Travis matrix, Scrutinizer config and readme badges. Use codecov.io. 2018-05-03 11:01:18 +12:00
Robbie Averill
564936ee01
Remove PHP 5.3 from Travis build matrix 2018-05-03 10:37:28 +12:00
Dylan Wagstaff
b89aefaa20
Merge pull request #54 from creative-commoners/pulls/2.0/fix-solr-check
FIX Implement Solr namespaces into Solr check, remove index filter
2018-01-26 09:51:31 +13:00
Daniel Hensby
152c11638b
Merge branch '1' 2018-01-15 16:31:52 +00:00
Robbie Averill
e6e5ac96df FIX Implement Solr namespaces into Solr check, remove index filter 2017-12-22 15:31:55 +13:00
Daniel Hensby
b78c2af4aa
Updating composer alias 2016-12-28 11:20:58 +00:00
43 changed files with 929 additions and 193 deletions

11
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 1:20 PM UTC, only on Wednesday and Thursday
schedule:
- cron: '20 13 * * 3,4'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

View File

@ -1,9 +0,0 @@
inherit: true
checks:
php:
code_rating: true
duplication: true
filter:
paths: [src/*, tests/*]

View File

@ -1,33 +0,0 @@
language: php
env:
global:
- COMPOSER_ROOT_VERSION="2.0.x-dev"
matrix:
include:
- php: 5.6
env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
- php: 7.0
env: DB=PGSQL PHPUNIT_TEST=1
- php: 7.1
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
- php: 7.2
env: DB=MYSQL PHPUNIT_TEST=1
before_script:
- phpenv rehash
- phpenv config-rm xdebug.ini
- composer validate
- composer require --no-update silverstripe/recipe-core:1.0.x-dev
- if [[ $DB == PGSQL ]]; then composer require --no-update silverstripe/postgresql:2.0.x-dev; fi
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
script:
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src/ tests/ *.php; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi

View File

@ -1,10 +1,7 @@
# SilverStripe Environment Checker Module # Silverstripe Environment Checker Module
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-environmentcheck.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-environmentcheck) [![CI](https://github.com/silverstripe/silverstripe-environmentcheck/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-environmentcheck/actions/workflows/ci.yml)
[![Code Quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-environmentcheck/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-environmentcheck/?branch=master) [![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
[![Code Coverage](https://codecov.io/gh/silverstripe/silverstripe-environmentcheck/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-environmentcheck)
[![Version](http://img.shields.io/packagist/v/silverstripe/environmentcheck.svg?style=flat-square)](https://packagist.org/packages/silverstripe/environmentcheck)
[![License](http://img.shields.io/packagist/l/silverstripe/environmentcheck.svg?style=flat-square)](LICENSE.md)
This module adds an API for running environment checks to your API. This module adds an API for running environment checks to your API.
@ -14,10 +11,10 @@ This module adds an API for running environment checks to your API.
## Requirements ## Requirements
* SilverStripe 4.x * Silverstripe 4.x
* PHP 5.6+ * PHP 7.3
For SilverStripe 3.x support, please use a `1.x` tagged release. For Silverstripe 3.x support, please use a `1.x` tagged release.
## Aren't these just unit tests? ## Aren't these just unit tests?
@ -85,12 +82,17 @@ SilverStripe\EnvironmentCheck\EnvironmentCheckSuite:
* `HasClassCheck`: Check that the given class exists. * `HasClassCheck`: Check that the given class exists.
This can be used to check that PHP modules or features are installed. This can be used to check that PHP modules or features are installed.
* `FileWriteableCheck`: Check that the given file is writeable. * `FileWriteableCheck`: Check that the given file is writeable.
* `FileAccessibilityAndValidationCheck`: Check that a given file is accessible and optionally matches a given format.
* `FileAgeCheck`: Checks for the maximum age of one or more files or folders. * `FileAgeCheck`: Checks for the maximum age of one or more files or folders.
Useful for files which should be frequently auto-generated, Useful for files which should be frequently auto-generated,
like static caches, as well as for backup files and folders. like static caches, as well as for backup files and folders.
* `ExternalURLCheck`: Checks that one or more URLs are reachable via HTTP. * `ExternalURLCheck`: Checks that one or more URLs are reachable via HTTP.
* `SMTPConnectCheck`: Checks if the SMTP connection configured through PHP.ini works as expected. * `SMTPConnectCheck`: Checks if the SMTP connection configured through PHP.ini works as expected.
* `SolrIndexCheck`: Checks if the Solr cores of given class are available. * `SolrIndexCheck`: Checks if the Solr cores of given class are available.
* `SessionCheck`: Checks that a given URL does not generate a session.
* `CacheHeadersCheck`: Check cache headers in response for directives that must either be included or excluded as well
checking for existence of ETag.
* `EnvTypeCheck`: Checks environment type, dev and test should not be used on production environments.
## Monitoring Checks ## Monitoring Checks
@ -106,7 +108,7 @@ SilverStripe\EnvironmentCheck\EnvironmentChecker:
from_email_address: admin@test.com from_email_address: admin@test.com
``` ```
Errors can be logged via the standard SilverStripe PSR-3 compatible logging. Each check will cause an individual log Errors can be logged via the standard Silverstripe PSR-3 compatible logging. Each check will cause an individual log
entry. You can choose to enable logging separately for warnings and errors, identified through the entry. You can choose to enable logging separately for warnings and errors, identified through the
result of `EnvironmentCheck->check()`. result of `EnvironmentCheck->check()`.

View File

@ -2,8 +2,12 @@
Name: environmentcheckinjector Name: environmentcheckinjector
--- ---
SilverStripe\Core\Injector\Injector: SilverStripe\Core\Injector\Injector:
CacheHeadersCheck:
class: SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck
DatabaseCheck: DatabaseCheck:
class: SilverStripe\EnvironmentCheck\Checks\DatabaseCheck class: SilverStripe\EnvironmentCheck\Checks\DatabaseCheck
EnvTypeCheck:
class: SilverStripe\EnvironmentCheck\Checks\EnvTypeCheck
ExternalURLCheck: ExternalURLCheck:
class: SilverStripe\EnvironmentCheck\Checks\ExternalURLCheck class: SilverStripe\EnvironmentCheck\Checks\ExternalURLCheck
FileAccessibilityAndValidationCheck: FileAccessibilityAndValidationCheck:
@ -22,3 +26,14 @@ SilverStripe\Core\Injector\Injector:
class: SilverStripe\EnvironmentCheck\Checks\SolrIndexCheck class: SilverStripe\EnvironmentCheck\Checks\SolrIndexCheck
URLCheck: URLCheck:
class: SilverStripe\EnvironmentCheck\Checks\URLCheck class: SilverStripe\EnvironmentCheck\Checks\URLCheck
EnvCheckClient:
factory: 'SilverStripe\EnvironmentCheck\Services\ClientFactory'
constructor:
timeout: 10.0
SilverStripe\EnvironmentCheck\Checks\SessionCheck:
dependencies:
client: '%$EnvCheckClient'
SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck:
dependencies:
client: '%$EnvCheckClient'

View File

@ -12,3 +12,14 @@ SilverStripe\Dev\DevelopmentAdmin:
controller: Silverstripe\EnvironmentCheck\Controllers\DevCheckController controller: Silverstripe\EnvironmentCheck\Controllers\DevCheckController
links: links:
check: 'Run registered environment checks and display their status' check: 'Run registered environment checks and display their status'
---
Name: environmentcheckroutes-dev_urls-confirmation-exceptions
After:
- 'dev_urls-confirmation-middleware'
---
SilverStripe\Core\Injector\Injector:
DevUrlsConfirmationMiddleware:
properties:
Bypasses:
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartsWith("dev/check")'

View File

@ -4,7 +4,17 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [2.0.0] - unreleased ## [2.1.0]
* Add new checks:
* Environment type
* Session
* Cache Headers
* Fix basic auth
* Add unit tests
* PSR-2 Conformance
## [2.0.0]
* Minimum versions required: * Minimum versions required:
* SilverStripe 4.x * SilverStripe 4.x

View File

@ -3,7 +3,12 @@
"description": "Provides an API for building environment tests", "description": "Provides an API for building environment tests",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"type": "silverstripe-vendormodule", "type": "silverstripe-vendormodule",
"keywords": ["silverstripe", "testing", "environment", "check"], "keywords": [
"silverstripe",
"testing",
"environment",
"check"
],
"authors": [ "authors": [
{ {
"name": "Will Rossiter", "name": "Will Rossiter",
@ -15,18 +20,16 @@
} }
], ],
"require": { "require": {
"silverstripe/framework": "^4.0", "php": "^7.4 || ^8.0",
"silverstripe/versioned": "^1.0" "silverstripe/framework": "^4.10",
"silverstripe/versioned": "^1.0",
"guzzlehttp/guzzle": "^6.3.3 || ^7"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^5.7", "phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.0" "squizlabs/php_codesniffer": "^3.0"
}, },
"extra": { "extra": [],
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"SilverStripe\\EnvironmentCheck\\": "src/", "SilverStripe\\EnvironmentCheck\\": "src/",
@ -35,4 +38,4 @@
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true "prefer-stable": true
} }

View File

@ -2,6 +2,9 @@
<ruleset name="SilverStripe"> <ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description> <description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>src</file>
<file>tests</file>
<rule ref="PSR2" > <rule ref="PSR2" >
<!-- Current exclusions --> <!-- Current exclusions -->
<exclude name="PSR1.Methods.CamelCapsMethodName" /> <exclude name="PSR1.Methods.CamelCapsMethodName" />

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true"> <phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuite name="Default"> <testsuites>
<directory>tests</directory> <testsuite name="Default">
</testsuite> <directory>tests</directory>
</testsuite>
</testsuites>
<filter> <filter>
<whitelist addUncoveredFilesFromWhitelist="true"> <whitelist addUncoveredFilesFromWhitelist="true">

View File

@ -0,0 +1,207 @@
<?php
namespace SilverStripe\EnvironmentCheck\Checks;
use SilverStripe\Control\Director;
use SilverStripe\Control\Controller;
use SilverStripe\ORM\ValidationResult;
use Psr\Http\Message\ResponseInterface;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\EnvironmentCheck\Traits\Fetcher;
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
/**
* Check cache headers for any response, can specify directives that must be included and
* also must be excluded from Cache-Control headers in response. Also checks for
* existence of ETag.
*
* @example SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck("/",["must-revalidate", "max-age=120"],["no-store"])
* @package environmentcheck
*/
class CacheHeadersCheck implements EnvironmentCheck
{
use Fetcher;
/**
* Settings that must be included in the Cache-Control header
*
* @var array
*/
protected $mustInclude = [];
/**
* Settings that must be excluded in the Cache-Control header
*
* @var array
*/
protected $mustExclude = [];
/**
* Result to keep track of status and messages for all checks, reuses
* ValidationResult for convenience.
*
* @var ValidationResult
*/
protected $result;
/**
* Set up with URL, arrays of header settings to check.
*
* @param string $url
* @param array $mustInclude Settings that must be included in Cache-Control
* @param array $mustExclude Settings that must be excluded in Cache-Control
*/
public function __construct($url = '', $mustInclude = [], $mustExclude = [])
{
$this->setURL($url);
$this->mustInclude = $mustInclude;
$this->mustExclude = $mustExclude;
}
/**
* Check that correct caching headers are present.
*
* @return void
*/
public function check()
{
// Using a validation result to capture messages
$this->result = new ValidationResult();
$response = $this->client->get($this->getURL());
$fullURL = $this->getURL();
if ($response === null) {
return [
EnvironmentCheck::ERROR,
"Cache headers check request failed for $fullURL",
];
}
//Check that Etag exists
$this->checkEtag($response);
// Check Cache-Control settings
$this->checkCacheControl($response);
if ($this->result->isValid()) {
return [
EnvironmentCheck::OK,
$this->getMessage(),
];
} else {
// @todo Ability to return a warning
return [
EnvironmentCheck::ERROR,
$this->getMessage(),
];
}
}
/**
* Collate messages from ValidationResult so that it is clear which parts
* of the check passed and which failed.
*
* @return string
*/
private function getMessage()
{
$ret = '';
// Filter good messages
$goodTypes = [ValidationResult::TYPE_GOOD, ValidationResult::TYPE_INFO];
$good = array_filter(
$this->result->getMessages() ?? [],
function ($val, $key) use ($goodTypes) {
if (in_array($val['messageType'], $goodTypes ?? [])) {
return true;
}
return false;
},
ARRAY_FILTER_USE_BOTH
);
if (!empty($good)) {
$ret .= "GOOD: " . implode('; ', array_column($good ?? [], 'message')) . " ";
}
// Filter bad messages
$badTypes = [ValidationResult::TYPE_ERROR, ValidationResult::TYPE_WARNING];
$bad = array_filter(
$this->result->getMessages() ?? [],
function ($val, $key) use ($badTypes) {
if (in_array($val['messageType'], $badTypes ?? [])) {
return true;
}
return false;
},
ARRAY_FILTER_USE_BOTH
);
if (!empty($bad)) {
$ret .= "BAD: " . implode('; ', array_column($bad ?? [], 'message'));
}
return $ret;
}
/**
* Check that ETag header exists
*
* @param ResponseInterface $response
* @return void
*/
private function checkEtag(ResponseInterface $response)
{
$eTag = $response->getHeaderLine('ETag');
$fullURL = Controller::join_links(Director::absoluteBaseURL(), $this->url);
if ($eTag) {
$this->result->addMessage(
"$fullURL includes an Etag header in response",
ValidationResult::TYPE_GOOD
);
return;
}
$this->result->addError(
"$fullURL is missing an Etag header",
ValidationResult::TYPE_WARNING
);
}
/**
* Check that the correct header settings are either included or excluded.
*
* @param ResponseInterface $response
* @return void
*/
private function checkCacheControl(ResponseInterface $response)
{
$cacheControl = $response->getHeaderLine('Cache-Control');
$vals = array_map('trim', explode(',', $cacheControl ?? ''));
$fullURL = Controller::join_links(Director::absoluteBaseURL(), $this->url);
// All entries from must contain should be present
if ($this->mustInclude == array_intersect($this->mustInclude ?? [], $vals)) {
$matched = implode(",", $this->mustInclude);
$this->result->addMessage(
"$fullURL includes all settings: {$matched}",
ValidationResult::TYPE_GOOD
);
} else {
$missing = implode(",", array_diff($this->mustInclude ?? [], $vals));
$this->result->addError(
"$fullURL is excluding some settings: {$missing}"
);
}
// All entries from must exclude should not be present
if (empty(array_intersect($this->mustExclude ?? [], $vals))) {
$missing = implode(",", $this->mustExclude);
$this->result->addMessage(
"$fullURL excludes all settings: {$missing}",
ValidationResult::TYPE_GOOD
);
} else {
$matched = implode(",", array_intersect($this->mustExclude ?? [], $vals));
$this->result->addError(
"$fullURL is including some settings: {$matched}"
);
}
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace SilverStripe\EnvironmentCheck\Checks;
use SilverStripe\Control\Director;
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
/**
* Check whether the environment setting is safe. Useful for live sites where a
* non "Live" setting might disclose sensitive information.
*
* @package environmentcheck
*/
class EnvTypeCheck implements EnvironmentCheck
{
/**
* Check the environment setting.
*
* @return array
*/
public function check()
{
$envSetting = Director::get_environment_type();
switch ($envSetting) {
case 'live':
return [
EnvironmentCheck::OK,
"Env setting is 'live'",
];
// Fallthrough
default:
case 'dev':
case 'test':
return [
EnvironmentCheck::ERROR,
"Env setting is '{$envSetting}' and may disclose information",
];
}
}
}

View File

@ -37,7 +37,7 @@ class ExternalURLCheck implements EnvironmentCheck
public function __construct($urls, $timeout = 15) public function __construct($urls, $timeout = 15)
{ {
if ($urls) { if ($urls) {
$this->urls = explode(' ', $urls); $this->urls = explode(' ', $urls ?? '');
} }
$this->timeout = $timeout; $this->timeout = $timeout;
} }
@ -55,7 +55,7 @@ class ExternalURLCheck implements EnvironmentCheck
foreach ($urls as $url) { foreach ($urls as $url) {
$ch = curl_init(); $ch = curl_init();
$chs[] = $ch; $chs[] = $ch;
curl_setopt_array($ch, $this->getCurlOpts($url)); curl_setopt_array($ch, $this->getCurlOpts($url) ?? []);
} }
// Parallel execution for faster performance // Parallel execution for faster performance
$mh = curl_multi_init(); $mh = curl_multi_init();

View File

@ -96,7 +96,7 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
$files = $this->getFiles(); $files = $this->getFiles();
if ($files) { if ($files) {
$fileTypeValidateFunc = $this->fileTypeValidateFunc; $fileTypeValidateFunc = $this->fileTypeValidateFunc;
if (method_exists($this, $fileTypeValidateFunc)) { if (method_exists($this, $fileTypeValidateFunc ?? '')) {
$invalidFiles = []; $invalidFiles = [];
$validFiles = []; $validFiles = [];
@ -109,7 +109,7 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
} }
// If at least one file was valid, count as passed // If at least one file was valid, count as passed
if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles) < count($files)) { if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles ?? []) < count($files ?? [])) {
$validFileList = PHP_EOL; $validFileList = PHP_EOL;
foreach ($validFiles as $vf) { foreach ($validFiles as $vf) {
$validFileList .= $vf . PHP_EOL; $validFileList .= $vf . PHP_EOL;
@ -130,7 +130,7 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
]; ];
} }
} else { } else {
if (count($invalidFiles) == 0) { if (count($invalidFiles ?? []) == 0) {
$checkReturn = [EnvironmentCheck::OK, 'All files valideted']; $checkReturn = [EnvironmentCheck::OK, 'All files valideted'];
} else { } else {
$invalidFileList = PHP_EOL; $invalidFileList = PHP_EOL;
@ -180,7 +180,7 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
*/ */
private function jsonValidate($file) private function jsonValidate($file)
{ {
$json = json_decode(file_get_contents($file)); $json = json_decode(file_get_contents($file ?? '') ?? '');
if (!$json) { if (!$json) {
return false; return false;
} }
@ -204,6 +204,6 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
*/ */
protected function getFiles() protected function getFiles()
{ {
return glob($this->path); return glob($this->path ?? '');
} }
} }

View File

@ -96,7 +96,7 @@ class FileAgeCheck implements EnvironmentCheck
*/ */
public function check() public function check()
{ {
$cutoffTime = strtotime($this->relativeAge, DBDatetime::now()->Format('U')); $cutoffTime = strtotime($this->relativeAge ?? '', DBDatetime::now()->Format('U'));
$files = $this->getFiles(); $files = $this->getFiles();
$invalidFiles = []; $invalidFiles = [];
$validFiles = []; $validFiles = [];
@ -127,10 +127,10 @@ class FileAgeCheck implements EnvironmentCheck
} }
// If at least one file was valid, count as passed // If at least one file was valid, count as passed
if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles) < count($files)) { if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles ?? []) < count($files ?? [])) {
return [EnvironmentCheck::OK, '']; return [EnvironmentCheck::OK, ''];
} }
if (count($invalidFiles) == 0) { if (count($invalidFiles ?? []) == 0) {
return [EnvironmentCheck::OK, '']; return [EnvironmentCheck::OK, ''];
} }
return [ return [
@ -146,6 +146,6 @@ class FileAgeCheck implements EnvironmentCheck
*/ */
protected function getFiles() protected function getFiles()
{ {
return glob($this->path); return glob($this->path ?? '');
} }
} }

View File

@ -34,22 +34,22 @@ class FileWriteableCheck implements EnvironmentCheck
if ($this->path[0] == '/') { if ($this->path[0] == '/') {
$filename = $this->path; $filename = $this->path;
} else { } else {
$filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $this->path); $filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $this->path ?? '');
} }
if (file_exists($filename)) { if (file_exists($filename ?? '')) {
$isWriteable = is_writeable($filename); $isWriteable = is_writeable($filename ?? '');
} else { } else {
$isWriteable = is_writeable(dirname($filename)); $isWriteable = is_writeable(dirname($filename ?? ''));
} }
if (!$isWriteable) { if (!$isWriteable) {
if (function_exists('posix_getgroups')) { if (function_exists('posix_getgroups')) {
$userID = posix_geteuid(); $userID = posix_geteuid();
$user = posix_getpwuid($userID); $user = posix_getpwuid($userID ?? 0);
$currentOwnerID = fileowner(file_exists($filename) ? $filename : dirname($filename)); $currentOwnerID = fileowner(file_exists($filename ?? '') ? $filename : dirname($filename ?? ''));
$currentOwner = posix_getpwuid($currentOwnerID); $currentOwner = posix_getpwuid($currentOwnerID ?? 0);
$message = "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe file is " $message = "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe file is "
. "currently owned by '$currentOwner[name]'. "; . "currently owned by '$currentOwner[name]'. ";
@ -60,8 +60,8 @@ class FileWriteableCheck implements EnvironmentCheck
$groups = posix_getgroups(); $groups = posix_getgroups();
$groupList = []; $groupList = [];
foreach ($groups as $group) { foreach ($groups as $group) {
$groupInfo = posix_getgrgid($group); $groupInfo = posix_getgrgid($group ?? 0);
if (in_array($currentOwner['name'], $groupInfo['members'])) { if (in_array($currentOwner['name'], $groupInfo['members'] ?? [])) {
$groupList[] = $groupInfo['name']; $groupList[] = $groupInfo['name'];
} }
} }

View File

@ -31,7 +31,7 @@ class HasClassCheck implements EnvironmentCheck
*/ */
public function check() public function check()
{ {
if (class_exists($this->className)) { if (class_exists($this->className ?? '')) {
return [EnvironmentCheck::OK, 'Class ' . $this->className.' exists']; return [EnvironmentCheck::OK, 'Class ' . $this->className.' exists'];
} }
return [EnvironmentCheck::ERROR, 'Class ' . $this->className.' doesn\'t exist']; return [EnvironmentCheck::ERROR, 'Class ' . $this->className.' doesn\'t exist'];

View File

@ -31,7 +31,7 @@ class HasFunctionCheck implements EnvironmentCheck
*/ */
public function check() public function check()
{ {
if (function_exists($this->functionName)) { if (function_exists($this->functionName ?? '')) {
return [EnvironmentCheck::OK, $this->functionName . '() exists']; return [EnvironmentCheck::OK, $this->functionName . '() exists'];
} }
return [EnvironmentCheck::ERROR, $this->functionName . '() doesn\'t exist']; return [EnvironmentCheck::ERROR, $this->functionName . '() doesn\'t exist'];

View File

@ -57,7 +57,7 @@ class SMTPConnectCheck implements EnvironmentCheck
*/ */
public function check() public function check()
{ {
$f = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout); $f = @fsockopen($this->host ?? '', $this->port ?? 0, $errno, $errstr, $this->timeout);
if (!$f) { if (!$f) {
return [ return [
EnvironmentCheck::ERROR, EnvironmentCheck::ERROR,
@ -67,7 +67,7 @@ class SMTPConnectCheck implements EnvironmentCheck
fwrite($f, "HELO its_me\r\n"); fwrite($f, "HELO its_me\r\n");
$response = fread($f, 26); $response = fread($f, 26);
if (substr($response, 0, 3) != '220') { if (substr($response ?? '', 0, 3) != '220') {
return [ return [
EnvironmentCheck::ERROR, EnvironmentCheck::ERROR,
sprintf('Invalid mail server response: %s', $response) sprintf('Invalid mail server response: %s', $response)

View File

@ -0,0 +1,71 @@
<?php
namespace SilverStripe\EnvironmentCheck\Checks;
use Psr\Http\Message\ResponseInterface;
use SilverStripe\EnvironmentCheck\Traits\Fetcher;
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
/**
* Check that a given URL does not generate a session.
*
* @author Adrian Humphreys
* @package environmentcheck
*/
class SessionCheck implements EnvironmentCheck
{
use Fetcher;
/**
* Set up check with URL
*
* @param string $url The route, excluding the domain
* @inheritdoc
*/
public function __construct($url = '')
{
$this->setURL($url);
}
/**
* Check that the response for URL does not create a session
*
* @return array
*/
public function check()
{
$response = $this->client->get($this->getURL());
$cookie = $this->getCookie($response);
$fullURL = $this->getURL();
if ($cookie) {
return [
EnvironmentCheck::ERROR,
"Sessions are being set for {$fullURL} : Set-Cookie => " . $cookie,
];
}
return [
EnvironmentCheck::OK,
"Sessions are not being created for {$fullURL} 👍",
];
}
/**
* Get PHPSESSID or SECSESSID cookie set from the response if it exists.
*
* @param ResponseInterface $response
* @return string|null Cookie contents or null if it doesn't exist
*/
public function getCookie(ResponseInterface $response)
{
$result = null;
$cookies = $response->getHeader('Set-Cookie');
foreach ($cookies as $cookie) {
if (strpos($cookie ?? '', 'SESSID') !== false) {
$result = $cookie;
}
}
return $result;
}
}

View File

@ -3,9 +3,11 @@
namespace SilverStripe\EnvironmentCheck\Checks; namespace SilverStripe\EnvironmentCheck\Checks;
use SilverStripe\EnvironmentCheck\EnvironmentCheck; use SilverStripe\EnvironmentCheck\EnvironmentCheck;
use SilverStripe\FullTextSearch\Solr\Solr;
use SilverStripe\FullTextSearch\Solr\SolrIndex;
/** /**
* Check the availability of all Solr indexes of given class. * Check the availability of all Solr indexes
* *
* If there are no indexes of given class found, the returned status will still be "OK". * If there are no indexes of given class found, the returned status will still be "OK".
* *
@ -13,19 +15,6 @@ use SilverStripe\EnvironmentCheck\EnvironmentCheck;
*/ */
class SolrIndexCheck implements EnvironmentCheck class SolrIndexCheck implements EnvironmentCheck
{ {
/**
* @var null|string
*/
protected $indexClass;
/**
* @param string $indexClass Limit the index checks to the specified class and all its subclasses.
*/
public function __construct($indexClass = null)
{
$this->indexClass = $indexClass;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* *
@ -35,18 +24,16 @@ class SolrIndexCheck implements EnvironmentCheck
{ {
$brokenCores = []; $brokenCores = [];
/** if (!class_exists(Solr::class)) {
* @todo Revisit this when silverstripe/fulltextsearch has 4.x compat
*/
if (!class_exists('\\Solr')) {
return [ return [
EnvironmentCheck::ERROR, EnvironmentCheck::ERROR,
'Class `Solr` not found. Is the fulltextsearch module installed?' 'Class `' . Solr::class . '` not found. Is the fulltextsearch module installed?'
]; ];
} }
$service = \Solr::service(); $service = Solr::service();
foreach (\Solr::get_indexes($this->indexClass) as $index) { foreach (Solr::get_indexes() as $index) {
/** @var SolrIndex $core */
$core = $index->getIndexName(); $core = $index->getIndexName();
if (!$service->coreIsActive($core)) { if (!$service->coreIsActive($core)) {
$brokenCores[] = $core; $brokenCores[] = $core;
@ -56,7 +43,7 @@ class SolrIndexCheck implements EnvironmentCheck
if (!empty($brokenCores)) { if (!empty($brokenCores)) {
return [ return [
EnvironmentCheck::ERROR, EnvironmentCheck::ERROR,
'The following indexes are unavailable: ' . implode($brokenCores, ', ') 'The following indexes are unavailable: ' . implode(', ', $brokenCores)
]; ];
} }

View File

@ -49,7 +49,7 @@ class URLCheck implements EnvironmentCheck
EnvironmentCheck::ERROR, EnvironmentCheck::ERROR,
sprintf('Error retrieving "%s" (Code: %d)', $this->url, $response->getStatusCode()) sprintf('Error retrieving "%s" (Code: %d)', $this->url, $response->getStatusCode())
]; ];
} elseif ($this->testString && (strpos($response->getBody(), $this->testString) === false)) { } elseif ($this->testString && (strpos($response->getBody() ?? '', $this->testString ?? '') === false)) {
return [ return [
EnvironmentCheck::WARNING, EnvironmentCheck::WARNING,
sprintf('Success retrieving "%s", but string "%s" not found', $this->url, $this->testString) sprintf('Success retrieving "%s", but string "%s" not found', $this->url, $this->testString)

View File

@ -43,7 +43,11 @@ class DevCheckController extends Controller
$suite = $name; $suite = $name;
} }
$checker = new EnvironmentChecker($suite, 'Environment status'); /** @var EnvironmentChecker */
$checker = EnvironmentChecker::create($suite, 'Environment status')
->setRequest($request)
->setIncludeDetails(true);
$checker->init($this->config()->permission); $checker->init($this->config()->permission);
return $checker; return $checker;

View File

@ -3,6 +3,7 @@
namespace SilverStripe\EnvironmentCheck\Controllers; namespace SilverStripe\EnvironmentCheck\Controllers;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\EnvironmentCheck\EnvironmentChecker; use SilverStripe\EnvironmentCheck\EnvironmentChecker;
/** /**
@ -27,7 +28,6 @@ class DevHealthController extends Controller
public function index() public function index()
{ {
// health check does not require permission to run // health check does not require permission to run
$checker = new EnvironmentChecker('health', 'Site health'); $checker = new EnvironmentChecker('health', 'Site health');
$checker->setErrorCode(500); $checker->setErrorCode(500);

View File

@ -101,7 +101,7 @@ class EnvironmentCheckSuite
/** /**
* Run this test suite and return the result code of the worst result. * Run this test suite and return the result code of the worst result.
* *
* @return int * @return EnvironmentCheckSuiteResult
*/ */
public function run() public function run()
{ {
@ -125,7 +125,7 @@ class EnvironmentCheckSuite
/** /**
* Get instances of all the environment checks. * Get instances of all the environment checks.
* *
* @return array * @return EnvironmentChecker[]
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
protected function checkInstances() protected function checkInstances()

View File

@ -2,8 +2,8 @@
namespace SilverStripe\EnvironmentCheck; namespace SilverStripe\EnvironmentCheck;
use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use SilverStripe\Control\Director; use SilverStripe\Control\Director;
use SilverStripe\Control\Email\Email; use SilverStripe\Control\Email\Email;
use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\HTTPResponse;
@ -12,7 +12,6 @@ use SilverStripe\Control\RequestHandler;
use SilverStripe\Core\Environment; use SilverStripe\Core\Environment;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\Deprecation; use SilverStripe\Dev\Deprecation;
use SilverStripe\Security\BasicAuth;
use SilverStripe\Security\Member; use SilverStripe\Security\Member;
use SilverStripe\Security\Permission; use SilverStripe\Security\Permission;
use SilverStripe\Security\Security; use SilverStripe\Security\Security;
@ -41,6 +40,11 @@ class EnvironmentChecker extends RequestHandler
*/ */
protected $title; protected $title;
/**
* @var bool
*/
protected $includeDetails = false;
/** /**
* @var int * @var int
*/ */
@ -104,42 +108,32 @@ class EnvironmentChecker extends RequestHandler
if (Environment::getEnv('ENVCHECK_BASICAUTH_USERNAME') if (Environment::getEnv('ENVCHECK_BASICAUTH_USERNAME')
&& Environment::getEnv('ENVCHECK_BASICAUTH_PASSWORD') && Environment::getEnv('ENVCHECK_BASICAUTH_PASSWORD')
) { ) {
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { // Check that details are both provided, and match
// authenticate the input user/pass with the configured credentials if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])
if (!( || $_SERVER['PHP_AUTH_USER'] != Environment::getEnv('ENVCHECK_BASICAUTH_USERNAME')
$_SERVER['PHP_AUTH_USER'] == Environment::getEnv('ENVCHECK_BASICAUTH_USERNAME') || $_SERVER['PHP_AUTH_PW'] != Environment::getEnv('ENVCHECK_BASICAUTH_PASSWORD')
&& $_SERVER['PHP_AUTH_PW'] == Environment::getEnv('ENVCHECK_BASICAUTH_PASSWORD') ) {
) // Fail check with basic auth challenge
) {
$response = new HTTPResponse(null, 401);
$response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
// Exception is caught by RequestHandler->handleRequest() and will halt further execution
$e = new HTTPResponse_Exception(null, 401);
$e->setResponse($response);
throw $e;
}
} else {
$response = new HTTPResponse(null, 401); $response = new HTTPResponse(null, 401);
$response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\""); $response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
// Exception is caught by RequestHandler->handleRequest() and will halt further execution throw new HTTPResponse_Exception($response);
$e = new HTTPResponse_Exception(null, 401);
$e->setResponse($response);
throw $e;
}
} else {
if (!$this->canAccess(null, $permission)) {
return $this->httpError(403);
} }
} elseif (!$this->canAccess(null, $permission)) {
// Fail check with silverstripe login challenge
$result = Security::permissionFailure(
$this,
"You must have the {$permission} permission to access this check"
);
throw new HTTPResponse_Exception($result);
} }
} }
/** /**
* Determine if the current member can access the environment checker
*
* @param null|int|Member $member * @param null|int|Member $member
* @param string $permission * @param string $permission
*
* @return bool * @return bool
*
* @throws HTTPResponse_Exception
*/ */
public function canAccess($member = null, $permission = 'ADMIN') public function canAccess($member = null, $permission = 'ADMIN')
{ {
@ -147,10 +141,6 @@ class EnvironmentChecker extends RequestHandler
$member = Security::getCurrentUser(); $member = Security::getCurrentUser();
} }
if (!$member) {
$member = BasicAuth::requireLogin($this->getRequest(), 'Environment Checker', $permission, false);
}
// We allow access to this controller regardless of live-status or ADMIN permission only // We allow access to this controller regardless of live-status or ADMIN permission only
// if on CLI. Access to this controller is always allowed in "dev-mode", or of the user is ADMIN. // if on CLI. Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
if (Director::isDev() if (Director::isDev()
@ -187,19 +177,31 @@ class EnvironmentChecker extends RequestHandler
$response->setStatusCode($this->errorCode); $response->setStatusCode($this->errorCode);
} }
$resultText = $result->customise([ $data = [
'URL' => Director::absoluteBaseURL(), 'URL' => Director::absoluteBaseURL(),
'Title' => $this->title, 'Title' => $this->title,
'Name' => $this->checkSuiteName, 'Name' => $this->checkSuiteName,
'ErrorCode' => $this->errorCode, 'ErrorCode' => $this->errorCode
])->renderWith(__CLASS__); ];
$emailContent = $result->customise(array_merge($data, [
'IncludeDetails' => true
]))->renderWith(__CLASS__);
if (!$this->includeDetails) {
$webContent = $result->customise(array_merge($data, [
'IncludeDetails' => false
]))->renderWith(__CLASS__);
} else {
$webContent = $emailContent;
}
if ($this->config()->get('email_results') && !$result->ShouldPass()) { if ($this->config()->get('email_results') && !$result->ShouldPass()) {
$email = new Email( $email = new Email(
$this->config()->get('from_email_address'), $this->config()->get('from_email_address'),
$this->config()->get('to_email_address'), $this->config()->get('to_email_address'),
$this->title, $this->title,
$resultText $emailContent
); );
$email->send(); $email->send();
} }
@ -221,14 +223,14 @@ class EnvironmentChecker extends RequestHandler
// output the result as JSON if requested // output the result as JSON if requested
if ($this->getRequest()->getExtension() == 'json' if ($this->getRequest()->getExtension() == 'json'
|| strpos($this->getRequest()->getHeader('Accept'), 'application/json') !== false || strpos($this->getRequest()->getHeader('Accept') ?? '', 'application/json') !== false
) { ) {
$response->setBody($result->toJSON()); $response->setBody($result->toJSON());
$response->addHeader('Content-Type', 'application/json'); $response->addHeader('Content-Type', 'application/json');
return $response; return $response;
} }
$response->setBody($resultText); $response->setBody($webContent);
return $response; return $response;
} }
@ -237,80 +239,99 @@ class EnvironmentChecker extends RequestHandler
* Sends a log entry to the configured PSR-3 LoggerInterface * Sends a log entry to the configured PSR-3 LoggerInterface
* *
* @param string $message * @param string $message
* @param int $level * @param int $level
*/ */
public function log($message, $level) public function log($message, $level)
{ {
Injector::inst()->get(LoggerInterface::class)->log($level, $message); Injector::inst()->get(LoggerInterface::class)->log($level, $message);
} }
/** /**
* Set the HTTP status code that should be returned when there's an error. * Set the HTTP status code that should be returned when there's an error.
* *
* @param int $errorCode * @param int $errorCode
*
* @return $this
*/ */
public function setErrorCode($errorCode) public function setErrorCode($errorCode)
{ {
$this->errorCode = $errorCode; $this->errorCode = $errorCode;
return $this;
} }
/** /**
* @deprecated * Set whether to include the full breakdown of services
*
* @param bool $includeDetails
*
* @return $this
*/
public function setIncludeDetails($includeDetails)
{
$this->includeDetails = $includeDetails;
return $this;
}
/**
* @deprecated 2.0.0 Use config API instead
* @param string $from * @param string $from
*/ */
public static function set_from_email_address($from) public static function set_from_email_address($from)
{ {
Deprecation::notice('2.0', 'Use config API instead'); Deprecation::notice('2.0.0', 'Use config API instead');
static::config()->set('from_email_address', $from); static::config()->set('from_email_address', $from);
} }
/** /**
* @deprecated * @deprecated 2.0.0 Use config API instead
* @return null|string * @return null|string
*/ */
public static function get_from_email_address() public static function get_from_email_address()
{ {
Deprecation::notice('2.0', 'Use config API instead'); Deprecation::notice('2.0.0', 'Use config API instead');
return static::config()->get('from_email_address'); return static::config()->get('from_email_address');
} }
/** /**
* @deprecated * @deprecated 2.0.0 Use config API instead
* @param string $to * @param string $to
*/ */
public static function set_to_email_address($to) public static function set_to_email_address($to)
{ {
Deprecation::notice('2.0', 'Use config API instead'); Deprecation::notice('2.0.0', 'Use config API instead');
static::config()->set('to_email_address', $to); static::config()->set('to_email_address', $to);
} }
/** /**
* @deprecated * @deprecated 2.0.0 Use config API instead
* @return null|string * @return null|string
*/ */
public static function get_to_email_address() public static function get_to_email_address()
{ {
Deprecation::notice('2.0', 'Use config API instead'); Deprecation::notice('2.0.0', 'Use config API instead');
return static::config()->get('to_email_address'); return static::config()->get('to_email_address');
} }
/** /**
* @deprecated * @deprecated 2.0.0 Use config API instead
* @param bool $results * @param bool $results
*/ */
public static function set_email_results($results) public static function set_email_results($results)
{ {
Deprecation::notice('2.0', 'Use config API instead'); Deprecation::notice('2.0.0', 'Use config API instead');
static::config()->set('email_results', $results); static::config()->set('email_results', $results);
} }
/** /**
* @deprecated * @deprecated 2.0.0 Use config API instead
* @return bool * @return bool
*/ */
public static function get_email_results() public static function get_email_results()
{ {
Deprecation::notice('2.0', 'Use config API instead'); Deprecation::notice('2.0.0', 'Use config API instead');
return static::config()->get('email_results'); return static::config()->get('email_results');
} }
} }

View File

@ -0,0 +1,49 @@
<?php
namespace SilverStripe\EnvironmentCheck\Services;
use GuzzleHttp\Client as GuzzleClient;
use SilverStripe\Core\Injector\Factory;
use SilverStripe\Core\Config\Configurable;
/**
* Factory class for creating HTTP client which are injected into some env check classes. Inject via YAML,
* arguments for Guzzle client can be supplied using "constructor" property or set as default_config.
*
* @see SilverStripe\EnvironmentCheck\Traits\Fetcher
*/
class ClientFactory implements Factory
{
use Configurable;
/**
* Default config for Guzzle client.
*
* @var array
*/
private static $default_config = [];
/**
* Wrapper to create a Guzzle client.
*
* {@inheritdoc}
*/
public function create($service, array $params = [])
{
return new GuzzleClient($this->getConfig($params));
}
/**
* Merge config provided from yaml with default config
*
* @param array $overrides
* @return array
*/
public function getConfig(array $overrides)
{
return array_merge(
$this->config()->get('default_config'),
$overrides
);
}
}

51
src/Traits/Fetcher.php Normal file
View File

@ -0,0 +1,51 @@
<?php
namespace SilverStripe\EnvironmentCheck\Traits;
use SilverStripe\Control\Director;
/**
* Simple helper for env checks which require HTTP clients.
*
* @package environmentcheck
*/
trait Fetcher
{
/**
* Client for making requests, set vi Injector.
*
* @see SilverStripe\EnvironmentCheck\Services
*
* @var GuzzleHttp\Client
*/
public $client = null;
/**
* Absolute URL for requests.
*
* @var string
*/
protected $url;
/**
* Set URL for requests.
*
* @param string $url Relative URL
* @return self
*/
public function setURL($url)
{
$this->url = Director::absoluteURL($url);
return $this;
}
/**
* Getter for URL
*
* @return string
*/
public function getURL()
{
return $this->url;
}
}

View File

@ -64,16 +64,18 @@
<h1 class="$Status">$Title: $Status</h1> <h1 class="$Status">$Title: $Status</h1>
<h2 class="website">Site: $URL</h2> <h2 class="website">Site: $URL</h2>
<% if $IncludeDetails %>
<table> <table>
<tr><th>Check</th> <th>Status</th> <th>Message</th></tr> <tr><th>Check</th> <th>Status</th> <th>Message</th></tr>
<% loop $Details %> <% loop $Details %>
<tr><td>$Check</td> <td class="$Status">$Status</td> <td>$Message.XML</td></tr> <tr><td>$Check</td> <td class="$Status">$Status</td> <td>$Message.XML</td></tr>
<% end_loop %> <% end_loop %>
</table> </table>
<% end_if %>
<% if $ShouldPass %> <% if $ShouldPass %>
<p>Site is available</p> <p>Site is available</p>
<p class="subtext">(you may check for the presence of the text 'Site is available' rather than an HTTP $ErrorCode error on this page, if you prefer.)</p> <p class="subtext">(you may check for the presence of the text 'Site is available' rather than an HTTP $ErrorCode error on this page, if you prefer.<% if not $IncludeDetails %> Full details are available for logged in users at <a href="{$AbsoluteBaseURL}dev/check/">dev/check</a><% end_if %>)</p>
<% else %> <% else %>
<% if $Name == "check" %> <% if $Name == "check" %>
<p><b>A subsystem of the site is unavailable, but the site remains operational</b></p> <p><b>A subsystem of the site is unavailable, but the site remains operational</b></p>

View File

@ -0,0 +1,101 @@
<?php
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use SilverStripe\Dev\SapphireTest;
use GuzzleHttp\Handler\MockHandler;
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
use SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck;
/**
* Test session checks.
*/
class CacheHeadersCheckTest extends SapphireTest
{
/**
* Test that directives that must be included, are.
*
* @return void
*/
public function testMustInclude()
{
// Create a mock and queue responses
$mock = new MockHandler([
new Response(200, ['Cache-Control' => 'must-revalidate', 'ETag' => '123']),
new Response(200, ['Cache-Control' =>'no-cache', 'ETag' => '123']),
new Response(200, ['ETag' => '123']),
new Response(200, ['Cache-Control' => 'must-revalidate, private', 'ETag' => '123']),
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$cacheHeadersCheck = new CacheHeadersCheck('/', ['must-revalidate']);
$cacheHeadersCheck->client = $client;
// Run checks for each response above
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
}
/**
* Test that directives that must be excluded, are.
*
* @return void
*/
public function testMustExclude()
{
// Create a mock and queue responses
$mock = new MockHandler([
new Response(200, ['Cache-Control' => 'must-revalidate', 'ETag' => '123']),
new Response(200, ['Cache-Control' =>'no-cache', 'ETag' => '123']),
new Response(200, ['ETag' => '123']),
new Response(200, ['Cache-Control' =>'private, no-store', 'ETag' => '123']),
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$cacheHeadersCheck = new CacheHeadersCheck('/', [], ["no-store", "no-cache", "private"]);
$cacheHeadersCheck->client = $client;
// Run checks for each response above
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
}
/**
* Test that Etag header must exist in response.
*
* @return void
*/
public function testEtag()
{
// Create a mock and queue responses
$mock = new MockHandler([
new Response(200, ['Cache-Control' => 'must-revalidate', 'ETag' => '123']),
new Response(200, ['Cache-Control' =>'no-cache']),
new Response(200, ['ETag' => '123']),
new Response(200, []),
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$cacheHeadersCheck = new CacheHeadersCheck('/');
$cacheHeadersCheck->client = $client;
// Run checks for each response above
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
}
}

View File

@ -10,8 +10,6 @@ use SilverStripe\Security\Member;
/** /**
* Class DatabaseCheckTest * Class DatabaseCheckTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class DatabaseCheckTest extends SapphireTest class DatabaseCheckTest extends SapphireTest

View File

@ -0,0 +1,73 @@
<?php
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
use SilverStripe\Core\Kernel;
use SilverStripe\Control\Director;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
use SilverStripe\EnvironmentCheck\Checks\EnvTypeCheck;
/**
* Test the env setting check.
*/
class EnvTypeCheckTest extends SapphireTest
{
/**
* Check is OK when in live mode
*
* @return void
*/
public function testEnvSettingLive()
{
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
$kernel->setEnvironment('live');
$this->assertTrue(Director::isLive());
$checker = Injector::inst()->get(EnvTypeCheck::class);
$result = $checker->check();
$this->assertSame($result[0], EnvironmentCheck::OK);
}
/**
* Check is ERROR when in test mode
*
* @return void
*/
public function testEnvSettingTest()
{
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
$kernel->setEnvironment('test');
$this->assertTrue(Director::isTest());
$checker = Injector::inst()->get(EnvTypeCheck::class);
$result = $checker->check();
$this->assertSame($result[0], EnvironmentCheck::ERROR);
}
/**
* Check is ERROR when in dev mode
*
* @return void
*/
public function testEnvSettingDev()
{
/** @var Kernel $kernel */
$kernel = Injector::inst()->get(Kernel::class);
$kernel->setEnvironment('dev');
$this->assertTrue(Director::isDev());
$checker = Injector::inst()->get(EnvTypeCheck::class);
$result = $checker->check();
$this->assertSame($result[0], EnvironmentCheck::ERROR);
}
}

View File

@ -9,8 +9,6 @@ use SilverStripe\EnvironmentCheck\EnvironmentCheck;
/** /**
* Class ExternalURLCheckTest * Class ExternalURLCheckTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class ExternalURLCheckTest extends SapphireTest class ExternalURLCheckTest extends SapphireTest

View File

@ -9,8 +9,6 @@ use SilverStripe\Dev\SapphireTest;
/** /**
* Class FileWritableCheckTest * Class FileWritableCheckTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class FileWritableCheckTest extends SapphireTest class FileWritableCheckTest extends SapphireTest

View File

@ -9,8 +9,6 @@ use SilverStripe\EnvironmentCheck\EnvironmentCheck;
/** /**
* Class HasClassCheckTest * Class HasClassCheckTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class HasClassCheckTest extends SapphireTest class HasClassCheckTest extends SapphireTest

View File

@ -9,8 +9,6 @@ use SilverStripe\Dev\SapphireTest;
/** /**
* Class HasFunctionCheckTest * Class HasFunctionCheckTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class HasFunctionCheckTest extends SapphireTest class HasFunctionCheckTest extends SapphireTest

View File

@ -0,0 +1,76 @@
<?php
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use SilverStripe\Dev\SapphireTest;
use GuzzleHttp\Handler\MockHandler;
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
use SilverStripe\EnvironmentCheck\Checks\SessionCheck;
/**
* Test session checks.
*/
class SessionCheckTest extends SapphireTest
{
/**
* @var SilverStripe\EnvironmentCheck\Checks\SessionCheck
*/
public $sessionCheck = null;
/**
* Create a session check for use by tests.
*
* @return void
*/
protected function setUp(): void
{
parent::setUp();
$this->sessionCheck = new SessionCheck('/');
}
/**
* Env check reports error when session cookies are being set.
*
* @return void
*/
public function testSessionSet()
{
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['Set-Cookie' => 'PHPSESSID:foo']),
new Response(200, ['Set-Cookie' => 'SECSESSID:bar'])
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->sessionCheck->client = $client;
// Check for PHPSESSID
$this->assertContains(EnvironmentCheck::ERROR, $this->sessionCheck->check());
// Check for SECSESSID
$this->assertContains(EnvironmentCheck::ERROR, $this->sessionCheck->check());
}
/**
* Env check responds OK when no session cookies are set in response.
*
* @return void
*/
public function testSessionNotSet()
{
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200)
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$this->sessionCheck->client = $client;
$this->assertContains(EnvironmentCheck::OK, $this->sessionCheck->check());
}
}

View File

@ -9,8 +9,6 @@ use SilverStripe\Dev\SapphireTest;
/** /**
* Class URLCheckTest * Class URLCheckTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class URLCheckTest extends SapphireTest class URLCheckTest extends SapphireTest

View File

@ -10,8 +10,6 @@ use SilverStripe\EnvironmentCheck\EnvironmentChecker;
/** /**
* Class DevCheckControllerTest * Class DevCheckControllerTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class DevCheckControllerTest extends SapphireTest class DevCheckControllerTest extends SapphireTest
@ -30,4 +28,14 @@ class DevCheckControllerTest extends SapphireTest
$this->assertInstanceOf(EnvironmentChecker::class, $controller->index($request)); $this->assertInstanceOf(EnvironmentChecker::class, $controller->index($request));
} }
public function testCheckIncludesDetails()
{
$controller = new DevCheckController();
$request = new HTTPRequest('GET', 'example.com');
$response = $controller->index($request)->index();
$this->assertStringContainsString('<table>', $response->getBody());
}
} }

View File

@ -10,8 +10,6 @@ use SilverStripe\EnvironmentCheck\EnvironmentChecker;
/** /**
* Class DevHealthControllerTest * Class DevHealthControllerTest
* *
* @mixin PHPUnit_Framework_TestCase
*
* @package environmentcheck * @package environmentcheck
*/ */
class DevHealthControllerTest extends SapphireTest class DevHealthControllerTest extends SapphireTest
@ -39,4 +37,15 @@ class DevHealthControllerTest extends SapphireTest
$this->assertInstanceOf(EnvironmentChecker::class, $controller->index($request)); $this->assertInstanceOf(EnvironmentChecker::class, $controller->index($request));
} }
public function testHealthDoesNotIncludeDetails()
{
$controller = new DevHealthController();
$request = new HTTPRequest('GET', 'example.com');
$response = $controller->index($request)->index();
$this->assertFalse(strpos($response->getBody(), '<table>'));
}
} }

View File

@ -20,7 +20,7 @@ class EnvironmentCheckerTest extends SapphireTest
{ {
protected $usesDatabase = true; protected $usesDatabase = true;
protected function tearDown() protected function tearDown(): void
{ {
EnvironmentCheckSuite::reset(); EnvironmentCheckSuite::reset();
parent::tearDown(); parent::tearDown();
@ -61,8 +61,8 @@ class EnvironmentCheckerTest extends SapphireTest
$logger->expects($this->once()) $logger->expects($this->once())
->method('log') ->method('log')
->withConsecutive( ->withConsecutive(
$this->equalTo(LogLevel::WARNING), [$this->equalTo(LogLevel::WARNING)],
$this->anything() [$this->anything()]
); );
Injector::inst()->registerService($logger, LoggerInterface::class); Injector::inst()->registerService($logger, LoggerInterface::class);