├── .editorconfig
├── .github
├── CODEOWNERS
├── FUNDING.yml
├── boring-cyborg.yml
├── renovate.json
├── settings.yml
└── workflows
│ ├── ci.yml
│ └── release-managment.yaml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── composer.json
├── composer.lock
├── etc
└── qa
│ ├── composer-require-checker.json
│ ├── phpcs.xml
│ ├── phpstan.neon
│ ├── phpunit.xml
│ └── psalm.xml
├── infection.json.dist
├── src
├── Lister.php
├── functions.php
└── functions_include.php
├── test-app
├── Commands
│ └── AwesomesauceCommand.php
├── Foo
│ └── Bar
│ │ └── BarAndFoo.php
└── Handlers
│ └── AwesomesauceHandler.php
├── test-classes
├── InstantiatableClass.php
└── NonInstantiatableClass.php
└── var
└── .gitkeep
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.json]
11 | indent_size = 2
12 |
13 | [*.yml]
14 | indent_size = 2
15 |
16 | [*.yaml]
17 | indent_size = 2
18 |
19 | [Makefile]
20 | indent_style = tab
21 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @WyriHaximus
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: WyriHaximus
2 |
--------------------------------------------------------------------------------
/.github/boring-cyborg.yml:
--------------------------------------------------------------------------------
1 | labelPRBasedOnFilePath:
2 | "Documentation 📚":
3 | - README.md
4 | - CONTRIBUTING.md
5 | "Dependencies 📦":
6 | - Dockerfile*
7 | - composer.*
8 | - package.json
9 | - package-lock.json
10 | - yarn.lock
11 | "Docker 🐳":
12 | - Dockerfile*
13 | - .docker/**/*
14 | "Image 🖼":
15 | - "**/*.gif"
16 | - "**/*.jpg"
17 | - "**/*.jpeg"
18 | - "**/*.png"
19 | - "**/*.webp"
20 | "CSS 👩🎨":
21 | - "**/*.css"
22 | "HTML 👷♀️":
23 | - "**/*.htm"
24 | - "**/*.html"
25 | "NEON 🦹♂️":
26 | - "**/*.neon"
27 | "MarkDown 📝":
28 | - "**/*.md"
29 | "YAML 🍄":
30 | - "**/*.yml"
31 | - "**/*.yaml"
32 | "JSON 👨💼":
33 | - "**/*.json"
34 | "Go 🐹":
35 | - "**/*.go"
36 | "JavaScript 🦏":
37 | - "**/*.js"
38 | - package.json
39 | - package-lock.json
40 | - yarn.lock
41 | "PHP 🐘":
42 | - "**/*.php"
43 | - composer.*
44 | "Configuration ⚙":
45 | - .github/*
46 | "CI 🚧":
47 | - .github/workflows/*
48 | - .scrutinizer.yml
49 | "Templates 🌲":
50 | - "**/*.twig"
51 | - "**/*.tpl"
52 | "Helm ☸":
53 | - .helm/**/*
54 | "Tests 🧪":
55 | - tests/**/*
56 | "Source 🔮":
57 | - src/**/*
58 |
59 | labelerFlags:
60 | labelOnPRUpdates: true
61 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "github>WyriHaximus/renovate-config:php-package"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.github/settings.yml:
--------------------------------------------------------------------------------
1 | repository:
2 | private: false
3 | has_issues: true
4 | has_wiki: false
5 | has_downloads: true
6 | default_branch: master
7 | allow_squash_merge: false
8 | allow_merge_commit: true
9 | allow_rebase_merge: false
10 |
11 | # Labels: define labels for Issues and Pull Requests
12 | labels:
13 | - name: "Dependencies 📦"
14 | color: 0025ff
15 | description: "Pull requests that update a dependency file"
16 | - name: "Image 🖼"
17 | color: 00ffff
18 | - name: "HTML 👷♀️"
19 | color: ffffff
20 | - name: "CSS 👩🎨"
21 | color: b3b3b3
22 | - name: "JavaScript 🦏"
23 | color: ffff00
24 | - name: "Go 🐹"
25 | color: 00ADD8
26 | - name: "JSON 👨💼"
27 | color: 00ADD8
28 | - name: "NEON 🦹♂️"
29 | color: CE3262
30 | - name: "MarkDown 📝"
31 | color: 000000
32 | - name: "YAML 🍄"
33 | color: ff1aff
34 | - name: "Templates 🌲"
35 | color: 009933
36 | - name: "Helm ☸"
37 | color: 091C84
38 | - name: "Tests 🧪"
39 | color: ffe6e6
40 | - name: "Source 🔮"
41 | color: e6ffe6
42 | - name: "Configuration ⚙"
43 | color: b3b3cc
44 | - name: "PHP 🐘"
45 | color: 8892BF
46 | description: "Hypertext Pre Processor"
47 | - name: "Docker 🐳"
48 | color: 0db7ed
49 | description: "Pull requests that relate to Docker"
50 | - name: "CI 🚧"
51 | color: ffff00
52 | - name: "Feature 🏗"
53 | color: 66ff99
54 | - name: "Documentation 📚"
55 | color: 6666ff
56 | - name: "Security 🕵️♀️"
57 | color: ff0000
58 | - name: "Hacktoberfest 🎃"
59 | color: 152347
60 | - name: "Bug 🐞"
61 | color: d73a4a
62 | description: "Something isn't working"
63 | oldname: bug
64 | - name: "Duplicate ♊"
65 | color: cfd3d7
66 | description: "This issue or pull request already exists"
67 | oldname: duplicate
68 | - name: "Enhancement ✨"
69 | color: a2eeef
70 | description: "New feature or request"
71 | oldname: enhancement
72 | - name: "Good First Issue"
73 | color: 7057ff
74 | description: "Good for newcomers"
75 | oldname: "good first issue"
76 | - name: "Help Wanted"
77 | color: 008672
78 | description: "Extra attention is needed"
79 | oldname: "help wanted"
80 | - name: Invalid
81 | color: e4e669
82 | description: "This doesn't seem right"
83 | oldname: invalid
84 | - name: "Question ❓"
85 | color: d876e3
86 | description: "Further information is requested"
87 | oldname: question
88 | - name: "Will not be fixed 🛑"
89 | color: ffffff
90 | description: "This will not be worked on"
91 | oldname: wontfix
92 | - name: "Sponsor Request ❤️"
93 | color: fedbf0
94 | description: "Issue/PR opened by sponsor"
95 |
96 | branches:
97 | - name: master
98 | protection:
99 | required_pull_request_reviews:
100 | required_approving_review_count: 1
101 | dismiss_stale_reviews: true
102 | require_code_owner_reviews: true
103 | # Required. Require status checks to pass before merging. Set to null to disable
104 | required_status_checks:
105 | # Required. Require branches to be up to date before merging.
106 | strict: true
107 | # Required. The list of status checks to require in order to merge into this branch
108 | contexts: []
109 | # Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable.
110 | enforce_admins: true
111 | # Required. Restrict who can push to this branch. Team and user restrictions are only available for organization-owned repositories. Set to null to disable.
112 | restrictions:
113 | apps: []
114 | users: []
115 | teams: []
116 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 | on:
3 | push:
4 | branches:
5 | - 'main'
6 | - 'master'
7 | - 'refs/heads/v[0-9]+.[0-9]+.[0-9]+'
8 | pull_request:
9 | ## This workflow needs the `pull-request` permissions to work for the package diffing
10 | ## Refs: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions
11 | permissions:
12 | pull-requests: write
13 | contents: read
14 | jobs:
15 | ci:
16 | name: Continuous Integration
17 | uses: WyriHaximus/github-workflows/.github/workflows/package.yaml@main
18 |
--------------------------------------------------------------------------------
/.github/workflows/release-managment.yaml:
--------------------------------------------------------------------------------
1 | name: Release Management
2 | on:
3 | pull_request:
4 | types:
5 | - opened
6 | - labeled
7 | - unlabeled
8 | - synchronize
9 | - reopened
10 | milestone:
11 | types:
12 | - closed
13 | permissions:
14 | contents: write
15 | issues: write
16 | pull-requests: write
17 | jobs:
18 | release-managment:
19 | name: Create Release
20 | uses: WyriHaximus/github-workflows/.github/workflows/package-release-managment.yaml@main
21 | with:
22 | milestone: ${{ github.event.milestone.title }}
23 | description: ${{ github.event.milestone.title }}
24 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Pull requests are highly appreciated. Here's a quick guide.
4 |
5 | Fork, then clone the repo:
6 |
7 | git clone git@github.com:your-username/php-tactician-command-handler-mapper.git
8 |
9 | Set up your machine:
10 |
11 | composer install
12 |
13 | Make sure the tests pass:
14 |
15 | make unit
16 |
17 | Make your change. Add tests for your change. Make the tests pass:
18 |
19 | make unit
20 |
21 | Before committing and submitting your pull request make sure it passes PSR2 coding style, unit tests pass and pass on all supported PHP versions:
22 |
23 | make contrib
24 |
25 | Push to your fork and [submit a pull request][pr].
26 |
27 | [pr]: https://help.github.com/articles/creating-a-pull-request/
28 |
29 | At this point you're waiting on me. I like to at least comment on pull requests
30 | within a day or two. I may suggest some changes or improvements or alternatives.
31 |
32 | Some things that will increase the chance that your pull request is accepted:
33 |
34 | * Write tests.
35 | * Follow PSR2 (travis will also check for this).
36 | * Write a [good commit message][commit].
37 |
38 | [commit]: http://chris.beams.io/posts/git-commit/
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Cees-Jan Kiewiet
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.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # set all to phony
2 | SHELL=bash
3 |
4 | .PHONY: *
5 |
6 | COMPOSER_SHOW_EXTENSION_LIST=$(shell composer show -t | grep -o "\-\-\(ext-\).\+" | sort | uniq | cut -d- -f4- | tr -d '\n' | grep . | sed '/^$$/d' | xargs | sed -e 's/ /, /g' | tr -cd '[:alnum:],' | sed 's/.$$//')
7 | SLIM_DOCKER_IMAGE=$(shell php -r 'echo count(array_intersect(["gd", "vips"], explode(",", "${COMPOSER_SHOW_EXTENSION_LIST}"))) > 0 ? "" : "-slim";')
8 | COMPOSER_CACHE_DIR=$(shell composer config --global cache-dir -q || echo ${HOME}/.composer-php/cache)
9 | PHP_VERSION:=$(shell docker run --rm -v "`pwd`:`pwd`" jess/jq jq -r -c '.config.platform.php' "`pwd`/composer.json" | php -r "echo str_replace('|', '.', explode('.', implode('|', explode('.', stream_get_contents(STDIN), 2)), 2)[0]);")
10 | COMPOSER_CONTAINER_CACHE_DIR=$(shell docker run --rm -it "ghcr.io/wyrihaximusnet/php:${PHP_VERSION}-nts-alpine${SLIM_DOCKER_IMAGE}-dev" composer config --global cache-dir -q || echo ${HOME}/.composer-php/cache)
11 |
12 | ifneq ("$(wildcard /.you-are-in-a-wyrihaximus.net-php-docker-image)","")
13 | IN_DOCKER=TRUE
14 | else
15 | IN_DOCKER=FALSE
16 | endif
17 |
18 | ifeq ("$(IN_DOCKER)","TRUE")
19 | DOCKER_RUN:=
20 | else
21 | DOCKER_RUN:=docker run --rm -it \
22 | -v "`pwd`:`pwd`" \
23 | -v "${COMPOSER_CACHE_DIR}:${COMPOSER_CONTAINER_CACHE_DIR}" \
24 | -w "`pwd`" \
25 | "ghcr.io/wyrihaximusnet/php:${PHP_VERSION}-nts-alpine${SLIM_DOCKER_IMAGE}-dev"
26 | endif
27 |
28 | ifneq (,$(findstring icrosoft,$(shell cat /proc/version)))
29 | THREADS=1
30 | else
31 | THREADS=$(shell nproc)
32 | endif
33 |
34 | all: ## Runs everything ###
35 | @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "###" | awk 'BEGIN {FS = ":.*?## "}; {printf "%s\n", $$1}' | xargs --open-tty $(MAKE)
36 |
37 | syntax-php: ## Lint PHP syntax
38 | $(DOCKER_RUN) vendor/bin/parallel-lint --exclude vendor .
39 |
40 | cs-fix: ## Fix any automatically fixable code style issues
41 | $(DOCKER_RUN) vendor/bin/phpcbf --parallel=$(THREADS) --cache=./var/.phpcs.cache.json --standard=./etc/qa/phpcs.xml || $(DOCKER_RUN) vendor/bin/phpcbf --parallel=$(THREADS) --cache=./var/.phpcs.cache.json --standard=./etc/qa/phpcs.xml || $(DOCKER_RUN) vendor/bin/phpcbf --parallel=$(THREADS) --cache=./var/.phpcs.cache.json --standard=./etc/qa/phpcs.xml -vvvv
42 |
43 | cs: ## Check the code for code style issues
44 | $(DOCKER_RUN) vendor/bin/phpcs --parallel=$(THREADS) --cache=./var/.phpcs.cache.json --standard=./etc/qa/phpcs.xml
45 |
46 | stan: ## Run static analysis (PHPStan)
47 | $(DOCKER_RUN) vendor/bin/phpstan analyse src tests --level max --ansi -c ./etc/qa/phpstan.neon
48 |
49 | psalm: ## Run static analysis (Psalm)
50 | $(DOCKER_RUN) vendor/bin/psalm --threads=$(THREADS) --shepherd --stats --config=./etc/qa/psalm.xml
51 |
52 | unit-testing: ## Run tests
53 | $(DOCKER_RUN) vendor/bin/phpunit --colors=always -c ./etc/qa/phpunit.xml --coverage-text --coverage-html ./var/tests-unit-coverage-html --coverage-clover ./var/tests-unit-clover-coverage.xml
54 | $(DOCKER_RUN) test -n "$(COVERALLS_REPO_TOKEN)" && test -n "$(COVERALLS_RUN_LOCALLY)" && test -f ./var/tests-unit-clover-coverage.xml && vendor/bin/php-coveralls -v --coverage_clover ./build/logs/clover.xml --json_path ./var/tests-unit-clover-coverage-upload.json || true
55 |
56 | unit-testing-raw: ## Run tests ###
57 | php vendor/phpunit/phpunit/phpunit --colors=always -c ./etc/qa/phpunit.xml --coverage-text --coverage-html ./var/tests-unit-coverage-html --coverage-clover ./var/tests-unit-clover-coverage.xml
58 | test -n "$(COVERALLS_REPO_TOKEN)" && test -n "$(COVERALLS_RUN_LOCALLY)" && test -f ./var/tests-unit-clover-coverage.xml && ./vendor/bin/php-coveralls -v --coverage_clover ./build/logs/clover.xml --json_path ./var/tests-unit-clover-coverage-upload.json || true
59 |
60 | mutation-testing: ## Run mutation testing
61 | $(DOCKER_RUN) vendor/bin/roave-infection-static-analysis-plugin --ansi --log-verbosity=all --threads=$(THREADS) --psalm-config etc/qa/psalm.xml || (cat ./var/infection.log && false)
62 |
63 | mutation-testing-raw: ## Run mutation testing ###
64 | php vendor/roave/infection-static-analysis-plugin/bin/roave-infection-static-analysis-plugin --ansi --log-verbosity=all --threads=$(THREADS) --psalm-config etc/qa/psalm.xml || (cat ./var/infection.log && false)
65 |
66 | composer-require-checker: ## Ensure we require every package used in this package directly
67 | $(DOCKER_RUN) vendor/bin/composer-require-checker --ignore-parse-errors --ansi -vvv --config-file=./etc/qa/composer-require-checker.json
68 |
69 | composer-unused: ## Ensure we don't require any package we don't use in this package directly
70 | $(DOCKER_RUN) vendor/bin/composer-unused --ansi
71 |
72 | composer-install: ## Install dependencies
73 | $(DOCKER_RUN) composer install --no-progress --ansi --no-interaction --prefer-dist -o
74 |
75 | backward-compatibility-check: ## Check code for backwards incompatible changes
76 | $(MAKE) backward-compatibility-check-raw || true
77 |
78 | backward-compatibility-check-raw: ## Check code for backwards incompatible changes, doesn't ignore the failure ###
79 | $(DOCKER_RUN) vendor/bin/roave-backward-compatibility-check
80 |
81 | shell: ## Provides Shell access in the expected environment ###
82 | $(DOCKER_RUN) ash
83 |
84 | task-list-ci: ## CI: Generate a JSON array of jobs to run, matches the commands run when running `make (|all)` ###
85 | @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "###" | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%s\n", $$1}' | jq --raw-input --slurp -c 'split("\n")| .[0:-1]'
86 |
87 | help: ## Show this help ###
88 | @printf "\033[33mUsage:\033[0m\n make [target]\n\n\033[33mTargets:\033[0m\n"
89 | @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[32m%-32s\033[0m %s\n", $$1, $$2}' | tr -d '#'
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # List all PHP classes in directories and files
2 |
3 | [](https://github.com/WyriHaximus/php-list-classes-in-directory/actions/workflows/ci.yml)
4 | [](https://packagist.org/packages/wyrihaximus/list-classes-in-directory)
5 | [](https://packagist.org/packages/wyrihaximus/list-classes-in-directory/stats)
6 | [](https://scrutinizer-ci.com/g/wyrihaximus/php-list-classes-in-directory/?branch=master)
7 | [](https://packagist.org/packages/wyrihaximus/list-classes-in-directory)
8 |
9 |
10 | # Install
11 |
12 | To install via [Composer](http://getcomposer.org/), use the command below, it will automatically detect the latest version and bind it with `^`.
13 |
14 | ```
15 | composer require wyrihaximus/list-classes-in-directory
16 | ```
17 |
18 | # Usage
19 |
20 | #### get a list of classes from multiple directories.
21 |
22 | ```php
23 | use function WyriHaximus\Lister;
24 |
25 | // $classes now contains a list of full qualified class names from 'src/' and 'tests/'
26 | $classes = Lister::classesInDirectories(
27 | __DIR__ . '/src',
28 | __DIR__ . '/tests'
29 | );
30 |
31 |
32 | // use listInstantiatableClassesInDirectories() or listNonInstantiatableClassesInDirectories() to only consider classes that can actually be instantiated, or not.
33 | $instantiatableClasses = Lister::instantiatableClassesInDirectory(
34 | __DIR__ . '/src',
35 | __DIR__ . '/tests'
36 | );
37 | $nonInstantiatableClasses = Lister::nonInstantiatableClassesInDirectory(
38 | __DIR__ . '/src',
39 | __DIR__ . '/tests'
40 | );
41 | ```
42 |
43 | #### get a list of classes from one directory.
44 | ```php
45 | use function WyriHaximus\Lister;
46 |
47 | // $classes now contains a list of full qualified class names from __DIR__
48 | $classes = Lister::classesInDirectory(__DIR__);
49 |
50 | // use listInstantiatableClassesInDirectory() or listNonInstantiatableClassesInDirectory() to only consider classes that can actually be instantiated, or not.
51 | $instantiatableClasses = Lister::instantiatableClassesInDirectory(__DIR__);
52 | $nonInstantiatableClasses = Lister::nonInstantiatableClassesInDirectory(__DIR__);
53 |
54 | ```
55 |
56 | #### get a list of classes from multiple files.
57 | ```php
58 | use function WyriHaximus\Lister;
59 |
60 | // $classes now contains a list of full qualified class names from 'Bar.php' and 'Foo.php'
61 | $classes = Lister::classesInFiles(
62 | __DIR__ . '/Bar.php',
63 | __DIR__ . '/Foo.php'
64 | );
65 | ```
66 |
67 | #### get a list of classes from one file.
68 | ```php
69 | use function WyriHaximus\Lister;
70 |
71 | // $classes now contains a list of full qualified class names from 'Foo.php'
72 | $classes = Lister::classesInFile(__DIR__.'/Foo.php');
73 | ```
74 |
75 | # Acknowledgement
76 |
77 | This package is a shorthand function for using [`better reflection`](https://github.com/Roave/BetterReflection/) and is based on one of the [`examples`](https://github.com/Roave/BetterReflection/blob/396a07c9d276cb9ffba581b24b2dadbb542d542e/demo/parsing-whole-directory/example2.php).
78 |
79 | # License
80 |
81 | The MIT License (MIT)
82 |
83 | Copyright (c) 2024 Cees-Jan Kiewiet
84 |
85 | Permission is hereby granted, free of charge, to any person obtaining a copy
86 | of this software and associated documentation files (the "Software"), to deal
87 | in the Software without restriction, including without limitation the rights
88 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
89 | copies of the Software, and to permit persons to whom the Software is
90 | furnished to do so, subject to the following conditions:
91 |
92 | The above copyright notice and this permission notice shall be included in all
93 | copies or substantial portions of the Software.
94 |
95 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
96 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
97 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
98 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
99 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
100 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
101 | SOFTWARE.
102 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wyrihaximus/list-classes-in-directory",
3 | "description": "Allows you to list full qualified class names in directories and files.",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Cees-Jan Kiewiet",
8 | "email": "ceesjank@gmail.com"
9 | }
10 | ],
11 | "require": {
12 | "php": "^8.2",
13 | "roave/better-reflection": "^6.5"
14 | },
15 | "require-dev": {
16 | "wyrihaximus/test-utilities": "^6.0.9"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "WyriHaximus\\": "src"
21 | },
22 | "files": [
23 | "src/functions_include.php"
24 | ]
25 | },
26 | "autoload-dev": {
27 | "psr-4": {
28 | "Test\\App\\": "test-app/",
29 | "Test\\Classes\\": "test-classes/",
30 | "WyriHaximus\\Tests\\": "tests/"
31 | },
32 | "files": [
33 | "test-app/Foo/Bar/BarAndFoo.php"
34 | ]
35 | },
36 | "config": {
37 | "allow-plugins": {
38 | "composer/package-versions-deprecated": true,
39 | "dealerdirect/phpcodesniffer-composer-installer": true,
40 | "ergebnis/composer-normalize": true,
41 | "icanhazstring/composer-unused": true,
42 | "infection/extension-installer": true
43 | },
44 | "platform": {
45 | "php": "8.2.13"
46 | },
47 | "sort-packages": true
48 | },
49 | "scripts": {
50 | "post-install-cmd": [
51 | "composer normalize"
52 | ],
53 | "post-update-cmd": [
54 | "composer normalize"
55 | ]
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/etc/qa/composer-require-checker.json:
--------------------------------------------------------------------------------
1 | {
2 | "symbol-whitelist" : [
3 | "null", "true", "false",
4 | "static", "self", "parent",
5 | "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object",
6 | "Safe\\date", "WyriHaximus\\Constants\\Boolean\\FALSE_", "WyriHaximus\\Constants\\Boolean\\TRUE_",
7 | "WyriHaximus\\Constants\\Numeric\\ONE", "WyriHaximus\\Constants\\Numeric\\ZERO",
8 | "ApiClients\\Tools\\Rx\\observableFromArray"
9 | ],
10 | "php-core-extensions" : [
11 | "Core",
12 | "date",
13 | "pcre",
14 | "Phar",
15 | "Reflection",
16 | "SPL",
17 | "standard"
18 | ],
19 | "scan-files" : []
20 | }
21 |
--------------------------------------------------------------------------------
/etc/qa/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ../../src/
10 | ../../tests/
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/etc/qa/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | ergebnis:
3 | noExtends:
4 | classesAllowedToBeExtended:
5 | - FilterIterator
6 |
7 | includes:
8 | - ../../vendor/wyrihaximus/test-utilities/rules.neon
9 |
--------------------------------------------------------------------------------
/etc/qa/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ../../tests/
6 |
7 |
8 |
9 |
10 | ../../src/
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/etc/qa/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "timeout": 120,
3 | "source": {
4 | "directories": [
5 | "src"
6 | ]
7 | },
8 | "logs": {
9 | "text": "./var/infection.log",
10 | "summary": "./var/infection-summary.log",
11 | "json": "./var/infection.json",
12 | "perMutator": "./var/infection-per-mutator.md"
13 | },
14 | "mutators": {
15 | "@default": true,
16 | "Identical": false
17 | },
18 | "phpUnit": {
19 | "configDir": "./etc/qa/"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Lister.php:
--------------------------------------------------------------------------------
1 |
29 | */
30 | public static function classesInDirectories(string ...$directories): iterable
31 | {
32 | $sourceLocator = new AggregateSourceLocator([
33 | new DirectoriesSourceLocator(
34 | array_values($directories),
35 | (new BetterReflection())->astLocator(),
36 | ),
37 | // ↓ required to autoload parent classes/interface from another directory than /src (e.g. /vendor)
38 | new AutoloadSourceLocator((new BetterReflection())->astLocator()),
39 | ]);
40 |
41 | foreach (self::classesInSourceLocator($sourceLocator) as $class) {
42 | yield $class->getName();
43 | }
44 | }
45 |
46 | /**
47 | * get a list of all classes in the given directory.
48 | *
49 | * @return iterable
50 | */
51 | public static function classesInDirectory(string $directory): iterable
52 | {
53 | yield from self::classesInDirectories($directory);
54 | }
55 |
56 | /**
57 | * get a list of all classes in the given file.
58 | *
59 | * @param non-empty-string $file
60 | *
61 | * @return iterable
62 | */
63 | public static function classesInFile(string $file): iterable
64 | {
65 | $sourceLocator = new AggregateSourceLocator([
66 | new SingleFileSourceLocator(
67 | $file,
68 | (new BetterReflection())->astLocator(),
69 | ),
70 | // ↓ required to autoload parent classes/interface from another directory (e.g. /vendor)
71 | new AutoloadSourceLocator((new BetterReflection())->astLocator()),
72 | ]);
73 |
74 | foreach (self::classesInSourceLocator($sourceLocator) as $class) {
75 | yield $class->getName();
76 | }
77 | }
78 |
79 | /**
80 | * get a list of all classes in the given files.
81 | *
82 | * @param non-empty-string ...$files
83 | *
84 | * @return iterable
85 | */
86 | public static function classesInFiles(string ...$files): iterable
87 | {
88 | foreach ($files as $file) {
89 | foreach (self::classesInFile($file) as $class) {
90 | yield $class;
91 | }
92 | }
93 | }
94 |
95 | /** @return iterable */
96 | public static function instantiatableClassesInDirectories(string ...$directories): iterable
97 | {
98 | $iterator = self::classesInDirectories(...$directories);
99 |
100 | /**
101 | * @psalm-suppress MissingTemplateParam
102 | * @psalm-suppress InvalidOperand
103 | */
104 | return new class (new ArrayIterator([...$iterator])) extends FilterIterator {
105 | private const DOES_NOT_ACCEPT_CLASS = false;
106 |
107 | public function accept(): bool
108 | {
109 | $className = $this->getInnerIterator()->current();
110 | if (! is_string($className)) {
111 | return self::DOES_NOT_ACCEPT_CLASS;
112 | }
113 |
114 | try {
115 | $reflectionClass = ReflectionClass::createFromName($className);
116 |
117 | return $reflectionClass->isInstantiable();
118 | } catch (IdentifierNotFound) {
119 | return self::DOES_NOT_ACCEPT_CLASS;
120 | }
121 | }
122 | };
123 | }
124 |
125 | /** @return iterable */
126 | public static function instantiatableClassesInDirectory(string $directory): iterable
127 | {
128 | yield from self::instantiatableClassesInDirectories($directory);
129 | }
130 |
131 | /** @return iterable */
132 | public static function nonInstantiatableClassesInDirectories(string ...$directories): iterable
133 | {
134 | $iterator = self::classesInDirectories(...$directories);
135 |
136 | /**
137 | * @psalm-suppress MissingTemplateParam
138 | * @psalm-suppress InvalidOperand
139 | */
140 | return new class (new ArrayIterator([...$iterator])) extends FilterIterator {
141 | private const DOES_NOT_ACCEPT_CLASS = false;
142 |
143 | public function accept(): bool
144 | {
145 | $className = $this->getInnerIterator()->current();
146 | if (! is_string($className)) {
147 | return self::DOES_NOT_ACCEPT_CLASS;
148 | }
149 |
150 | try {
151 | $reflectionClass = ReflectionClass::createFromName($className);
152 |
153 | return $reflectionClass->isInstantiable() === self::DOES_NOT_ACCEPT_CLASS;
154 | } catch (IdentifierNotFound) {
155 | return self::DOES_NOT_ACCEPT_CLASS;
156 | }
157 | }
158 | };
159 | }
160 |
161 | /** @return iterable */
162 | public static function nonInstantiatableClassesInDirectory(string $directory): iterable
163 | {
164 | yield from self::nonInstantiatableClassesInDirectories($directory);
165 | }
166 |
167 | /**
168 | * @internal
169 | *
170 | * @return iterable
171 | */
172 | public static function classesInSourceLocator(AggregateSourceLocator $sourceLocator): iterable
173 | {
174 | /** @psalm-suppress UndefinedClass */
175 | yield from (new DefaultReflector($sourceLocator))->reflectAllClasses();
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | function listClassesInDirectories(string ...$directories): iterable
18 | {
19 | return Lister::classesInDirectories(...$directories);
20 | }
21 |
22 | /**
23 | * get a list of all classes in the given directory.
24 | *
25 | * @return iterable
26 | */
27 | function listClassesInDirectory(string $directory): iterable
28 | {
29 | return Lister::classesInDirectory($directory);
30 | }
31 |
32 | /**
33 | * get a list of all classes in the given file.
34 | *
35 | * @param non-empty-string $file
36 | *
37 | * @return iterable
38 | */
39 | function listClassesInFile(string $file): iterable
40 | {
41 | return Lister::classesInFile($file);
42 | }
43 |
44 | /**
45 | * get a list of all classes in the given files.
46 | *
47 | * @param non-empty-string ...$files
48 | *
49 | * @return iterable
50 | */
51 | function listClassesInFiles(string ...$files): iterable
52 | {
53 | return Lister::classesInFiles(...$files);
54 | }
55 |
56 | /** @return iterable */
57 | function listInstantiatableClassesInDirectories(string ...$directories): iterable
58 | {
59 | return Lister::instantiatableClassesInDirectories(...$directories);
60 | }
61 |
62 | /** @return iterable */
63 | function listInstantiatableClassesInDirectory(string $directory): iterable
64 | {
65 | return Lister::instantiatableClassesInDirectories($directory);
66 | }
67 |
68 | /** @return iterable */
69 | function listNonInstantiatableClassesInDirectories(string ...$directories): iterable
70 | {
71 | return Lister::nonInstantiatableClassesInDirectories(...$directories);
72 | }
73 |
74 | /** @return iterable */
75 | function listNonInstantiatableClassesInDirectory(string $directory): iterable
76 | {
77 | return Lister::nonInstantiatableClassesInDirectory($directory);
78 | }
79 |
80 | /**
81 | * @internal
82 | *
83 | * @return iterable
84 | */
85 | function listClassesInSourceLocator(AggregateSourceLocator $sourceLocator): iterable
86 | {
87 | return Lister::classesInSourceLocator($sourceLocator);
88 | }
89 |
--------------------------------------------------------------------------------
/src/functions_include.php:
--------------------------------------------------------------------------------
1 | value = $value;
24 | }
25 |
26 | /**
27 | * @return string
28 | */
29 | public function getValue()
30 | {
31 | return $this->value;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test-app/Foo/Bar/BarAndFoo.php:
--------------------------------------------------------------------------------
1 | getValue();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test-classes/InstantiatableClass.php:
--------------------------------------------------------------------------------
1 |