Skip to content

Commit a63c1bc

Browse files
committed
Initial version
0 parents  commit a63c1bc

21 files changed

+1708
-0
lines changed

.editorconfig

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
root = true
2+
3+
[*]
4+
5+
end_of_line = lf
6+
charset = utf-8
7+
indent_style = space
8+
indent_size = 4
9+
trim_trailing_whitespace = true
10+
insert_final_newline = false
11+
12+
[*.php]
13+
14+
end_of_line = lf
15+
charset = utf-8
16+
indent_style = space
17+
indent_size = 4
18+
insert_final_newline = true
19+
trim_trailing_whitespace = true

.gitattributes

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/tests export-ignore
2+
/examples export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore
5+
/phpunit.xml.dist export-ignore
6+
/phpcs.xml.dist export-ignore
7+
/captainhook.json export-ignore
8+
/.travis.yml export-ignore
9+
/ruleset.xml export-ignore
10+
/.editorconfig export-ignore
11+
/phpstan.neon export-ignore
12+
/phpstan-baseline.neon export-ignore
13+
/psalm.xml export-ignore
14+
/psalm-baseline.xml export-ignore
15+
/vendor-bin export-ignore

.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/.idea
2+
/composer.lock
3+
/vendor
4+
/vendor-bin/**/vendor
5+
/vendor-bin/**/composer.lock
6+
/.phpunit*
7+
/tests/_output
8+
/tests/_reports
9+
/build

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Andreas Leathley
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
Squirrel Types
2+
=====================
3+
4+
[![Software License](https://img.shields.io/badge/coverage-100%25-brightgreen)](LICENSE) [![Packagist Version](https://img.shields.io/packagist/v/squirrelphp/types.svg?style=flat-round)](https://packagist.org/packages/squirrelphp/types) [![PHP Version](https://img.shields.io/packagist/php-v/squirrelphp/types.svg)](https://packagist.org/packages/squirrelphp/types) [![Software License](https://img.shields.io/badge/license-MIT-success.svg?style=flat-round)](LICENSE)
5+
6+
Explicit handling of type coercions and enforcement in PHP in order to deal with unknown data in a more predictable and safe way.
7+
8+
Installation
9+
------------
10+
11+
composer require squirrelphp/types
12+
13+
Table of contents
14+
-----------------
15+
16+
- [Introduction](#introduction)
17+
- [Coercion behavior overview](#coercion-behavior-overview)
18+
- [Test if value can be coerced](#test-if-value-can-be-coerced)
19+
- [Coerce a value](#coerce-a-value)
20+
- [Enforce a type for a value](#enforce-a-type-for-a-value)
21+
22+
Introduction
23+
------------
24+
25+
When getting values from a database or an API (or even just another PHP component you do not know) you have an expectation of what type of value you will get, but you might get something more or less different.
26+
27+
A database might give you the string "1" for a boolean, or the integer 1, and that would often be fine (many databases have no boolean type, so some coercion is often necessary). But getting the string "hello" or the integer -30 should not be fine for a boolean and most likely points to a mistake - maybe the expected type is wrong, or the wrong field in the database was retrieved.
28+
29+
This small component coerces and enforces types and is fully tested to behave in a predictable way. This should often be better than using other coercion methods like explicit casts `(bool)` / `boolval()`, as these will coerce any value, while this component will reject unreasonable values and throw a TypeError. It coerces less values than PHP does in coercive typing mode, as PHP accepts quite a few questionable values for legacy/BC reasons.
30+
31+
Coercion behavior overview
32+
--------------------------
33+
34+
All argument flags mentioned below are set to false by default for a more conservative coercion behavior. Enable them only if it is necessary for your use case.
35+
36+
### toBool
37+
38+
- Always accepts bools
39+
- Accepts "0" and "1" strings
40+
- Accepts 0 and 1 ints
41+
- Only allows "" string if the argument `$allowEmptyString` is set to true
42+
- Only allows 0.0 and 1.0 floats if the argument `$allowFloat` is set to true
43+
44+
### toInt
45+
46+
- Always accepts ints
47+
- Accepts floats and numeric strings without a fractional part
48+
- Only allows bools if the argument `$allowBool` is set to true
49+
50+
### toFloat
51+
52+
- Always accepts ints and floats
53+
- Accepts numeric strings
54+
- Only allows bools if the argument `$allowBool` is set to true
55+
56+
### toString
57+
58+
- Always accepts strings
59+
- Accepts any ints and floats
60+
- Only allows bools if the argument `$allowBool` is set to true
61+
- Only allows Stringable objects if the argument `$allowStringable` is set to true
62+
63+
Test if value can be coerced
64+
----------------------------
65+
66+
All these functions have the mixed `$value` as their first argument and return true or false:
67+
68+
### Coerceable::toBool
69+
70+
Returns true if `$value` is one of the following:
71+
72+
- A boolean
73+
- A string with value '0' or '1'
74+
- An int with value 0 or 1
75+
- An empty string - only if the argument `$allowEmptyString` is set to true
76+
- A float with value 0 or 1 - only if the `$allowFloat` argument is set to true
77+
78+
For any other values it returns false.
79+
80+
### Coerceable::toInt
81+
82+
Returns true if `$value` is one of the following:
83+
84+
- An integer
85+
- A float without fractional part
86+
- A numeric string without fractional part
87+
- A boolean - only if the `$allowBool` argument is set to true
88+
89+
For any other values it returns false.
90+
91+
### Coerceable::toFloat
92+
93+
Returns true if `$value` is one of the following:
94+
95+
- An integer or float
96+
- A numeric string
97+
- A boolean - only if the `$allowBool` argument is set to true
98+
99+
For any other values it returns false.
100+
101+
### Coerceable::toString
102+
103+
Returns true if `$value` is one of the following:
104+
105+
- A string
106+
- An integer or float
107+
- A boolean - only if the `$allowBool` argument is set to true
108+
- A Stringable object - only if the `$allowStringable` argument is set to true
109+
110+
For any other values it returns false.
111+
112+
Coerce a value
113+
--------------
114+
115+
All these functions have the mixed `$value` as their first argument and return the type they are coercing (or throw a TypeError).
116+
117+
### Coerce::toBool
118+
119+
Returns a boolean if the given value is coerceable, see [Coerceable::toBool for valid values](#coerceabletobool), or a TypeError if the value is not coerceable.
120+
121+
### Coerce::toInt
122+
123+
Returns an integer if the given value is coerceable, see [Coerceable::toInt for valid values](#coerceabletoint), or a TypeError if the value is not coerceable.
124+
125+
### Coerce::toFloat
126+
127+
Returns a float if the given value is coerceable, see [Coerceable::toFloat for valid values](#coerceabletofloat), or a TypeError if the value is not coerceable.
128+
129+
### Coerce::toString
130+
131+
Returns a string if the given value is coerceable, see [Coerceable::toString for valid values](#coerceabletoint), or a TypeError if the value is not coerceable.
132+
133+
Enforce a type for a value
134+
--------------------------
135+
136+
All these functions have the mixed `$value` as their only argument and return the type they are enforcing, according to the same logic as strict mode in PHP.
137+
138+
### Enforce::asBool
139+
140+
Returns `$value` as a boolean if it is a boolean. Throws a TypeError otherwise.
141+
142+
### Enforce::asInt
143+
144+
Returns `$value` as an integer if it is an integer. Throws a TypeError otherwise.
145+
146+
### Enforce::asFloat
147+
148+
Returns `$value` as a float if it is an integer or a float. Throws a TypeError otherwise.
149+
150+
### Enforce::asString
151+
152+
Returns `$value` as a string if it is a string. Throws a TypeError otherwise.

captainhook.json

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"commit-msg": {
3+
"enabled": true,
4+
"actions": [
5+
{
6+
"action": "\\CaptainHook\\App\\Hook\\Message\\Action\\Beams",
7+
"options": {
8+
"subjectLength": 50,
9+
"bodyLineLength": 72
10+
},
11+
"conditions": []
12+
}
13+
]
14+
},
15+
"pre-push": {
16+
"enabled": false,
17+
"actions": []
18+
},
19+
"pre-commit": {
20+
"enabled": true,
21+
"actions": [
22+
{
23+
"action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting",
24+
"options": [],
25+
"conditions": []
26+
},
27+
{
28+
"action": "composer phpunit",
29+
"options": [],
30+
"conditions": []
31+
},
32+
{
33+
"action": "composer phpstan",
34+
"options": [],
35+
"conditions": []
36+
},
37+
{
38+
"action": "composer psalm",
39+
"options": [],
40+
"conditions": []
41+
},
42+
{
43+
"action": "composer phpcs",
44+
"options": [],
45+
"conditions": []
46+
}
47+
]
48+
},
49+
"prepare-commit-msg": {
50+
"enabled": false,
51+
"actions": []
52+
},
53+
"post-commit": {
54+
"enabled": false,
55+
"actions": []
56+
},
57+
"post-merge": {
58+
"enabled": false,
59+
"actions": []
60+
},
61+
"post-checkout": {
62+
"enabled": false,
63+
"actions": []
64+
}
65+
}

composer.json

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"name": "squirrelphp/types",
3+
"type": "library",
4+
"description": "Explicit handling of type coercions and enforcement in PHP in order to deal with unknown data in a more predictable and safe way.",
5+
"keywords": [
6+
"php",
7+
"types",
8+
"coercions",
9+
"enforcement",
10+
"input",
11+
"data processing"
12+
],
13+
"homepage": "https://github.com/squirrelphp/types",
14+
"license": "MIT",
15+
"authors": [
16+
{
17+
"name": "Andreas Leathley",
18+
"email": "[email protected]"
19+
}
20+
],
21+
"require": {
22+
"php": ">=8.0",
23+
"squirrelphp/debug": "^2.0"
24+
},
25+
"require-dev": {
26+
"bamarni/composer-bin-plugin": "^1.3",
27+
"phpunit/phpunit": "^9.0"
28+
},
29+
"config": {
30+
"sort-packages": false,
31+
"allow-plugins": {
32+
"bamarni/composer-bin-plugin": true
33+
}
34+
},
35+
"autoload": {
36+
"psr-4": {
37+
"Squirrel\\Types\\": "src/"
38+
}
39+
},
40+
"autoload-dev": {
41+
"psr-4": {
42+
"Squirrel\\Types\\Tests\\": "tests/"
43+
}
44+
},
45+
"scripts": {
46+
"phpstan": "vendor/bin/phpstan analyse",
47+
"phpstan_full": "vendor/bin/phpstan clear-result-cache && vendor/bin/phpstan analyse",
48+
"phpstan_base": "vendor/bin/phpstan analyse --configuration=phpstan.neon --generate-baseline=phpstan-baseline.neon",
49+
"psalm": "vendor/bin/psalm --show-info=false",
50+
"psalm_full": "vendor/bin/psalm --clear-cache && vendor/bin/psalm --show-info=false",
51+
"psalm_base": "vendor/bin/psalm --set-baseline=psalm-baseline.xml",
52+
"phpunit": "vendor/bin/phpunit --colors=always",
53+
"phpunit_clover": "vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml",
54+
"phpcs": "vendor/bin/phpcs --standard=ruleset.xml --extensions=php --cache src tests",
55+
"phpcsfix": "vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --cache src tests",
56+
"coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html tests/_reports",
57+
"binupdate": "@composer bin all update --ansi",
58+
"bininstall": "@composer bin all install --ansi"
59+
}
60+
}

phpstan-baseline.neon

Whitespace-only changes.

phpstan.neon

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
includes:
2+
- phpstan-baseline.neon
3+
4+
parameters:
5+
level: 9
6+
paths:
7+
- src
8+
checkMissingIterableValueType: false

phpunit.xml.dist

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="vendor/autoload.php">
3+
<coverage>
4+
<include>
5+
<directory suffix=".php">src</directory>
6+
</include>
7+
</coverage>
8+
<testsuites>
9+
<testsuite name="Unit Tests">
10+
<directory>tests</directory>
11+
</testsuite>
12+
</testsuites>
13+
</phpunit>

psalm-baseline.xml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<files psalm-version="4.23.0@f1fe6ff483bf325c803df9f510d09a03fd796f88">
3+
<file src="src/Coerce.php">
4+
<ImpureMethodCall occurrences="1">
5+
<code>__toString</code>
6+
</ImpureMethodCall>
7+
</file>
8+
<file src="tests/CoerceTest.php">
9+
<PropertyNotSetInConstructor occurrences="2">
10+
<code>CoerceTest</code>
11+
<code>CoerceTest</code>
12+
</PropertyNotSetInConstructor>
13+
</file>
14+
<file src="tests/EnforceTest.php">
15+
<PropertyNotSetInConstructor occurrences="2">
16+
<code>EnforceTest</code>
17+
<code>EnforceTest</code>
18+
</PropertyNotSetInConstructor>
19+
</file>
20+
</files>

0 commit comments

Comments
 (0)