93 Commits

Author SHA1 Message Date
Ismo Vuorinen
8fee83b5db chore(meta): update renovate.json 2024-07-23 04:03:50 +03:00
Ismo Vuorinen
197d08014b feat: php 8.3, phpunit 11 (#57)
* Upgrade runner php to 8.3, phpunit to 11
* Fix tests, tweaked the code, added missing ext- to composer.json
* Fix exception related code smells
2024-02-17 12:47:20 +02:00
renovate[bot]
fa16ba60aa chore(deps): update dependency squizlabs/php_codesniffer to v3.9.0 (#58)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 12:15:41 +02:00
renovate[bot]
f629c6245c chore(deps): update dependency phpunit/phpunit to v10.5.10
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 12:14:04 +00:00
renovate[bot]
fd353750ce chore(deps): update dependency phpunit/phpunit to v10.5.9
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-23 02:03:34 +00:00
renovate[bot]
d5ec1ad002 chore(deps): update dependency phpunit/phpunit to v10.5.8
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-20 00:07:45 +00:00
renovate[bot]
06035d333f chore(deps): update actions/cache action to v4 (#52) 2024-01-18 11:00:07 +02:00
renovate[bot]
4a9b906609 chore(deps): update dependency phpunit/phpunit to v10.5.7 (#51) 2024-01-14 23:21:16 +02:00
renovate[bot]
8e5a066d56 chore(deps): update dependency phpunit/phpunit to v10.5.6
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-13 18:17:40 +00:00
renovate[bot]
02797dcb87 chore(deps): update dependency squizlabs/php_codesniffer to v3.8.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 00:13:58 +00:00
renovate[bot]
49cc7942c9 chore(deps): update dependency phpunit/phpunit to v10.5.5
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-28 01:03:38 +00:00
renovate[bot]
f71c02e3cf chore(deps): update dependency phpunit/phpunit to v10.5.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-14 19:45:52 +00:00
renovate[bot]
bcc0632342 chore(deps): update dependency squizlabs/php_codesniffer to v3.8.0 (#46)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-09 05:53:19 +02:00
renovate[bot]
0bf1f431f4 chore(deps): update dependency phpunit/phpunit to v10.5.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-06 00:22:28 +00:00
renovate[bot]
e70264e636 chore(deps): update dependency phpunit/phpunit to v10.5.1 (#44) 2023-12-02 21:07:02 +02:00
renovate[bot]
57ec630e76 chore(deps): update dependency phpunit/phpunit to v10.4.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-27 04:00:22 +00:00
renovate[bot]
6500a63fe7 chore(deps): update dependency phpunit/phpunit to v10.4.1 (#42) 2023-10-09 02:32:20 +03:00
renovate[bot]
fb5c122e04 chore(deps): update dependency phpunit/phpunit to v10.3.5
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-20 01:09:15 +00:00
renovate[bot]
ecaee28c8f chore(deps): update dependency phpunit/phpunit to v10.3.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-13 03:10:48 +00:00
renovate[bot]
a091044c2e chore(deps): update dependency phpunit/phpunit to v10.3.3 (#39)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-05 12:45:56 +03:00
renovate[bot]
9be26fc32f chore(deps): update actions/checkout action to v4 (#38)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-05 12:43:20 +03:00
renovate[bot]
b339143dfa chore(deps): update dependency phpunit/phpunit to v10.3.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-16 00:13:07 +00:00
renovate[bot]
e6b093bc11 chore(deps): update dependency phpunit/phpunit to v10.3.1 (#36) 2023-08-05 11:06:53 +03:00
renovate[bot]
0f8af77c93 chore(deps): update dependency phpunit/phpunit to v10.2.7
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-03 01:41:06 +00:00
renovate[bot]
c58f9399e6 chore(deps): update dependency phpunit/phpunit to v10.2.6 (#34) 2023-07-18 02:31:12 +03:00
renovate[bot]
3b4c8f2c88 chore(deps): update dependency phpunit/phpunit to v10.2.5
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-14 06:15:54 +00:00
renovate[bot]
0605cc33ad chore(deps): update dependency phpunit/phpunit to v10.2.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-11 02:03:49 +00:00
renovate[bot]
82b37c5eeb chore(deps): update dependency phpunit/phpunit to v10.2.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-01 06:48:31 +00:00
renovate[bot]
78ffde9769 chore(deps): update dependency phpunit/phpunit to v10.2.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-11 09:57:49 +00:00
renovate[bot]
ad7ca7e1b8 chore(deps): update dependency phpunit/phpunit to v10.2.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-06 00:51:46 +00:00
renovate[bot]
1ff724bf3b chore(deps): update dependency phpunit/phpunit to v10.2.0 (#28) 2023-06-04 00:55:53 +03:00
renovate[bot]
05626ae8bd chore(deps): update dependency phpunit/phpunit to v10.1.3
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-05-12 04:25:05 +00:00
renovate[bot]
3a73168f93 chore(deps): update dependency phpunit/phpunit to v10.1.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-04-22 12:01:42 +00:00
renovate[bot]
20912f3275 chore(deps): update dependency phpunit/phpunit to v10.1.1
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-18 01:06:07 +00:00
renovate[bot]
8408bdc9cf chore(deps): update dependency phpunit/phpunit to v10.1.0 (#24) 2023-04-16 03:46:35 +03:00
renovate[bot]
cf3593964b chore(deps): update dependency phpunit/phpunit to v10.0.19
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-03-27 23:36:56 +00:00
renovate[bot]
9139a1869b chore(deps): update dependency phpunit/phpunit to v10.0.18 (#22) 2023-03-23 01:34:25 +02:00
renovate[bot]
233e7bb812 chore(deps): update dependency phpunit/phpunit to v10.0.17
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-21 00:52:12 +00:00
renovate[bot]
439d7f0c85 chore(deps): update dependency phpunit/phpunit to v10.0.16
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-14 01:13:29 +00:00
renovate[bot]
f167744d75 chore(deps): update dependency phpunit/phpunit to v10 (#15)
* chore(deps): update dependency phpunit/phpunit to v10
* Updated phpunit configuration

---------

Signed-off-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ismo Vuorinen <ismo.vuorinen@vincit.fi>
2023-03-09 11:43:35 +02:00
renovate[bot]
6b553a3ee4 chore(deps): update dependency phpunit/phpunit to v9.6.4
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-02-28 04:47:30 +00:00
renovate[bot]
600a883955 chore(deps): update dependency squizlabs/php_codesniffer to v3.7.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-02-23 05:37:00 +00:00
renovate[bot]
9e375493e1 chore(deps): update dependency phpunit/phpunit to v9.6.3
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-02-04 19:47:04 +00:00
renovate[bot]
fcbaa5a35f chore(deps): update dependency phpunit/phpunit to v9.6.2
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-02-04 12:40:16 +00:00
ecc2aeb70c Merge pull request #14 from ivuorinen/renovate/phpunit-phpunit-9.x 2023-02-04 08:19:26 +02:00
renovate[bot]
2dbb927d5f chore(deps): update dependency phpunit/phpunit to v9.6.1
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-02-03 22:27:18 +00:00
renovate[bot]
b7ba508aa7 chore(deps): update dependency phpunit/phpunit to v9.5.28
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-14 17:42:24 +00:00
renovate[bot]
5e88b79107 chore(deps): update dependency phpunit/phpunit to v9.5.27
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-12-10 01:44:12 +00:00
6ba6e9f1e2 Update renovate.json 2022-11-28 16:42:30 +02:00
24c7881afa Merge pull request #11 from ivuorinen/renovate/phpunit-phpunit-9.x
Update dependency phpunit/phpunit to v9.5.26
2022-10-28 11:30:34 +03:00
renovate[bot]
71081cd7dd Update dependency phpunit/phpunit to v9.5.26 2022-10-28 06:20:33 +00:00
d76cccc158 Merge pull request #10 from ivuorinen/renovate/phpunit-phpunit-9.x 2022-09-26 00:36:31 +03:00
renovate[bot]
3711a88872 Update dependency phpunit/phpunit to v9.5.25 2022-09-25 04:41:11 +00:00
89d1bad6ee Merge pull request #9 from ivuorinen/renovate/phpunit-phpunit-9.x
Update dependency phpunit/phpunit to v9.5.24
2022-08-30 15:58:00 +03:00
renovate[bot]
3abea54ff3 Update dependency phpunit/phpunit to v9.5.24 2022-08-30 12:50:29 +00:00
6e530d1d41 Merge pull request #8 from ivuorinen/renovate/phpunit-phpunit-9.x 2022-08-22 20:00:49 +03:00
renovate[bot]
5450f7ab26 Update dependency phpunit/phpunit to v9.5.23 2022-08-22 16:45:30 +00:00
cfac572690 Merge pull request #7 from ivuorinen/renovate/phpunit-phpunit-9.x 2022-08-20 17:59:18 +03:00
renovate[bot]
9603b8e4a3 Update dependency phpunit/phpunit to v9.5.22 2022-08-20 10:37:23 +00:00
Ismo Vuorinen
dca55f5fa2 Updated phpunit configuration 2022-06-26 18:46:35 +03:00
ec34171c66 Merge pull request #6 from ivuorinen/renovate/pin-dependencies
Pin dependency squizlabs/php_codesniffer to 3.7.1
2022-06-26 18:42:22 +03:00
renovate[bot]
b16235330d Pin dependency squizlabs/php_codesniffer to 3.7.1 2022-06-26 15:41:20 +00:00
9397223e8f Merge pull request #4 from ivuorinen/renovate/phpunit-phpunit-9.x
Update dependency phpunit/phpunit to v9
2022-06-26 18:41:08 +03:00
Ismo Vuorinen
264c35b39c Updated tests, added phpcs 2022-06-26 18:40:33 +03:00
1bd38c91a3 Merge branch 'master' into renovate/phpunit-phpunit-9.x 2022-06-26 18:24:03 +03:00
7110644115 CI: Run composer validate and phpunit tests 2022-06-26 18:23:27 +03:00
renovate[bot]
22f59dfc66 Update dependency phpunit/phpunit to v9 2022-06-26 15:20:38 +00:00
151a5e9bac Merge pull request #3 from ivuorinen/renovate/pin-dependencies
Pin dependency phpunit/phpunit to v
2022-06-26 18:20:24 +03:00
renovate[bot]
34cd28cd80 Pin dependency phpunit/phpunit to v 2022-06-26 11:31:47 +00:00
3bb0fd2fba Merge pull request #2 from ivuorinen/renovate/configure 2022-06-26 14:31:35 +03:00
renovate[bot]
3b19201543 Add renovate.json 2022-06-26 08:48:37 +00:00
Ismo Vuorinen
23062bdcc0 Tests are our examples, using images as test material 2017-09-14 14:24:44 +03:00
Ismo Vuorinen
aab19fa5b3 Fixed throwing errors, added tests to test them 2017-09-14 14:22:36 +03:00
Ismo Vuorinen
6dfc559079 Better test name for black and red images 2017-09-14 14:20:31 +03:00
Ismo Vuorinen
1f0a4b878c CodeStyle changes, documentation, small fixes 2017-09-14 13:50:17 +03:00
Ismo Vuorinen
b9c5530928 Now actually running tests, dropping PHP5.5 2017-09-14 12:15:19 +03:00
Ismo Vuorinen
6998e75451 Tweaking travis config, removing composer.lock 2017-09-14 12:06:02 +03:00
Ismo Vuorinen
22e19941b0 Testing Travis config variables 2017-09-14 11:55:45 +03:00
Ismo Vuorinen
a9ab21d1a7 Reworked tests, added some images for real testing 2017-09-13 22:31:59 +03:00
Ismo Vuorinen
60614e47be Code style changes and added travis build badge to README 2017-09-13 22:31:27 +03:00
Ismo Vuorinen
6c1dca08da Ditching PHP 5.6, you should too 2017-09-13 22:04:03 +03:00
Ismo Vuorinen
6330f36d7c Travis config test 2017-09-13 21:59:23 +03:00
Ismo Vuorinen
4e59352f5f PHPUnit in PHP 5.* should now work 2017-09-13 21:54:42 +03:00
Ismo Vuorinen
bc6ce3d8a4 PHPUnit 6 requires PHP 7, trying composer config pipes 2017-09-13 20:35:40 +03:00
Ismo Vuorinen
6b786f27df Travis config changes 2017-09-13 20:29:30 +03:00
Ismo Vuorinen
81aa917de2 First tests 2017-09-13 19:30:05 +03:00
Ismo Vuorinen
3a2b7e8616 Create .travis.yml
Boilerplate Travis config
2017-09-13 18:06:47 +03:00
Ismo Vuorinen
9e5d44cc66 Create CODE_OF_CONDUCT.md 2017-09-13 18:03:48 +03:00
Ismo Vuorinen
a2978e901c Create CONTRIBUTING.md 2017-09-13 18:03:12 +03:00
Ismo Vuorinen
e4cab9f4f0 Merge pull request #1 from ivuorinen/add-license-1
Create LICENSE
2017-09-13 18:01:16 +03:00
Ismo Vuorinen
e280838fb5 Create LICENSE 2017-09-13 17:59:13 +03:00
Ismo Vuorinen
7aabe2f861 .gitignore:
- Added vendor folder
composer.json:
- Added required description
- Fixed autoloader classmap
- Removed extra-section
example.php:
- Now has working autoloader and non-autoloader examples
- Better wording in comments and caching example, also includes destination configuration example
Palette.php:
- Added MIT license block and changed the license links from GPL2 to MIT to reflect composer.json file
- Moved file from from a directory to a better one, reflecting class namespace.
- Combined tests to fewer try {} catch {} -blocks
- Added comments to help understand the code
2013-07-01 16:03:59 +03:00
Ismo Vuorinen
f0f65c1b94 Readme updated with examples and description 2013-06-27 19:17:35 +03:00
21 changed files with 706 additions and 372 deletions

11
.editorconfig Normal file
View File

@@ -0,0 +1,11 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4

39
.github/workflows/php.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: PHP Composer
on:
push:
branches: ["master"]
pull_request:
branches: ["master"]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@1a18b2267f80291a81ca1d33e7c851fe09e7dfc4
with:
php-version: "8.3"
- name: Validate composer.json and composer.lock
run: composer validate --strict
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run test suite
run: composer run-script test

5
.gitignore vendored
View File

@@ -1,2 +1,5 @@
*.json *.json
!composer.json composer.lock
!composer.json
vendor
build

22
.travis.yml Normal file
View File

@@ -0,0 +1,22 @@
language: php
php:
- 8.3
- nightly
matrix:
allow_failures:
- php: nightly
fast_finish: true
cache:
directories:
- $HOME/.composer/cache/files
before_script:
- phpenv global "$TRAVIS_PHP_VERSION"
- composer config extra.platform.php $TRAVIS_PHP_VERSION
- composer require php-coveralls/php-coveralls
install:
- flags="--ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress"
- composer install $flags
script:
- ./vendor/bin/phpunit -c phpunit.xml --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls -v

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ivuorinen@me.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

92
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,92 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at ivuorinen@me.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

21
LICENSE Normal file
View File

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

View File

@@ -1,2 +1,38 @@
palette Palette [![Build Status](https://travis-ci.org/ivuorinen/palette.svg?branch=master)](https://travis-ci.org/ivuorinen/palette)
======= =======
Palette is a PHP class that takes your images and returns used colors, sorts them by usage and saves the results.
## Usage ##
After adding Palette to your ``composer.json`` file and installed to your vendor folder, you can use the class like this:
### With default settings ###
```php
$image = "example/example.jpg";
$palette = new \ivuorinen\Palette\Palette($image);
print_r($palette->colorsArray);
```
### With custom settings ###
```php
$palette = new \ivuorinen\Palette\Palette();
$palette->filename = "example/example.jpg"; // Our image
$palette->precision = 10; // Precision of color collection
$palette->returnColors = 10; // How many colors we want
$palette->destination = './data/' . md5($palette->filename) . '.json';
// Do the work (same as ``Palette::run()``)
$this->getPalette();
$this->save(); // Not needed, but caching results <3
// We now have ``./data/7233c3b944f5299c6983c77c94e75dce.json`` (if everything went smoothly)
// and we can test against it before running palette generation. Which you should do really.
print_r($palette->colorsArray);
```

View File

@@ -1,6 +1,12 @@
{ {
"name": "ivuorinen/palette", "name": "ivuorinen/palette",
"keywords": ["image", "colors", "palette", "psr-2"], "description": "Get most used colors from an image",
"keywords": [
"image",
"colors",
"palette",
"psr-2"
],
"homepage": "https://github.com/ivuorinen/palette", "homepage": "https://github.com/ivuorinen/palette",
"type": "library", "type": "library",
"license": "MIT", "license": "MIT",
@@ -14,12 +20,28 @@
"source": "https://github.com/ivuorinen/palette", "source": "https://github.com/ivuorinen/palette",
"issues": "https://github.com/ivuorinen/palette/issues" "issues": "https://github.com/ivuorinen/palette/issues"
}, },
"require": { "autoload": {
"php": ">=5.2.0" "psr-4": {
}, "ivuorinen\\Palette\\": "src/"
"extra": {
"branch-alias": {
"dev-master": "master"
} }
},
"autoload-dev": {
"psr-4": {
"ivuorinen\\Palette\\Tests\\": "tests/"
}
},
"require": {
"php": "^8.3",
"ext-gd": "*",
"ext-exif": "*"
},
"require-dev": {
"phpunit/phpunit": "^11",
"squizlabs/php_codesniffer": "^3"
},
"scripts": {
"test": "vendor/bin/phpunit",
"lint": "vendor/bin/phpcs",
"lint-fix": "vendor/bin/phpcbf"
} }
} }

View File

@@ -1,49 +0,0 @@
<?php
error_reporting(E_ALL);
require '../palette.php';
$testimages_full_process = array(
'example.gif',
'example.jpg',
'example.png'
);
$testimages_controlled_process = array(
'example.gif',
'example.jpg',
'example.png'
);
echo "<h1>Full process (creates palette, saves to same dir as palette.php)</h1>\n";
foreach ($testimages_full_process as $image) {
// Initiation with image triggers Palette::run()
$test = new \ivuorinen\Palette\Palette($image);
echo "<pre>"
. "Processing {$test->filename}\n"
. print_r($test->colorsArray, true)
. "</pre>\n";
}
echo "<h1>Controlled process</h1>\n";
foreach ($testimages_controlled_process as $image) {
$test = new \ivuorinen\Palette\Palette();
// We set the image, precision and amount of colors to return
$test->filename = $image; // Full, or relative path to our image
$test->precision = 10; // Bigger is faster, smaller returns more colors
$test->returnColors = 5; // How many colors we want in our array at most
// Get the colors as an array
$colors = $test->getPalette();
echo "<pre>"
. "Processing {$test->filename}\n"
. print_r($colors, true)
. "</pre>\n";
}
echo "<h1>This one fails</h1>\n";
$test = new \ivuorinen\Palette\Palette('/bin/sh');

View File

@@ -1,313 +0,0 @@
<?php
/**
* Palette
* Parses image and returns most used colors
*
* PHP version 5
*
* @category Default
* @package Palette
* @author Ismo Vuorinen <ivuorinen@me.com>
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @link https://github.com/ivuorinen/palette
*/
namespace ivuorinen\Palette;
/**
* Palette
*
* @category Default
* @package Palette
* @author Ismo Vuorinen <ivuorinen@me.com>
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @link https://github.com/ivuorinen/palette
* @version 1.0.0
* @example example/example.php Usage examples
**/
class Palette
{
/**
* Precision of palette, higher is more precise
* @var int
*/
public $precision;
/**
* Number of colors to return
* @var int
*/
public $returnColors;
/**
* Array of colors we use internally
* @var array
*/
public $colorsArray;
/**
* Full path to image file name
* @var string
*/
public $filename;
/**
* Destination for .json-file, full path and filename
*
* @var string
**/
public $destination;
/**
* Constructor
*
* If you have specified $filename, Palette::run() gets triggered.
* Palette::run() uses default parameters and processes given image
* and saves the result as an json-file to datafiles -folder.
*
* @param string $filename Full path to image
**/
public function __construct($filename = null)
{
// Define shortcut to directory separator
if (! defined('DS')) {
define("DS", DIRECTORY_SEPARATOR);
}
$this->precision = 10;
$this->returnColors = 10;
$this->colorsArray = array();
$this->filename = $filename;
$this->destination = dirname(__FILE__)
. DS . 'datafiles'
. DS . basename($filename) . '.json';
if (! empty($this->filename)) {
$this->run();
}
}
/**
* run the process
*
* if you want to change parameters you can init new Palette, then change
* settings and after that run the palette generation and saving
*
* @return bool Returns true always
*/
public function run()
{
if (empty($this->destination)) {
throw new Exception("No destination provided, can't save.");
}
$this->getPalette();
$this->save();
return true;
}
/**
* getPalette
* Returns colors used in an image specified in $filename
*
* @return array|bool If we get array that has colors return the array
**/
public function getPalette()
{
// We check for input
try {
if (empty($this->filename)) {
return false;
}
} catch (\Exception $e) {
throw new \Exception("Image was not provided");
}
// We check for readability
try {
if (! is_readable($this->filename)) {
throw new \Exception("Image {$this->filename} is not readable");
return false;
}
} catch (\Exception $e) {
user_error($e->getMessage(), E_USER_ERROR);
}
$this->colorsArray = $this->countColors();
if (! empty($this->colorsArray) and is_array($this->colorsArray)) {
return $this->colorsArray;
} else {
return false;
}
}
/**
* countColors returns an array of colors in the image
*
* @return array Array of colors sorted by times used
*/
private function countColors()
{
$this->precision = max(1, abs((int) $this->precision));
$colors = array();
// Test for image type
$img = $this->imageTypeToResource();
if (! $img && $img !== null) {
user_error("Unable to open: {$this->filename}", E_USER_ERROR);
return false;
}
// Get image size and check if it's really an image
$size = @getimagesize($this->filename);
if ($size === false) {
user_error("Unable to get image size data: {$this->filename}", E_USER_ERROR);
return false;
}
for ($x = 0; $x < $size[0]; $x += $this->precision) {
for ($y = 0; $y < $size[1]; $y += $this->precision) {
$thisColor = imagecolorat($img, $x, $y);
$rgb = imagecolorsforindex($img, $thisColor);
$red = round(round(($rgb['red'] / 0x33)) * 0x33);
$green = round(round(($rgb['green'] / 0x33)) * 0x33);
$blue = round(round(($rgb['blue'] / 0x33)) * 0x33);
$thisRGB = sprintf('%02X%02X%02X', $red, $green, $blue);
if (array_key_exists($thisRGB, $colors)) {
$colors[$thisRGB]++;
} else {
$colors[$thisRGB] = 1;
}
}
}
arsort($colors);
return array_slice($colors, 0, $this->returnColors, true);
}
/**
* save
* Get array of colors, json_encode it and save to destination
*
* @param string $destination Where to save json-file
*
* @return false
**/
public function save()
{
try {
if (empty($this->destination)) {
throw new \Exception("No destination given for save");
}
} catch (\Exception $e) {
user_error($e->getMessage(), E_USER_ERROR);
}
try {
$this->checkDestination();
} catch (\Exception $e) {
user_error($e->getMessage(), E_USER_ERROR);
}
try {
if (empty($this->colorsArray)) {
throw new \Exception("Couldn't detect colors from image: {$this->filename}");
}
} catch (\Exception $e) {
user_error($e->getMessage(), E_USER_ERROR);
}
// Encode for saving
$colorsData = json_encode($this->colorsArray);
// Save and return the result of save operation
file_put_contents($this->destination, $colorsData);
if (is_readable($this->destination)) {
return true;
} else {
return false;
}
}
/**
* imageTypeToResource returns image resource
*
* Function takes $this->filename and returns
* imagecreatefrom{gif|jpeg|png} for further processing
*
* @return resource Image resource based on content
*/
private function imageTypeToResource()
{
$type = exif_imagetype($this->filename);
switch ($type) {
case '1': // IMAGETYPE_GIF
$img = @imagecreatefromgif($this->filename);
break;
case '2': // IMAGETYPE_JPEG
$img = @imagecreatefromjpeg($this->filename);
break;
case '3': // IMAGETYPE_PNG
$img = @imagecreatefrompng($this->filename);
break;
default:
$image_type_code = image_type_to_mime_type($type);
user_error("Unknown image type: {$image_type_code} ({$type}): {$this->filename}");
return false;
break;
}
return $img;
}
/**
* checkDestination tries to make sure you have directory to save to
*
* Tests done:
* - Does the destination folder exists?
* - Is it writable?
* - Can it be made writable?
*
* @return bool|exception True or false, with exceptions
*/
private function checkDestination()
{
$destination_dir = dirname($this->destination);
// Test if we have destination directory
try {
if (! file_exists($destination_dir)) {
mkdir($destination_dir, 0755);
}
if (! file_exists($destination_dir)) {
throw new \Exception("Couldn't create missing destination dir: {$destination_dir}");
}
} catch (Exception $e) {
user_error($e->getMessage());
return false;
}
// Test if we can write to it
try {
if (! is_writable($destination_dir)) {
chmod($destination_dir, 0755);
} else {
return true;
}
if (! is_writable($destination_dir)) {
throw new \Exception("Destination directory not writable: {$destination_dir}");
}
} catch (\Exception $e) {
user_error($e->getMessage());
}
return true;
}
}

21
phpcs.xml.dist Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<ruleset
name="PHP_CodeSniffer"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/squizlabs/PHP_CodeSniffer/master/phpcs.xsd"
>
<description>PHP Code Sniffer configuration file.</description>
<file>.</file>
<arg name="basepath" value="."/>
<arg name="colors"/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="10"/>
<!-- base rule: set to PSR12-->
<!-- https://www.php-fig.org/psr/psr-12/ -->
<rule ref="PSR12">
<exclude-pattern>*/vendor/*</exclude-pattern>
</rule>
</ruleset>

13
phpunit.xml Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" colors="true" processIsolation="false" stopOnFailure="false" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>./src/</directory>
</include>
</source>
</phpunit>

View File

@@ -0,0 +1,14 @@
<?php
namespace ivuorinen\Palette\Exceptions;
use ErrorException;
use Throwable;
class GenericException extends ErrorException
{
public function __construct(string $message, int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

287
src/Palette.php Normal file
View File

@@ -0,0 +1,287 @@
<?php
/**
* Palette
* Parses image and returns most used colors
*
* MIT License
* ===========
*
* Copyright (c) 2013 Ismo Vuorinen <ivuorinen@me.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* @author Ismo Vuorinen <ivuorinen@me.com>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @copyright 2013 Ismo Vuorinen
* @link https://github.com/ivuorinen/palette
*/
namespace ivuorinen\Palette;
use GdImage;
use ivuorinen\Palette\Exceptions\GenericException;
/**
* Palette
*
* @author Ismo Vuorinen <ivuorinen@me.com>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @link https://github.com/ivuorinen/palette
**/
class Palette
{
/** @var int Precision of palette, higher is more precise */
public int $precision;
/** @var int Number of colors to return */
public int $returnColors;
/** @var array Array of colors we use internally */
public array $colorsArray;
/** @var string Full path to image file name */
public ?string $filename;
/** @var string Destination for .json-file, full path and filename */
public string $destination;
/**
* Constructor
*
* If you have specified $filename, Palette::run() gets triggered.
* Palette::run() uses default parameters and processes given image
* and saves the result as an json-file to datafiles -folder.
*
* @param string $filename Full path to image
*
* @throws \Exception
*/
public function __construct(string $filename)
{
// Define shortcut to directory separator
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
$this->precision = 10;
$this->returnColors = 10;
$this->colorsArray = array();
$this->filename = $filename;
$this->destination = __DIR__
. DS . 'datafiles'
. DS . basename($filename) . '.json';
if (!empty($this->filename)) {
$this->run();
}
}
/**
* run the process
*
* if you want to change parameters you can init new Palette, then change
* settings and after that run the palette generation and saving
*
* @return bool Returns true always
* @throws \Exception
*/
public function run(): bool
{
if (empty($this->destination)) {
throw new GenericException("No destination provided, can't save.");
}
$this->getPalette();
$this->save();
return true;
}
/**
* getPalette
* Returns colors used in an image specified in $filename
*
* @return array|bool If we get array that has colors return the array
* @throws \Exception
*/
public function getPalette(): array|bool
{
// We check for input
if (empty($this->filename)) {
throw new GenericException('Image was not provided');
}
// We check for readability
if (!is_readable($this->filename)) {
throw new GenericException("Image {$this->filename} is not readable");
}
$this->colorsArray = $this->countColors();
if (!empty($this->colorsArray) && is_array($this->colorsArray)) {
return $this->colorsArray;
}
return false;
}
/**
* countColors returns an array of colors in the image
*
* @return array Array of colors sorted by times used
* @throws GenericException
*/
private function countColors(): array
{
$this->precision = max(1, abs($this->precision));
$colors = array();
// Test for image type
$img = $this->imageTypeToResource();
// Get image size and check if it's really an image
$size = @getimagesize($this->filename);
if ($size === false) {
throw new GenericException("Unable to get image size data: {$this->filename}");
}
// This is pretty naive approach,
// but looping through the image is the only way I thought of
for ($x = 0; $x < $size[0]; $x += $this->precision) {
for ($y = 0; $y < $size[1]; $y += $this->precision) {
$thisColor = imagecolorat($img, $x, $y);
$rgb = imagecolorsforindex($img, $thisColor);
$red = round(round($rgb['red'] / 0x33) * 0x33);
$green = round(round($rgb['green'] / 0x33) * 0x33);
$blue = round(round($rgb['blue'] / 0x33) * 0x33);
$thisRGB = sprintf('%02X%02X%02X', $red, $green, $blue);
if (array_key_exists($thisRGB, $colors)) {
$colors[$thisRGB]++;
} else {
$colors[$thisRGB] = 1;
}
}
}
arsort($colors);
return array_slice($colors, 0, $this->returnColors, true);
}
/**
* save
* Get array of colors, json_encode it and save to destination
*
* @return bool
* @throws \Exception
*/
public function save(): bool
{
// Check for destination
if (empty($this->destination)) {
throw new \InvalidArgumentException('No destination given for save');
}
// Check destination for write permissions
$this->checkDestination();
// Check for data we should write
if (empty($this->colorsArray)) {
throw new GenericException("Couldn't detect colors from image: {$this->filename}");
}
// Encode for saving
$colorsData = json_encode($this->colorsArray, JSON_THROW_ON_ERROR);
// Save and return the result of save operation
file_put_contents($this->destination, $colorsData);
return is_readable($this->destination);
}
/**
* imageTypeToResource returns image resource
*
* Function takes $this->filename and returns
* imagecreatefrom{gif|jpeg|png} for further processing
*
* @return GdImage Image resource based on content
* @throws GenericException
*/
private function imageTypeToResource(): GdImage
{
try {
if (filesize($this->filename) < 12) {
throw new GenericException('File size smaller than 12');
}
$type = exif_imagetype($this->filename);
} catch (\Exception $e) {
throw new GenericException($e->getMessage());
}
switch ($type) {
case '1': // IMAGETYPE_GIF
return @\imagecreatefromgif($this->filename);
case '2': // IMAGETYPE_JPEG
return @\imagecreatefromjpeg($this->filename);
case '3': // IMAGETYPE_PNG
return @\imagecreatefrompng($this->filename);
default:
$image_type_code = image_type_to_mime_type($type);
throw new GenericException("Unknown image type: {$image_type_code} ({$type}): {$this->filename}");
}
}
/**
* checkDestination tries to make sure you have directory to save to
*
* Tests done:
* - Does the destination folder exists?
* - Is it writable?
* - Can it be made writable?
*
* @return boolean True or false, with exceptions
* @throws \Exception
*/
private function checkDestination(): bool
{
$destination_dir = dirname($this->destination);
// Test if we have destination directory
if (!@mkdir($destination_dir, 0755) && !is_dir($destination_dir)) {
throw new GenericException("Couldn't create missing destination dir: {$destination_dir}");
}
// Test if we can write to it
if (is_writable($destination_dir)) {
return true;
}
chmod($destination_dir, 0755);
if (!is_writable($destination_dir)) {
throw new GenericException("Destination directory not writable: {$destination_dir}");
}
return true;
}
}

69
tests/PaletteTest.php Normal file
View File

@@ -0,0 +1,69 @@
<?php
namespace ivuorinen\Palette\Tests;
use PHPUnit\Framework\TestCase;
class PaletteTest extends TestCase
{
public function testClassIsFoundAndHasDefaultAttributes(): void
{
$palette = new \ivuorinen\Palette\Palette('');
$this->assertInstanceOf('ivuorinen\Palette\Palette', $palette);
$this->assertIsInt($palette->precision);
$this->assertIsInt($palette->returnColors);
$this->assertIsArray($palette->colorsArray);
$this->assertIsString($palette->filename);
$this->assertIsString($palette->destination);
}
public function testKnownImagesWithOneColor(): void
{
$location = __DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR;
$images = ['black.png' => '000000', 'red.png' => 'CC3333'];
foreach ($images as $imageFile => $hex) {
$image = $location . $imageFile;
$this->assertFileExists($image);
$palette = new \ivuorinen\Palette\Palette($image);
$this->assertCount(1, $palette->colorsArray);
$this->assertArrayHasKey($hex, $palette->colorsArray);
$this->assertEquals($image, $palette->filename);
}
}
public function testKnownImagesWithManyColors(): void
{
$location = __DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR;
$images = ['example.gif', 'example.jpg', 'example.png'];
foreach ($images as $imageFile) {
$image = $location . $imageFile;
$this->assertFileExists($image);
$palette = new \ivuorinen\Palette\Palette($image);
$this->assertCount(10, $palette->colorsArray);
$this->assertEquals($image, $palette->filename);
}
}
public function testFailureNoImage(): void
{
$palette = new \ivuorinen\Palette\Palette('');
$this->expectException(\ErrorException::class);
$this->expectExceptionMessage('Image was not provided');
$palette->getPalette();
}
public function testFailureNotAnImage(): void
{
$this->expectException(\ErrorException::class);
$palette = new \ivuorinen\Palette\Palette('NOT_HERE');
$this->expectExceptionMessage('Image ' . $palette->filename . ' is not readable');
$palette->getPalette();
}
}

BIN
tests/assets/black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

BIN
tests/assets/red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB