Neverending data validation can be exhausting. Either you have to validate your data over and over again in every function you use it, or you have to rely it has already been validated somewhere else and risk potential problems.
Smelly, right?
Replacing validation hell with Types will make your code much more readable and less vulnerable to bugs.
Types wrap your data in value objects that are guaranteed to be valid and normalized; or not to exist at all. It allows you to use specific type hints instead of primitive types or arrays. Your code will be unbreakable and your IDE will love it.
- Installation
- How does it work
- String-extractable types
- Int-extractable types
- Float-extractable types
- Array-extractable types
- Array-types
- Enum-extractable types
- Primitive types
- DateTimes
- Writing your own types
- How to contribute
The recommended way to install is via Composer:
composer require smartemailing/types
It is easy. You just initialize desired value object by simple one-liner.
From this point, you have sanitized, normalized and valid data; or SmartEmailing\Types\InvalidTypeException to handle.
Types consist from:
- String-extractable types - validated strings (E-mail address, Domains, Hexadecimal strings, ...)
- Int-extractable types - validated integers (Port)
- Float-extractable types - validated floats (SigmoidValue, Part, ...)
- Enum-extractable types - enumerables (CountryCode, CurrencyCode, GDPR's Lawful purpose, ...)
- Composite (Array-extractable) types - structures containing multiple another types (Address, ...)
- DateTimes - extraction of DateTime and DateTimeImmutable
- Primitive types extractors and unique arrays
Different types provide different methods related to them, but all types share this extraction API:
<?php
declare(strict_types = 1);
use SmartEmailing\Types\Emailaddress;
use SmartEmailing\Types\InvalidTypeException;
// Valid input
$emailaddress = Emailaddress::from('[email protected]'); // returns Emailaddress object
$emailaddress = Emailaddress::from($emailaddress); // returns original $emailaddress
// Invalid input
$emailaddress = Emailaddress::from('bla bla'); // throws InvalidTypeException
$emailaddress = Emailaddress::from(1); // throws InvalidTypeException
$emailaddress = Emailaddress::from(false); // throws InvalidTypeException
$emailaddress = Emailaddress::from(null); // throws InvalidTypeException
$emailaddress = Emailaddress::from([]); // throws InvalidTypeException
$emailaddress = Emailaddress::from(new \StdClass()); // throws InvalidTypeException
// Nullables
$emailaddress = Emailaddress::fromOrNull(null); // returns NULL
$emailaddress = Emailaddress::fromOrNull('bla bla'); // throws InvalidTypeException
$emailaddress = Emailaddress::fromOrNull('bla bla', true); // returns null instead of throwingThis is really useful for strict-typing (validation) multidimensional arrays like API requests, forms or database data.
<?php
use SmartEmailing\Types\Emailaddress;
use SmartEmailing\Types\InvalidTypeException;
$input = [
'emailaddress' => '[email protected]',
'already_types_emailaddress' => Emailaddress::from('[email protected]'),
'invalid_data' => 'bla bla',
];
// Valid input
$emailaddress = Emailaddress::extract($input, 'emailaddress'); // returns Emailaddress object
$emailaddress = Emailaddress::extract($input, 'already_types_emailaddress'); // returns original Emailaddress object
// Invalid input
$emailaddress = Emailaddress::extract($input, 'invalid_data'); // throws InvalidTypeException
$emailaddress = Emailaddress::extract($input, 'not_existing_key'); // throws InvalidTypeException
// Nullables
$emailaddress = Emailaddress::extractOrNull($input, 'not_existing_key'); // returns null
$emailaddress = Emailaddress::extractOrNull($input, 'invalid_data'); // throws InvalidTypeException
$emailaddress = Emailaddress::extractOrNull($input, 'invalid_data', true); // returns null instead of throwing
// Default values
$emailaddress
= Emailaddress::extractOrNull($input, 'not_existing_key')
?? Emailaddress::from('[email protected]');
// uses null coalescing operator to assign default value if key not present or null
$emailaddress
= Emailaddress::extractOrNull($input, 'not_existing_key', true)
?? Emailaddress::from('[email protected]');
// uses null coalescing operator to assign default value if key not present or null or invalidString-extractable types are based on validated strings. All values are trimmed before validation.
They can be easily converted back to string by string-type casting or calling $type->getValue().
SmartEmailing\Types\Emailaddress
Lowercased and ASCII-transformed e-mail address ([email protected])
Type-specific methods:
getLocalPart() : stringreturns local part of e-mail address (hello)getDomain() : \SmartEmailing\Types\Domainreturns domain part (gmail.com, represented asTypes\Domain)
SmartEmailing\Types\NonEmptyString
Trimmed non-empty string.
SmartEmailing\Types\Domain
Lowercased domain name (mx1.googlemx.google.com)
Type-specific methods:
getSecondLevelDomain() : \SmartEmailing\Types\Domainreturns second-level domain. (google.com)
SmartEmailing\Types\Hex32
Lowercased 32-characters long hexadecimal string useful as container for MD5 or UUID without dashes. (741ecf779c9244358e6b85975bd13452)
SmartEmailing\Types\HexColor
Uppercased 7-characters long string useful as container for color. (#006EDB)
SmartEmailing\Types\HexColorAlpha
Uppercased 9-character long string useful as a container for color with an alpha channel (transparency). (#FFFFFFFF)
If you provide a standard 6-digit hex color (e.g., #000000), it will be automatically converted by appending FF for 100% opacity (#000000FF).
SmartEmailing\Types\Guid
Lowercased Guid with dashes (741ecf77-9c92-4435-8e6b-85975bd13452)
SmartEmailing\Types\IpAddress
IP address v4 or v6. (127.0.0.1, [2001:0db8:0a0b:12f0:0000:0000:0000:0001], 2001:db8:a0b:12f0::1)
Type-specific methods:
getVersion() : intreturns IP address version,4or6
SmartEmailing\Types\UrlType
URL based on Nette\Http\Url (https://www.google.com/search?q=all+work+and+no+play+makes+jack+a+dull+boy)
- all spaces in string are urlencoded
- all non-ascii characters are urlencoded
Type-specific methods:
getAuthority() : stringreturns authority (www.google.com)getHost() : stringreturns Host (www.google.com)getQueryString() : stringreturns Query string (q=all%20work%20and%20no%20play%20makes%20jack%20a%20dull%20boy)getPath() : stringreturns URl Path (/search)getAbsoluteUrl() : stringComplete URL asstring, alias forgetValue()getQueryParameter(string $name, mixed $default = null): mixedReturn value of parameter$namegetBaseUrl(): stringReturn URL without path, query string and hash part (https://www.google.cz/)getScheme(): stringReturn URL scheme (https)hasParameters(string[] $names): boolReturnstrueif URL parameters contain all parameters defined in$namesarraygetParameters(): arrayReturns all URL parameters as string-indexed arraywithQueryParameter(string $name, mixed $value): UrlTypeReturns new instance with added query parameter.
SmartEmailing\Types\CompanyRegistrationNumber
Whitespace-free company registration number for following countries:
CZ, SK, CY
SmartEmailing\Types\PhoneNumber
Whitespace-free phone number in international format for following countries:
CZ, SK, AT, BE, FR, HU, GB, DE, US, PL, IT, SE, SI, MH, NL, CY, IE, DK, FI, LU, TR
Type-specific methods:
getCountry() : SmartEmailing\Types\CountryCodeOriginating country (CZ)
SmartEmailing\Types\ZipCode
Whitespace-free ZIP code valid in following countries:
CZ, SK, UK, US
SmartEmailing\Types\JsonString
Valid JSON-encoded data as string
Type-specific methods:
static from(mixed $data) : SmartEmailing\Types\JsonStringAccepts string or array. Crates JsonString from valid json string or raw data (array)static encode(mixed $data) : SmartEmailing\Types\JsonStringcreate JsonString from raw data (array)getDecodedValue() : mixeddecode JsonString back to raw data
SmartEmailing\Types\Base64String
Valid Base 64-encoded data as string
Type-specific methods:
static encode(string $value) : SmartEmailing\Types\Base64Stringcreate Base64String from stringgetDecodedValue() : stringdecode Base64String back to original string
SmartEmailing\Types\Iban
Type-specific methods:
getFormatted(string $type = SmartEmailing\Types\Iban::FORMAT_ELECTRONIC): stringreturns formatted Iban string. Format types:FORMAT_ELECTRONIC,FORMAT_PRINT.getCountry(): SmartEmailing\Types\CountryCodegetChecksum(): int
SmartEmailing\Types\SwiftBic
Valid Swift/Bic codes.
SmartEmailing\Types\VatId
Type-specific methods:
static isValid(string $vatId): boolreturns true if the vat id is valid otherwise returns falsegetCountry(): ?CountryreturnsCountryunder which the subject should falls or null.getPrefix(): ?stringreturns string that prefixing vat id likeELfromEL123456789or null.getVatNumber(): stringreturns vat number without prefix like123456789getValue(): stringreturn whole vat idEL123456789
SmartEmailing\Types\CurrencyCode
Valid currency codes by ISO 4217
SmartEmailing\Types\CountryCode
Valid country codes by ISO 3166-1 alpha-2
Int-extractable types are based on validated integers.
They can be easily converted back to int by int-type casting or calling $type->getValue().
SmartEmailing\Types\Port
Port number
Integer interval, <0, 65535>
SmartEmailing\Types\Quantity
Quantity of items
Integer interval, <1, PHP_INT_MAX>
SmartEmailing\Types\UnsignedInt
Usigned integer
Integer interval, <0, PHP_INT_MAX>
Float-extractable types are based on validated floats.
They can be easily converted back to float by float-type casting or calling $type->getValue().
SmartEmailing\Types\Part
Portion of the whole
Float interval <0.0, 1.0>
Type-specific methods:
static fromRatio(float $value, float $whole): Partcreates new instance by division$valueand$whole.getPercent(): floatreturns(Ratio's value) * 100to get percent representation
SmartEmailing\Types\SigmoidValue
Result of Sigmoid function, useful when building neural networks.
Float interval <-1.0, 1.0>.
SmartEmailing\Types\ReLUValue
Result of Rectified Linear Unit function, useful when building neural networks.
Float interval <0.0, Infinity).
SmartEmailing\Types\UnsignedFloat
Usigned float
Float interval, <0, PHP_FLOAT_MAX>
Array-extractable types are composite types encapsulating one or more another types.
They are created from associative array. All Array-extractable types implement method
toArray() : array which returns normalized array or type's data.
SmartEmailing\Types\DateTimeRange
Range between two \DateTimeInterfaces. Safe for datetimes out of range of unix timestamp.
Can be created from:
DateTimeRange::from(
[
'from' => 'YYYY-MM-DD HH:MM:SS',
'to' => 'YYYY-MM-DD HH:MM:SS',
]
)Type-specific methods:
getFrom(): \DateTimeImmutablereturnsFromdate and time as\DateTimeImmutableinstancegetTo(): \DateTimeImmutablereturnsTodate and time as\DateTimeImmutableinstancegetDurationInSeconds(): intreturns number of seconds betweenFromandTodatescontains(\DateTimeInterface $dateTime): boolreturnstrueif provided\DateTimeInterfacelies betweenFromandTodates.getLengthInSeconds(): intreturns duration length in seconds
SmartEmailing\Types\Duration
Human-readable time interval.
Can be created from:
Duration::from(
[
'value' => 1,
'unit' => TimeUnit::HOURS,
]
);
Duration::from(
'1 hours'
);Type-specific methods:
getDateTimeModify(): stringreturns string that is compatible with\DateTime::modify()and\DateTimeImmutable::modify()getUnit(): TimeUnitreturnsTimeUnitenum typegetValue() intreturns number of unitsstatic fromDateTimeModify(string $dateTimeModify): selfcreates new instance from string compatible with\DateTime::modify()and\DateTimeImmutable::modify()
SmartEmailing\Types\Address
Location address contains street and number, town, zip code and country.
Can be created from:
Address::from(
[
'street_and_number' => '29 Neibolt Street',
'town' => 'Derry',
'zip_code' => '03038',
'country' => 'US',
]
);Type-specific methods:
getStreetAndNumber(): stringreturns street and numbergetTown(): stringreturns TowngetZipCode(): ZipCodereturns ZipCode instancegetCountry(): CountryCodereturns CountryCode instance
SmartEmailing\Types\Price
Price object containing number of currency units with VAT, number of currency units without VAT and currency.
Can be created from:
Price::from(
[
'with_vat' => 432.1,
'without_vat' => 123.45,
'currency' => CurrencyCode::EUR,
]
);Type-specific methods:
getWithoutVat(): floatreturns price without VATgetWithVat(): floatreturns price with VATgetCurrency(): CurrencyCodereturns CurrencyCode instance
SmartEmailing\Types\LoginCredentials
Value object containing login and plain password. You should use it just in-memory in authentication process and than drop it.
Can be created from:
LoginCredentials::from(
[
'login' => 'admin',
'password' => 'BLzW75kJxEa7YXuqF9Di',
]
);Type-specific methods:
getLogin(): stringreturns logingetPassword(): stringreturns password
SmartEmailing\Types\KeyValue
Value object containing string key and string value.
Can be created from:
KeyValuePair::from(
[
'key' => 'overlook',
'value' => 'all_work_and_no_play_makes_jack_a_dull_boy',
]
);Type-specific methods:
getKey(): stringreturns keygetValue(): stringreturns value
SmartEmailing\Types\ScalarLeavesArray
Value object containing single or multi-dimensional array with only scalar or NULL values in it's leaves. Array keys stay untouched.
Can be created from:
ScalarLeavesArray::from(
[
[
'a',
],
[
1,
],
[
'b',
[
true,
[
null,
],
[],
],
],
]
);Array-types-specific extractors:
static extractOrEmpty(array $data, string $key): selfBehaves like standard::extract()method, but returns empty ScalarLeavesArray when$data[$key]isnullor not set.
Types provide another kind of Array-extractable types: Unique primitive-type arrays.
Their purpose is to hold unique set of primitives.
They implement \Countable and \IteratorAggregate and natively support
set operations.
All Array-types share following features:
static empty() : selfCreates new empty instance of desired array-type.split(int $chunkSize): self[]Splits current instance into array of several instances, each with maximum data-set size of$chunkSize.merge(self $toBeMerged): selfReturns new instance with data-set combined from parent and$toBeMergedinstances. Both source instances stay unchanged.deduct(self $toBeDeducted): selfReturns new instance with data-set containing all items from parent that are not contained in$toBeDeducted. Both source instances stay unchanged.count(): intReturns data-set size.isEmpty(): boolReturnstrueif data-set is empty,falseotherwise.
Array-types-specific extractors:
static extractOrEmpty(array $data, string $key): selfBehaves like standard::extract()method, but returns empty set when$data[$key]isnullor not set.static extractNotEmpty(array $data, string $key): selfBehaves like standard::extract()method, but throwsInvalidTypeExceptionwhen$data[$key]is not set,nullor empty array.
SmartEmailing\Types\UniqueIntArray
UniqueIntArray is able to hold unique set of integers.
Can be created from:
// duplicate values will be discarted
// keys are ignored
UniqueIntArray::from(
[
1, 2, 2, 3, 3, 3, 4
]
);Type-specific methods:
getValues(): int[]Returns data-set of unique integers as array.toArray(): int[]Is just alias forgetValues().add(int $id): boolAdds another integer to the data-set. Returnsfalseif integer has already been there.remove(int $id): voidRemoves integer from the data-set, if present.contains(int $id): boolReturnstrueif$idis contained in the data-set,falseotherwise.
SmartEmailing\Types\UniqueIntArray
UniqueStringArray is able to hold unique set of strings.
Can be created from:
// duplicate values will be discarted
// keys are ignored
UniqueStringArray::from(
[
'a',
'b',
'c',
'all work and no play makes jack a dull boy',
'all work and no play makes jack a dull boy',
'all work and no play makes jack a dull boy',
]
);Type-specific methods:
getValues(): string[]Returns data-set of unique strings as array.toArray(): string[]Is just alias forgetValues().add(string $id): boolAdds another string to the data-set. Returnsfalseif string has already been there.remove(string $id): voidRemoves string from the data-set, if present.contains(string $id): boolReturnstrueif$idis contained in the set,falseotherwise.
Enum-extractable types are types that can contain single value from defined set. They are based on kkk
All Enum-extractable types share following features:
getValue() : stringReturns enum-valueequals(self $enum): boolReturnstrueif$enumcontains same value as parent.equalsValue(string $value): selfReturnstrueif parent contains the same value as$value.
Enums can be created using standard extractors or using their constants:
CurrencyCode::from(
CurrencyCode::EUR
);
CurrencyCode::from(
'EUR'
);SmartEmailing\Types\LawfulBasisForProcessing
GDPR's lawful basis for processing
SmartEmailing\Types\CountryCode
ISO-3166-1 Alpha 2 country code
SmartEmailing\Types\CurrencyCode
ISO-4217 three-letter currency code
SmartEmailing\Types\FieldOfApplication
Most common fields of human applications.
SmartEmailing\Types\TimeUnit
Time unit compatible with \DateTime::modify() argument format
SmartEmailing\Types\Relation
Represents Relation or Gate - AND / OR
Types are able to get and extract primitives using IntType, IntArray, FloatType, FloatArray, StringType, StringArray, BoolType, BoolArray and Array classes. See examples below:
<?php
declare(strict_types = 1);
use SmartEmailing\Types\Arrays;
use SmartEmailing\Types\BoolArray;
use SmartEmailing\Types\BoolType;
use SmartEmailing\Types\FloatArray;
use SmartEmailing\Types\FloatType;
use SmartEmailing\Types\IntArray;
use SmartEmailing\Types\IntType;
use SmartEmailing\Types\StringArray;
use SmartEmailing\Types\StringType;
use SmartEmailing\Types\InvalidTypeException;
IntType::from(666); // 666
IntType::from('666'); // 666
IntType::from(666.1); // throws InvalidTypeException
IntType::from('abcd'); // throws InvalidTypeException
IntType::from('abcd'); // throws InvalidTypeException
IntType::fromOrNull(null); // null
IntType::fromOrNull(1); // 1
IntType::fromOrNull('abcd'); // throws InvalidTypeException
IntType::fromOrNull('abcd', true); // null
FloatType::from(1.1); // 1.1
FloatType::from('1.1'); // 1.1
FloatType::from(1); // 1.0
FloatType::from('1'); // 1.0
FloatType::from('xxx'); // throws InvalidTypeException
FloatType::fromOrNull(null); // null
FloatType::fromOrNull(1.0); // 1.0
FloatType::fromOrNull('abcd'); // throws InvalidTypeException
FloatType::fromOrNull('abcd', true); // null
StringType::from('xxx'); // 'xxx'
StringType::from(5); // '5'
StringType::from(5.0); // '5'
StringType::from(5.1); // '5.1'
StringType::fromOrNull(null); // null
StringType::fromOrNull('abcd'); // 'abcd'
StringType::fromOrNull([]); // throws InvalidTypeException
StringType::fromOrNull([], true); // null
BoolType::from(true); // true
BoolType::from(false); // false
BoolType::from(1); // true
BoolType::from(0); // false
BoolType::from('1'); // true
BoolType::from('0'); // false
BoolType::from('true'); // true
BoolType::from('false'); // false
Arrays::from([1, 2]); // [1, 2]
Arrays::from([1, 'abcd']); // [1, 'abcd']
IntArray::from([1, '2']); // [1, 2]
IntArray::fromOrNull([1, '2']); // returns int[]|null
FloatArray::from([1, '2']); // [1.0, 2.0]
FloatArray::fromOrNull([1, '2']); // returns float[]|null
StringArray::from([1, '2']); // ['1', '2']
StringArray::fromOrNull([1, '2']); // returns string[]|null
BoolArray::from([1, '1']); // [true, true]
BoolArray::fromOrNull([1, '1']); // returns bool[]|null
// All primitive types have their extract equivalent:
IntType::extract($data, 'key');
IntType::extractOrNull($data, 'key');
IntType::extractOrNull($data, 'key', true);
StringType::extract($data, 'key');
StringType::extractOrNull($data, 'key');
StringType::extractOrNull($data, 'key', true);
FloatType::extract($data, 'key');
FloatType::extractOrNull($data, 'key');
FloatType::extractOrNull($data, 'key', true);
Arrays::extract($data, 'key'); //returns mixed[]
Arrays::extractOrNull($data, 'key'); //returns mixed[]|null
IntArray::extract($data, 'key'); //returns int[]
IntArray::extractOrNull($data, 'key'); //returns int[]|null
FloatArray::extract($data, 'key'); //returns float[]
FloatArray::extractOrNull($data, 'key'); //returns float[]|null
StringArray::extract($data, 'key'); //returns string[]
StringArray::extractOrNull($data, 'key'); //returns string[]|null
BoolArray::extract($data, 'key'); //returns bool[]
BoolArray::extractOrNull($data, 'key'); //returns bool[]|nullTypes are able to get and extract \DateTime and \DateTimeImmutable
objects using DateTimes and DateTimesImmutable classes.
Supported format Y-m-d H:s:i.
API is the same as for other types, so available methods are (shown for DateTimes):
from(string $dateTime ) : \DateTimeextract(array $data, string $index) : \DateTimeextractOrNull(array $data, string $index, bool $getNullIfInvalid) : ?\DateTime
Types are able to get and extract \DateTime and \DateTimeImmutable
objects using Dates and DatesImmutable classes. Dates are created with time sets on 00:00:00.
Supported format Y-m-d.
API is the same as for other types, so available methods are (shown for Dates):
from(string $dateTime ) : \DateTimeextract(array $data, string $index) : \DateTimeextractOrNull(array $data, string $index, bool $getNullIfInvalid) : ?\DateTime
Implementing your custom type is easy!
At first you have to decide what extractable-type should your new custom type be and
use particular extractable-trait in it's class to enhance it by all extractable features.
The only thing you have to do next is implement class construtor and throw InvalidTypeException in case of invalid data.
You can see examples for every extractable-type below.
- String-extractable types
- Int-extractable types
- Float-extractable types
- Enum-extractable types - You do not have to implement constructor here, just add your
public staticmembers. - Composite (Array-extractable) - Handy for forms-data or API requests validation
One more thought - if you think your new type will be useful for others, please, contribute!
Thank you for your interest in improving Types!️ ❤️ 🖖
Before you open pull request, please, make sure you did not forget to write tests for your code.
Then run following commands:
-
vendor/bin/tester testsRun tests locally. It takes just two seconds :-) -
bin/cbfThis will check the code and automatically fix some code style issues like indentation or line breaks. -
bin/csThis will run another code style check that will notify you about problems that must be fixed manually. Please, fix them, and re-run the command. -
bin/stanPHP Static analysis tool - this will check the code for some smelly constructions that should be refactored. Please, fix them, and re-run the command. -
PR ready!
Do you like our code? If you want to be part of SmartEmailing, we are hiring.