├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── composer.json
├── composer.lock
├── docs
└── Color.md
├── doctrine
└── mapping
│ └── Money.Money.php
├── phpunit.xml
├── src
├── Color.php
├── DateRange.php
├── EmailAddress.php
├── FullName.php
├── GeoPoint.php
├── IpAddress.php
└── IpRange.php
└── tests
├── ColorTest.php
├── DateRangeTest.php
├── EmailAddressTest.php
├── FullNameTest.php
├── GeoPointTest.php
├── IpAddressTest.php
└── IpRangeTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io
2 |
3 | ### PHP Storm
4 | .idea
5 |
6 | ### APP Specific ###
7 | data/coverage/
8 |
9 | ### SublimeText ###
10 | # cache files for sublime text
11 | *.tmlanguage.cache
12 | *.tmPreferences.cache
13 | *.stTheme.cache
14 |
15 | # workspace files are user-specific
16 | *.sublime-workspace
17 |
18 | # project files should be checked into the repository, unless a significant
19 | # proportion of contributors will probably not be using SublimeText
20 | # *.sublime-project
21 |
22 | # sftp configuration file
23 | sftp-config.json
24 |
25 | # Package control specific files
26 | Package Control.last-run
27 | Package Control.ca-list
28 | Package Control.ca-bundle
29 | Package Control.system-ca-bundle
30 | Package Control.cache/
31 | Package Control.ca-certs/
32 | bh_unicode_properties.cache
33 |
34 | # Sublime-github package stores a github token in this file
35 | # https://packagecontrol.io/packages/sublime-github
36 | GitHub.sublime-settings
37 |
38 |
39 | ### macOS ###
40 | *.DS_Store
41 | .AppleDouble
42 | .LSOverride
43 |
44 | # Icon must end with two \r
45 | Icon
46 |
47 | # Thumbnails
48 | ._*
49 |
50 | # Files that might appear in the root of a volume
51 | .DocumentRevisions-V100
52 | .fseventsd
53 | .Spotlight-V100
54 | .TemporaryItems
55 | .Trashes
56 | .VolumeIcon.icns
57 | .com.apple.timemachine.donotpresent
58 |
59 | # Directories potentially created on remote AFP share
60 | .AppleDB
61 | .AppleDesktop
62 | Network Trash Folder
63 | Temporary Items
64 | .apdisk
65 |
66 |
67 | ### Composer ###
68 | composer.phar
69 | /vendor/
70 |
71 | ### Git ###
72 | *.orig
73 |
74 | ### PHPUnit ###
75 | .phpunit.*
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | build:
2 | environment:
3 | php:
4 | version: 8.0
5 | tests:
6 | override:
7 | -
8 | command: 'phpunit --coverage-clover=embeddables-coverage'
9 | coverage:
10 | file: 'embeddables-coverage'
11 | format: 'clover'
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | sudo: false
3 |
4 | php:
5 | - 8.0
6 | - 8.1
7 |
8 | env:
9 | global:
10 | - TEST_COMMAND="composer test"
11 |
12 | before_install:
13 | - travis_retry composer self-update
14 |
15 | install:
16 | - travis_retry composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction
17 |
18 | script:
19 | - $TEST_COMMAND
20 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to Contribute
2 | Please feel free to create a new issue about any problem(s) you saw. We love pull requests from everyone. Some things that will increase the chance that your pull request is accepted:
3 |
4 | - Follow the PSR-1 and PSR-2 standards, **always**.
5 | - Write a [good commit message](https://gist.github.com/matthewhudson/1475276).
6 | - Write unit tests.
7 |
8 | Please use `develop` branch to send pull request. Don't send any pull request directly `master`.
9 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## MIT License
2 |
3 | Copyright (c) 2016 Biberlabs
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:
--------------------------------------------------------------------------------
1 | # DDD Embeddables
2 |
3 | ----
4 |
5 | [](https://secure.travis-ci.org/biberlabs/ddd-embeddables)
6 | [](https://scrutinizer-ci.com/g/biberlabs/ddd-embeddables/?branch=master)
7 | [](https://scrutinizer-ci.com/g/biberlabs/ddd-embeddables/?branch=master)
8 |
9 | A collection of reusable value objects written in PHP and targeting versions 8.0 and above. Value objects are essential building blocks of **Domain Driven Design** approach and described by Martin Fowler in _P of EAA page 486_ as below:
10 |
11 | > "Value object is a small simple object, like money or a date range, whose equality isn't based on identity."
12 |
13 | > – Martin Fowler
14 |
15 | All classes in this library annotated as `ORM\Embeddable` with appropriately adjusted column attributes. This makes them ready to use in any project with Doctrine ORM as [Embeddables](http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/embeddables.html).
16 |
17 | ## Installation & Usage
18 | Install the library using [composer](https://getcomposer.org).
19 |
20 | ```bash
21 | $ composer require biberlabs/ddd-embeddables
22 | ```
23 |
24 | and use it in your entities:
25 |
26 | ```php
27 | 1000
59 | -- OR
60 | SELECT u FROM User u WHERE u.name.surname = :surname
61 | SELECT u FROM User u WHERE u.name.title = :title
62 | ```
63 |
64 | Value objects enables us to write much more cleaner and readable rules when dealing with the domain rules, application-wide. For example:
65 |
66 | ```php
67 | $username = $user->getEmail()->getLocalpart();
68 | ```
69 |
70 | or
71 |
72 | ```php
73 | $blacklist = ['spam4me.io', 'foo.com'];
74 | if(in_array($user->getEmail()->getDomain(), $blacklist)) {
75 | // ...
76 | }
77 | ```
78 |
79 | even
80 |
81 | ```php
82 | if($company->hasMap()) {
83 | $latLng = $company->getAddress()->getGeoPoint()->toArray();
84 | //..
85 | }
86 |
87 | class Company
88 | {
89 | // ...
90 |
91 | /**
92 | * Returns a boolean TRUE if the geolocation of the company is known,
93 | * FALSE otherwise.
94 | *
95 | * @return bool
96 | */
97 | public function hasMap()
98 | {
99 | return $this->getAddress()->getGeoPoint() !== null;
100 | }
101 | }
102 | ```
103 |
104 | ## Running Tests
105 | You can run unit tests locally via issuing the command below:
106 |
107 | ```bash
108 | $ composer test
109 | ```
110 |
111 | Please make sure that all test are green before creating a PR.
112 |
113 | ```
114 | PHPUnit 9.5.25 #StandWithUkraine
115 |
116 | ................................................................. 65 / 75 ( 86%)
117 | .......... 75 / 75 (100%)
118 |
119 | Time: 00:00.037, Memory: 6.00 MB
120 |
121 | OK (75 tests, 124 assertions)
122 | ```
123 |
124 | ## Contributing
125 | If you want to contribute to **ddd-embeddables** and make it better, your help is very welcome.
126 |
127 | Please take a look our [CONTRIBUTING.md](CONTRIBUTING.md) before creating a pull request.
128 |
129 | ## Further Reading
130 | Are you interested in Domain Driven Design? Here is a list of good resources to dig in-depth.
131 |
132 | - [Value Objects](http://martinfowler.com/bliki/ValueObject.html) - Martin Fowler
133 | - [Separating Concerns using Embeddables](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/embeddables.html) - Doctrine ORM documentation
134 | - [Domain-Driven Design in PHP](https://leanpub.com/ddd-in-php/read) - Leanpub 380 pages e-book.
135 | - [Agregate Componenet Pattern In Action](https://lostechies.com/jimmybogard/2009/02/05/ddd-aggregate-component-pattern-in-action/) - Another good article from 2009
136 | - [Domain Driven Design Concepts in ZF2](https://olegkrivtcov.wordpress.com/2014/03/22/domain-driven-design-ddd-concepts-in-zf2/) - An article written by Oleg Krivtsov in 2014
137 | - [Domain Driven Design Quickly](https://www.infoq.com/minibooks/domain-driven-design-quickly) - A quickly-readable minibook and introduction to the fundamentals of DDD by InfoQ.
138 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "biberlabs/ddd-embeddables",
3 | "description": "A collection of re-usable value objects ready to use as Embeddable in Doctrine entities",
4 | "type": "library",
5 | "license": "MIT",
6 | "require": {
7 | "php": "^8.0",
8 | "doctrine/orm": "^2.11",
9 | "composer-runtime-api": "^2"
10 | },
11 | "require-dev": {
12 | "phpunit/phpunit": "^9.5"
13 | },
14 | "suggest": {
15 | "ext-gmp": "Calculate without integer limits",
16 | "ext-intl": "Format Money objects with intl"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "DDD\\Embeddable\\": "src/"
21 | }
22 | },
23 | "autoload-dev": {
24 | "psr-4": {
25 | "Test\\Embeddable\\": "tests/"
26 | }
27 | },
28 | "authors": [
29 | {
30 | "name": "M. Yilmaz SUSLU",
31 | "email": "yilmazsuslu@gmail.com"
32 | }
33 | ],
34 | "scripts": {
35 | "test": "vendor/bin/phpunit --coverage-text"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/docs/Color.md:
--------------------------------------------------------------------------------
1 | # Color Value Object
2 | `DDD\Embeddebles\Color` is a simple value object, it helps developer when persisting and using color information application-wide, in DDD way.
3 |
4 | An example use case, there is a `Product` entity:
5 |
6 | ```
7 | setColor(new Color('#FF00FF'));
36 | // or
37 | $prod->setColor(new Color('F0F'));
38 | // or
39 | $prod->setColor(Color::fromRGB(255,0,255));
40 |
41 | $name = $prod->getName();
42 | $background = $prod->getColor()->toHex();
43 | $label = $prod->getColor()->getName(); // "Fuchsia"
44 |
45 | // Directly convert to json
46 | $json = json_encode($prod->getColor());
47 | // {"hex":"#FF00FF","rgb":[255,0,255],"name":"Fuchsia"}
48 |
49 | $rgb = $prod->getColor()->toRGB(); // [255,0,255]
50 | ```
--------------------------------------------------------------------------------
/doctrine/mapping/Money.Money.php:
--------------------------------------------------------------------------------
1 | addDriver(new PhpMappingDriver($dir), 'Money\Money');
14 | */
15 | $metadata->isEmbeddedClass = true;
16 |
17 | $metadata->mapField(array(
18 | 'fieldName' => 'amount',
19 | 'type' => 'integer',
20 | 'nullable' => true
21 | ));
22 |
23 | $metadata->mapField(array(
24 | 'fieldName' => 'currency',
25 | 'type' => 'string',
26 | 'nullable' => true,
27 | 'length' => 3,
28 | ));
29 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | src
6 |
7 |
8 |
9 |
10 | ./tests
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Color.php:
--------------------------------------------------------------------------------
1 | 'White'
7 | *
8 | * Partially implements the flow in this good SO answer:
9 | *
10 | * @see http://stackoverflow.com/a/2994015/199593
11 | *
12 | * @author M. Yilmaz SUSLU
13 | * @license MIT
14 | *
15 | * @since Oct 2016
16 | */
17 |
18 | namespace DDD\Embeddable;
19 |
20 | use Doctrine\ORM\Mapping as ORM;
21 | use InvalidArgumentException;
22 | use JsonSerializable;
23 | use function preg_match;
24 |
25 | /**
26 | * @ORM\Embeddable
27 | */
28 | class Color implements JsonSerializable
29 | {
30 | /**
31 | * persistable, hexadecimal representation of the color
32 | *
33 | * Persisted hex value is always upper-cased and doesn't contain a '#' prefix.
34 | *
35 | * @ORM\Column(type="string", length=6, nullable=true)
36 | */
37 | private ?string $color = null;
38 |
39 | public function __construct(?string $hex = null)
40 | {
41 | if (! $hex) {
42 | return;
43 | }
44 |
45 | $hex = $this->normalize($hex);
46 |
47 | if (! $this->isValidHex($hex)) {
48 | throw new InvalidArgumentException(sprintf('Given hex value %s is invalid', $hex));
49 | }
50 |
51 | $this->color = $hex;
52 | }
53 |
54 | /**
55 | * Returns the color in hexadecimal format
56 | */
57 | public function toHex() : string
58 | {
59 | return ($this->color) ? '#' . $this->color : '';
60 | }
61 |
62 | /**
63 | * Returns the color in RGB format, empty array if color is not set.
64 | */
65 | public function toRGB() : array
66 | {
67 | if ($this->isEmpty()) {
68 | return [];
69 | }
70 |
71 | [$r, $g, $b] = sscanf($this->color, "%02x%02x%02x");
72 |
73 | return [$r, $g, $b];
74 | }
75 |
76 | /**
77 | * Returns the color as RGB string
78 | *
79 | * Example: "rgb(60,60,240)"
80 | */
81 | public function toRGBString() : string
82 | {
83 | $rgb = $this->toRGB();
84 |
85 | return 'rgb(' . implode(',', $rgb) . ')';
86 | }
87 |
88 | /**
89 | * Returns programmatically generated, the nearest name for the color.
90 | *
91 | * Note: This method is pretty slow even for a single call because
92 | * iterates all over the color name => [r,g,b] stack below to find
93 | * the nearest color in whole haystack, this means approx. ~3500 iteration.
94 | *
95 | * Caching a serialized version of this object is highly recommended.
96 | *
97 | * @todo There may faster algorithms exists to achieve same thing.
98 | */
99 | public function getName() : string
100 | {
101 | return $this->generateName($this->toRGB());
102 | }
103 |
104 | /**
105 | * Array representation of the color
106 | */
107 | public function toArray() : array
108 | {
109 | if ($this->isEmpty()) {
110 | return [];
111 | }
112 |
113 | $rgb = $this->toRGB();
114 |
115 | return [
116 | 'hex' => $this->toHex(),
117 | 'rgb' => $rgb,
118 | 'name' => $this->getName(),
119 | ];
120 | }
121 |
122 | /**
123 | * Implement json serializable interface.
124 | */
125 | public function jsonSerialize() : array
126 | {
127 | return $this->toArray();
128 | }
129 |
130 | /**
131 | * Creates a new Color instance from r,g,b values.
132 | */
133 | public static function fromRGB(int $r, int $g, int $b) : Color
134 | {
135 | $hex = dechex($r) . dechex($g) . dechex($b);
136 |
137 | return new self($hex);
138 | }
139 |
140 | /**
141 | * String representation of the color, an upper-cased hex value.
142 | *
143 | * @return string Example: #FFCC60
144 | */
145 | public function __toString()
146 | {
147 | return $this->toHex();
148 | }
149 |
150 | /**
151 | * Returns a boolean TRUE if color is literally empty
152 | */
153 | public function isEmpty() : bool
154 | {
155 | return empty($this->color);
156 | }
157 |
158 | /**
159 | * Returns distance between two colors.
160 | *
161 | * @see https://stackoverflow.com/questions/4057475/rounding-colours
162 | * @param array $color2 RGB color
163 | *
164 | * @param array $color1 RGB color
165 | */
166 | private function distanceL2(array $color1, array $color2) : float
167 | {
168 | return sqrt(
169 | (($color1[0] - $color2[0]) ** 2) +
170 | (($color1[1] - $color2[1]) ** 2) +
171 | (($color1[2] - $color2[2]) ** 2)
172 | );
173 | }
174 |
175 | /**
176 | * Generates a proper name for given RGB color.
177 | */
178 | private function generateName(array $rgb) : string
179 | {
180 | $distances = [];
181 | foreach ($this->getColorNames() as $name => $rgbValue) {
182 | $distances[$name] = $this->distanceL2($rgbValue, $rgb);
183 | }
184 |
185 | $minColor = '';
186 | $minVal = 2 ** 30;
187 | /** A big value */
188 |
189 | foreach ($distances as $k => $v) {
190 | if ($v < $minVal) {
191 | $minVal = $v;
192 | $minColor = $k;
193 | }
194 | }
195 |
196 | // Closest color name to given RGB code
197 | return $minColor;
198 | }
199 |
200 | /**
201 | * Validates given hex color code, returns a boolean TRUE if it is valid.
202 | *
203 | * @param string $hex Three or six digit HEX code like #FFAC43, #000
204 | */
205 | private function isValidHex(string $hex) : bool
206 | {
207 | // Allow both #FFF and #FFFFFF conventions
208 | return preg_match('/^[A-F0-9]{6}$/', $hex) || preg_match('/^[A-F0-9]{3}$/', $hex);
209 | }
210 |
211 | /**
212 | * Filters and normalizes given hexadecimal color code.
213 | *
214 | * @param string $hex An unfiltered hex value.
215 | */
216 | private function normalize(string $hex) : string
217 | {
218 | // Get rid of first # sign if it exists
219 | if ($hex[0] === '#') {
220 | $hex = substr($hex, 1);
221 | }
222 |
223 | if (strlen($hex) === 3) {
224 | // Convert to 6 digit version
225 | $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
226 | }
227 |
228 | return strtoupper($hex);
229 | }
230 |
231 | /**
232 | * Returns a (pretty big) static list of well-known color names
233 | * with their RGB codes.
234 | *
235 | * Including, caching or requiring these values from an external
236 | * resource is always possible, but it would be costlier than this.
237 | *
238 | * Since all PHP classes cached by Opcache in runtime, this will
239 | * run pretty fast on a production box. As a drawback, instance size of Color
240 | * in memory will be higher (in kilobytes). There is no free lunch.
241 | *
242 | * For a simple entity which needs just a "color" property (such as Product),
243 | * this value object may fit perfectly.
244 | */
245 | private function getColorNames() : array
246 | {
247 | return [
248 | 'Acid Green' => [176, 191, 26],
249 | 'Aero Blue' => [201, 255, 229],
250 | 'Aero' => [124, 185, 232],
251 | 'African Violet' => [178, 132, 190],
252 | 'Air Force Blue (RAF)' => [93, 138, 168],
253 | 'Air Force Blue (USAF)' => [0, 48, 143],
254 | 'Air Superiority Blue' => [114, 160, 193],
255 | 'Alabama Crimson' => [175, 0, 42],
256 | 'Alice Blue' => [240, 248, 255],
257 | 'Alizarin Crimson' => [227, 38, 54],
258 | 'Alloy Orange' => [196, 98, 16],
259 | 'Almond' => [239, 222, 205],
260 | 'Amaranth Deep Purple' => [171, 39, 79],
261 | 'Amaranth Pink' => [241, 156, 187],
262 | 'Amaranth Purple' => [171, 39, 79],
263 | 'Amaranth Red' => [211, 33, 45],
264 | 'Amaranth' => [229, 43, 80],
265 | 'Amazon' => [59, 122, 87],
266 | 'Amber (SAE/ECE)' => [255, 126, 0],
267 | 'Amber' => [255, 191, 0],
268 | 'American Rose' => [255, 3, 62],
269 | 'Amethyst' => [153, 102, 204],
270 | 'Android Green' => [164, 198, 57],
271 | 'Anti-Flash White' => [242, 243, 244],
272 | 'Antique Brass' => [205, 149, 117],
273 | 'Antique Bronze' => [102, 93, 30],
274 | 'Antique Fuchsia' => [145, 92, 131],
275 | 'Antique Ruby' => [132, 27, 45],
276 | 'Antique White' => [250, 235, 215],
277 | 'Ao (English)' => [0, 128, 0],
278 | 'Apple Green' => [141, 182, 0],
279 | 'Apricot' => [251, 206, 177],
280 | 'Aqua' => [0, 255, 255],
281 | 'Aquamarine' => [127, 255, 212],
282 | 'Arctic Lime' => [208, 255, 20],
283 | 'Army Green' => [75, 83, 32],
284 | 'Arsenic' => [59, 68, 75],
285 | 'Artichoke' => [143, 151, 121],
286 | 'Arylide Yellow' => [233, 214, 107],
287 | 'Ash Grey' => [178, 190, 181],
288 | 'Asparagus' => [135, 169, 107],
289 | 'Atomic Tangerine' => [255, 153, 102],
290 | 'Auburn' => [165, 42, 42],
291 | 'Aureolin' => [253, 238, 0],
292 | 'Auro Metal Saurus' => [110, 127, 128],
293 | 'Avocado' => [86, 130, 3],
294 | 'Azure (Web Color)' => [240, 255, 255],
295 | 'Azure Mist' => [240, 255, 255],
296 | 'Azure' => [0, 127, 255],
297 | 'Azureish White' => [219, 233, 244],
298 | "B'dazzled Blue" => [46, 88, 148],
299 | 'Baby Blue Eyes' => [161, 202, 241],
300 | 'Baby Blue' => [137, 207, 240],
301 | 'Baby Pink' => [244, 194, 194],
302 | 'Baby Powder' => [254, 254, 250],
303 | 'Baker-Miller Pink' => [255, 145, 175],
304 | 'Ball Blue' => [33, 171, 205],
305 | 'Banana Mania' => [250, 231, 181],
306 | 'Banana Yellow' => [255, 225, 53],
307 | 'Bangladesh Green' => [0, 106, 78],
308 | 'Barbie Pink' => [224, 33, 138],
309 | 'Barn Red' => [124, 10, 2],
310 | 'Battleship Grey' => [132, 132, 130],
311 | 'Bazaar' => [152, 119, 123],
312 | 'Beau Blue' => [188, 212, 230],
313 | 'Beaver' => [159, 129, 112],
314 | 'Beige' => [245, 245, 220],
315 | 'Big Dip O’ruby' => [156, 37, 66],
316 | 'Bisque' => [255, 228, 196],
317 | 'Bistre Brown' => [150, 113, 23],
318 | 'Bistre' => [61, 43, 31],
319 | 'Bitter Lemon' => [202, 224, 13],
320 | 'Bitter Lime' => [191, 255, 0],
321 | 'Bittersweet Shimmer' => [191, 79, 81],
322 | 'Bittersweet' => [254, 111, 94],
323 | 'Black Bean' => [61, 12, 2],
324 | 'Black Leather Jacket' => [37, 53, 41],
325 | 'Black Olive' => [59, 60, 54],
326 | 'Black' => [0, 0, 0],
327 | 'Blanched Almond' => [255, 235, 205],
328 | 'Blast-Off Bronze' => [165, 113, 100],
329 | 'Bleu De France' => [49, 140, 231],
330 | 'Blizzard Blue' => [172, 229, 238],
331 | 'Blond' => [250, 240, 190],
332 | 'Blue (Crayola)' => [31, 117, 254],
333 | 'Blue (Munsell)' => [0, 147, 175],
334 | 'Blue (NCS)' => [0, 135, 189],
335 | 'Blue (Pantone)' => [0, 24, 168],
336 | 'Blue (Pigment)' => [51, 51, 153],
337 | 'Blue (RYB)' => [2, 71, 254],
338 | 'Blue Bell' => [162, 162, 208],
339 | 'Blue Lagoon' => [172, 229, 238],
340 | 'Blue Sapphire' => [18, 97, 128],
341 | 'Blue Yonder' => [80, 114, 167],
342 | 'Blue' => [0, 0, 255],
343 | 'Blue-Gray' => [102, 153, 204],
344 | 'Blue-Green' => [13, 152, 186],
345 | 'Blue-Magenta Violet' => [85, 53, 146],
346 | 'Blue-Violet' => [138, 43, 226],
347 | 'Blueberry' => [79, 134, 247],
348 | 'Bluebonnet' => [28, 28, 240],
349 | 'Blush' => [222, 93, 131],
350 | 'Bole' => [121, 68, 59],
351 | 'Bondi Blue' => [0, 149, 182],
352 | 'Bone' => [227, 218, 201],
353 | 'Boston University Red' => [204, 0, 0],
354 | 'Bottle Green' => [0, 106, 78],
355 | 'Boysenberry' => [135, 50, 96],
356 | 'Brandeis Blue' => [0, 112, 255],
357 | 'Brass' => [181, 166, 66],
358 | 'Brick Red' => [203, 65, 84],
359 | 'Bright Cerulean' => [29, 172, 214],
360 | 'Bright Green' => [102, 255, 0],
361 | 'Bright Lavender' => [191, 148, 228],
362 | 'Bright Lilac' => [216, 145, 239],
363 | 'Bright Maroon' => [195, 33, 72],
364 | 'Bright Navy Blue' => [25, 116, 210],
365 | 'Bright Pink' => [255, 0, 127],
366 | 'Bright Turquoise' => [8, 232, 222],
367 | 'Bright Ube' => [209, 159, 232],
368 | 'Brilliant Azure' => [51, 153, 255],
369 | 'Brilliant Lavender' => [244, 187, 255],
370 | 'Brilliant Rose' => [255, 85, 163],
371 | 'Brink Pink' => [251, 96, 127],
372 | 'British Racing Green' => [0, 66, 37],
373 | 'Bronze Yellow' => [115, 112, 0],
374 | 'Bronze' => [205, 127, 50],
375 | 'Brown (Traditional)' => [150, 75, 0],
376 | 'Brown (Web)' => [165, 42, 42],
377 | 'Brown Yellow' => [204, 153, 102],
378 | 'Brown-Nose' => [107, 68, 35],
379 | 'Brunswick Green' => [27, 77, 62],
380 | 'Bubble Gum' => [255, 193, 204],
381 | 'Bubbles' => [231, 254, 255],
382 | 'Bud Green' => [123, 182, 97],
383 | 'Buff' => [240, 220, 130],
384 | 'Bulgarian Rose' => [72, 6, 7],
385 | 'Burgundy' => [128, 0, 32],
386 | 'Burlywood' => [222, 184, 135],
387 | 'Burnt Orange' => [204, 85, 0],
388 | 'Burnt Sienna' => [233, 116, 81],
389 | 'Burnt Umber' => [138, 51, 36],
390 | 'Byzantine' => [189, 51, 164],
391 | 'Byzantium' => [112, 41, 99],
392 | 'Cadet Blue' => [95, 158, 160],
393 | 'Cadet Grey' => [145, 163, 176],
394 | 'Cadet' => [83, 104, 114],
395 | 'Cadmium Green' => [0, 107, 60],
396 | 'Cadmium Orange' => [237, 135, 45],
397 | 'Cadmium Red' => [227, 0, 34],
398 | 'Cadmium Yellow' => [255, 246, 0],
399 | 'Café Au Lait' => [166, 123, 91],
400 | 'Café Noir' => [75, 54, 33],
401 | 'Cal Poly Green' => [30, 77, 43],
402 | 'Cambridge Blue' => [163, 193, 173],
403 | 'Camel' => [193, 154, 107],
404 | 'Cameo Pink' => [239, 187, 204],
405 | 'Camouflage Green' => [120, 134, 107],
406 | 'Canary Yellow' => [255, 239, 0],
407 | 'Candy Apple Red' => [255, 8, 0],
408 | 'Candy Pink' => [228, 113, 122],
409 | 'Capri' => [0, 191, 255],
410 | 'Caput Mortuum' => [89, 39, 32],
411 | 'Cardinal' => [196, 30, 58],
412 | 'Caribbean Green' => [0, 204, 153],
413 | 'Carmine (M&P)' => [215, 0, 64],
414 | 'Carmine Pink' => [235, 76, 66],
415 | 'Carmine Red' => [255, 0, 56],
416 | 'Carmine' => [150, 0, 24],
417 | 'Carnation Pink' => [255, 166, 201],
418 | 'Carnelian' => [179, 27, 27],
419 | 'Carolina Blue' => [86, 160, 211],
420 | 'Carrot Orange' => [237, 145, 33],
421 | 'Castleton Green' => [0, 86, 63],
422 | 'Catalina Blue' => [6, 42, 120],
423 | 'Catawba' => [112, 54, 66],
424 | 'Cedar Chest' => [201, 90, 73],
425 | 'Ceil' => [146, 161, 207],
426 | 'Celadon Blue' => [0, 123, 167],
427 | 'Celadon Green' => [47, 132, 124],
428 | 'Celadon' => [172, 225, 175],
429 | 'Celeste' => [178, 255, 255],
430 | 'Celestial Blue' => [73, 151, 208],
431 | 'Cerise Pink' => [236, 59, 131],
432 | 'Cerise' => [222, 49, 99],
433 | 'Cerulean Blue' => [42, 82, 190],
434 | 'Cerulean Frost' => [109, 155, 195],
435 | 'Cerulean' => [0, 123, 167],
436 | 'CG Blue' => [0, 122, 165],
437 | 'CG Red' => [224, 60, 49],
438 | 'Chamoisee' => [160, 120, 90],
439 | 'Champagne' => [247, 231, 206],
440 | 'Charcoal' => [54, 69, 79],
441 | 'Charleston Green' => [35, 43, 43],
442 | 'Charm Pink' => [230, 143, 172],
443 | 'Chartreuse (Traditional)' => [223, 255, 0],
444 | 'Chartreuse (Web)' => [127, 255, 0],
445 | 'Cherry Blossom Pink' => [255, 183, 197],
446 | 'Cherry' => [222, 49, 99],
447 | 'Chestnut' => [149, 69, 53],
448 | 'China Pink' => [222, 111, 161],
449 | 'China Rose' => [168, 81, 110],
450 | 'Chinese Red' => [170, 56, 30],
451 | 'Chinese Violet' => [133, 96, 136],
452 | 'Chocolate (Traditional)' => [123, 63, 0],
453 | 'Chocolate (Web)' => [210, 105, 30],
454 | 'Chrome Yellow' => [255, 167, 0],
455 | 'Cinereous' => [152, 129, 123],
456 | 'Cinnabar' => [227, 66, 52],
457 | 'Cinnamon' => [210, 105, 30],
458 | 'Citrine' => [228, 208, 10],
459 | 'Citron' => [159, 169, 31],
460 | 'Claret' => [127, 23, 52],
461 | 'Classic Rose' => [251, 204, 231],
462 | 'Cobalt Blue' => [0, 71, 171],
463 | 'Cocoa Brown' => [210, 105, 30],
464 | 'Coconut' => [150, 90, 62],
465 | 'Coffee' => [111, 78, 55],
466 | 'Columbia Blue' => [196, 216, 226],
467 | 'Congo Pink' => [248, 131, 121],
468 | 'Cool Black' => [0, 46, 99],
469 | 'Cool Grey' => [140, 146, 172],
470 | 'Copper (Crayola)' => [218, 138, 103],
471 | 'Copper Penny' => [173, 111, 105],
472 | 'Copper Red' => [203, 109, 81],
473 | 'Copper Rose' => [153, 102, 102],
474 | 'Copper' => [184, 115, 51],
475 | 'Coquelicot' => [255, 56, 0],
476 | 'Coral Pink' => [248, 131, 121],
477 | 'Coral Red' => [255, 64, 64],
478 | 'Coral' => [255, 127, 80],
479 | 'Cordovan' => [137, 63, 69],
480 | 'Corn' => [251, 236, 93],
481 | 'Cornell Red' => [179, 27, 27],
482 | 'Cornflower Blue' => [100, 149, 237],
483 | 'Cornsilk' => [255, 248, 220],
484 | 'Cosmic Latte' => [255, 248, 231],
485 | 'Cotton Candy' => [255, 188, 217],
486 | 'Coyote Brown' => [129, 97, 62],
487 | 'Cream' => [255, 253, 208],
488 | 'Crimson Glory' => [190, 0, 50],
489 | 'Crimson Red' => [153, 0, 0],
490 | 'Crimson' => [220, 20, 60],
491 | 'Cyan (Process)' => [0, 183, 235],
492 | 'Cyan Azure' => [78, 130, 180],
493 | 'Cyan Cobalt Blue' => [40, 88, 156],
494 | 'Cyan Cornflower Blue' => [24, 139, 194],
495 | 'Cyan' => [0, 255, 255],
496 | 'Cyan-Blue Azure' => [70, 130, 191],
497 | 'Cyber Grape' => [88, 66, 124],
498 | 'Cyber Yellow' => [255, 211, 0],
499 | 'Daffodil' => [255, 255, 49],
500 | 'Dandelion' => [240, 225, 48],
501 | 'Dark Blue' => [0, 0, 139],
502 | 'Dark Blue-Gray' => [102, 102, 153],
503 | 'Dark Brown' => [101, 67, 33],
504 | 'Dark Brown-Tangelo' => [136, 101, 78],
505 | 'Dark Byzantium' => [93, 57, 84],
506 | 'Dark Candy Apple Red' => [164, 0, 0],
507 | 'Dark Cerulean' => [8, 69, 126],
508 | 'Dark Chestnut' => [152, 105, 96],
509 | 'Dark Coral' => [205, 91, 69],
510 | 'Dark Cyan' => [0, 139, 139],
511 | 'Dark Electric Blue' => [83, 104, 120],
512 | 'Dark Goldenrod' => [184, 134, 11],
513 | 'Dark Gray (X11)' => [169, 169, 169],
514 | 'Dark Green (X11)' => [0, 100, 0],
515 | 'Dark Green' => [1, 50, 32],
516 | 'Dark Gunmetal' => [0, 100, 0],
517 | 'Dark Imperial Blue' => [110, 110, 249],
518 | 'Dark Jungle Green' => [26, 36, 33],
519 | 'Dark Khaki' => [189, 183, 107],
520 | 'Dark Lava' => [72, 60, 50],
521 | 'Dark Lavender' => [115, 79, 150],
522 | 'Dark Liver (Horses)' => [84, 61, 55],
523 | 'Dark Liver' => [83, 75, 79],
524 | 'Dark Magenta' => [139, 0, 139],
525 | 'Dark Medium Gray' => [169, 169, 169],
526 | 'Dark Midnight Blue' => [0, 51, 102],
527 | 'Dark Moss Green' => [74, 93, 35],
528 | 'Dark Olive Green' => [85, 107, 47],
529 | 'Dark Orange' => [255, 140, 0],
530 | 'Dark Orchid' => [153, 50, 204],
531 | 'Dark Pastel Blue' => [119, 158, 203],
532 | 'Dark Pastel Green' => [3, 192, 60],
533 | 'Dark Pastel Purple' => [150, 111, 214],
534 | 'Dark Pastel Red' => [194, 59, 34],
535 | 'Dark Pink' => [231, 84, 128],
536 | 'Dark Powder Blue' => [0, 51, 153],
537 | 'Dark Puce' => [79, 58, 60],
538 | 'Dark Purple' => [48, 25, 52],
539 | 'Dark Raspberry' => [135, 38, 87],
540 | 'Dark Red' => [139, 0, 0],
541 | 'Dark Salmon' => [233, 150, 122],
542 | 'Dark Scarlet' => [86, 3, 25],
543 | 'Dark Sea Green' => [143, 188, 143],
544 | 'Dark Sienna' => [60, 20, 20],
545 | 'Dark Sky Blue' => [140, 190, 214],
546 | 'Dark Slate Blue' => [72, 61, 139],
547 | 'Dark Slate Gray' => [47, 79, 79],
548 | 'Dark Spring Green' => [23, 114, 69],
549 | 'Dark Tan' => [145, 129, 81],
550 | 'Dark Tangerine' => [255, 168, 18],
551 | 'Dark Taupe' => [72, 60, 50],
552 | 'Dark Terra Cotta' => [204, 78, 92],
553 | 'Dark Turquoise' => [0, 206, 209],
554 | 'Dark Vanilla' => [209, 190, 168],
555 | 'Dark Violet' => [148, 0, 211],
556 | 'Dark Yellow' => [155, 135, 12],
557 | 'Dartmouth Green' => [0, 112, 60],
558 | 'Davy\'s Grey' => [85, 85, 85],
559 | 'Debian Red' => [215, 10, 83],
560 | 'Deep Aquamarine' => [64, 130, 109],
561 | 'Deep Carmine Pink' => [239, 48, 56],
562 | 'Deep Carmine' => [169, 32, 62],
563 | 'Deep Carrot Orange' => [233, 105, 44],
564 | 'Deep Cerise' => [218, 50, 135],
565 | 'Deep Champagne' => [250, 214, 165],
566 | 'Deep Chestnut' => [185, 78, 72],
567 | 'Deep Coffee' => [112, 66, 65],
568 | 'Deep Fuchsia' => [193, 84, 193],
569 | 'Deep Green' => [5, 102, 8],
570 | 'Deep Green-Cyan Turquoise' => [14, 124, 97],
571 | 'Deep Jungle Green' => [0, 75, 73],
572 | 'Deep Koamaru' => [51, 51, 102],
573 | 'Deep Lemon' => [245, 199, 26],
574 | 'Deep Lilac' => [153, 85, 187],
575 | 'Deep Magenta' => [204, 0, 204],
576 | 'Deep Maroon' => [130, 0, 0],
577 | 'Deep Mauve' => [212, 115, 212],
578 | 'Deep Moss Green' => [53, 94, 59],
579 | 'Deep Peach' => [255, 203, 164],
580 | 'Deep Pink' => [255, 20, 147],
581 | 'Deep Puce' => [169, 92, 104],
582 | 'Deep Red' => [133, 1, 1],
583 | 'Deep Ruby' => [132, 63, 91],
584 | 'Deep Saffron' => [255, 153, 51],
585 | 'Deep Sky Blue' => [0, 191, 255],
586 | 'Deep Space Sparkle' => [74, 100, 108],
587 | 'Deep Spring Bud' => [85, 107, 47],
588 | 'Deep Taupe' => [126, 94, 96],
589 | 'Deep Tuscan Red' => [102, 66, 77],
590 | 'Deep Violet' => [51, 0, 102],
591 | 'Deer' => [186, 135, 89],
592 | 'Denim' => [21, 96, 189],
593 | 'Desaturated Cyan' => [102, 153, 153],
594 | 'Desert Sand' => [237, 201, 175],
595 | 'Desert' => [193, 154, 107],
596 | 'Desire' => [234, 60, 83],
597 | 'Diamond' => [185, 242, 255],
598 | 'Dim Gray' => [105, 105, 105],
599 | 'Dirt' => [155, 118, 83],
600 | 'Dodger Blue' => [30, 144, 255],
601 | 'Dogwood Rose' => [215, 24, 104],
602 | 'Dollar Bill' => [133, 187, 101],
603 | 'Donkey Brown' => [102, 76, 40],
604 | 'Drab' => [150, 113, 23],
605 | 'Duke Blue' => [0, 0, 156],
606 | 'Dust Storm' => [229, 204, 201],
607 | 'Dutch White' => [239, 223, 187],
608 | 'Earth Yellow' => [225, 169, 95],
609 | 'Ebony' => [85, 93, 80],
610 | 'Ecru' => [194, 178, 128],
611 | 'Eerie Black' => [27, 27, 27],
612 | 'Eggplant' => [97, 64, 81],
613 | 'Eggshell' => [240, 234, 214],
614 | 'Egyptian Blue' => [16, 52, 166],
615 | 'Electric Blue' => [125, 249, 255],
616 | 'Electric Crimson' => [255, 0, 63],
617 | 'Electric Cyan' => [0, 255, 255],
618 | 'Electric Green' => [0, 255, 0],
619 | 'Electric Indigo' => [111, 0, 255],
620 | 'Electric Lavender' => [244, 187, 255],
621 | 'Electric Lime' => [204, 255, 0],
622 | 'Electric Purple' => [191, 0, 255],
623 | 'Electric Ultramarine' => [63, 0, 255],
624 | 'Electric Violet' => [143, 0, 255],
625 | 'Electric Yellow' => [255, 255, 51],
626 | 'Emerald' => [80, 200, 120],
627 | 'Eminence' => [108, 48, 130],
628 | 'English Green' => [27, 77, 62],
629 | 'English Lavender' => [180, 131, 149],
630 | 'English Red' => [171, 75, 82],
631 | 'English Violet' => [86, 60, 92],
632 | 'Eton Blue' => [150, 200, 162],
633 | 'Eucalyptus' => [68, 215, 168],
634 | 'Fallow' => [193, 154, 107],
635 | 'Falu Red' => [128, 24, 24],
636 | 'Fandango Pink' => [222, 82, 133],
637 | 'Fandango' => [181, 51, 137],
638 | 'Fashion Fuchsia' => [244, 0, 161],
639 | 'Fawn' => [229, 170, 112],
640 | 'Feldgrau' => [77, 93, 83],
641 | 'Feldspar' => [253, 213, 177],
642 | 'Fern Green' => [79, 121, 66],
643 | 'Ferrari Red' => [255, 40, 0],
644 | 'Field Drab' => [108, 84, 30],
645 | 'Fire Engine Red' => [206, 32, 41],
646 | 'Firebrick' => [178, 34, 34],
647 | 'Flame' => [226, 88, 34],
648 | 'Flamingo Pink' => [252, 142, 172],
649 | 'Flattery' => [107, 68, 35],
650 | 'Flavescent' => [247, 233, 142],
651 | 'Flax' => [238, 220, 130],
652 | 'Flirt' => [162, 0, 109],
653 | 'Floral White' => [255, 250, 240],
654 | 'Fluorescent Orange' => [255, 191, 0],
655 | 'Fluorescent Pink' => [255, 20, 147],
656 | 'Fluorescent Yellow' => [204, 255, 0],
657 | 'Folly' => [255, 0, 79],
658 | 'Forest Green (Traditional)' => [1, 68, 33],
659 | 'Forest Green (Web)' => [34, 139, 34],
660 | 'French Beige' => [166, 123, 91],
661 | 'French Bistre' => [133, 109, 77],
662 | 'French Blue' => [0, 114, 187],
663 | 'French Fuchsia' => [253, 63, 146],
664 | 'French Lilac' => [134, 96, 142],
665 | 'French Lime' => [158, 253, 56],
666 | 'French Mauve' => [212, 115, 212],
667 | 'French Pink' => [253, 108, 158],
668 | 'French Plum' => [129, 20, 83],
669 | 'French Puce' => [78, 22, 9],
670 | 'French Raspberry' => [199, 44, 72],
671 | 'French Rose' => [246, 74, 138],
672 | 'French Sky Blue' => [119, 181, 254],
673 | 'French Violet' => [136, 6, 206],
674 | 'French Wine' => [172, 30, 68],
675 | 'Fresh Air' => [166, 231, 255],
676 | 'Fuchsia (Crayola)' => [193, 84, 193],
677 | 'Fuchsia Pink' => [255, 119, 255],
678 | 'Fuchsia Purple' => [204, 57, 123],
679 | 'Fuchsia Rose' => [199, 67, 117],
680 | 'Fuchsia' => [255, 0, 255],
681 | 'Fulvous' => [228, 132, 0],
682 | 'Fuzzy Wuzzy' => [204, 102, 102],
683 | 'Gainsboro' => [220, 220, 220],
684 | 'Gamboge Orange (Brown)' => [153, 102, 0],
685 | 'Gamboge' => [228, 155, 15],
686 | 'Generic Viridian' => [0, 127, 102],
687 | 'Ghost White' => [248, 248, 255],
688 | 'Giants Orange' => [254, 90, 29],
689 | 'Ginger' => [176, 101, 0],
690 | 'Glaucous' => [96, 130, 182],
691 | 'Glitter' => [230, 232, 250],
692 | 'GO Green' => [0, 171, 102],
693 | 'Gold (Metallic)' => [212, 175, 55],
694 | 'Gold (Web, Golden)' => [255, 215, 0],
695 | 'Gold Fusion' => [133, 117, 78],
696 | 'Golden Brown' => [153, 101, 21],
697 | 'Golden Poppy' => [252, 194, 0],
698 | 'Golden Yellow' => [255, 223, 0],
699 | 'Goldenrod' => [218, 165, 32],
700 | 'Granny Smith Apple' => [168, 228, 160],
701 | 'Grape' => [111, 45, 168],
702 | 'Gray (HTML/CSS Gray)' => [128, 128, 128],
703 | 'Gray (X11 Gray)' => [190, 190, 190],
704 | 'Gray' => [128, 128, 128],
705 | 'Gray-Asparagus' => [70, 89, 69],
706 | 'Gray-Blue' => [140, 146, 172],
707 | 'Green (Color Wheel, X11 Green)' => [0, 255, 0],
708 | 'Green (Crayola)' => [28, 172, 120],
709 | 'Green (HTML/CSS Color)' => [0, 128, 0],
710 | 'Green (Munsell)' => [0, 168, 119],
711 | 'Green (NCS)' => [0, 159, 107],
712 | 'Green (Pantone)' => [0, 173, 67],
713 | 'Green (Pigment)' => [0, 165, 80],
714 | 'Green (RYB)' => [102, 176, 50],
715 | 'Green-Blue' => [17, 100, 180],
716 | 'Green-Cyan' => [0, 153, 102],
717 | 'Green-Yellow' => [173, 255, 47],
718 | 'Grizzly' => [136, 88, 24],
719 | 'Grullo' => [169, 154, 134],
720 | 'Gunmetal' => [42, 52, 57],
721 | 'Guppie Green' => [0, 255, 127],
722 | 'Halayà Úbe' => [102, 56, 84],
723 | 'Han Blue' => [68, 108, 207],
724 | 'Han Purple' => [82, 24, 250],
725 | 'Hansa Yellow' => [233, 214, 107],
726 | 'Harlequin Green' => [70, 203, 24],
727 | 'Harlequin' => [63, 255, 0],
728 | 'Harvard Crimson' => [201, 0, 22],
729 | 'Harvest Gold' => [218, 145, 0],
730 | 'Heart Gold' => [128, 128, 0],
731 | 'Heliotrope Gray' => [170, 152, 169],
732 | 'Heliotrope Magenta' => [170, 0, 187],
733 | 'Heliotrope' => [223, 115, 255],
734 | 'Hollywood Cerise' => [244, 0, 161],
735 | 'Honeydew' => [240, 255, 240],
736 | 'Honolulu Blue' => [0, 109, 176],
737 | 'Hooker\'s Green' => [73, 121, 107],
738 | 'Hot Magenta' => [255, 29, 206],
739 | 'Hot Pink' => [255, 105, 180],
740 | 'Hunter Green' => [53, 94, 59],
741 | 'Iceberg' => [113, 166, 210],
742 | 'Icterine' => [252, 247, 94],
743 | 'Illuminating Emerald' => [49, 145, 119],
744 | 'Imperial Blue' => [0, 35, 149],
745 | 'Imperial Purple' => [102, 2, 60],
746 | 'Imperial Red' => [237, 41, 57],
747 | 'Imperial' => [96, 47, 107],
748 | 'Inchworm' => [178, 236, 93],
749 | 'Independence' => [76, 81, 109],
750 | 'India Green' => [19, 136, 8],
751 | 'Indian Red' => [205, 92, 92],
752 | 'Indian Yellow' => [227, 168, 87],
753 | 'Indigo (Web)' => [75, 0, 130],
754 | 'Indigo Dye' => [9, 31, 146],
755 | 'Indigo' => [75, 0, 130],
756 | 'International Klein Blue' => [0, 47, 167],
757 | 'Iris' => [90, 79, 207],
758 | 'Irresistible' => [179, 68, 108],
759 | 'Isabelline' => [244, 240, 236],
760 | 'Islamic Green' => [0, 144, 0],
761 | 'Italian Sky Blue' => [178, 255, 255],
762 | 'Ivory' => [255, 255, 240],
763 | 'Jade' => [0, 168, 107],
764 | 'Japanese Carmine' => [157, 41, 51],
765 | 'Japanese Indigo' => [38, 67, 72],
766 | 'Japanese Violet' => [91, 50, 86],
767 | 'Jasmine' => [248, 222, 126],
768 | 'Jasper' => [215, 59, 62],
769 | 'Jazzberry Jam' => [165, 11, 94],
770 | 'Jelly Bean' => [218, 97, 78],
771 | 'Jet' => [52, 52, 52],
772 | 'Jonquil' => [244, 202, 22],
773 | 'Jordy Blue' => [138, 185, 241],
774 | 'June Bud' => [189, 218, 87],
775 | 'Jungle Green' => [41, 171, 135],
776 | 'Kelly Green' => [76, 187, 23],
777 | 'Kenyan Copper' => [124, 28, 5],
778 | 'Keppel' => [58, 176, 158],
779 | 'Khaki (HTML/CSS, Khaki)' => [195, 176, 145],
780 | 'Khaki (X11, Light Khaki)' => [240, 230, 140],
781 | 'Kobe' => [136, 45, 23],
782 | 'Kobi' => [231, 159, 196],
783 | 'Kobicha' => [107, 68, 35],
784 | 'Kombu Green' => [53, 66, 48],
785 | 'KU Crimson' => [232, 0, 13],
786 | 'La Salle Green' => [8, 120, 48],
787 | 'Languid Lavender' => [214, 202, 221],
788 | 'Lapis Lazuli' => [38, 97, 156],
789 | 'Laser Lemon' => [255, 255, 102],
790 | 'Laurel Green' => [169, 186, 157],
791 | 'Lava' => [207, 16, 32],
792 | 'Lavender (Floral)' => [181, 126, 220],
793 | 'Lavender (Web)' => [230, 230, 250],
794 | 'Lavender Blue' => [204, 204, 255],
795 | 'Lavender Blush' => [255, 240, 245],
796 | 'Lavender Gray' => [196, 195, 208],
797 | 'Lavender Indigo' => [148, 87, 235],
798 | 'Lavender Magenta' => [238, 130, 238],
799 | 'Lavender Mist' => [230, 230, 250],
800 | 'Lavender Pink' => [251, 174, 210],
801 | 'Lavender Purple' => [150, 123, 182],
802 | 'Lavender Rose' => [251, 160, 227],
803 | 'Lawn Green' => [124, 252, 0],
804 | 'Lemon Chiffon' => [255, 250, 205],
805 | 'Lemon Curry' => [204, 160, 29],
806 | 'Lemon Glacier' => [253, 255, 0],
807 | 'Lemon Lime' => [227, 255, 0],
808 | 'Lemon Meringue' => [246, 234, 190],
809 | 'Lemon Yellow' => [255, 244, 79],
810 | 'Lemon' => [255, 247, 0],
811 | 'Lenurple' => [186, 147, 216],
812 | 'Liberty' => [84, 90, 167],
813 | 'Licorice' => [26, 17, 16],
814 | 'Light Apricot' => [253, 213, 177],
815 | 'Light Blue' => [173, 216, 230],
816 | 'Light Brilliant Red' => [254, 46, 46],
817 | 'Light Brown' => [181, 101, 29],
818 | 'Light Carmine Pink' => [230, 103, 113],
819 | 'Light Cobalt Blue' => [136, 172, 224],
820 | 'Light Coral' => [240, 128, 128],
821 | 'Light Cornflower Blue' => [147, 204, 234],
822 | 'Light Crimson' => [245, 105, 145],
823 | 'Light Cyan' => [224, 255, 255],
824 | 'Light Deep Pink' => [255, 92, 205],
825 | 'Light French Beige' => [200, 173, 127],
826 | 'Light Fuchsia Pink' => [249, 132, 239],
827 | 'Light Goldenrod Yellow' => [250, 250, 210],
828 | 'Light Gray' => [211, 211, 211],
829 | 'Light Grayish Magenta' => [204, 153, 204],
830 | 'Light Green' => [144, 238, 144],
831 | 'Light Hot Pink' => [255, 179, 222],
832 | 'Light Khaki' => [240, 230, 140],
833 | 'Light Medium Orchid' => [211, 155, 203],
834 | 'Light Moss Green' => [173, 223, 173],
835 | 'Light Orchid' => [230, 168, 215],
836 | 'Light Pastel Purple' => [177, 156, 217],
837 | 'Light Pink' => [255, 182, 193],
838 | 'Light Red Ochre' => [233, 116, 81],
839 | 'Light Salmon Pink' => [255, 153, 153],
840 | 'Light Salmon' => [255, 160, 122],
841 | 'Light Sea Green' => [32, 178, 170],
842 | 'Light Sky Blue' => [135, 206, 250],
843 | 'Light Slate Gray' => [119, 136, 153],
844 | 'Light Steel Blue' => [176, 196, 222],
845 | 'Light Taupe' => [179, 139, 109],
846 | 'Light Thulian Pink' => [230, 143, 172],
847 | 'Light Yellow' => [255, 255, 224],
848 | 'Lilac' => [200, 162, 200],
849 | 'Lime (Color Wheel)' => [191, 255, 0],
850 | 'Lime (Web, X11 Green)' => [0, 255, 0],
851 | 'Lime Green' => [50, 205, 50],
852 | 'Limerick' => [157, 194, 9],
853 | 'Lincoln Green' => [25, 89, 5],
854 | 'Linen' => [250, 240, 230],
855 | 'Lion' => [193, 154, 107],
856 | 'Liseran Purple' => [222, 111, 161],
857 | 'Little Boy Blue' => [108, 160, 220],
858 | 'Liver (Dogs)' => [184, 109, 41],
859 | 'Liver (Organ)' => [108, 46, 31],
860 | 'Liver Chestnut' => [152, 116, 86],
861 | 'Liver' => [103, 76, 71],
862 | 'Livid' => [102, 153, 204],
863 | 'Lumber' => [255, 228, 205],
864 | 'Lust' => [230, 32, 32],
865 | 'Macaroni And Cheese' => [255, 189, 136],
866 | 'Magenta (Crayola)' => [255, 85, 163],
867 | 'Magenta (Dye)' => [202, 31, 123],
868 | 'Magenta (Pantone)' => [208, 65, 126],
869 | 'Magenta (Process)' => [255, 0, 144],
870 | 'Magenta Haze' => [159, 69, 118],
871 | 'Magenta' => [255, 0, 255],
872 | 'Magenta-Pink' => [204, 51, 139],
873 | 'Magic Mint' => [170, 240, 209],
874 | 'Magnolia' => [248, 244, 255],
875 | 'Mahogany' => [192, 64, 0],
876 | 'Maize' => [251, 236, 93],
877 | 'Majorelle Blue' => [96, 80, 220],
878 | 'Malachite' => [11, 218, 81],
879 | 'Manatee' => [151, 154, 170],
880 | 'Mango Tango' => [255, 130, 67],
881 | 'Mantis' => [116, 195, 101],
882 | 'Mardi Gras' => [136, 0, 133],
883 | 'Marigold' => [234, 162, 33],
884 | 'Maroon (Crayola)' => [195, 33, 72],
885 | 'Maroon (HTML/CSS)' => [128, 0, 0],
886 | 'Maroon (X11)' => [176, 48, 96],
887 | 'Mauve Taupe' => [145, 95, 109],
888 | 'Mauve' => [224, 176, 255],
889 | 'Mauvelous' => [239, 152, 170],
890 | 'May Green' => [76, 145, 65],
891 | 'Maya Blue' => [115, 194, 251],
892 | 'Meat Brown' => [229, 183, 59],
893 | 'Medium Aquamarine' => [102, 221, 170],
894 | 'Medium Blue' => [0, 0, 205],
895 | 'Medium Candy Apple Red' => [226, 6, 44],
896 | 'Medium Carmine' => [175, 64, 53],
897 | 'Medium Champagne' => [243, 229, 171],
898 | 'Medium Electric Blue' => [3, 80, 150],
899 | 'Medium Jungle Green' => [28, 53, 45],
900 | 'Medium Lavender Magenta' => [221, 160, 221],
901 | 'Medium Orchid' => [186, 85, 211],
902 | 'Medium Persian Blue' => [0, 103, 165],
903 | 'Medium Purple' => [147, 112, 219],
904 | 'Medium Red-Violet' => [187, 51, 133],
905 | 'Medium Ruby' => [170, 64, 105],
906 | 'Medium Sea Green' => [60, 179, 113],
907 | 'Medium Sky Blue' => [128, 218, 235],
908 | 'Medium Slate Blue' => [123, 104, 238],
909 | 'Medium Spring Bud' => [201, 220, 135],
910 | 'Medium Spring Green' => [0, 250, 154],
911 | 'Medium Taupe' => [103, 76, 71],
912 | 'Medium Turquoise' => [72, 209, 204],
913 | 'Medium Tuscan Red' => [121, 68, 59],
914 | 'Medium Vermilion' => [217, 96, 59],
915 | 'Medium Violet-Red' => [199, 21, 133],
916 | 'Mellow Apricot' => [248, 184, 120],
917 | 'Mellow Yellow' => [248, 222, 126],
918 | 'Melon' => [253, 188, 180],
919 | 'Metallic Seaweed' => [10, 126, 140],
920 | 'Metallic Sunburst' => [156, 124, 56],
921 | 'Mexican Pink' => [228, 0, 124],
922 | 'Midnight Blue' => [25, 25, 112],
923 | 'Midnight Green (Eagle Green)' => [0, 73, 83],
924 | 'Mikado Yellow' => [255, 196, 12],
925 | 'Mindaro' => [227, 249, 136],
926 | 'Ming' => [54, 116, 125],
927 | 'Mint Cream' => [245, 255, 250],
928 | 'Mint Green' => [152, 255, 152],
929 | 'Mint' => [62, 180, 137],
930 | 'Misty Rose' => [255, 228, 225],
931 | 'Moccasin' => [250, 235, 215],
932 | 'Mode Beige' => [150, 113, 23],
933 | 'Moonstone Blue' => [115, 169, 194],
934 | 'Mordant Red 19' => [174, 12, 0],
935 | 'Moss Green' => [138, 154, 91],
936 | 'Mountain Meadow' => [48, 186, 143],
937 | 'Mountbatten Pink' => [153, 122, 141],
938 | 'MSU Green' => [24, 69, 59],
939 | 'Mughal Green' => [48, 96, 48],
940 | 'Mulberry' => [197, 75, 140],
941 | 'Mustard' => [255, 219, 88],
942 | 'Myrtle Green' => [49, 120, 115],
943 | 'Nadeshiko Pink' => [246, 173, 198],
944 | 'Napier Green' => [42, 128, 0],
945 | 'Naples Yellow' => [250, 218, 94],
946 | 'Navajo White' => [255, 222, 173],
947 | 'Navy Purple' => [148, 87, 235],
948 | 'Navy' => [0, 0, 128],
949 | 'Neon Carrot' => [255, 163, 67],
950 | 'Neon Fuchsia' => [254, 65, 100],
951 | 'Neon Green' => [57, 255, 20],
952 | 'New Car' => [33, 79, 198],
953 | 'New York Pink' => [215, 131, 127],
954 | 'Non-Photo Blue' => [164, 221, 237],
955 | 'North Texas Green' => [5, 144, 51],
956 | 'Nyanza' => [233, 255, 219],
957 | 'Ocean Boat Blue' => [0, 119, 190],
958 | 'Ochre' => [204, 119, 34],
959 | 'Office Green' => [0, 128, 0],
960 | 'Old Burgundy' => [67, 48, 46],
961 | 'Old Gold' => [207, 181, 59],
962 | 'Old Heliotrope' => [86, 60, 92],
963 | 'Old Lace' => [253, 245, 230],
964 | 'Old Lavender' => [121, 104, 120],
965 | 'Old Mauve' => [103, 49, 71],
966 | 'Old Moss Green' => [134, 126, 54],
967 | 'Old Rose' => [192, 128, 129],
968 | 'Old Silver' => [132, 132, 130],
969 | 'Olive Drab (' => [107, 142, 35],
970 | 'Olive Drab' => [60, 52, 31],
971 | 'Olive' => [128, 128, 0],
972 | 'Olivine' => [154, 185, 115],
973 | 'Onyx' => [53, 56, 57],
974 | 'Opera Mauve' => [183, 132, 167],
975 | 'Orange (Aerospace)' => [255, 79, 0],
976 | 'Orange (Color Wheel)' => [255, 127, 0],
977 | 'Orange (Crayola)' => [255, 117, 56],
978 | 'Orange (Engineering)' => [186, 22, 12],
979 | 'Orange (Golden Gate)' => [192, 54, 44],
980 | 'Orange (Pantone)' => [255, 88, 0],
981 | 'Orange (RYB)' => [251, 153, 2],
982 | 'Orange (Web)' => [255, 165, 0],
983 | 'Orange Peel' => [255, 159, 0],
984 | 'Orange-Red' => [255, 69, 0],
985 | 'Orange-Yellow' => [248, 213, 104],
986 | 'Orchid Pink' => [242, 189, 205],
987 | 'Orchid' => [218, 112, 214],
988 | 'Orioles Orange' => [251, 79, 20],
989 | 'Otter Brown' => [101, 67, 33],
990 | 'OU Crimson Red' => [153, 0, 0],
991 | 'Outer Space' => [65, 74, 76],
992 | 'Outrageous Orange' => [255, 110, 74],
993 | 'Oxford Blue' => [0, 33, 71],
994 | 'Pacific Blue' => [28, 169, 201],
995 | 'Pakistan Green' => [0, 102, 0],
996 | 'Palatinate Blue' => [39, 59, 226],
997 | 'Palatinate Purple' => [104, 40, 96],
998 | 'Pale Aqua' => [188, 212, 230],
999 | 'Pale Blue' => [175, 238, 238],
1000 | 'Pale Brown' => [152, 118, 84],
1001 | 'Pale Carmine' => [175, 64, 53],
1002 | 'Pale Cerulean' => [155, 196, 226],
1003 | 'Pale Chestnut' => [221, 173, 175],
1004 | 'Pale Copper' => [218, 138, 103],
1005 | 'Pale Cornflower Blue' => [171, 205, 239],
1006 | 'Pale Cyan' => [135, 211, 248],
1007 | 'Pale Gold' => [230, 190, 138],
1008 | 'Pale Goldenrod' => [238, 232, 170],
1009 | 'Pale Green' => [152, 251, 152],
1010 | 'Pale Lavender' => [220, 208, 255],
1011 | 'Pale Magenta' => [249, 132, 229],
1012 | 'Pale Magenta-Pink' => [255, 153, 204],
1013 | 'Pale Pink' => [250, 218, 221],
1014 | 'Pale Plum' => [221, 160, 221],
1015 | 'Pale Red-Violet' => [219, 112, 147],
1016 | 'Pale Robin Egg Blue' => [150, 222, 209],
1017 | 'Pale Silver' => [201, 192, 187],
1018 | 'Pale Spring Bud' => [236, 235, 189],
1019 | 'Pale Taupe' => [188, 152, 126],
1020 | 'Pale Turquoise' => [175, 238, 238],
1021 | 'Pale Violet' => [204, 153, 255],
1022 | 'Pale Violet-Red' => [219, 112, 147],
1023 | 'Pansy Purple' => [120, 24, 74],
1024 | 'Paolo Veronese Green' => [0, 155, 125],
1025 | 'Papaya Whip' => [255, 239, 213],
1026 | 'Paradise Pink' => [230, 62, 98],
1027 | 'Paris Green' => [80, 200, 120],
1028 | 'Pastel Blue' => [174, 198, 207],
1029 | 'Pastel Brown' => [131, 105, 83],
1030 | 'Pastel Gray' => [207, 207, 196],
1031 | 'Pastel Green' => [119, 221, 119],
1032 | 'Pastel Magenta' => [244, 154, 194],
1033 | 'Pastel Orange' => [255, 179, 71],
1034 | 'Pastel Pink' => [222, 165, 164],
1035 | 'Pastel Purple' => [179, 158, 181],
1036 | 'Pastel Red' => [255, 105, 97],
1037 | 'Pastel Violet' => [203, 153, 201],
1038 | 'Pastel Yellow' => [253, 253, 150],
1039 | 'Patriarch' => [128, 0, 128],
1040 | 'Payne\'s Grey' => [83, 104, 120],
1041 | 'Peach Puff' => [255, 218, 185],
1042 | 'Peach' => [255, 203, 164],
1043 | 'Peach-Orange' => [255, 204, 153],
1044 | 'Peach-Yellow' => [250, 223, 173],
1045 | 'Pear' => [209, 226, 49],
1046 | 'Pearl Aqua' => [136, 216, 192],
1047 | 'Pearl' => [234, 224, 200],
1048 | 'Pearly Purple' => [183, 104, 162],
1049 | 'Peridot' => [230, 226, 0],
1050 | 'Periwinkle' => [204, 204, 255],
1051 | 'Permanent Geranium Lake' => [225, 44, 44],
1052 | 'Persian Blue' => [28, 57, 187],
1053 | 'Persian Green' => [0, 166, 147],
1054 | 'Persian Indigo' => [50, 18, 122],
1055 | 'Persian Orange' => [217, 144, 88],
1056 | 'Persian Pink' => [247, 127, 190],
1057 | 'Persian Plum' => [112, 28, 28],
1058 | 'Persian Red' => [204, 51, 51],
1059 | 'Persian Rose' => [254, 40, 162],
1060 | 'Persimmon' => [236, 88, 0],
1061 | 'Peru' => [205, 133, 63],
1062 | 'Phlox' => [223, 0, 255],
1063 | 'Phthalo Blue' => [0, 15, 137],
1064 | 'Phthalo Green' => [18, 53, 36],
1065 | 'Picton Blue' => [69, 177, 232],
1066 | 'Pictorial Carmine' => [195, 11, 78],
1067 | 'Piggy Pink' => [253, 221, 230],
1068 | 'Pine Green' => [1, 121, 111],
1069 | 'Pineapple' => [86, 60, 92],
1070 | 'Pink (Pantone)' => [215, 72, 148],
1071 | 'Pink Flamingo' => [252, 116, 253],
1072 | 'Pink Lace' => [255, 221, 244],
1073 | 'Pink Lavender' => [216, 178, 209],
1074 | 'Pink Pearl' => [231, 172, 207],
1075 | 'Pink Raspberry' => [152, 0, 54],
1076 | 'Pink Sherbet' => [247, 143, 167],
1077 | 'Pink' => [255, 192, 203],
1078 | 'Pink-Orange' => [255, 153, 102],
1079 | 'Pistachio' => [147, 197, 114],
1080 | 'Platinum' => [229, 228, 226],
1081 | 'Plum (Web)' => [221, 160, 221],
1082 | 'Plum' => [142, 69, 133],
1083 | 'Pomp And Power' => [134, 96, 142],
1084 | 'Popstar' => [190, 79, 98],
1085 | 'Portland Orange' => [255, 90, 54],
1086 | 'Powder Blue' => [176, 224, 230],
1087 | 'Princeton Orange' => [245, 128, 37],
1088 | 'Prune' => [112, 28, 28],
1089 | 'Prussian Blue' => [0, 49, 83],
1090 | 'Psychedelic Purple' => [223, 0, 255],
1091 | 'Puce Red' => [114, 47, 55],
1092 | 'Puce' => [204, 136, 153],
1093 | 'Pullman Brown (UPS Brown)' => [100, 65, 23],
1094 | 'Pullman Green' => [59, 51, 28],
1095 | 'Pumpkin' => [255, 117, 24],
1096 | 'Purple (HTML)' => [128, 0, 128],
1097 | 'Purple (Munsell)' => [159, 0, 197],
1098 | 'Purple (X11)' => [160, 32, 240],
1099 | 'Purple Heart' => [105, 53, 156],
1100 | 'Purple Mountain Majesty' => [150, 120, 182],
1101 | 'Purple Navy' => [78, 81, 128],
1102 | 'Purple Pizzazz' => [254, 78, 218],
1103 | 'Purple Taupe' => [80, 64, 77],
1104 | 'Purpureus' => [154, 78, 174],
1105 | 'Quartz' => [81, 72, 79],
1106 | 'Queen Blue' => [67, 107, 149],
1107 | 'Queen Pink' => [232, 204, 215],
1108 | 'Quinacridone Magenta' => [142, 58, 89],
1109 | 'Rackley' => [93, 138, 168],
1110 | 'Radical Red' => [255, 53, 94],
1111 | 'Raisin Black' => [36, 33, 36],
1112 | 'Rajah' => [251, 171, 96],
1113 | 'Raspberry Glace' => [145, 95, 109],
1114 | 'Raspberry Pink' => [226, 80, 152],
1115 | 'Raspberry Rose' => [179, 68, 108],
1116 | 'Raspberry' => [227, 11, 93],
1117 | 'Raw Sienna' => [214, 138, 89],
1118 | 'Raw Umber' => [130, 102, 68],
1119 | 'Razzle Dazzle Rose' => [255, 51, 204],
1120 | 'Razzmatazz' => [227, 37, 107],
1121 | 'Razzmic Berry' => [141, 78, 133],
1122 | 'Rebecca Purple' => [102, 51, 153],
1123 | 'Red (Crayola)' => [238, 32, 77],
1124 | 'Red (Munsell)' => [242, 0, 60],
1125 | 'Red (NCS)' => [196, 2, 51],
1126 | 'Red (Pantone)' => [237, 41, 57],
1127 | 'Red (Pigment)' => [237, 28, 36],
1128 | 'Red (RYB)' => [254, 39, 18],
1129 | 'Red Devil' => [134, 1, 17],
1130 | 'Red' => [255, 0, 0],
1131 | 'Red-Brown' => [165, 42, 42],
1132 | 'Red-Orange' => [255, 83, 73],
1133 | 'Red-Purple' => [228, 0, 120],
1134 | 'Red-Violet' => [199, 21, 133],
1135 | 'Redwood' => [164, 90, 82],
1136 | 'Regalia' => [82, 45, 128],
1137 | 'Registration Black' => [0, 0, 0],
1138 | 'Resolution Blue' => [0, 35, 135],
1139 | 'Rhythm' => [119, 118, 150],
1140 | 'Rich Black (FOGRA29)' => [1, 11, 19],
1141 | 'Rich Black (FOGRA39)' => [1, 2, 3],
1142 | 'Rich Black' => [0, 64, 64],
1143 | 'Rich Brilliant Lavender' => [241, 167, 254],
1144 | 'Rich Carmine' => [215, 0, 64],
1145 | 'Rich Electric Blue' => [8, 146, 208],
1146 | 'Rich Lavender' => [167, 107, 207],
1147 | 'Rich Lilac' => [182, 102, 210],
1148 | 'Rich Maroon' => [176, 48, 96],
1149 | 'Rifle Green' => [68, 76, 56],
1150 | 'Roast Coffee' => [112, 66, 65],
1151 | 'Robin Egg Blue' => [0, 204, 204],
1152 | 'Rocket Metallic' => [138, 127, 128],
1153 | 'Roman Silver' => [131, 137, 150],
1154 | 'Rose Bonbon' => [249, 66, 158],
1155 | 'Rose Ebony' => [103, 72, 70],
1156 | 'Rose Gold' => [183, 110, 121],
1157 | 'Rose Madder' => [227, 38, 54],
1158 | 'Rose Pink' => [255, 102, 204],
1159 | 'Rose Quartz' => [170, 152, 169],
1160 | 'Rose Red' => [194, 30, 86],
1161 | 'Rose Taupe' => [144, 93, 93],
1162 | 'Rose Vale' => [171, 78, 82],
1163 | 'Rose' => [255, 0, 127],
1164 | 'Rosewood' => [101, 0, 11],
1165 | 'Rosso Corsa' => [212, 0, 0],
1166 | 'Rosy Brown' => [188, 143, 143],
1167 | 'Royal Azure' => [0, 56, 168],
1168 | 'Royal Blue' => [65, 105, 225],
1169 | 'Royal Fuchsia' => [202, 44, 146],
1170 | 'Royal Purple' => [120, 81, 169],
1171 | 'Royal Yellow' => [250, 218, 94],
1172 | 'Ruber' => [206, 70, 118],
1173 | 'Rubine Red' => [209, 0, 86],
1174 | 'Ruby Red' => [155, 17, 30],
1175 | 'Ruby' => [224, 17, 95],
1176 | 'Ruddy Brown' => [187, 101, 40],
1177 | 'Ruddy Pink' => [225, 142, 150],
1178 | 'Ruddy' => [255, 0, 40],
1179 | 'Rufous' => [168, 28, 7],
1180 | 'Russet' => [128, 70, 27],
1181 | 'Russian Green' => [103, 146, 103],
1182 | 'Russian Violet' => [50, 23, 77],
1183 | 'Rust' => [183, 65, 14],
1184 | 'Rusty Red' => [218, 44, 67],
1185 | 'Sacramento State Green' => [0, 86, 63],
1186 | 'Saddle Brown' => [139, 69, 19],
1187 | 'Safety Orange (Blaze Orange)' => [255, 103, 0],
1188 | 'Safety Orange' => [255, 120, 0],
1189 | 'Safety Yellow' => [238, 210, 2],
1190 | 'Saffron' => [244, 196, 48],
1191 | 'Sage' => [188, 184, 138],
1192 | 'Salmon Pink' => [255, 145, 164],
1193 | 'Salmon' => [250, 128, 114],
1194 | 'Sand Dune' => [150, 113, 23],
1195 | 'Sand' => [194, 178, 128],
1196 | 'Sandstorm' => [236, 213, 64],
1197 | 'Sandy Brown' => [244, 164, 96],
1198 | 'Sandy Taupe' => [150, 113, 23],
1199 | 'Sangria' => [146, 0, 10],
1200 | 'Sap Green' => [80, 125, 42],
1201 | 'Sapphire Blue' => [0, 103, 165],
1202 | 'Sapphire' => [15, 82, 186],
1203 | 'Satin Sheen Gold' => [203, 161, 53],
1204 | 'Scarlet' => [253, 14, 53],
1205 | 'Schauss Pink' => [255, 145, 175],
1206 | 'School Bus Yellow' => [255, 216, 0],
1207 | 'Screamin\' Green' => [118, 255, 122],
1208 | 'Sea Blue' => [0, 105, 148],
1209 | 'Sea Green' => [46, 139, 87],
1210 | 'Seal Brown' => [89, 38, 11],
1211 | 'Seashell' => [255, 245, 238],
1212 | 'Selective Yellow' => [255, 186, 0],
1213 | 'Sepia' => [112, 66, 20],
1214 | 'Shadow Blue' => [119, 139, 165],
1215 | 'Shadow' => [138, 121, 93],
1216 | 'Shampoo' => [255, 207, 241],
1217 | 'Shamrock Green' => [0, 158, 96],
1218 | 'Sheen Green' => [143, 212, 0],
1219 | 'Shimmering Blush' => [217, 134, 149],
1220 | 'Shocking Pink (Crayola)' => [255, 111, 255],
1221 | 'Shocking Pink' => [252, 15, 192],
1222 | 'Sienna' => [136, 45, 23],
1223 | 'Silver Chalice' => [172, 172, 172],
1224 | 'Silver Lake Blue' => [93, 137, 186],
1225 | 'Silver Pink' => [196, 174, 173],
1226 | 'Silver Sand' => [191, 193, 194],
1227 | 'Silver' => [192, 192, 192],
1228 | 'Sinopia' => [203, 65, 11],
1229 | 'Skobeloff' => [0, 116, 116],
1230 | 'Sky Blue' => [135, 206, 235],
1231 | 'Sky Magenta' => [207, 113, 175],
1232 | 'Slate Blue' => [106, 90, 205],
1233 | 'Slate Gray' => [112, 128, 144],
1234 | 'Smalt (Dark Powder Blue)' => [0, 51, 153],
1235 | 'Smitten' => [200, 65, 134],
1236 | 'Smoke' => [115, 130, 118],
1237 | 'Smoky Black' => [16, 12, 8],
1238 | 'Smoky Topaz' => [147, 61, 65],
1239 | 'Snow' => [255, 250, 250],
1240 | 'Soap' => [206, 200, 239],
1241 | 'Solid Pink' => [137, 56, 67],
1242 | 'Sonic Silver' => [117, 117, 117],
1243 | 'Space Cadet' => [29, 41, 81],
1244 | 'Spanish Bistre' => [128, 117, 50],
1245 | 'Spanish Blue' => [0, 112, 184],
1246 | 'Spanish Carmine' => [209, 0, 71],
1247 | 'Spanish Crimson' => [229, 26, 76],
1248 | 'Spanish Gray' => [152, 152, 152],
1249 | 'Spanish Green' => [0, 145, 80],
1250 | 'Spanish Orange' => [232, 97, 0],
1251 | 'Spanish Pink' => [247, 191, 190],
1252 | 'Spanish Red' => [230, 0, 38],
1253 | 'Spanish Sky Blue' => [0, 255, 255],
1254 | 'Spanish Violet' => [76, 40, 130],
1255 | 'Spanish Viridian' => [0, 127, 92],
1256 | 'Spartan Crimson' => [158, 19, 22],
1257 | 'Spicy Mix' => [139, 95, 77],
1258 | 'Spiro Disco Ball' => [15, 192, 252],
1259 | 'Spring Bud' => [167, 252, 0],
1260 | 'Spring Green' => [0, 255, 127],
1261 | 'St. Patrick\'s Blue' => [35, 41, 122],
1262 | 'Star Command Blue' => [0, 123, 184],
1263 | 'Steel Blue' => [70, 130, 180],
1264 | 'Steel Pink' => [204, 51, 204],
1265 | 'Stil De Grain Yellow' => [250, 218, 94],
1266 | 'Stizza' => [153, 0, 0],
1267 | 'Stormcloud' => [79, 102, 106],
1268 | 'Straw' => [228, 217, 111],
1269 | 'Strawberry' => [252, 90, 141],
1270 | 'Sunglow' => [255, 204, 51],
1271 | 'Sunray' => [227, 171, 87],
1272 | 'Sunset Orange' => [253, 94, 83],
1273 | 'Sunset' => [250, 214, 165],
1274 | 'Super Pink' => [207, 107, 169],
1275 | 'Tan' => [210, 180, 140],
1276 | 'Tangelo' => [249, 77, 0],
1277 | 'Tangerine Yellow' => [255, 204, 0],
1278 | 'Tangerine' => [242, 133, 0],
1279 | 'Tango Pink' => [228, 113, 122],
1280 | 'Taupe Gray' => [139, 133, 137],
1281 | 'Taupe' => [72, 60, 50],
1282 | 'Tea Green' => [208, 240, 192],
1283 | 'Tea Rose' => [244, 194, 194],
1284 | 'Teal Blue' => [54, 117, 136],
1285 | 'Teal Deer' => [153, 230, 179],
1286 | 'Teal Green' => [0, 130, 127],
1287 | 'Teal' => [0, 128, 128],
1288 | 'Telemagenta' => [207, 52, 118],
1289 | 'Tenné' => [205, 87, 0],
1290 | 'Terra Cotta' => [226, 114, 91],
1291 | 'Thistle' => [216, 191, 216],
1292 | 'Thulian Pink' => [222, 111, 161],
1293 | 'Tickle Me Pink' => [252, 137, 172],
1294 | 'Tiffany Blue' => [10, 186, 181],
1295 | 'Tiger\'s Eye' => [224, 141, 60],
1296 | 'Timberwolf' => [219, 215, 210],
1297 | 'Titanium Yellow' => [238, 230, 0],
1298 | 'Tomato' => [255, 99, 71],
1299 | 'Toolbox' => [116, 108, 192],
1300 | 'Topaz' => [255, 200, 124],
1301 | 'Tractor Red' => [253, 14, 53],
1302 | 'Trolley Grey' => [128, 128, 128],
1303 | 'Tropical Rain Forest' => [0, 117, 94],
1304 | 'Tropical Violet' => [205, 164, 222],
1305 | 'True Blue' => [0, 115, 207],
1306 | 'Tufts Blue' => [65, 125, 193],
1307 | 'Tulip' => [255, 135, 141],
1308 | 'Tumbleweed' => [222, 170, 136],
1309 | 'Turkish Rose' => [181, 114, 129],
1310 | 'Turquoise Blue' => [0, 255, 239],
1311 | 'Turquoise Green' => [160, 214, 180],
1312 | 'Turquoise' => [64, 224, 208],
1313 | 'Tuscan Brown' => [111, 78, 55],
1314 | 'Tuscan Red' => [124, 72, 72],
1315 | 'Tuscan Tan' => [166, 123, 91],
1316 | 'Tuscan' => [250, 214, 165],
1317 | 'Tuscany' => [192, 153, 153],
1318 | 'Twilight Lavender' => [138, 73, 107],
1319 | 'Tyrian Purple' => [102, 2, 60],
1320 | 'UA Blue' => [0, 51, 170],
1321 | 'UA Red' => [217, 0, 76],
1322 | 'Ube' => [136, 120, 195],
1323 | 'UCLA Blue' => [83, 104, 149],
1324 | 'UCLA Gold' => [255, 179, 0],
1325 | 'UFO Green' => [60, 208, 112],
1326 | 'Ultra Pink' => [255, 111, 255],
1327 | 'Ultra Red' => [252, 108, 133],
1328 | 'Ultramarine Blue' => [65, 102, 245],
1329 | 'Ultramarine' => [63, 0, 255],
1330 | 'Umber' => [99, 81, 71],
1331 | 'Unbleached Silk' => [255, 221, 202],
1332 | 'United Nations Blue' => [91, 146, 229],
1333 | 'University of California Gold' => [183, 135, 39],
1334 | 'University of Tennessee Orange' => [247, 127, 0],
1335 | 'Unmellow Yellow' => [255, 255, 102],
1336 | 'UP Forest Green' => [1, 68, 33],
1337 | 'UP Maroon' => [123, 17, 19],
1338 | 'Upsdell Red' => [174, 32, 41],
1339 | 'Urobilin' => [225, 173, 33],
1340 | 'USAFA Blue' => [0, 79, 152],
1341 | 'USC Cardinal' => [153, 0, 0],
1342 | 'USC Gold' => [255, 204, 0],
1343 | 'Utah Crimson' => [211, 0, 63],
1344 | 'Vanilla Ice' => [243, 143, 169],
1345 | 'Vanilla' => [243, 229, 171],
1346 | 'Vegas Gold' => [197, 179, 88],
1347 | 'Venetian Red' => [200, 8, 21],
1348 | 'Verdigris' => [67, 179, 174],
1349 | 'Vermilion' => [217, 56, 30],
1350 | 'Veronica' => [160, 32, 240],
1351 | 'Very Light Azure' => [116, 187, 251],
1352 | 'Very Light Blue' => [102, 102, 255],
1353 | 'Very Light Malachite Green' => [100, 233, 134],
1354 | 'Very Light Tangelo' => [255, 176, 119],
1355 | 'Very Pale Orange' => [255, 223, 191],
1356 | 'Very Pale Yellow' => [255, 255, 191],
1357 | 'Violet (Color Wheel)' => [127, 0, 255],
1358 | 'Violet (RYB)' => [134, 1, 175],
1359 | 'Violet (Web)' => [238, 130, 238],
1360 | 'Violet' => [143, 0, 255],
1361 | 'Violet-Blue' => [50, 74, 178],
1362 | 'Violet-Red' => [247, 83, 148],
1363 | 'Viridian Green' => [0, 150, 152],
1364 | 'Viridian' => [64, 130, 109],
1365 | 'Vista Blue' => [124, 158, 217],
1366 | 'Vivid Amber' => [204, 153, 0],
1367 | 'Vivid Auburn' => [146, 39, 36],
1368 | 'Vivid Burgundy' => [159, 29, 53],
1369 | 'Vivid Cerise' => [218, 29, 129],
1370 | 'Vivid Cerulean' => [0, 170, 238],
1371 | 'Vivid Crimson' => [204, 0, 51],
1372 | 'Vivid Gamboge' => [255, 153, 0],
1373 | 'Vivid Lime Green' => [166, 214, 8],
1374 | 'Vivid Malachite' => [0, 204, 51],
1375 | 'Vivid Mulberry' => [184, 12, 227],
1376 | 'Vivid Orange Peel' => [255, 160, 0],
1377 | 'Vivid Orange' => [255, 95, 0],
1378 | 'Vivid Orchid' => [204, 0, 255],
1379 | 'Vivid Raspberry' => [255, 0, 108],
1380 | 'Vivid Red' => [247, 13, 26],
1381 | 'Vivid Red-Tangelo' => [223, 97, 36],
1382 | 'Vivid Sky Blue' => [0, 204, 255],
1383 | 'Vivid Tangelo' => [240, 116, 39],
1384 | 'Vivid Tangerine' => [255, 160, 137],
1385 | 'Vivid Vermilion' => [229, 96, 36],
1386 | 'Vivid Violet' => [159, 0, 255],
1387 | 'Vivid Yellow' => [255, 227, 2],
1388 | 'Volt' => [206, 255, 0],
1389 | 'Warm Black' => [0, 66, 66],
1390 | 'Waterspout' => [164, 244, 249],
1391 | 'Weldon Blue' => [124, 152, 171],
1392 | 'Wenge' => [100, 84, 82],
1393 | 'Wheat' => [245, 222, 179],
1394 | 'White Smoke' => [245, 245, 245],
1395 | 'White' => [255, 255, 255],
1396 | 'Wild Blue Yonder' => [162, 173, 208],
1397 | 'Wild Orchid' => [212, 112, 162],
1398 | 'Wild Strawberry' => [255, 67, 164],
1399 | 'Wild Watermelon' => [252, 108, 133],
1400 | 'Willpower Orange' => [253, 88, 0],
1401 | 'Windsor Tan' => [167, 85, 2],
1402 | 'Wine Dregs' => [103, 49, 71],
1403 | 'Wine' => [114, 47, 55],
1404 | 'Wisteria' => [201, 160, 220],
1405 | 'Wood Brown' => [193, 154, 107],
1406 | 'Xanadu' => [115, 134, 120],
1407 | 'Yale Blue' => [15, 77, 146],
1408 | 'Yankees Blue' => [28, 40, 65],
1409 | 'Yellow (Crayola)' => [252, 232, 131],
1410 | 'Yellow (Munsell)' => [239, 204, 0],
1411 | 'Yellow (NCS)' => [255, 211, 0],
1412 | 'Yellow (Pantone)' => [254, 223, 0],
1413 | 'Yellow (Process)' => [255, 239, 0],
1414 | 'Yellow (RYB)' => [254, 254, 51],
1415 | 'Yellow Orange' => [255, 174, 66],
1416 | 'Yellow Rose' => [255, 240, 0],
1417 | 'Yellow' => [255, 255, 0],
1418 | 'Yellow-Green' => [154, 205, 50],
1419 | 'Zaffre' => [0, 20, 168],
1420 | 'Zinnwaldite Brown' => [44, 22, 8],
1421 | 'Zomp' => [57, 167, 14],
1422 | ];
1423 | }
1424 | }
1425 |
--------------------------------------------------------------------------------
/src/DateRange.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 | namespace DDD\Embeddable;
11 |
12 | use DateTime;
13 | use Doctrine\ORM\Mapping as ORM;
14 | use InvalidArgumentException;
15 | use JsonSerializable;
16 |
17 | /**
18 | * @ORM\Embeddable
19 | */
20 | class DateRange implements JsonSerializable
21 | {
22 | /**
23 | * start date
24 | *
25 | * @ORM\Column(type="datetime", nullable=true)
26 | */
27 | private ?DateTime $dateFrom = null;
28 |
29 | /**
30 | * end date
31 | *
32 | * @ORM\Column(type="datetime", nullable=true)
33 | */
34 | private ?DateTime $dateTo = null;
35 |
36 | public function __construct(DateTime $start = null, DateTime $end = null)
37 | {
38 | if ($start === null || $end === null) {
39 | return;
40 | }
41 |
42 | if ($start >= $end) {
43 | throw new InvalidArgumentException('Start date is greater or equal to end date');
44 | }
45 |
46 | $this->dateFrom = $start;
47 | $this->dateTo = $end;
48 | }
49 |
50 | public function getDateFrom() : DateTime
51 | {
52 | return $this->dateFrom;
53 | }
54 |
55 | public function getDateTo() : DateTime
56 | {
57 | return $this->dateTo;
58 | }
59 |
60 | /**
61 | * Formats date range to string using given $format
62 | *
63 | * @param string $f Any format accepted by php date()
64 | */
65 | public function format(string $f = 'c') : string
66 | {
67 | if ($this->isEmpty()) {
68 | return '';
69 | }
70 |
71 | return $this->getDateFrom()->format($f).' - '.$this->getDateTo()->format($f);
72 | }
73 |
74 | /**
75 | * String representation of date range.
76 | *
77 | * @return string
78 | */
79 | public function __toString()
80 | {
81 | return $this->format('c');
82 | }
83 |
84 | /**
85 | * Returns duration of the date range in seconds.
86 | */
87 | public function getDurationInSeconds() : float|int
88 | {
89 | if (!$this->dateFrom) {
90 | return 0;
91 | }
92 |
93 | $interval = $this->dateFrom->diff($this->dateTo);
94 |
95 | return ($interval->y * 365 * 24 * 60 * 60) +
96 | ($interval->m * 30 * 24 * 60 * 60) +
97 | ($interval->d * 24 * 60 * 60) +
98 | ($interval->h * 60 * 60) +
99 | ($interval->i * 60) +
100 | $interval->s;
101 | }
102 |
103 | /**
104 | * Array representation of the range
105 | *
106 | * @return array
107 | */
108 | public function toArray($format = 'c')
109 | {
110 | if ($this->isEmpty()) {
111 | return [];
112 | }
113 |
114 | return [
115 | 'start' => $this->getDateFrom()->format($format),
116 | 'end' => $this->getDateTo()->format($format),
117 | ];
118 | }
119 |
120 | /**
121 | * Implement json serializable interface.
122 | */
123 | public function jsonSerialize(): array
124 | {
125 | return $this->toArray();
126 | }
127 |
128 | /**
129 | * Returns a boolean TRUE if the range instance is
130 | * literally empty, FALSE otherwise.
131 | */
132 | public function isEmpty() : bool
133 | {
134 | return !$this->dateFrom || !$this->dateTo;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/EmailAddress.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 |
11 | namespace DDD\Embeddable;
12 |
13 | use Doctrine\ORM\Mapping as ORM;
14 | use InvalidArgumentException;
15 |
16 | /**
17 | * @ORM\Embeddable
18 | */
19 | class EmailAddress
20 | {
21 | /**
22 | * @ORM\Column(type="string", length=100, nullable=true)
23 | */
24 | private ?string $address = null;
25 |
26 | public function __construct(string $email = null)
27 | {
28 | // This is only a soft validation to reduce headaches earlier.
29 | // You SHOULD sanitize & validate actual email according to your needs, before using it as a value object!
30 | if ($email && ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
31 | throw new InvalidArgumentException('Given e-mail address ' . $email . ' is not a valid');
32 | }
33 |
34 | $this->address = $email;
35 | }
36 |
37 | /**
38 | * String representation of an email.
39 | */
40 | public function __toString(): string
41 | {
42 | return $this->address ?: '';
43 | }
44 |
45 | /**
46 | * Returns domain part of the email address like gmail.com, yahoo.com etc.
47 | */
48 | public function getDomain() : ?string
49 | {
50 | return $this->address ? explode('@', $this->address)[1] : null;
51 | }
52 |
53 | /**
54 | * Returns local part of the email address
55 | */
56 | public function getLocalPart() : ?string
57 | {
58 | return $this->address ? explode('@', $this->address, 2)[0] : null;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/FullName.php:
--------------------------------------------------------------------------------
1 |
7 | * @license MIT
8 | *
9 | * @since Sep 2016
10 | */
11 |
12 | namespace DDD\Embeddable;
13 |
14 | use Doctrine\ORM\Mapping as ORM;
15 | use InvalidArgumentException;
16 | use JsonSerializable;
17 |
18 | /**
19 | * @ORM\Embeddable
20 | */
21 | class FullName implements JsonSerializable
22 | {
23 | /**
24 | * Simple honorific title enumeration.
25 | *
26 | * It mainly helps keeping titles consistent and easily validating them.
27 | * Also, very useful to populate title dropdowns.
28 | */
29 | public const TITLES = [
30 | 'Mr.',
31 | 'Ms.', // for women regardless of their marital status
32 | 'Mrs.',
33 | 'Miss',
34 | 'Mx.', // a title that does not indicate gender
35 | 'Adv.', // advocate
36 | 'Capt.', // captain
37 | 'Dr.',
38 | 'Prof.', // professor
39 | ];
40 |
41 | /**
42 | * honorific title prefixing a person's name.
43 | *
44 | * @ORM\Column(type="string", length=5, nullable=true)
45 | */
46 | private ?string $title = null;
47 |
48 | /**
49 | * first name of the person.
50 | *
51 | * @ORM\Column(type="string", length=64, nullable=true)
52 | */
53 | private ?string $name = null;
54 |
55 | /**
56 | * middle name of the person.
57 | *
58 | * @ORM\Column(type="string", length=64, nullable=true)
59 | */
60 | private ?string $middleName = null;
61 |
62 | /**
63 | * last name of the person.
64 | *
65 | * @ORM\Column(type="string", length=64, nullable=true)
66 | */
67 | private ?string $surname = null;
68 |
69 | /**
70 | * String representation of a full name.
71 | */
72 | public function __toString()
73 | {
74 | // Array filter removes empty items.
75 | return implode(' ', array_filter([$this->title, $this->name, $this->middleName, $this->surname]));
76 | }
77 |
78 | /**
79 | * @param string|null $fullName Space separated full name. Ex: Steven Paul Jobs
80 | */
81 | public function __construct(string $fullName = null, string $title = null)
82 | {
83 | if ($fullName) {
84 | $this->buildFromString($fullName);
85 | }
86 |
87 | if ($title) {
88 | $this->setTitle($title);
89 | }
90 | }
91 |
92 | /**
93 | * A private setter for title.
94 | *
95 | * @throws InvalidArgumentException
96 | */
97 | private function setTitle(string $title) : void
98 | {
99 | if (! str_ends_with($title, '.')) {
100 | // Allow titles without dots too
101 | $title .= '.';
102 | }
103 |
104 | if (! in_array($title, self::TITLES)) {
105 | throw new InvalidArgumentException('Given title is invalid: ' . $title);
106 | }
107 |
108 | $this->title = $title;
109 | }
110 |
111 | /**
112 | * Set name, surname and last fields using.
113 | */
114 | private function buildFromString(string $fullName) : void
115 | {
116 | $names = array_filter(explode(' ', $fullName)); // explode from spaces
117 | $i = 1;
118 | $total = count($names);
119 |
120 | foreach ($names as $word) {
121 | // skip trailing/double spaces
122 | if ($i === 1) {
123 | $this->name = $word;
124 | } elseif ($i >= 2) {
125 | if ($i === $total) {
126 | $this->surname = $word;
127 | } else {
128 | // merge all middle names
129 | $this->middleName = trim($this->middleName . ' ' . $word);
130 | }
131 | }
132 |
133 | ++$i;
134 | }
135 | }
136 |
137 | /**
138 | * Gets the honorific title prefixing person's name.
139 | */
140 | public function getTitle() : ?string
141 | {
142 | return $this->title;
143 | }
144 |
145 | /**
146 | * Gets the first name of the person.
147 | */
148 | public function getName() : ?string
149 | {
150 | return $this->name;
151 | }
152 |
153 | /**
154 | * Gets the middle name of the person.
155 | */
156 | public function getMiddleName() : ?string
157 | {
158 | return $this->middleName;
159 | }
160 |
161 | /**
162 | * Gets the last name of the person.
163 | */
164 | public function getSurname() : ?string
165 | {
166 | return $this->surname;
167 | }
168 |
169 | /**
170 | * Returns array representation of the full name.
171 | */
172 | public function toArray() : array
173 | {
174 | return [
175 | 'title' => $this->title,
176 | 'name' => $this->name,
177 | 'middleName' => $this->middleName,
178 | 'surname' => $this->surname,
179 | ];
180 | }
181 |
182 | /**
183 | * Returns array representation of the full name.
184 | */
185 | public function jsonSerialize() : array
186 | {
187 | return $this->toArray();
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/GeoPoint.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 | namespace DDD\Embeddable;
11 |
12 | use Doctrine\ORM\Mapping as ORM;
13 | use InvalidArgumentException;
14 | use JsonSerializable;
15 |
16 | /**
17 | * @ORM\Embeddable
18 | */
19 | class GeoPoint implements JsonSerializable
20 | {
21 | /**
22 | * geo point value, stored as associative array
23 | *
24 | * @ORM\Column(type="json", nullable=true)
25 | */
26 | private array $point = [];
27 |
28 | public function __construct(float $latitude = null, float $longitude = null)
29 | {
30 | if ($latitude && $longitude) {
31 | if ($latitude < -90.0 || $latitude > 90.0 || $longitude < -180.0 || $longitude > 180.0) {
32 | throw new InvalidArgumentException('Given latitude longitude pair is invalid.');
33 | }
34 |
35 | $this->point = [
36 | 'lat' => $latitude,
37 | 'lng' => $longitude,
38 | ];
39 | }
40 | }
41 |
42 | /**
43 | * return elasticsearch-friendly geo point format.
44 | *
45 | * Beware: Elastic uses lat/lon as keys. Google uses lat/lng.
46 | */
47 | public function toElastic() : array
48 | {
49 | if (empty($this->point)) {
50 | return [];
51 | }
52 |
53 | return [
54 | 'lat' => $this->point['lat'],
55 | 'lon' => $this->point['lng'],
56 | ];
57 | }
58 |
59 | /**
60 | * Array representation of the geo point
61 | */
62 | public function toArray() : array
63 | {
64 | return $this->point;
65 | }
66 |
67 | /**
68 | * Implement json serializable interface.
69 | */
70 | public function jsonSerialize() : array
71 | {
72 | return $this->toArray();
73 | }
74 |
75 | /**
76 | * String representation of the point, lat lng order, separated by a space.
77 | */
78 | public function __toString(): string
79 | {
80 | if (empty($this->point)) {
81 | return '';
82 | }
83 |
84 | return $this->point['lat'].' '.$this->point['lng'];
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/IpAddress.php:
--------------------------------------------------------------------------------
1 |
8 | * @license MIT
9 | *
10 | * @since Sep 2016
11 | */
12 |
13 | namespace DDD\Embeddable;
14 |
15 | use Doctrine\ORM\Mapping as ORM;
16 | use InvalidArgumentException;
17 |
18 | /**
19 | * @ORM\Embeddable
20 | */
21 | class IpAddress
22 | {
23 | /**
24 | * ipv4 or v6 address
25 | *
26 | * @ORM\Column(type="string", length=45, nullable=true)
27 | */
28 | private ?string $address = null;
29 |
30 | /**
31 | * Constructor
32 | *
33 | * @see https://secure.php.net/manual/en/filter.filters.flags.php
34 | *
35 | * @throws InvalidArgumentException
36 | */
37 | public function __construct(string $addr = null)
38 | {
39 | if (! $addr) {
40 | return;
41 | }
42 |
43 | $filtered = filter_var($addr, FILTER_VALIDATE_IP);
44 | if ($filtered === false) {
45 | throw new InvalidArgumentException('Given IP ' . $addr . ' is not a valid IP address');
46 | }
47 |
48 | $this->address = $filtered;
49 | }
50 |
51 | /**
52 | * String representation of ip address.
53 | */
54 | public function __toString(): string
55 | {
56 | return $this->address ?: '';
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/IpRange.php:
--------------------------------------------------------------------------------
1 |
9 | * @license MIT
10 | *
11 | * @since Sep 2016
12 | */
13 |
14 | namespace DDD\Embeddable;
15 |
16 | use Doctrine\ORM\Mapping as ORM;
17 | use JsonSerializable;
18 |
19 | /**
20 | * @ORM\Embeddable
21 | */
22 | class IpRange implements JsonSerializable
23 | {
24 | /**
25 | * beginning IP address of the range
26 | *
27 | * @ORM\Embedded(class="DDD\Embeddable\IpAddress")
28 | */
29 | protected ?IpAddress $startIp = null;
30 |
31 | /**
32 | * high IP address of the range
33 | *
34 | * @ORM\Embedded(class="DDD\Embeddable\IpAddress")
35 | */
36 | protected ?IpAddress $endIp = null;
37 |
38 | public function __construct(IpAddress $startIp = null, IpAddress $endIp = null)
39 | {
40 | $this->startIp = $startIp;
41 | $this->endIp = $endIp;
42 | }
43 |
44 | /**
45 | * Returns the low IP addresses of this range.
46 | */
47 | public function getStartIp() : ?IpAddress
48 | {
49 | return $this->startIp;
50 | }
51 |
52 | /**
53 | * Returns the high IP addresses of this range.
54 | */
55 | public function getEndIp() : ?IpAddress
56 | {
57 | return $this->endIp;
58 | }
59 |
60 | /**
61 | * Create a new range from CIDR notation.
62 | * CIDR notation is a compact representation of an IP address(es) and its associated routing prefix.
63 | */
64 | public static function fromCIDR(string $cidr) : IpRange
65 | {
66 | [$subnet, $bits] = explode('/', $cidr);
67 | $start = long2ip((ip2long($subnet)) & ((-1 << (32 - (int)$bits))));
68 | $end = long2ip((ip2long($subnet)) + pow(2, (32 - (int)$bits))-1);
69 |
70 | return new IpRange(new IpAddress($start), new IpAddress($end));
71 | }
72 |
73 | /**
74 | * String representation of a range.
75 | *
76 | * Example output: "192.168.0.10 - 192.168.0.255"
77 | */
78 | public function __toString() : string
79 | {
80 | return $this->isEmpty() ? '' : $this->startIp . ' - ' . $this->endIp;
81 | }
82 |
83 | /**
84 | * Array representation of the ip range
85 | */
86 | public function toArray() : array
87 | {
88 | if ($this->isEmpty()) {
89 | return [];
90 | }
91 |
92 | return [
93 | 'startIp' => (string)$this->getStartIp(),
94 | 'endIp' => (string)$this->getEndIp(),
95 | ];
96 | }
97 |
98 | /**
99 | * Returns boolean TRUE if the range is empty, false otherwise.
100 | */
101 | public function isEmpty() : bool
102 | {
103 | return $this->startIp === null || $this->endIp === null;
104 | }
105 |
106 | /**
107 | * Implement json serializable interface.
108 | */
109 | public function jsonSerialize() : array
110 | {
111 | return $this->toArray();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/tests/ColorTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Oct 2016
9 | */
10 |
11 | namespace Test\Embeddable;
12 |
13 | use DDD\Embeddable\Color;
14 | use InvalidArgumentException;
15 | use JsonSerializable;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class ColorTest extends TestCase
19 | {
20 | /**
21 | * @dataProvider badHexCodes
22 | */
23 | public function testInvalidHexNotAccepted(mixed $hex) : void
24 | {
25 | $this->expectException(InvalidArgumentException::class);
26 | new Color($hex);
27 | }
28 |
29 | /**
30 | * @dataProvider goodHexCodes
31 | */
32 | public function testAlwaysNormalizeHexValues(string $val, string $normalized) : void
33 | {
34 | $color = new Color($val);
35 | $this->assertSame($normalized, (string)$color);
36 | }
37 |
38 | public function testRGBConversionWorks() : void
39 | {
40 | // Given
41 | $underTest = new Color('FFF');
42 |
43 | // When then
44 | $this->assertSame([255, 255, 255], $underTest->toRGB());
45 | $this->assertSame('rgb(255,255,255)', $underTest->toRGBString());
46 | $this->assertInstanceOf(JsonSerializable::class, $underTest);
47 | $this->assertSame($underTest->jsonSerialize(), $underTest->toArray());
48 | }
49 |
50 | public function testFromRgb() : void
51 | {
52 | $actual = Color::fromRGB(255, 255, 255)->toHex();
53 | $this->assertSame('#FFFFFF', $actual);
54 | }
55 |
56 | public function testEmptyState() : void
57 | {
58 | // Given
59 | $underTest = new Color();
60 |
61 | // When then
62 | $this->assertSame([], $underTest->toArray());
63 | $this->assertSame([], $underTest->toRGB());
64 | $this->assertSame('', (string)$underTest);
65 | }
66 |
67 | /**
68 | * @dataProvider sampleColorNames
69 | */
70 | public function testSerializedColorHasAName(string $hex, string $expectedName) : void
71 | {
72 | $arr = (new Color($hex))->toArray();
73 | $this->assertSame($expectedName, $arr['name']);
74 | }
75 |
76 | public function goodHexCodes() : array
77 | {
78 | return [
79 | ['#CCA', '#CCCCAA'],
80 | ['#cca', '#CCCCAA'],
81 | ['#ccccaa', '#CCCCAA'],
82 | ['CCA', '#CCCCAA'],
83 | ['c9a', '#CC99AA'],
84 | ['aba', '#AABBAA'],
85 | ['000', '#000000'],
86 | ['9A4', '#99AA44'],
87 | ];
88 | }
89 |
90 | public function badHexCodes() : array
91 | {
92 | return [
93 | ['#FFFA'],
94 | ['#CACAC'],
95 | ['TXDFA8'],
96 | ['#TXDFA8'],
97 | [-1],
98 | ['rgb(1,2,3)'],
99 | ['Hello'],
100 | ];
101 | }
102 |
103 | public function sampleColorNames() : array
104 | {
105 | return [
106 | ['CCCCCC', 'Pastel Gray'],
107 | ['FF0000', 'Red'],
108 | ['0F0', 'Electric Green'],
109 | ['00F', 'Blue'],
110 | ['FF00FF', 'Fuchsia'],
111 | ['FE00FE', 'Fuchsia'],
112 | ];
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tests/DateRangeTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 |
11 | namespace Test\Embeddable;
12 |
13 | use DateTime;
14 | use DDD\Embeddable\DateRange;
15 | use InvalidArgumentException;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class DateRangeTest extends TestCase
19 | {
20 | public function testDateRanges() : void
21 | {
22 | // Given
23 | $start = new DateTime('31 December 2016');
24 | $end = new DateTime('1 January 2017');
25 | $startStr = '2016-12-31T00:00:00+00:00';
26 | $endStr = '2017-01-01T00:00:00+00:00';
27 |
28 | // When
29 | $underTest = new DateRange($start, $end);
30 |
31 | // Then
32 | $this->assertSame('2016 - 2017', $underTest->format('Y'));
33 | $this->assertSame('2016-12 - 2017-01', $underTest->format('Y-m'));
34 | $this->assertStringStartsWith('2016-12-31', $underTest->format());
35 | $this->assertSame(86400, $underTest->getDurationInSeconds());
36 | $this->assertSame(['start' => $startStr, 'end' => $endStr], $underTest->toArray());
37 | $this->assertSame(['start' => $startStr, 'end' => $endStr], $underTest->jsonSerialize());
38 | $this->assertSame($start->format('c') . ' - ' . $end->format('c'), (string)$underTest);
39 | }
40 |
41 | public function testEmptyState() : void
42 | {
43 | $underTest = new DateRange();
44 | $this->assertInstanceOf(DateRange::class, $underTest);
45 | $this->assertSame([], $underTest->toArray());
46 | $this->assertSame('', (string)$underTest);
47 | $this->assertSame(0, $underTest->getDurationInSeconds());
48 | }
49 |
50 | public function testOlderDateRages() : void
51 | {
52 | // Given
53 | $start = new DateTime('23 April 1923');
54 | $end = new DateTime('24 April 1923');
55 |
56 | // When
57 | $actual = (new DateRange($start, $end))->getDurationInSeconds();
58 |
59 | // Then
60 | $this->assertSame(86400, $actual);
61 | }
62 |
63 | public function testEqualDatesNotAccepted() : void
64 | {
65 | $this->expectException(InvalidArgumentException::class);
66 |
67 | // same dates
68 | $start = new DateTime('23 April 1923');
69 | $end = new DateTime('23 April 1923');
70 |
71 | new DateRange($start, $end);
72 | }
73 |
74 | public function testEndDateShouldGreaterThanStartDate() : void
75 | {
76 | $this->expectException(InvalidArgumentException::class);
77 |
78 | // end date is before the start date
79 | $start = new DateTime('23 April 1923');
80 | $end = new DateTime('21 April 1923');
81 |
82 | new DateRange($start, $end);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/tests/EmailAddressTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 | namespace Test\Embeddable;
11 |
12 | use DDD\Embeddable\EmailAddress;
13 | use InvalidArgumentException;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class EmailAddressTest extends TestCase
17 | {
18 | /**
19 | * @dataProvider invalidEmailProvider
20 | */
21 | public function testInvalidAddressesAreNotAccepted(string $email) : void
22 | {
23 | $this->expectException(InvalidArgumentException::class);
24 | new EmailAddress($email);
25 | }
26 |
27 | /**
28 | * @dataProvider validEmailProvider
29 | */
30 | public function testValidAddressesAreAccepted(string $email, string $expectedDomain, string $expectedLocalPart) : void
31 | {
32 | // Given
33 | $underTest = new EmailAddress($email);
34 |
35 | // When Then
36 | $this->assertSame($email, (string) $underTest);
37 | $this->assertSame($expectedDomain, $underTest->getDomain());
38 | $this->assertSame($expectedLocalPart, $underTest->getLocalPart());
39 | }
40 |
41 | public function testEmptyState() : void
42 | {
43 | // Given
44 | $underTest = new EmailAddress();
45 |
46 | // When Then
47 | $this->assertInstanceOf(EmailAddress::class, $underTest);
48 | $this->assertNull($underTest->getDomain());
49 | $this->assertNull($underTest->getLocalPart());
50 | }
51 |
52 | private function invalidEmailProvider() : array
53 | {
54 | return [
55 | ['fake-email'],
56 | ['fake-mail@'],
57 | ['w@fake mail.com'],
58 | ['v@for.vendetta/com'],
59 | ['!?#- 1*@bad.c0m'],
60 | ['Abc.example.com'],
61 | ['A@b@c@example.com'],
62 | ['a"b(c)d,e:f;gi[j\k]l@example.com'],
63 | ['just"not"right@example.com'],
64 | ['john..doe@example.com'],
65 | ];
66 | }
67 |
68 | private function validEmailProvider() : array
69 | {
70 | return [
71 | ['devil@hell.travel', 'hell.travel', 'devil'],
72 | ['jack+london@gmail.com', 'gmail.com', 'jack+london'],
73 | ['cool@mail.com', 'mail.com', 'cool'],
74 | ['great@mail.co.uk', 'mail.co.uk', 'great'],
75 | ['good@email.address.com', 'email.address.com', 'good'],
76 | ['so-good@sub.domain.com.br', 'sub.domain.com.br', 'so-good'],
77 | ['a1@b2.eu', 'b2.eu', 'a1'],
78 | ];
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/FullNameTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 |
11 | namespace Test\Embeddable;
12 |
13 | use DDD\Embeddable\FullName;
14 | use InvalidArgumentException;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class FullNameTest extends TestCase
18 | {
19 | /**
20 | * @dataProvider nameProvider
21 | */
22 | public function testFullNameBuilderFlow(?string $name, ?string $title, ?string $midName, ?string $expected) : void
23 | {
24 | $obj = new FullName($name, $title);
25 | $this->assertSame($expected, (string)$obj);
26 | }
27 |
28 | public function testInvalidTitleIsNotAccepted() : void
29 | {
30 | $this->expectException(InvalidArgumentException::class);
31 | new FullName('John Doe', 'Mz.');
32 | }
33 |
34 | /**
35 | * @throws \JsonException
36 | */
37 | public function testGettersWorkingProperly() : void
38 | {
39 | $obj = new FullName('Steven Paul Jobs', 'Mr');
40 | $this->assertSame('Steven', $obj->getName());
41 | $this->assertSame('Paul', $obj->getMiddleName());
42 | $this->assertSame('Jobs', $obj->getSurname());
43 | $this->assertSame('Mr.', $obj->getTitle());
44 |
45 | $obj2 = new FullName("Nazim Hikmet Ran");
46 | $this->assertNull($obj2->getTitle());
47 |
48 | $expected = '{"title":null,"name":"Nazim","middleName":"Hikmet","surname":"Ran"}';
49 | $this->assertSame($expected, json_encode($obj2, JSON_THROW_ON_ERROR));
50 | }
51 |
52 | public function testEmptyState() : void
53 | {
54 | $name = new FullName();
55 | $this->assertInstanceOf(FullName::class, $name);
56 | $this->assertSame('', (string)$name);
57 | }
58 |
59 | private function nameProvider() : array
60 | {
61 | return [
62 | 'title without dot' => ['Steve Jobs', 'Mr', null, 'Mr. Steve Jobs'],
63 | 'just one name' => ['Richard', null, null, 'Richard'],
64 | 'with a middle name' => ['Steven Paul Jobs', null, 'Paul', 'Steven Paul Jobs'],
65 | 'middle name with dots' => ['John D. Carmack', null, 'D.', 'John D. Carmack'],
66 | 'multiple spaces' => ['Steven Paul Jobs', null, 'Paul', 'Steven Paul Jobs'],
67 | 'name and surname only' => ['Steve Jobs', null, null, 'Steve Jobs'],
68 | 'title with a dot' => ['Steve Jobs', 'Mr.', null, 'Mr. Steve Jobs'],
69 | 'nickname as middle name' => ['Richard "rms" Stallman', null, '"rms"', 'Richard "rms" Stallman'],
70 | 'real name of Audrey Hepburn which is really long' => [
71 | 'Edda Kathleen van Heemstra Hepburn',
72 | 'Mrs',
73 | 'Kathleen van Heemstra',
74 | 'Mrs. Edda Kathleen van Heemstra Hepburn'
75 | ],
76 | 'trailing slashes' => [' Steve Wozniak ', null, null, 'Steve Wozniak'],
77 | 'empty strings' => ['', null, null, ''],
78 | ];
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/GeoPointTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 |
11 | namespace Test\Embeddable;
12 |
13 | use DDD\Embeddable\GeoPoint;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class GeoPointTest extends TestCase
17 | {
18 | public function testValidGeoPoint() : void
19 | {
20 | $underTest = new GeoPoint(24, 34);
21 | $this->assertSame(['lat' => 24.0, 'lng' => 34.0], $underTest->toArray());
22 | $this->assertSame(['lat' => 24.0, 'lng' => 34.0], $underTest->jsonSerialize());
23 | $this->assertSame(['lat' => 24.0, 'lon' => 34.0], $underTest->toElastic());
24 | }
25 |
26 | public function testInvalidGeoPoint() : void
27 | {
28 | $this->expectException(\InvalidArgumentException::class);
29 | new GeoPoint(-300, 300);
30 | }
31 |
32 | public function testEmptyState() : void
33 | {
34 | $underTest = new GeoPoint();
35 | $this->assertInstanceOf(GeoPoint::class, $underTest);
36 | $this->assertSame([], $underTest->toArray());
37 | $this->assertSame([], $underTest->toElastic());
38 | $this->assertSame('', (string)$underTest);
39 | }
40 |
41 | public function testStringRepresentation() : void
42 | {
43 | $underTest = new GeoPoint(41.520112, 29.453401);
44 | $this->assertSame('41.520112 29.453401', (string)$underTest);
45 | }
46 |
47 | public function testToElastic() : void
48 | {
49 | // Given
50 | $underTest = new GeoPoint(41, 29);
51 | $expected = ['lat' => 41.0, 'lon' => 29.0];
52 |
53 | // When
54 | $actual = $underTest->toElastic();
55 |
56 | // Then
57 | $this->assertSame($expected, $actual);
58 | }
59 |
60 | public function testToArray() : void
61 | {
62 | // Given
63 | $underTest = new GeoPoint(42, 30);
64 | $expected = ['lat' => 42.0, 'lng' => 30.0];
65 |
66 | // When
67 | $actual = $underTest->toArray();
68 |
69 | // Then
70 | $this->assertSame($expected, $actual);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/IpAddressTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 |
11 | namespace Test\Embeddable;
12 |
13 | use DDD\Embeddable\IpAddress;
14 | use InvalidArgumentException;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class IpAddressTest extends TestCase
18 | {
19 | public function testInvalidAddressesAreNotAccepted() : void
20 | {
21 | $this->expectException(InvalidArgumentException::class);
22 | new IpAddress('0.0.0');
23 | }
24 |
25 | /**
26 | * @dataProvider ipProvider
27 | */
28 | public function testValidAddressesAreAccepted(string $addr, string $expected) : void
29 | {
30 | $underTest = new IpAddress($addr);
31 | $this->assertSame($expected, (string)$underTest);
32 | }
33 |
34 | public function testEmptyState() : void
35 | {
36 | $underTest = new IpAddress();
37 | $this->assertSame('', (string)$underTest);
38 | }
39 |
40 | private function ipProvider() : array
41 | {
42 | return [
43 | ['127.0.0.1', '127.0.0.1'],
44 | ['192.168.1.1', '192.168.1.1'],
45 | ];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/IpRangeTest.php:
--------------------------------------------------------------------------------
1 |
6 | * @license MIT
7 | *
8 | * @since Sep 2016
9 | */
10 |
11 | namespace Test\Embeddable;
12 |
13 | use DDD\Embeddable\IpRange;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class IpRangeTest extends TestCase
17 | {
18 | /**
19 | * @dataProvider cidrList
20 | */
21 | public function testCIDRNotationWorks(string $cidr, string $expectedLow, string $expectedHigh) : void
22 | {
23 | // Given
24 | $underTest = IpRange::fromCIDR($cidr);
25 | $expected = [
26 | 'startIp' => $expectedLow,
27 | 'endIp' => $expectedHigh,
28 | ];
29 |
30 | // When
31 | $actual = $underTest->toArray();
32 | $this->assertSame($expected, $actual);
33 |
34 | // Then
35 | $this->assertSame($expectedLow, (string)$underTest->getStartIp());
36 | $this->assertSame($expectedHigh, (string)$underTest->getEndIp());
37 | }
38 |
39 | public function testEmptyState() : void
40 | {
41 | // Given
42 | $underTest = new IpRange();
43 |
44 | // When
45 | $actual = $underTest->toArray();
46 |
47 | // Then
48 | $this->assertSame([], $actual);
49 | $this->assertSame('', (string)$underTest);
50 | $this->assertTrue($underTest->isEmpty());
51 | }
52 |
53 | private function cidrList() : array
54 | {
55 | return [
56 | ['176.240.112.0/24', '176.240.112.0', '176.240.112.255'],
57 | ['176.240.112.0/32', '176.240.112.0', '176.240.112.0'],
58 | ['10.0.0.0/22', '10.0.0.0', '10.0.3.255'],
59 | ];
60 | }
61 | }
62 |
--------------------------------------------------------------------------------