Skip to content

Commit 74385fb

Browse files
authored
chore/phpstan (#10)
* add stan * fix stan
1 parent f91bbdb commit 74385fb

14 files changed

+198
-28
lines changed

.circleci/config.yml

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ workflows:
1515
requires:
1616
- ci-caching/build-docker-images
1717
- ci-php/code-style:
18+
dockerComposeFile: "./docker/docker-compose.yml"
19+
dependencyCheckSumFile: "./composer.json"
20+
requires:
21+
- ci-php/install-dependencies
22+
- ci-php/static-analysis:
1823
dockerComposeFile: "./docker/docker-compose.yml"
1924
dependencyCheckSumFile: "./composer.json"
2025
requires:

Makefile

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
1-
.PHONY: clean code-style pcov-enable pcov-disable
2-
.DEFAULT_GOAL := code-style
1+
.PHONY: clean code-style coverage help test static-analysis install-dependencies install-dependencies-lowest update-dependencies pcov-enable pcov-disable infection-testing
2+
.DEFAULT_GOAL := test
33

4+
PHPUNIT = ./vendor/bin/phpunit -c ./phpunit.xml
5+
PHPSTAN = ./vendor/bin/phpstan
46
PHPCS = ./vendor/bin/phpcs --extensions=php
7+
CONSOLE = ./bin/console
8+
INFECTION = ./vendor/bin/infection
59

610
clean:
711
rm -rf ./build ./vendor
812

913
code-style: pcov-disable
1014
mkdir -p build/logs/phpcs
11-
${PHPCS}
15+
${PHPCS} --report-full --report-gitblame --standard=PSR12 ./src --exclude=Generic.Commenting.Todo --report-junit=build/logs/phpcs/junit.xml
16+
17+
coverage: pcov-enable
18+
${PHPUNIT} && ./vendor/bin/coverage-check clover.xml 100
19+
20+
test: pcov-disable
21+
${PHPUNIT}
22+
23+
static-analysis: pcov-disable
24+
mkdir -p build/logs/phpstan
25+
${PHPSTAN} analyse --no-progress --memory-limit=128M
1226

1327
update-dependencies:
1428
composer update
@@ -19,6 +33,13 @@ install-dependencies:
1933
install-dependencies-lowest:
2034
composer install --prefer-lowest
2135

36+
infection-testing:
37+
make coverage
38+
cp -f build/logs/phpunit/junit.xml build/logs/phpunit/coverage/junit.xml
39+
sudo php-ext-disable pcov
40+
${INFECTION} --coverage=build/logs/phpunit/coverage --min-msi=93 --threads=`nproc`
41+
sudo php-ext-enable pcov
42+
2243
pcov-enable:
2344
sudo php-ext-enable pcov
2445

@@ -32,6 +53,13 @@ help:
3253
# Targets:
3354
# clean Cleans the coverage and the vendor directory
3455
# code-style Check codestyle using phpcs
56+
# coverage Generate code coverage (html, clover)
3557
# help You're looking at it!
58+
# test (default) Run all the tests with phpunit
59+
# static-analysis Run static analysis using phpstan
60+
# infection-testing Run infection/mutation testing
61+
# install-dependencies Run composer install
62+
# install-dependencies-lowest Run composer install with --prefer-lowest
63+
# update-dependencies Run composer update
3664
# pcov-enable Enable pcov
3765
# pcov-disable Disable pcov

composer.json

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
"pimple/pimple": "^3.5"
3232
},
3333
"require-dev": {
34+
"infection/infection": "^0.25.3",
35+
"johnkary/phpunit-speedtrap": "^4.0",
36+
"phpstan/phpstan": "^1.2",
37+
"phpunit/phpunit": "^9.5",
38+
"rregeer/phpunit-coverage-check": "^0.3.1",
3439
"squizlabs/php_codesniffer": "^3.6"
3540
},
3641
"bin": [

infection.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"timeout": 10,
3+
"source": {
4+
"directories": [
5+
"src"
6+
]
7+
},
8+
"logs": {
9+
"text": "build\/logs\/infection\/infection.log",
10+
"summary": "build\/logs\/infection\/infection-summary.log"
11+
},
12+
"mutators": {
13+
"@default": true
14+
},
15+
"phpUnit": {
16+
"customPath": "vendor/bin/phpunit"
17+
}
18+
}

phpstan.neon

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: 9
3+
paths: [ src ]
4+
ignoreErrors:
5+
- "#Cannot access offset '[a-z]+' on mixed.#"

phpunit.xml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
processIsolation="false"
10+
stopOnFailure="false"
11+
forceCoversAnnotation="true"
12+
bootstrap="tests/bootstrap.php"
13+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd">
14+
<coverage>
15+
<include>
16+
<directory>src</directory>
17+
</include>
18+
<report>
19+
<clover outputFile="clover.xml"/>
20+
<html outputDirectory="build/logs/phpunit/coverage"/>
21+
<text outputFile="php://stdout" showOnlySummary="true"/>
22+
<xml outputDirectory="build/logs/phpunit/coverage/coverage-xml"/>
23+
</report>
24+
</coverage>
25+
<php>
26+
<ini name="max_execution_time" value="-1"/>
27+
<ini name="html_errors" value="false"/>
28+
<ini name="memory_limit" value="2G"/>
29+
</php>
30+
<testsuites>
31+
<testsuite name="Unit">
32+
<directory>./tests/Unit</directory>
33+
</testsuite>
34+
</testsuites>
35+
<logging>
36+
<junit outputFile="build/logs/phpunit/junit.xml"/>
37+
</logging>
38+
<listeners>
39+
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener"/>
40+
</listeners>
41+
</phpunit>

src/AppContainer.php

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
class AppContainer
1212
{
1313
/**
14-
* @param string $env
1514
* @return Container
1615
*/
1716
public static function init(): Container

src/Command/ConvertAvscToJsonCommand.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ public function execute(InputInterface $input, OutputInterface $output): ?int
7070
}
7171

7272
++$count;
73-
$avsc = file_get_contents($file->getRealPath());
73+
74+
/** @var string $filepath */
75+
$filepath = $file->getRealPath();
76+
77+
/** @var string $avsc */
78+
$avsc = file_get_contents($filepath);
7479
$json = $this->converter->convert($avsc, ['markNoDefaultAsRequired' => $noDefaultAsRequired]);
7580

7681
if (false === file_exists($outputDirectory)) {

src/Command/ConvertSingleAvscToJsonCommand.php

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public function execute(InputInterface $input, OutputInterface $output): ?int
5454
return -1;
5555
}
5656

57+
/** @var string $avsc */
5758
$avsc = file_get_contents($avscSchema);
5859
$json = $this->converter->convert($avsc, ['markNoDefaultAsRequired' => $noDefaultAsRequired]);
5960

src/Converter/AvscToJson.php

+67-12
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,33 @@
66

77
class AvscToJson implements ConverterInterface
88
{
9+
/** @var array<string,mixed> $options */
910
private array $options;
1011

12+
/**
13+
* @param string $avscSchema
14+
* @param array<string,mixed> $options
15+
* @return string
16+
*/
1117
public function convert(string $avscSchema, array $options): string
1218
{
1319
$this->options = $options;
14-
$avscArray = json_decode($avscSchema, true);
20+
21+
/** @var mixed[] $avscArray */
22+
$avscArray = json_decode($avscSchema, true, JSON_THROW_ON_ERROR);
1523
$jsonArray = $this->convertAvro($avscArray);
24+
25+
/** @var string $rawJson */
1626
$rawJson = json_encode($jsonArray);
1727
$json = $this->fixAvroTypes($rawJson);
1828

1929
return $json;
2030
}
2131

32+
/**
33+
* @param mixed[] $avscArray
34+
* @return mixed[]
35+
*/
2236
private function convertAvro(array $avscArray): array
2337
{
2438
$jsonArray = [];
@@ -30,21 +44,28 @@ private function convertAvro(array $avscArray): array
3044
if ('type' === $key && 'array' === $value) {
3145
$jsonArray[$key] = $value;
3246

47+
/** @var string|mixed[] $items */
48+
$items = $avscArray['items'];
49+
3350
if (
34-
true === $this->isBasicType($avscArray['items'])
35-
|| (true === is_array($avscArray['items']) && true === $this->isBasicTypeArray($avscArray['items']))
51+
true === $this->isBasicType($items)
52+
|| (true === is_array($items) && true === $this->isBasicTypeArray($items))
53+
) {
54+
$jsonArray['items'] = $items;
55+
} elseif (
56+
true === is_array($items)
57+
&& true === isset($items['type'])
58+
&& 'record' === $items['type']
3659
) {
37-
$jsonArray['items'] = $avscArray['items'];
38-
} elseif (true === isset($avscArray['items']['type']) && 'record' === $avscArray['items']['type']) {
39-
$jsonArray['items'] = $this->convertAvro($avscArray['items']);
40-
} else {
41-
$jsonArray['items'] = $this->getAnyOf($avscArray['items']);
60+
$jsonArray['items'] = $this->convertAvro($items);
61+
} elseif (true === is_array($items)) {
62+
$jsonArray['items'] = $this->getAnyOf($items);
4263
}
4364
}
44-
if ('name' === $key) {
65+
if ('name' === $key && true === is_string($value)) {
4566
$jsonArray['title'] = $this->snakeToPascal($value);
4667
}
47-
if ('fields' === $key) {
68+
if ('fields' === $key && true === is_array($value)) {
4869
$jsonArray['properties'] = $this->convertAvroFieldsToJsonFields($value);
4970
$requiredFields = $this->getRequiredFields($value);
5071

@@ -57,11 +78,16 @@ private function convertAvro(array $avscArray): array
5778
return $jsonArray;
5879
}
5980

81+
/**
82+
* @param mixed[] $avroFields
83+
* @return mixed[]
84+
*/
6085
private function convertAvroFieldsToJsonFields(array $avroFields): array
6186
{
6287
$fields = [];
6388

6489
foreach ($avroFields as $field) {
90+
/** @var string|mixed[] $fieldType */
6591
$fieldType = $field['type'];
6692

6793
if (
@@ -80,13 +106,19 @@ private function convertAvroFieldsToJsonFields(array $avroFields): array
80106
return $fields;
81107
}
82108

109+
/**
110+
* @param mixed[] $avroFields
111+
* @return mixed[]
112+
*/
83113
private function getRequiredFields(array $avroFields): array
84114
{
85115
$requiredFields = [];
86116

87117
foreach ($avroFields as $field) {
88118
if (
89-
true === $this->options['markNoDefaultAsRequired'] && false === array_key_exists('default', $field)
119+
true === $this->options['markNoDefaultAsRequired']
120+
&& true === is_array($field)
121+
&& false === array_key_exists('default', $field)
90122
) {
91123
$requiredFields[] = $field['name'];
92124
} elseif (false === $this->options['markNoDefaultAsRequired']) {
@@ -97,17 +129,28 @@ private function getRequiredFields(array $avroFields): array
97129
return $requiredFields;
98130
}
99131

132+
/**
133+
* @param mixed[] $types
134+
* @return mixed[]
135+
*/
100136
private function getAnyOf(array $types)
101137
{
102138
$anyOf = [];
103139

104140
foreach ($types as $type) {
141+
if (false === is_string($type) && false === is_array($type)) {
142+
continue;
143+
}
105144
$anyOf['anyOf'][] = $this->getAnyOfType($type);
106145
}
107146

108147
return $anyOf;
109148
}
110149

150+
/**
151+
* @param string|mixed[] $type
152+
* @return mixed[]|string[]
153+
*/
111154
private function getAnyOfType($type)
112155
{
113156
if (true === is_string($type)) {
@@ -117,9 +160,16 @@ private function getAnyOfType($type)
117160
}
118161
}
119162

163+
/**
164+
* @param mixed[] $fieldTypes
165+
* @return bool
166+
*/
120167
private function isBasicTypeArray(array $fieldTypes): bool
121168
{
122169
foreach ($fieldTypes as $type) {
170+
if (false === is_string($type) && false === is_array($type)) {
171+
continue;
172+
}
123173
if (false === $this->isBasicType($type)) {
124174
return false;
125175
}
@@ -128,6 +178,10 @@ private function isBasicTypeArray(array $fieldTypes): bool
128178
return true;
129179
}
130180

181+
/**
182+
* @param string|mixed[] $type
183+
* @return bool
184+
*/
131185
private function isBasicType($type)
132186
{
133187
if (false === is_string($type)) {
@@ -142,13 +196,14 @@ private function snakeToPascal(string $input): string
142196
return str_replace(' ', '', ucwords(str_replace('_', ' ', $input)));
143197
}
144198

145-
private function fixAvroTypes(string $rawJson)
199+
private function fixAvroTypes(string $rawJson): string
146200
{
147201
$json = str_replace('int', 'integer', $rawJson);
148202
$json = str_replace('long', 'number', $json);
149203
$json = str_replace('float', 'number', $json);
150204
$json = str_replace('double', 'number', $json);
151205
$json = str_replace('bytes', 'string', $json);
206+
152207
return $json;
153208
}
154209
}

src/Converter/ConverterInterface.php

+5
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@
66

77
interface ConverterInterface
88
{
9+
/**
10+
* @param string $avscSchema
11+
* @param array<string,mixed> $options
12+
* @return string
13+
*/
914
public function convert(string $avscSchema, array $options): string;
1015
}

0 commit comments

Comments
 (0)