Showing preview only (431K chars total). Download the full file or copy to clipboard to get everything.
Repository: crunzphp/crunz
Branch: 3.10
Commit: 98dc47f7997a
Files: 174
Total size: 386.9 KB
Directory structure:
gitextract_lvj5ppvr/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1_Bug_report.md
│ │ └── 2_Feature_request.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── php.yaml
├── .gitignore
├── .php-cs-fixer.dist.php
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── UPGRADE.md
├── bootstrap.php
├── composer.json
├── config/
│ └── services.php
├── crunz
├── docker/
│ └── php82/
│ ├── Dockerfile
│ └── php.ini
├── docker-compose.yml
├── phpstan-baseline.neon
├── phpstan.neon
├── phpunit.xml
├── resources/
│ └── config/
│ └── crunz.yml
├── src/
│ ├── Application/
│ │ ├── Cron/
│ │ │ ├── CronExpressionFactoryInterface.php
│ │ │ └── CronExpressionInterface.php
│ │ ├── Query/
│ │ │ └── TaskInformation/
│ │ │ ├── TaskInformation.php
│ │ │ ├── TaskInformationHandler.php
│ │ │ └── TaskInformationView.php
│ │ └── Service/
│ │ ├── ClosureSerializerInterface.php
│ │ ├── ConfigurationInterface.php
│ │ └── LoggerFactoryInterface.php
│ ├── Application.php
│ ├── CacheDirectoryFactory/
│ │ └── CacheDirectoryFactory.php
│ ├── Clock/
│ │ ├── Clock.php
│ │ └── ClockInterface.php
│ ├── Configuration/
│ │ ├── ConfigFileNotExistsException.php
│ │ ├── ConfigFileNotReadableException.php
│ │ ├── Configuration.php
│ │ ├── ConfigurationParser.php
│ │ ├── ConfigurationParserInterface.php
│ │ ├── Definition.php
│ │ └── FileParser.php
│ ├── Console/
│ │ └── Command/
│ │ ├── Command.php
│ │ ├── ConfigGeneratorCommand.php
│ │ ├── ScheduleListCommand.php
│ │ ├── ScheduleRunCommand.php
│ │ └── TaskGeneratorCommand.php
│ ├── EnvFlags/
│ │ └── EnvFlags.php
│ ├── Event.php
│ ├── EventRunner.php
│ ├── Exception/
│ │ ├── CrunzException.php
│ │ ├── EmptyTimezoneException.php
│ │ ├── MailerException.php
│ │ ├── NotImplementedException.php
│ │ ├── TaskNotExistException.php
│ │ └── WrongTaskNumberException.php
│ ├── Filesystem/
│ │ ├── Filesystem.php
│ │ └── FilesystemInterface.php
│ ├── Finder/
│ │ ├── Finder.php
│ │ └── FinderInterface.php
│ ├── HttpClient/
│ │ ├── CurlHttpClient.php
│ │ ├── FallbackHttpClient.php
│ │ ├── HttpClientException.php
│ │ ├── HttpClientInterface.php
│ │ ├── HttpClientLoggerDecorator.php
│ │ └── StreamHttpClient.php
│ ├── Infrastructure/
│ │ ├── Dragonmantank/
│ │ │ └── CronExpression/
│ │ │ ├── DragonmantankCronExpression.php
│ │ │ └── DragonmantankCronExpressionFactory.php
│ │ ├── Laravel/
│ │ │ └── LaravelClosureSerializer.php
│ │ └── Psr/
│ │ └── Logger/
│ │ ├── EnabledLoggerDecorator.php
│ │ ├── PsrStreamLogger.php
│ │ └── PsrStreamLoggerFactory.php
│ ├── Invoker.php
│ ├── Logger/
│ │ ├── ConsoleLogger.php
│ │ ├── ConsoleLoggerInterface.php
│ │ ├── Logger.php
│ │ └── LoggerFactory.php
│ ├── Mailer.php
│ ├── Output/
│ │ └── OutputFactory.php
│ ├── Path/
│ │ └── Path.php
│ ├── Pinger/
│ │ ├── PingableException.php
│ │ ├── PingableInterface.php
│ │ └── PingableTrait.php
│ ├── Process/
│ │ └── Process.php
│ ├── Schedule/
│ │ └── ScheduleFactory.php
│ ├── Schedule.php
│ ├── Stubs/
│ │ └── BasicTask.php
│ ├── Task/
│ │ ├── Collection.php
│ │ ├── CollectionInterface.php
│ │ ├── Loader.php
│ │ ├── LoaderInterface.php
│ │ ├── TaskException.php
│ │ ├── TaskNumber.php
│ │ ├── Timezone.php
│ │ └── WrongTaskInstanceException.php
│ ├── Timezone/
│ │ ├── Provider.php
│ │ └── ProviderInterface.php
│ └── UserInterface/
│ └── Cli/
│ ├── ClosureRunCommand.php
│ └── DebugTaskCommand.php
└── tests/
├── EndToEnd/
│ ├── ClosureRunTest.php
│ ├── ConfigProviderTest.php
│ ├── ConfigRecognitionTest.php
│ ├── DebugTaskTest.php
│ ├── LoggerTest.php
│ ├── TasksSourceRecognitionTest.php
│ ├── VersionTest.php
│ └── WrongTaskTest.php
├── Functional/
│ ├── ConfigProviderTest.php
│ ├── DifferentBaseCacheDirTest.php
│ ├── ScheduleListTest.php
│ ├── ScheduleRunTest.php
│ └── TaskGeneratorTest.php
├── TestCase/
│ ├── EndToEnd/
│ │ └── Environment/
│ │ ├── Environment.php
│ │ └── EnvironmentBuilder.php
│ ├── EndToEndTestCase.php
│ ├── FakeConfiguration.php
│ ├── FakeLoader.php
│ ├── FakeTaskCollection.php
│ ├── Faker.php
│ ├── Logger/
│ │ ├── NullLogger.php
│ │ └── SpyPsrLogger.php
│ ├── SerializableTaskRunnerStub.php
│ ├── TaskRunnerStub.php
│ ├── TemporaryFile.php
│ ├── TestClock.php
│ └── UnitTestCase.php
├── Unit/
│ ├── Application/
│ │ ├── Cron/
│ │ │ └── AbstractCronExpressionTestCase.php
│ │ └── Query/
│ │ └── TaskInformation/
│ │ └── TaskInformationHandlerTest.php
│ ├── CacheDirectoryFactory/
│ │ └── CacheDirectoryFactoryTest.php
│ ├── Configuration/
│ │ ├── ConfigurationParserTest.php
│ │ ├── ConfigurationTest.php
│ │ └── FileParserTest.php
│ ├── Console/
│ │ └── Command/
│ │ ├── ScheduleListCommandTest.php
│ │ └── ScheduleRunCommandTest.php
│ ├── EnvFlags/
│ │ └── EnvFlagsTest.php
│ ├── EventRunnerTest.php
│ ├── EventTest.php
│ ├── Filesystem/
│ │ └── FilesystemTest.php
│ ├── Finder/
│ │ └── FinderTest.php
│ ├── HttpClient/
│ │ └── StreamHttpClientTest.php
│ ├── Infrastructure/
│ │ ├── Dragonmantank/
│ │ │ └── CronExpression/
│ │ │ └── DragonmantankCronExpressionTestCase.php
│ │ └── Psr/
│ │ └── Logger/
│ │ ├── EnabledLoggerDecoratorTest.php
│ │ ├── PsrStreamLoggerFactoryTest.php
│ │ └── PsrStreamLoggerTest.php
│ ├── InvokerTest.php
│ ├── Logger/
│ │ ├── ConsoleLoggerTest.php
│ │ └── LoggerFactoryTest.php
│ ├── MailerTest.php
│ ├── Output/
│ │ └── OutputFactoryTest.php
│ ├── Path/
│ │ └── PathTest.php
│ ├── Pingable.php
│ ├── Pinger/
│ │ └── PingableTest.php
│ ├── Process/
│ │ └── ProcessTest.php
│ ├── Schedule/
│ │ └── ScheduleFactoryTest.php
│ ├── ScheduleTest.php
│ ├── Service/
│ │ ├── AbstractClosureSerializerTestCase.php
│ │ ├── LaravelClosureSerializerTest.php
│ │ └── LaravelClosureSerializerTestCase.php
│ ├── Task/
│ │ ├── TaskNumberTest.php
│ │ └── TimezoneTest.php
│ ├── Timezone/
│ │ └── ProviderTest.php
│ └── UserInterface/
│ └── Cli/
│ └── ClosureRunCommandTest.php
├── crunz.yml
├── resources/
│ ├── fixtures/
│ │ └── finder/
│ │ ├── direct/
│ │ │ └── directHere.php
│ │ └── symlink/
│ │ └── symlinkHere.php
│ └── tasks/
│ ├── ClosureTasks.php
│ ├── CustomOutputTasks.php
│ ├── FailTasks.php
│ ├── NoOverlappingClosureTasks.php
│ ├── PhpVersionTasks.php
│ └── WrongTasks.php
└── tasks/
└── TestTasks.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
charset = utf-8
================================================
FILE: .gitattributes
================================================
* text=auto
/tests export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.editorconfig export-ignore
README.md export-ignore
LICENSE.md export-ignore
CHANGELOG.md export-ignore
UPGRADE.md export-ignore
.travis.yml export-ignore
appveyor.yml export-ignore
phpunit.xml export-ignore
/.github export-ignore
.php-cs-fixer.dist.php export-ignore
docker-compose.yml export-ignore
/docker export-ignore
phpstan.neon export-ignore
composer-install.php export-ignore
bootstrap.php export-ignore
/resources/docs export-ignore
phpstan-baseline.neon export-ignore
================================================
FILE: .github/FUNDING.yml
================================================
github: PabloKowalczyk
================================================
FILE: .github/ISSUE_TEMPLATE/1_Bug_report.md
================================================
---
name: "\U0001F41B Bug Report"
about: Report errors and problems
---
**Crunz version**: x.y.z
**PHP version**: x.y.z
**Operating system type and version**:
**Description**
<!-- A clear and concise description of the problem. -->
**How to reproduce**
<!-- Code and/or config needed to reproduce the problem. -->
**Possible Solution**
<!--- Optional: only if you have suggestions on a fix/reason for the bug -->
**Additional context**
<!-- Optional: any other context about the problem: log messages, screenshots, etc. -->
================================================
FILE: .github/ISSUE_TEMPLATE/2_Feature_request.md
================================================
---
name: "\U0001F680 Feature Request"
about: RFC and ideas for new features and improvements
---
**Description**
<!-- A clear and concise description of the new feature. -->
**Example**
<!-- A simple example of the new feature in action (include PHP code, etc.)
If the new feature changes an existing feature, include a simple before/after comparison. -->
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
| Q | A
| ------------- | ---
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
<!--
Write a short README entry for your feature/bugfix here (replace this comment block.)
This will help people understand your PR and can be used as a start of the Doc PR.
-->
================================================
FILE: .github/workflows/php.yaml
================================================
name: PHP
on:
pull_request:
branches:
- '3.9'
- '3.10'
push: null
permissions: {}
concurrency:
group: '${{ github.workflow }}-${{ github.ref }}'
cancel-in-progress: true
jobs:
tests:
name: '${{ matrix.php }} / Symfony ${{ matrix.symfony_version }} / ${{ matrix.dependencies }} / ${{ matrix.os }}'
strategy:
matrix:
os:
- 'ubuntu-22.04'
php:
- '8.2'
- '8.3'
- '8.4'
- '8.5'
dependencies:
- 'lowest'
- 'highest'
symfony_version:
- '~6.4.0'
- '~7.4.0'
- '~8.0.0'
include:
- os: 'windows-2022'
php: '8.2'
dependencies: 'highest'
symfony_version: '~6.4.0'
exclude:
- os: 'ubuntu-22.04'
php: '8.2'
dependencies: 'lowest'
symfony_version: '~8.0.0'
- os: 'ubuntu-22.04'
php: '8.2'
dependencies: 'highest'
symfony_version: '~8.0.0'
- os: 'ubuntu-22.04'
php: '8.3'
dependencies: 'lowest'
symfony_version: '~8.0.0'
- os: 'ubuntu-22.04'
php: '8.3'
dependencies: 'highest'
symfony_version: '~8.0.0'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- id: symfony_packages
shell: bash
run: |
jq --raw-output '"with_string=" + (["--with=" + ((."require" + ."require-dev") | keys[] | select(contains("symfony/"))) + ":${{ matrix.symfony_version }}"] | join(" "))' composer.json \
>>"$GITHUB_OUTPUT"
- uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}
composer-options: ${{ steps.symfony_packages.outputs.with_string }}
- run: composer exec -- phpunit --testsuite EndToEnd
- run: composer exec -- phpunit --testsuite Integration
- run: composer exec -- phpunit --testsuite Unit
static_analysis:
name: Static analysis
strategy:
matrix:
include:
- php: '8.2'
symfony_version: '~6.4.0'
dependencies: 'highest'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- id: symfony_packages
shell: bash
run: |
jq --raw-output '"with_string=" + (["--with=" + ((."require" + ."require-dev") | keys[] | select(contains("symfony/"))) + ":${{ matrix.symfony_version }}"] | join(" "))' composer.json \
>>"$GITHUB_OUTPUT"
- uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}
composer-options: ${{ steps.symfony_packages.outputs.with_string }}
- run: composer normalize --dry-run
- uses: actions/cache@v4
with:
path: .php-cs-fixer.cache
key: php-cs-fixer-cache
- uses: actions/cache@v4
with:
path: /tmp/phpstan
key: phpstan-cache
- run: composer run crunz:analyze
================================================
FILE: .gitignore
================================================
/vendor
/tasks
composer.phar
/composer.lock
.DS_Store
*.log
.idea/
var/
.php-cs-fixer.cache
/crunz.phar
/crunz.yml
/.phpunit.result.cache
================================================
FILE: .php-cs-fixer.dist.php
================================================
<?php
declare(strict_types=1);
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
return (new PhpCsFixer\Config())
->setParallelConfig(ParallelConfigFactory::detect())
->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'protected_to_private' => false,
'combine_consecutive_unsets' => true,
'combine_consecutive_issets' => true,
'compact_nullable_type_declaration' => true,
'declare_strict_types' => true,
'dir_constant' => true,
'ereg_to_preg' => true,
'explicit_indirect_variable' => true,
'explicit_string_variable' => true,
'function_to_constant' => true,
'is_null' => true,
'modernize_types_casting' => true,
'linebreak_after_opening_tag' => true,
'list_syntax' => ['syntax' => 'short'],
'mb_str_functions' => true,
'native_function_invocation' => [
'include' => ['@all'],
],
'no_alias_functions' => true,
'no_homoglyph_names' => true,
'no_php4_constructor' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
'php_unit_dedicate_assert' => true,
'php_unit_expectation' => true,
'php_unit_mock' => true,
'php_unit_namespaced' => true,
'php_unit_method_casing' => ['case' => 'snake_case'],
'random_api_migration' => true,
'strict_comparison' => true,
'strict_param' => true,
'ternary_to_null_coalescing' => true,
'void_return' => true,
'concat_space' => [
'spacing' => 'one',
],
'single_line_throw' => false,
'php_unit_test_case_static_method_calls' => [
'call_type' => 'self',
],
])
->setRiskyAllowed(true)
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
->in(__DIR__ . '/config')
->append(
[
__FILE__,
__DIR__ . '/composer-install.php',
]
)
)
;
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## Unreleased
## [v3.7.0] - 2024-08-12
### Changed
- [crunzphp#77] Require at least Symfony 6.4
- [crunzphp#79] Update PHPCSFixer
- [crunzphp#78] Allow Symfony v7
## [v3.6.0] - 2023-11-10
### Added
- [crunzphp#68] Getter for 'from', 'to' event's configuration, thanks to [@lucatacconi]
## [v3.5.1] - 2023-11-03
### Fixed
- [crunzphp#62] Task Life Time functions don't respect the timezone
## [v3.5.0] - 2023-10-15
### Added
- [crunzphp#39] Add PHP v8.2 support
- [crunzphp#64] Test Symfony v6.3
- [crunzphp#66] Add PHP v8.3 support
### Removed
- [crunzphp#38] Drop PHP v7.4 support
- [crunzphp#54] Drop Symfony v6.0 support
- [crunzphp#63] Drop Symfony v6.1 support
- [crunzphp#65] Drop Symfony v6.2 support
## [v3.4.1] - 2022-11-01
### Fixed
- [crunzphp#44] Do not require BlockingStoreInterface in preventOverlapping()
## [v3.4.0] - 2022-10-03
### Added
- [crunzphp#31] Add format option to `schedule:list`
## [v3.3.0] - 2022-06-19
### Deprecated
- [crunzphp#24] Deprecate non string parameters
## [v3.2.2] - 2022-05-28
### Fixed
- [crunzphp#5] Fix Symfony 6.1 deprecations
## [v3.2.1] - 2022-01-09
### Fixed
- [#401] Fix version number
## [v3.2.0] - 2022-01-09
### Added
- [#398] Add hourlyAt()
- [#390] Symfony 6 Support, thanks to [@bashgeek]
### Changed
- [#396] Remove `opis/closure` and use `laravel/serializable-closure` instead
### Removed
- [#393] Drop Symfony 5.2 support
- [#394] Drop Symfony 5.3 support
## [v3.1.0] - 2021-11-24
### Changed
- [#385] Replace `Swiftmailer` with `symfony/mailer`
## [v3.0.1] - 2021-05-25
### Fixed
- [#361] Log to specific event log file, thanks to [@drjayvee]
## [v2.3.1] - 2021-05-25
### Fixed
- [#361] Log to specific event log file, thanks to [@drjayvee]
## [v3.0.0] - 2021-04-25
### Changed
- [#349] Require at least PHP v7.4
- [#356] Require package "dragonmantank/cron-expression" at least "v3.1"
### Removed
- [#351] Drop Symfony v3.4 support
- [#352] Drop Symfony v5.1 support
- [#354] Remove most "Crunz\Event::every*" methods
## [v2.3.0] - 2021-03-14
### Deprecated
- [#344] Deprecate most "Event::every*" methods
### Removed
- [#323] Drop Symfony 4.3 support
- [#324] Drop Symfony 5.0 support
## [v2.2.4] - 2020-12-18
### Fixed
- [#333] Include symlinks in Finder, thanks to [@iluuu1994]
## [v2.2.3] - 2020-11-29
### Fixed
- [#334] Fix disabling logger not working
## [v2.2.2] - 2020-10-12
### Fixed
- [#326] Fix lock key on closures
## [v2.2.1] - 2020-10-08
### Fixed
- [#321] Add PHP8 support
## [v2.2.0] - 2020-06-18
### Added
- [#287] Add `task:debug` command
- [#233] Add option to ignore empty context in monolog, thanks to [@rrushton]
- [#298] Add `logger_factory` config option
### Removed
- [#292] Drop Symfony 4.2 support
## [v2.1.0] - 2020-02-02
### Added
- [#274] Symfony 5 support
### Changed
- [#240] cron-expression package, thanks to [@mareksuscak]
- [#280] Hide `closure:run` command
## [v2.0.4] - 2019-12-08
### Fixed
- [#268] Fix Symfony 4.4 deprecations
## [v1.12.4] - 2019-12-08
### Fixed
- [#268] Fix Symfony 4.4 deprecations
## [v2.0.3] - 2019-11-17
### Fixed
- [#261] Release lock on error
- [#264] Revert converting closure result to `int`
## [v1.12.3] - 2019-11-17
### Fixed
- [#261] Release lock on error
## [v2.0.2] - 2019-10-06
### Fixed
- [#251] Update PHPUnit to avoid PHP7.4 deprecations
## [v1.12.2] - 2019-10-05
### Fixed
- [#243] Sandbox task loading
- [#245] Fix PHP 7.4 compatibility
## [v2.0.1] - 2019-05-10
### Fixed
- [#229] Fix recursive tasks scan
## [v1.12.1] - 2019-05-01
### Fixed
- [#229] Fix recursive tasks scan
## [v2.0.0] - 2019-04-24
### Changed
- [#101] Throw exception on empty timezone
- [#204] More than five parts cron expressions will throw exception
- [#221] Throw `Crunz\Task\WrongTaskInstanceException` when task is not `Schedule` instance
- [#222] Make `\Crunz\Event::setProcess` private
- [#225] Bump dependencies
### Removed
- [#103] Removed `Crunz\Output\VerbosityAwareOutput` class
- [#206] Remove legacy paths recognition
- [#224] Remove `mail` transport
## [v1.12.0] - 2019-04-07
### Added
- [#178], [#217] `timezone_log` configuration option to decide whether
configured `timezone` should be used for logs, thanks to [@SadeghPM]
### Deprecated
- Using `\Crunz\Event::setProcess` is deprecated, this method was intended to be `private`,
but for some reason is `public`.
In `v2.0` this method will became private and result in exception if you call it.
- [#199] Not returning `\Crunz\Schedule` instance from your task is deprecated.
In `v2` this will result in exception.
## [v1.11.2] - 2019-03-16
### Fixed
- [#209], [#210] Composer installs crunz executable to vendor/bin instead of symlink
## [v1.11.1] - 2019-01-27
### Fixed
- [#190] Fix Crunz bin path when running closures
## [v1.11.0] - 2019-01-24
### Fixed
- [#181] Fix missing tasks source
- [#180] Fix deprecation messages not showing
### Deprecated
- Relying on tasks' source/config file recognition related to Crunz bin
## [v1.11.0-rc.1] - 2018-12-22
### Fixed
- [#171] Fix lock storage bug
- [#173] Remove Symfony 4.2 deprecations
- [#166] Improve task collection debugging
## [v1.11.0-beta.2] - 2018-11-10
### Fixed
- [#162] Fix command error output [closes [#161]]
## [v1.11.0-beta.1] - 2018-10-23
### Added
- [#153] Add support for `symfony/lock`, Thanks to [@digilist]
### Fixed
- [#146] Make paths relative to current working directory - "cwd".
- [#158] Accept only string task number.
## [v1.10.1] - 2018-09-22
### Fixed
- [#139] Do not require `cURL` extension
## [v1.10.0] - 2018-09-22
### Fixed
- [#137] Treat whole output of failed command as "error output".
### Removed
- [#136] Remove guzzle
## [v1.9.0] - 2018-08-18
### Changed
- [#132] Improved container caching in shared servers
### Fixed
- [#131] Crunz can be used with `dragonmantank/cron-expression` package
### Deprecated
- Passing more than five parts (e.g `* * * * * *`) to `Crunz\Event::cron()`
## [v1.8.0] - 2018-08-15
### Added
- [#120] Added `--force` option to `schedule:run` command
- [#129] Add `--task` option for `schedule:run` command
### Fixed
- [#123] Spellfix: `comand` -> `command`, Thanks to [@FallDi]
## [v1.7.3] - 2018-06-15
- [#118] Undefined index: year in `vendor/lavary/crunz/src/Event.php` on line 370, Thanks to [@mindcreations]
## [v1.7.2] - 2018-06-13
### Fixed
- [#116] Do not replace Symfony's polyfills.
## [v1.7.1] - 2018-06-01
### Fixed
- [#110] Fixed config file path guessing.
## [v1.7.0] - 2018-05-27
### Added
- [#94] Added timezone option
### Deprecated
- `timezone` option in config file is now required, lack of it will result in Exception in version `2.0`
### Removed
- [#104] Remove splitCamel helper.
## [v1.6.1] - 2018-05-13
### Fixed
- [#90] Send output by email only if it is not empty.
## [v1.6.0] - 2018-04-22
### Added
- [#69] Option for allowing line breaks in logs, Thanks to [@TomasDuda]
- [#79] Introduce DI container
### Fixed
- [#43] Typos stopping email transport of 'mail', Thanks to [@m-hume]
- [#46] sendOutputTo and appendOutputTo fix, Thanks to [@m-hume]
- [#80] Fixed prevent overlapping on windows
- [#81] Fix Event::in on windows
- [#84] Make comparing date segments strict.
- [#86] Fix closure running on windows
- [#85] Fix changing user
- [#87] Remove error handler
## [v1.5.1] - 2018-04-12
### Added
- [#76] Introduce editorconfig
- [#75] Added changelog file.
### Fixed
- [#77] Fix high cpu usage
[#401]: https://github.com/lavary/crunz/pull/401
[#398]: https://github.com/lavary/crunz/pull/398
[#396]: https://github.com/lavary/crunz/pull/396
[#394]: https://github.com/lavary/crunz/pull/394
[#393]: https://github.com/lavary/crunz/pull/393
[#390]: https://github.com/lavary/crunz/pull/390
[#385]: https://github.com/lavary/crunz/pull/385
[#361]: https://github.com/lavary/crunz/pull/361
[#356]: https://github.com/lavary/crunz/pull/356
[#354]: https://github.com/lavary/crunz/pull/354
[#352]: https://github.com/lavary/crunz/pull/352
[#351]: https://github.com/lavary/crunz/pull/351
[#349]: https://github.com/lavary/crunz/pull/349
[#344]: https://github.com/lavary/crunz/pull/344
[#334]: https://github.com/lavary/crunz/pull/334
[#333]: https://github.com/lavary/crunz/pull/333
[#326]: https://github.com/lavary/crunz/pull/326
[#324]: https://github.com/lavary/crunz/pull/324
[#323]: https://github.com/lavary/crunz/pull/323
[#321]: https://github.com/lavary/crunz/pull/321
[#298]: https://github.com/lavary/crunz/pull/298
[#292]: https://github.com/lavary/crunz/pull/292
[#287]: https://github.com/lavary/crunz/pull/287
[#280]: https://github.com/lavary/crunz/pull/280
[#274]: https://github.com/lavary/crunz/pull/274
[#268]: https://github.com/lavary/crunz/pull/268
[#264]: https://github.com/lavary/crunz/pull/264
[#261]: https://github.com/lavary/crunz/pull/261
[#251]: https://github.com/lavary/crunz/pull/251
[#245]: https://github.com/lavary/crunz/pull/245
[#243]: https://github.com/lavary/crunz/pull/243
[#240]: https://github.com/lavary/crunz/pull/240
[#233]: https://github.com/lavary/crunz/pull/233
[#229]: https://github.com/lavary/crunz/pull/229
[#225]: https://github.com/lavary/crunz/pull/225
[#224]: https://github.com/lavary/crunz/pull/224
[#222]: https://github.com/lavary/crunz/pull/222
[#221]: https://github.com/lavary/crunz/pull/221
[#217]: https://github.com/lavary/crunz/pull/217
[#210]: https://github.com/lavary/crunz/pull/210
[#209]: https://github.com/lavary/crunz/pull/209
[#206]: https://github.com/lavary/crunz/pull/206
[#204]: https://github.com/lavary/crunz/pull/204
[#199]: https://github.com/lavary/crunz/pull/199
[#190]: https://github.com/lavary/crunz/pull/190
[#181]: https://github.com/lavary/crunz/pull/181
[#180]: https://github.com/lavary/crunz/pull/180
[#178]: https://github.com/lavary/crunz/pull/178
[#173]: https://github.com/lavary/crunz/pull/173
[#171]: https://github.com/lavary/crunz/pull/171
[#166]: https://github.com/lavary/crunz/pull/166
[#164]: https://github.com/lavary/crunz/pull/164
[#163]: https://github.com/lavary/crunz/pull/163
[#162]: https://github.com/lavary/crunz/pull/162
[#161]: https://github.com/lavary/crunz/pull/161
[#159]: https://github.com/lavary/crunz/pull/159
[#158]: https://github.com/lavary/crunz/pull/158
[#157]: https://github.com/lavary/crunz/pull/157
[#155]: https://github.com/lavary/crunz/pull/155
[#154]: https://github.com/lavary/crunz/pull/154
[#153]: https://github.com/lavary/crunz/pull/153
[#151]: https://github.com/lavary/crunz/pull/151
[#150]: https://github.com/lavary/crunz/pull/150
[#149]: https://github.com/lavary/crunz/pull/149
[#148]: https://github.com/lavary/crunz/pull/148
[#147]: https://github.com/lavary/crunz/pull/147
[#146]: https://github.com/lavary/crunz/pull/146
[#142]: https://github.com/lavary/crunz/pull/142
[#141]: https://github.com/lavary/crunz/pull/141
[#140]: https://github.com/lavary/crunz/pull/140
[#139]: https://github.com/lavary/crunz/pull/139
[#138]: https://github.com/lavary/crunz/pull/138
[#137]: https://github.com/lavary/crunz/pull/137
[#136]: https://github.com/lavary/crunz/pull/136
[#133]: https://github.com/lavary/crunz/pull/133
[#132]: https://github.com/lavary/crunz/pull/132
[#131]: https://github.com/lavary/crunz/pull/131
[#130]: https://github.com/lavary/crunz/pull/130
[#129]: https://github.com/lavary/crunz/pull/129
[#123]: https://github.com/lavary/crunz/pull/123
[#120]: https://github.com/lavary/crunz/pull/120
[#119]: https://github.com/lavary/crunz/pull/119
[#118]: https://github.com/lavary/crunz/pull/118
[#117]: https://github.com/lavary/crunz/pull/117
[#116]: https://github.com/lavary/crunz/pull/116
[#113]: https://github.com/lavary/crunz/pull/113
[#112]: https://github.com/lavary/crunz/pull/112
[#111]: https://github.com/lavary/crunz/pull/111
[#110]: https://github.com/lavary/crunz/pull/110
[#109]: https://github.com/lavary/crunz/pull/109
[#107]: https://github.com/lavary/crunz/pull/107
[#105]: https://github.com/lavary/crunz/pull/105
[#104]: https://github.com/lavary/crunz/pull/104
[#103]: https://github.com/lavary/crunz/pull/103
[#102]: https://github.com/lavary/crunz/pull/102
[#101]: https://github.com/lavary/crunz/pull/101
[#100]: https://github.com/lavary/crunz/pull/100
[#98]: https://github.com/lavary/crunz/pull/98
[#97]: https://github.com/lavary/crunz/pull/97
[#96]: https://github.com/lavary/crunz/pull/96
[#95]: https://github.com/lavary/crunz/pull/95
[#94]: https://github.com/lavary/crunz/pull/94
[#92]: https://github.com/lavary/crunz/pull/92
[#90]: https://github.com/lavary/crunz/pull/90
[#89]: https://github.com/lavary/crunz/pull/89
[#88]: https://github.com/lavary/crunz/pull/88
[#87]: https://github.com/lavary/crunz/pull/87
[#86]: https://github.com/lavary/crunz/pull/86
[#85]: https://github.com/lavary/crunz/pull/85
[#84]: https://github.com/lavary/crunz/pull/84
[#82]: https://github.com/lavary/crunz/pull/82
[#81]: https://github.com/lavary/crunz/pull/81
[#80]: https://github.com/lavary/crunz/pull/80
[#79]: https://github.com/lavary/crunz/pull/79
[#77]: https://github.com/lavary/crunz/pull/77
[#76]: https://github.com/lavary/crunz/pull/76
[#75]: https://github.com/lavary/crunz/pull/75
[#74]: https://github.com/lavary/crunz/pull/74
[#73]: https://github.com/lavary/crunz/pull/73
[#72]: https://github.com/lavary/crunz/pull/72
[#69]: https://github.com/lavary/crunz/pull/69
[#50]: https://github.com/lavary/crunz/pull/50
[#46]: https://github.com/lavary/crunz/pull/46
[#43]: https://github.com/lavary/crunz/pull/43
[#36]: https://github.com/lavary/crunz/pull/36
[#25]: https://github.com/lavary/crunz/pull/25
[#24]: https://github.com/lavary/crunz/pull/24
[#23]: https://github.com/lavary/crunz/pull/23
[#17]: https://github.com/lavary/crunz/pull/17
[#16]: https://github.com/lavary/crunz/pull/16
[crunzphp#5]: https://github.com/crunzphp/crunz/pull/5
[crunzphp#24]: https://github.com/crunzphp/crunz/pull/24
[crunzphp#31]: https://github.com/crunzphp/crunz/pull/31
[crunzphp#38]: https://github.com/crunzphp/crunz/pull/38
[crunzphp#39]: https://github.com/crunzphp/crunz/pull/39
[crunzphp#44]: https://github.com/crunzphp/crunz/pull/44
[crunzphp#54]: https://github.com/crunzphp/crunz/pull/54
[crunzphp#62]: https://github.com/crunzphp/crunz/pull/62
[crunzphp#63]: https://github.com/crunzphp/crunz/pull/63
[crunzphp#64]: https://github.com/crunzphp/crunz/pull/64
[crunzphp#65]: https://github.com/crunzphp/crunz/pull/65
[crunzphp#66]: https://github.com/crunzphp/crunz/pull/66
[crunzphp#68]: https://github.com/crunzphp/crunz/pull/68
[crunzphp#77]: https://github.com/crunzphp/crunz/pull/77
[crunzphp#78]: https://github.com/crunzphp/crunz/pull/78
[crunzphp#79]: https://github.com/crunzphp/crunz/pull/79
[v1.5.1]: https://github.com/crunzphp/crunz/compare/v1.5.0...v1.5.1
[v1.6.0]: https://github.com/crunzphp/crunz/compare/v1.5.1...v1.6.0
[v1.6.1]: https://github.com/crunzphp/crunz/compare/v1.6.0...v1.6.1
[v1.7.0]: https://github.com/crunzphp/crunz/compare/v1.6.1...v1.7.0
[v1.7.1]: https://github.com/crunzphp/crunz/compare/v1.7.0...v1.7.1
[v1.7.2]: https://github.com/crunzphp/crunz/compare/v1.7.1...v1.7.2
[v1.7.3]: https://github.com/crunzphp/crunz/compare/v1.7.2...v1.7.3
[v1.8.0]: https://github.com/crunzphp/crunz/compare/v1.7.3...v1.8.0
[v1.9.0]: https://github.com/crunzphp/crunz/compare/v1.8.0...v1.9.0
[v1.10.0]: https://github.com/crunzphp/crunz/compare/v1.9.0...v1.10.0
[v1.10.1]: https://github.com/crunzphp/crunz/compare/v1.10.0...v1.10.1
[v1.11.0-beta.1]: https://github.com/crunzphp/crunz/compare/v1.10.1...v1.11.0-beta.1
[v1.11.0-beta.2]: https://github.com/crunzphp/crunz/compare/v1.11.0-beta.1...v1.11.0-beta.2
[v1.11.0-rc.1]: https://github.com/crunzphp/crunz/compare/v1.11.0-beta.2...v1.11.0-rc.1
[v1.11.0]: https://github.com/crunzphp/crunz/compare/v1.11.0-rc.1...v1.11.0
[v1.11.1]: https://github.com/crunzphp/crunz/compare/v1.11.0...v1.11.1
[v1.11.2]: https://github.com/crunzphp/crunz/compare/v1.11.1...v1.11.2
[v1.12.0]: https://github.com/crunzphp/crunz/compare/v1.11.2...v1.12.0
[v1.12.1]: https://github.com/crunzphp/crunz/compare/v1.12.0...v1.12.1
[v1.12.2]: https://github.com/crunzphp/crunz/compare/v1.12.1...v1.12.2
[v1.12.3]: https://github.com/crunzphp/crunz/compare/v1.12.2...v1.12.3
[v1.12.4]: https://github.com/crunzphp/crunz/compare/v1.12.3...v1.12.4
[v2.0.0]: https://github.com/crunzphp/crunz/compare/v1.12.0...v2.0.0
[v2.0.1]: https://github.com/crunzphp/crunz/compare/v2.0.0...v2.0.1
[v2.0.2]: https://github.com/crunzphp/crunz/compare/v2.0.1...v2.0.2
[v2.0.3]: https://github.com/crunzphp/crunz/compare/v2.0.2...v2.0.3
[v2.0.4]: https://github.com/crunzphp/crunz/compare/v2.0.3...v2.0.4
[v2.1.0]: https://github.com/crunzphp/crunz/compare/v2.0.4...v2.1.0
[v2.2.0]: https://github.com/crunzphp/crunz/compare/v2.1.0...v2.2.0
[v2.2.1]: https://github.com/crunzphp/crunz/compare/v2.2.0...v2.2.1
[v2.2.2]: https://github.com/crunzphp/crunz/compare/v2.2.1...v2.2.2
[v2.2.3]: https://github.com/crunzphp/crunz/compare/v2.2.2...v2.2.3
[v2.2.4]: https://github.com/crunzphp/crunz/compare/v2.2.3...v2.2.4
[v2.3.0]: https://github.com/crunzphp/crunz/compare/v2.2.4...v2.3.0
[v2.3.1]: https://github.com/crunzphp/crunz/compare/v2.3.0...v2.3.1
[v3.0.0]: https://github.com/crunzphp/crunz/compare/v2.3.1...v3.0.0
[v3.0.1]: https://github.com/crunzphp/crunz/compare/v3.0.0...v3.0.1
[v3.1.0]: https://github.com/crunzphp/crunz/compare/v3.0.1...v3.1.0
[v3.2.0]: https://github.com/crunzphp/crunz/compare/v3.1.0...v3.2.0
[v3.2.1]: https://github.com/crunzphp/crunz/compare/v3.2.0...v3.2.1
[v3.2.2]: https://github.com/crunzphp/crunz/compare/v3.2.1...v3.2.2
[v3.3.0]: https://github.com/crunzphp/crunz/compare/v3.2.2...v3.3.0
[v3.4.0]: https://github.com/crunzphp/crunz/compare/v3.3.0...v3.4.0
[v3.4.1]: https://github.com/crunzphp/crunz/compare/v3.4.0...v3.4.1
[v3.5.0]: https://github.com/crunzphp/crunz/compare/v3.4.1...v3.5.0
[v3.5.1]: https://github.com/crunzphp/crunz/compare/v3.5.0...v3.5.1
[v3.6.0]: https://github.com/crunzphp/crunz/compare/v3.5.1...v3.6.0
[v3.7.0]: https://github.com/crunzphp/crunz/compare/v3.6.0...v3.7.0
[@andrewmy]: https://github.com/andrewmy
[@arthurbarros]: https://github.com/arthurbarros
[@bashgeek]: https://github.com/bashgeek
[@codermarcel]: https://github.com/codermarcel
[@digilist]: https://github.com/digilist
[@drjayvee]: https://github.com/drjayvee
[@erfan723]: https://github.com/erfan723
[@FallDi]: https://github.com/FallDi
[@iluuu1994]: https://github.com/iluuu1994
[@jhoughtelin]: https://github.com/jhoughtelin
[@lucatacconi]: https://github.com/lucatacconi
[@m-hume]: https://github.com/m-hume
[@mareksuscak]: https://github.com/mareksuscak
[@mindcreations]: https://github.com/mindcreations
[@PhilETaylor]: https://github.com/PhilETaylor
[@radarhere]: https://github.com/radarhere
[@rrushton]: https://github.com/rrushton
[@SadeghPM]: https://github.com/SadeghPM
[@timurbakarov]: https://github.com/timurbakarov
[@TomasDuda]: https://github.com/TomasDuda
[@vinkla]: https://github.com/vinkla
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Moe Reza Lavarian
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.
================================================
FILE: Makefile
================================================
sh-php:
docker compose exec --user=www-data php82 sh
================================================
FILE: README.md
================================================
# Crunz needs your funding 💲
## Support further Crunz development by [GitHub](https://github.com/sponsors/PabloKowalczyk).
Check [more info](https://github.com/crunzphp/crunz/issues/111).
# Crunz
Install a cron job once and for all, manage the rest from the code.
Crunz is a framework-agnostic package to schedule periodic tasks (cron jobs) in PHP using a fluent API.
Crunz is capable of executing any kind of executable command as well as PHP closures.
[](https://packagist.org/packages/crunzphp/crunz)
[](https://packagist.org/packages/crunzphp/crunz/stats)
[](https://packagist.org/packages/crunzphp/crunz/stats)
## Roadmap
| Version | Release date | Active support until | Bug support until | Status |
|---------|--------------|----------------------|-------------------|----------------|
| v1.x | April 2016 | April 2019 | April 2020 | End of life |
| v2.x | April 2019 | April 2021 | April 2022 | End of life |
| v3.x | April 2021 | TBD | TBD | Active support |
| v4.x | TBD | TBD | TBD | Development |
## Installation
To install it:
```bash
composer require crunzphp/crunz
```
If the installation is successful, a command-line utility named **crunz** is symlinked to the `vendor/bin` directory of your project.
## How It Works?
The idea is very simple: instead of installing cron jobs in a crontab file, we define them in one or several PHP files, by using the Crunz interface.
Here's a basic example:
```php
<?php
// tasks/backupTasks.php
use Crunz\Schedule;
$schedule = new Schedule();
$task = $schedule->run('cp project project-bk');
$task->daily();
return $schedule;
```
To run the tasks, you only need to install an ordinary cron job (a crontab entry) which runs **every minute**, and delegates the responsibility to Crunz' event runner:
```bash
* * * * * cd /project && vendor/bin/crunz schedule:run
```
The command `schedule:run` is responsible for collecting all the PHP task files and run the tasks which are due.
## Task Files
Task files resemble crontab files. Just like crontab files they can contain one or more tasks.
Normally we create our task files in the `tasks/` directory within the project's root directory.
> By default, Crunz assumes all the task files reside in the `tasks/` directory within the project's root directory.
There are two ways to specify the source directory: 1) Configuration file 2) As a parameter to the event runner command.
We can explicitly set the source path by passing it to the event runner as a parameter:
```bash
* * * * * cd /project && vendor/bin/crunz schedule:run /path/to/tasks/directory
```
### Creating a Simple Task
In the terminal, change the directory to your project's root directory and run the following commands:
```bash
mkdir tasks && cd tasks
nano GeneralTasks.php
```
Then, add a task as below:
```php
<?php
// tasks/FirstTasks.php
use Crunz\Schedule;
$schedule = new Schedule();
$task = $schedule->run('cp project project-bk');
$task
->daily()
->description('Create a backup of the project directory.');
// ...
// IMPORTANT: You must return the schedule object
return $schedule;
```
There are some conventions for creating a task file, which you need to follow. First of all, the filename should end with `Tasks.php` unless we change this via the configuration settings.
In addition to that, we **must** return the instance of `Schedule` class at the end of each file, otherwise, all the tasks inside the file will be skipped by the event runner.
Since Crunz scans the tasks directory recursively, we can either put all the tasks in one file or across different files (or directories) based on their usage. This behavior helps us have a well organized tasks directory.
## The Command
We can run **any** command or script by using `run()`. This method accepts two arguments: **the command to be executed**, and **the command options** (as an associative array) if there's any.
### Normal Command or Script
```php
<?php
use Crunz\Schedule;
$schedule = new Schedule();
$task = $schedule->run(PHP_BINARY . ' backup.php', ['--destination' => 'path/to/destination']);
$task
->everyMinute()
->description('Copying the project directory');
return $schedule;
```
In the above example, `--destination` is an option supported by `backup.php` script.
### Closures
We can also write to a closure instead of a command:
```php
<?php
use Crunz\Schedule;
$schedule = new Schedule();
$x = 12;
$task = $schedule->run(function() use ($x) {
// Do some cool stuff in here
});
$task
->everyMinute()
->description('Copying the project directory');
return $schedule;
```
## Frequency of Execution
There are a variety of ways to specify **when** and **how often** a task should run. We can combine these methods together to get our desired frequencies.
### Units of Time
There are a group of methods which specify a unit of time (bigger than minute) as frequency. They usually end with `ly` suffix, as in `hourly()`, `daily()`, `weekly`, `monthly()`, `quarterly()`, and `yearly` .
All the events scheduled with this set of methods happen at the **beginning** of that time unit. For example `weekly()` will run the event on Sundays, and `monthly()` will run on the first day of each month.
The task below will run **daily at midnight** (start of the daily time period).
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' backup.php');
$task->daily();
// ...
```
Here's another one, which runs on the **first day of each month**.
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' email.php');
$task->monthly();
// ...
```
### Running Events at Certain Times
To schedule a one-off tasks, you may use `on()` method like this:
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' email.php');
$task->on('13:30 2016-03-01');
// ...
```
The above task will run on the first of march 2016 at 01:30 pm.
> `On()` accepts any date format parsed by PHP's [strtotime](http://php.net/manual/en/function.strtotime.php) function.
To specify the time of a task we use `at()` method:
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->daily()
->at('13:30');
// ...
```
If we only pass a time to the `on()` method, it will have the same effect as using `at()`
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->daily()
->on('13:30');
// is the sames as
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->daily()
->at('13:30');
// ...
```
We can combine the "Unit of Time" methods eg. daily(), monthly() with the at() or on() constraint in a single statement if we wish.
The following task will be run every hour at the 15th minute
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' feedmecookie.php');
$task
->hourlyAt('15');
// ...
```
>hourlyOn('15') could have been used instead of hourlyAt('15') with the same result
The following task will be run Monday at 13:30
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' startofwork.php');
$task
->weeklyOn(1,'13:30');
// ...
```
>Sunday is considered day 0 of the week.
If we wished for the task to run on Tuesday (day 2 of the week) at 09:00 we would have used:
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' startofwork.php');
$task
->weeklyOn(2,'09:00');
// ...
```
## Task Life Time
In a crontab entry, we can not easily specify a task's lifetime (the period of time when the task is active). However, it's been made easy in Crunz:
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->from('12:30 2016-03-04')
->to('04:55 2016-03-10');
//
```
Or alternatively we can use the `between()` method to accomplish the same result:
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->between('12:30 2016-03-04', '04:55 2016-03-10');
//
```
If we don't specify the date portion, the task will be active **every** day but only within the specified duration:
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->between('12:30', '04:55');
//
```
The above task runs **every five minutes** between **12:30 pm** and **4:55 pm** every day.
An example of restricting a task from running only during a certain range of minutes each hour can be achieved as follows:
```php
<?php
//
$hour = date('H');
$startminute = $hour.':05';
$endminute = $hour.':15';
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->hourly()
->between($startminute, $endminute);
//
```
The above task runs **every hour** between **minutes 5 to 15**
### Weekdays
Crunz also provides a set of methods which specify a certain day in the week.
* `mondays()`
* `tuesdays()`
* `wednesdays()`
* `thursdays()`
* `fridays()`
* `saturdays()`
* `sundays()`
* `weekdays()`
These methods have been designed to be used as a constraint and should not be used alone. The reason is that weekday methods just modify the `Day of Week` field of a cron job expression.
Consider the following example:
```php
<?php
// Cron equivalent: * * * * 1
$task = $schedule->run(PHP_BINARY . ' startofwork.php');
$task->mondays();
```
At first glance, the task seems to run **every Monday**, but since it only modifies the "day of week" field of the cron job expression, the task runs **every minute on Mondays**.
This is the correct way of using weekday methods:
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' startofwork.php');
$task
->mondays()
->at('13:30');
// ...
```
>(An easier to read alternative with a similar result ->weeklyOn(0,'13:30') to that shown in a previously example above)
### The Classic Way
We can also do the scheduling the old way, just like we do in a crontab file:
```php
<?php
$task = $schedule->run(PHP_BINARY . ' email.php');
$task->cron('30 12 * 5-6,9 Mon,Fri');
```
### Setting Individual Fields
Crunz's methods are not limited to the ready-made methods explained. We can also set individual fields to compose custom frequencies similar to how a classic crontab would composed them. These methods either accept an array of values, or list arguments separated by commas:
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->minute(['1-30', 45, 55])
->hour('1-5', 7, 8)
->dayOfMonth(12, 15)
->month(1);
```
Or:
```php
<?php
// ...
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->minute('30')
->hour('13')
->month([1,2])
->dayofWeek('Mon', 'Fri', 'Sat');
// ...
```
Based on our use cases, we can choose and combine the proper set of methods, which are easier to use.
## Running Conditions
Another thing that we cannot do very easily in a traditional crontab file is to make conditions for running the tasks. This has been made easy by `when()` and `skip()` methods.
Consider the following code:
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->between('12:30 2016-03-04', '04:55 2016-03-10')
->when(function() {
if ((bool) (time() % 2)) {
return true;
}
return false;
});
```
Method `when()` accepts a callback, which must return `TRUE` for the task to run. This is really useful when we need to check our resources before performing a resource-hungry task.
We can also skip a task under certain conditions, by using `skip()` method. If the passed callback returns `TRUE`, the task will be skipped.
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->between('12:30 2016-03-04', '04:55 2016-03-10')
->skip(function() {
if ((bool) (time() % 2)) {
return true;
}
return false;
});
//
```
We can use these methods **several** times for a single task. They are evaluated sequentially.
## Changing Directories
You can use the `in()` method to change directory before running a command:
```php
<?php
// ...
$task = $schedule->run('./deploy.sh');
$task
->in('/home')
->weekly()
->sundays()
->at('12:30')
->appendOutputTo('/var/log/backup.log');
// ...
return $schedule;
```
## Parallelism and the Locking Mechanism
Crunz runs the scheduled events in parallel (in separate processes), so all the events which have the same frequency of execution, will run at the same time asynchronously. To achieve this, Crunz utilizes the [symfony/Process](http://symfony.com/doc/current/components/process.html) library for running the tasks in sub-processes.
If the execution of a task lasts until the next interval or even beyond that, we say that the same instances of a task are overlapping. In some cases, this is a not a problem. But there are times, when these tasks are modifying database data or files, which might cause unexpected behaviors, or even data loss.
To prevent critical tasks from overlapping each other, Crunz provides a locking mechanism through `preventOverlapping()` method, which, ensures no task runs if the previous instance is already running.
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->preventOverlapping();
//
```
By default, crunz uses file based locking (if no parameters are passed to `preventOverlapping`). For alternative lock mechanisms, crunz uses the [symfony/lock](https://symfony.com/doc/current/components/lock.html) component that provides lock mechanisms with various stores. To use this component, you can pass a store to the `preventOverlapping()` method. In the following example, the file based `FlockStore` is used to provide an alternative lock file path.
```php
<?php
use Symfony\Component\Lock\Store\FlockStore;
$store = new FlockStore(__DIR__ . '/locks');
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->preventOverlapping($store);
```
## Keeping the Output
Cron jobs usually have outputs, which is normally emailed to the owner of the crontab file, or the user(s) set by the `MAILTO` environment variable inside the crontab file.
We can also redirect the standard output to a physical file using `>` or `>>` operators:
```bash
* * * * * /command/to/run >> /var/log/crons/cron.log
```
This kind of output logging has been automated in Crunz. To automatically send each event's output to a log file, we can set `log_output` and `output_log_file` options in the configuration file accordingly:
```yaml
# Configuration settings
## ...
log_output: true
output_log_file: /var/log/crunz.log
## ...
```
This will send the events' output (if executed successfully) to `/var/log/crunz.log` file. However, we need to make sure we are permitted to write to the respective file.
If we need to log the outputs on an event-basis, we can use `appendOutputTo()` or `sendOutputTo()` methods like this:
```php
<?php
//
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->appendOutputTo('/var/log/crunz/emails.log');
//
```
Method `appendOutputTo()` **appends** the output to the specified file. To override the log file with new data after each run, we use `saveOutputTo()` method.
It is also possible to send the errors as emails to a group of recipients by setting `email_output` and `mailer` settings in the configuration file.
## Error Handling
Crunz makes error handling easy by logging and also allowing you add a set of callbacks in case of an error.
## Error Callbacks
You can set as many callbacks as needed to run in case of an error:
```php
<?php
use Crunz\Schedule;
$schedule = new Schedule();
$task = $schedule->run('command/to/run');
$task->everyFiveMinutes();
$schedule
->onError(function() {
// Send mail
})
->onError(function() {
// Do something else
});
return $schedule;
```
If there's an error the two defined callbacks will be executed.
## Error Logging
To log the possible errors during each run, we can set `log_error` and `error_log_file` settings in the configuration file as below:
```yaml
# Configuration settings
# ...
log_errors: true
errors_log_file: /var/log/error.log
# ...
```
As a result, if the execution of an event is unsuccessful for some reasons, the error message is appended to the specified error log file. Each entry provides useful information including the time it happened, the event description, the executed command which caused the error, and the error message itself.
It is also possible to send the errors as emails to a group of recipients by setting `email_error` and `mailer` settings in the configuration file.
## Custom logger
To use your own logger create class implementing `\Crunz\Application\Service\LoggerFactoryInterface`, for example:
```php
<?php
namespace Vendor\Package;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\Application\Service\LoggerFactoryInterface;
use Psr\Log\AbstractLogger;
use Psr\Log\LoggerInterface;
final class MyEchoLoggerFactory implements LoggerFactoryInterface
{
public function create(ConfigurationInterface $configuration): LoggerInterface
{
return new class extends AbstractLogger {
/** @inheritDoc */
public function log(
$level,
$message,
array $context = array()
) {
echo "crunz.{$level}: {$message}";
}
};
}
}
```
then use this class name in config:
```yaml
# ./crunz.yml file
logger_factory: 'Vendor\Package\MyEchoLoggerFactory'
```
Done.
## Pre-Process and Post-Process Hooks
There are times when we want to do some kind of operations before and after an event. This is possible by attaching pre-process and post-process callbacks to the respective event.
To do this, we use `before()` and `after()` on both `Event` and `Schedule` objects, meaning we can have pre and post hooks on an event-basis as well as schedule basis. The hooks bind to schedule will run before all events, and after all the events are finished.
```php
<?php
use Crunz\Schedule;
$schedule = new Schedule();
$task = $schedule->run(PHP_BINARY . ' email.php');
$task
->everyFiveMinutes()
->before(function() {
// Do something before the task runs
})
->before(function() {
// Do something else
})
->after(function() {
// After the task is run
});
$schedule
->before(function () {
// Do something before all events
})
->after(function () {
// Do something after all events are finished
})
->before(function () {
// Do something before all events
});
```
> We might need to use these methods as many times we need by chaining them.
Post-execution callbacks are only called if the execution of the event has been successful.
## Other Useful Commands
We've already used a few of `crunz` commands like `schedule:run` and `publish:config`.
To see all the valid options and arguments of `crunz`, we can run the following command:
```bash
vendor/bin/crunz --help
```
### Listing Tasks
One of these commands is `crunz schedule:list`, which lists the defined tasks (in collected `*.Tasks.php` files) in a tabular format.
```text
vendor/bin/crunz schedule:list
+---+---------------+-------------+--------------------+
| # | Task | Expression | Command to Run |
+---+---------------+-------------+--------------------+
| 1 | Sample Task | * * * * 1 * | command/to/execute |
+---+---------------+-------------+--------------------+
```
By default, list is in text format, but format can be changed by `--format` option.
List in `json` format, command:
```bash
vendor/bin/crunz schedule:list --format json
```
will output:
```json
[
{
"number": 1,
"task": "Sample Task",
"expression": "* * * * 1",
"command": "command/to/execute"
}
]
```
### Force run
While in development it may be useful to force run all tasks regardless of their actual run time,
which can be achieved by adding `--force` to `schedule:run`:
```bash
vendor/bin/crunz schedule:run --force
```
To force run a single task, use the schedule:list command above to determine the Task number and run as follows:
```bash
vendor/bin/crunz schedule:run --task 1 --force
```
### Generating Tasks
There is also a useful command named `make:task`, which generates a task file skeleton with all the defaults, so we won't have to write them from scratch. We can modify the output file later based on our requirements.
For example, to create a task, which runs `/var/www/script.php` every hour on Mondays, we run the following command:
```text
vendor/bin/crunz make:task exampleOne --run scripts.php --in /var/www --frequency everyHour --constraint mondays
Where do you want to save the file? (Press enter for the current directory)
```
When we run this command, Crunz will ask about the location we want to save the file. By default, it is our source tasks directory.
As a result, the event is defined in a file named `exampleOneTasks.php` within the specified tasks directory.
To see if the event has been created successfully, we list the events:
```text
crunz schedule:list
+---+------------------+-------------+----------------+
| # | Task | Expression | Command to Run |
+---+------------------+-------------+----------------+
| 1 | Task description | 0 * * * 1 * | scripts.php |
+---+------------------+-------------+----------------+
```
To see all the options of `make:task` command with all the defaults, we run this:
```bash
vendor/bin/crunz make:task --help
```
### Debugging tasks
To show basic information about task run:
```bash
vendor/bin/crunz task:debug 1
```
Above command should output something like this:
```text
+----------------------+-----------------------------------+
| Debug information for task '1' |
+----------------------+-----------------------------------+
| Command to run | php -v |
| Description | Inner task |
| Prevent overlapping | No |
+----------------------+-----------------------------------+
| Cron expression | * * * * * |
| Comparisons timezone | Europe/Warsaw (from config) |
+----------------------+-----------------------------------+
| Example run dates |
| #1 | 2020-03-08 09:27:00 Europe/Warsaw |
| #2 | 2020-03-08 09:28:00 Europe/Warsaw |
| #3 | 2020-03-08 09:29:00 Europe/Warsaw |
| #4 | 2020-03-08 09:30:00 Europe/Warsaw |
| #5 | 2020-03-08 09:31:00 Europe/Warsaw |
+----------------------+-----------------------------------+
```
## Configuration
There are a few configuration options provided by Crunz in YAML format. To modify the configuration settings, it is highly recommended to have your own copy of the configuration file, instead of modifying the original one.
To create a copy of the configuration file, first we need to publish the configuration file:
```bash
/project/vendor/bin/crunz publish:config
The configuration file was generated successfully
```
As a result, a copy of the configuration file will be created within our project's root directory.
The configuration file looks like this:
```yaml
# Crunz Configuration Settings
# This option defines where the task files and
# directories reside.
# The path is relative to the project's root directory,
# where the Crunz is installed (Trailing slashes will be ignored).
source: tasks
# The suffix is meant to target the task files inside the ":source" directory.
# Please note if you change this value, you need
# to make sure all the existing tasks files are renamed accordingly.
suffix: Tasks.php
# Timezone is used to calculate task run time
# This option is very important and not setting it is deprecated
# and will result in exception in 2.0 version.
timezone: ~
# This option define which timezone should be used for log files
# If false, system default timezone will be used
# If true, the timezone in config file that is used to calculate task run time will be used
timezone_log: false
# By default the errors are not logged by Crunz
# You may set the value to true for logging the errors
log_errors: false
# This is the absolute path to the errors' log file
# You need to make sure you have the required permission to write to this file though.
errors_log_file:
# By default the output is not logged as they are redirected to the
# null output.
# Set this to true if you want to keep the outputs
log_output: false
# This is the absolute path to the global output log file
# The events which have dedicated log files (defined with them), won't be
# logged to this file though.
output_log_file:
# By default line breaks in logs aren't allowed.
# Set the value to true to allow them.
log_allow_line_breaks: false
# By default empty context arrays are shown in the log.
# Set the value to true to remove them.
log_ignore_empty_context: false
# This option determines whether the output should be emailed or not.
email_output: false
# This option determines whether the error messages should be emailed or not.
email_errors: false
# Global Swift Mailer settings
#
mailer:
# Possible values: smtp, mail, and sendmail
transport: smtp
recipients:
sender_name:
sender_email:
# SMTP settings
#
smtp:
host:
port:
username:
password:
encryption:
```
As you can see there are a few options like `source` which is used to specify the source tasks directory. The other options are used for error/output logging/emailing purposes.
Each time we run Crunz commands, it will look into the project's root directory to see if there's any user-modified configuration file. If the configuration file doesn't exists, it will use the one shipped with the package.
## Setting the base cache directory
You can change the base cache directory of crunz by setting the `CRUNZ_BASE_CACHE_DIR` environment variable.
The default base cache directory is `\sys_get_temp_dir()`. The subdirectory `.crunz` is always added to the base cache directory.
## Development ENV flags
The following environment flags should be used only while in development.
Typical end-users do not need to, and should not, change them.
### `CRUNZ_CONTAINER_DEBUG`
Flag used to enable/disable container debug mode, useful only for development.
Enabled by default in `docker-compose`.
### `CRUNZ_DEPRECATION_HANDLER`
Flag used to enable/disable Crunz deprecation handler, useful only for integration tests.
Disabled by default for tests.
## Sponsors
[](https://www.blackfire.io/?utm_source=crunz&utm_medium=readme&utm_campaign=free-open-source)
## Support
You can support further Crunz development by [GitHub](https://github.com/sponsors/PabloKowalczyk).
## Contributing
### Which branch should I choose?
Bug fixes and readme changes should target `3.9`, new features should target `3.10`.
## If You Need Help
Please submit all issues and questions using GitHub issues and I will try to help you.
## Credits
* [PabloKowalczyk](https://github.com/PabloKowalczyk)
* [Reza Lavarian](https://github.com/lavary)
* [All Contributors](https://github.com/crunzphp/crunz/graphs/contributors)
## License
Crunz is free software distributed under the terms of the MIT license.
================================================
FILE: UPGRADE.md
================================================
# Upgrading from v3.2 to v3.3
## Pass only string parameters to `\Crunz\Schedule::run`
Convert this:
```php
$schedule = new Schedule();
$schedule->run('php', ['-v' => true, 2]);
```
into this:
```php
$schedule = new Schedule();
$schedule->run('php', ['-v' => '1', '2']);
```
# Upgrading from v1.12 to v2.0
## Stop using `mail` transport for mailer
As of `v6.0` SwiftMailer dropped support for `mail` transport,
so `Crunz` `v2.0` won't support it either,
please use `smtp` or `sendmail` transport.
# Upgrading from v1.11 to v1.12
## Always return `\Crunz\Schedule` from task files
Example of wrong task file:
```php
<?php
return [];
```
Example of correct task file:
```php
<?php
use Crunz\Schedule;
$scheduler = new Schedule();
$scheduler
->run('php -v')
->description('PHP version')
->everyMinute();
// Crunz\Schedule instance returned
return $scheduler;
```
## Stop using `\Crunz\Event::setProcess`
If you, for some reason, use above method you should stop it.
This method was intended to be `private` and will be in `v2.0`,
which will lead to exception if you call it.
Example of wrong usage
```php
<?php
use Crunz\Schedule;
$process = new \Symfony\Component\Process\Process('php -i');
$scheduler = new Schedule();
$task = $scheduler->run('php -v');
$task
// setProcess is deprecated
->setProcess($process)
->description('PHP version')
->everyMinute()
;
return $scheduler;
```
# Upgrading from v1.10 to v1.11
## Run `Crunz` in directory with your `crunz.yml`
Searching for Crunz's config is now related to `cwd`, not to `vendor/bin/crunz`.
For example, if your `crunz.yml` is in `/var/www/project/crunz.yml`, then run Crunz with `cd` first:
```bash
cd /var/www/project && vendor/bin/crunz schedule:list
```
Cron job also should be changed:
```bash
* * * * * cd /var/www/project && vendor/bin/crunz schedule:run
```
# Upgrading from v1.9 to v1.10
### Do not pass more than five parts to `Crunz\Event::cron()`
Example correct call:
```yaml
$event = new Crunz\Event;
$event->cron('0 * * * *');
```
# Upgrading from v1.7 to v1.8
### Add `timezone` to your `crunz.yml`
Example config file:
```yaml
source: tasks
suffix: Tasks.php
timezone: Europe/Warsaw
```
================================================
FILE: bootstrap.php
================================================
<?php
require_once __DIR__ . '/vendor/autoload.php';
if (!\defined('IS_WINDOWS')) {
\define('IS_WINDOWS', PHP_OS_FAMILY === "Windows");
}
// Disable deprecation helper
$envFlags = new \Crunz\EnvFlags\EnvFlags();
$envFlags->disableDeprecationHandler();
// Make sure current working directory is "tests"
$filesystem = new \Crunz\Filesystem\Filesystem();
if (\str_contains($filesystem->getCwd(), 'tests')) {
return;
}
if (!\chdir('tests')) {
throw new RuntimeException("Unable to change current directory to 'tests'.");
}
================================================
FILE: composer.json
================================================
{
"name": "crunzphp/crunz",
"description": "Schedule your tasks right from the code.",
"license": "MIT",
"type": "library",
"keywords": [
"scheduler",
"cron jobs",
"cron",
"Task Scheduler",
"PHP Task Scheduler",
"Job Scheduler",
"Job Manager",
"Event Runner"
],
"authors": [
{
"name": "Reza M. Lavaryan",
"email": "mrl.8081@gmail.com"
},
{
"name": "PabloKowalczyk",
"homepage": "https://github.com/PabloKowalczyk",
"role": "Developer"
}
],
"homepage": "https://github.com/crunzphp/crunz",
"support": {
"issues": "https://github.com/crunzphp/crunz/issues"
},
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/PabloKowalczyk"
}
],
"require": {
"php": ">=8.2",
"composer-runtime-api": "^2.0",
"dragonmantank/cron-expression": "^3.4.0",
"laravel/serializable-closure": "^2.0",
"psr/log": "^2.0 || ^3.0",
"symfony/config": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/console": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/dependency-injection": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/filesystem": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/lock": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/mailer": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/process": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/string": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/yaml": "^6.4.25 || ^7.4.0 || ^8.0.0"
},
"require-dev": {
"ext-json": "*",
"ext-mbstring": "*",
"ergebnis/composer-normalize": "2.28.3",
"friendsofphp/php-cs-fixer": "3.90.0",
"phpstan/phpstan": "2.0.2",
"phpstan/phpstan-phpunit": "2.0.1",
"phpstan/phpstan-strict-rules": "2.0.0",
"phpunit/phpunit": "10.5.63",
"symfony/error-handler": "^6.4.25 || ^7.4.0 || ^8.0.0",
"symfony/phpunit-bridge": "^6.4.25 || ^7.4.0 || ^8.0.0"
},
"conflict": {
"laravel/serializable-closure": ">=2.0.9,<2.0.11"
},
"minimum-stability": "beta",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Crunz\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Crunz\\Tests\\": "tests/"
}
},
"bin": [
"crunz"
],
"config": {
"allow-plugins": {
"ergebnis/composer-normalize": true
},
"sort-packages": true
},
"scripts": {
"crunz:analyze": [
"@php vendor/bin/php-cs-fixer fix --diff --dry-run -v",
"@phpstan:check"
],
"crunz:cs-fix": "@php vendor/bin/php-cs-fixer fix --diff -v --ansi",
"phpstan:check": "@php vendor/bin/phpstan analyse -c phpstan.neon src tests crunz config bootstrap.php"
}
}
================================================
FILE: config/services.php
================================================
<?php
declare(strict_types=1);
use Crunz\Application\Cron\CronExpressionFactoryInterface;
use Crunz\Application\Query\TaskInformation\TaskInformationHandler;
use Crunz\Application\Service\ClosureSerializerInterface;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\Clock\Clock;
use Crunz\Clock\ClockInterface;
use Crunz\Configuration\Configuration;
use Crunz\Configuration\ConfigurationParser;
use Crunz\Configuration\ConfigurationParserInterface;
use Crunz\Configuration\Definition;
use Crunz\Configuration\FileParser;
use Crunz\Console\Command\ConfigGeneratorCommand;
use Crunz\Console\Command\ScheduleListCommand;
use Crunz\Console\Command\ScheduleRunCommand;
use Crunz\Console\Command\TaskGeneratorCommand;
use Crunz\EventRunner;
use Crunz\Filesystem\Filesystem as CrunzFilesystem;
use Crunz\Filesystem\FilesystemInterface;
use Crunz\Finder\Finder;
use Crunz\Finder\FinderInterface;
use Crunz\HttpClient\CurlHttpClient;
use Crunz\HttpClient\FallbackHttpClient;
use Crunz\HttpClient\HttpClientInterface;
use Crunz\HttpClient\HttpClientLoggerDecorator;
use Crunz\HttpClient\StreamHttpClient;
use Crunz\Infrastructure\Dragonmantank\CronExpression\DragonmantankCronExpressionFactory;
use Crunz\Infrastructure\Laravel\LaravelClosureSerializer;
use Crunz\Invoker;
use Crunz\Logger\ConsoleLogger;
use Crunz\Logger\ConsoleLoggerInterface;
use Crunz\Logger\LoggerFactory;
use Crunz\Mailer;
use Crunz\Output\OutputFactory;
use Crunz\Schedule\ScheduleFactory;
use Crunz\Task\Collection;
use Crunz\Task\CollectionInterface;
use Crunz\Task\Loader;
use Crunz\Task\LoaderInterface;
use Crunz\Task\Timezone;
use Crunz\Timezone\Provider;
use Crunz\Timezone\ProviderInterface;
use Crunz\UserInterface\Cli\ClosureRunCommand;
use Crunz\UserInterface\Cli\DebugTaskCommand;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Yaml;
$simpleServices = [
Definition::class,
Yaml::class,
Processor::class,
Invoker::class,
ProviderInterface::class => Provider::class,
Filesystem::class,
ScheduleFactory::class,
StreamHttpClient::class,
CurlHttpClient::class,
FilesystemInterface::class => CrunzFilesystem::class,
FinderInterface::class => Finder::class,
LoaderInterface::class => Loader::class,
CronExpressionFactoryInterface::class => DragonmantankCronExpressionFactory::class,
ClosureSerializerInterface::class => LaravelClosureSerializer::class,
ClockInterface::class => Clock::class,
];
/* @var ContainerBuilder $container */
$container
->register(ScheduleRunCommand::class, ScheduleRunCommand::class)
->setPublic(true)
->setArguments(
[
new Reference(CollectionInterface::class),
new Reference(ConfigurationInterface::class),
new Reference(EventRunner::class),
new Reference(Timezone::class),
new Reference(ScheduleFactory::class),
new Reference(LoaderInterface::class),
]
)
;
$container
->register(ClosureRunCommand::class, ClosureRunCommand::class)
->setArguments(
[
new Reference(ClosureSerializerInterface::class),
]
)
->setPublic(true)
;
$container
->register(ConfigGeneratorCommand::class, ConfigGeneratorCommand::class)
->setPublic(true)
->setArguments(
[
new Reference(ProviderInterface::class),
new Reference(Filesystem::class),
new Reference(FilesystemInterface::class),
]
)
;
$container
->register(ScheduleListCommand::class, ScheduleListCommand::class)
->setPublic(true)
->setArguments(
[
new Reference(ConfigurationInterface::class),
new Reference(CollectionInterface::class),
new Reference(LoaderInterface::class),
]
)
;
$container
->register(TaskGeneratorCommand::class, TaskGeneratorCommand::class)
->setPublic(true)
->setArguments(
[
new Reference(ConfigurationInterface::class),
new Reference(FilesystemInterface::class),
]
)
;
$container
->register(DebugTaskCommand::class, DebugTaskCommand::class)
->setPublic(true)
->setArguments(
[
new Reference(TaskInformationHandler::class),
]
)
;
$container
->register(CollectionInterface::class, Collection::class)
->setPublic(false)
->setArguments(
[
new Reference(ConfigurationInterface::class),
new Reference(FinderInterface::class),
new Reference(ConsoleLoggerInterface::class),
]
)
;
$container
->register(FileParser::class, FileParser::class)
->setPublic(false)
->setArguments(
[
new Reference(Yaml::class),
]
)
;
$container
->register(ConfigurationInterface::class, Configuration::class)
->setPublic(false)
->setArguments(
[
new Reference(ConfigurationParserInterface::class),
new Reference(FilesystemInterface::class),
]
)
;
$container
->register(Mailer::class, Mailer::class)
->setPublic(false)
->setArguments(
[
new Reference(ConfigurationInterface::class),
]
)
;
$container
->register(LoggerFactory::class, LoggerFactory::class)
->setPublic(false)
->setArguments(
[
new Reference(ConfigurationInterface::class),
new Reference(Timezone::class),
new Reference(ConsoleLoggerInterface::class),
new Reference(ClockInterface::class),
]
)
;
$container
->register(EventRunner::class, EventRunner::class)
->setPublic(false)
->setArguments(
[
new Reference(Invoker::class),
new Reference(ConfigurationInterface::class),
new Reference(Mailer::class),
new Reference(LoggerFactory::class),
new Reference(HttpClientInterface::class),
new Reference(ConsoleLoggerInterface::class),
]
)
;
$container
->register(Timezone::class, Timezone::class)
->setPublic(false)
->setArguments(
[
new Reference(ConfigurationInterface::class),
new Reference(ConsoleLoggerInterface::class),
]
)
;
$container
->register(OutputInterface::class, ConsoleOutput::class)
->setPublic(true)
->setFactory([new Reference(OutputFactory::class), 'createOutput'])
;
$container
->register(OutputFactory::class, OutputFactory::class)
->setPublic(false)
->setArguments(
[
new Reference(InputInterface::class),
]
)
;
$container
->register(InputInterface::class, ArgvInput::class)
->setPublic(true)
;
$container
->register(SymfonyStyle::class, SymfonyStyle::class)
->setPublic(true)
->setArguments(
[
new Reference(InputInterface::class),
new Reference(OutputInterface::class),
]
)
;
$container
->register(ConsoleLoggerInterface::class, ConsoleLogger::class)
->setPublic(false)
->setArguments(
[
new Reference(SymfonyStyle::class),
]
)
;
$container
->register(ConsoleLoggerInterface::class, ConsoleLogger::class)
->setPublic(false)
->setArguments(
[
new Reference(SymfonyStyle::class),
]
)
;
$container
->register(FallbackHttpClient::class, FallbackHttpClient::class)
->setPublic(false)
->setArguments(
[
new Reference(StreamHttpClient::class),
new Reference(CurlHttpClient::class),
new Reference(ConsoleLoggerInterface::class),
]
)
;
$container
->register(HttpClientInterface::class, HttpClientLoggerDecorator::class)
->setPublic(false)
->setArguments(
[
new Reference(FallbackHttpClient::class),
new Reference(ConsoleLoggerInterface::class),
]
)
;
$container
->register(ConfigurationParserInterface::class, ConfigurationParser::class)
->setPublic(false)
->setArguments(
[
new Reference(Definition::class),
new Reference(Processor::class),
new Reference(FileParser::class),
new Reference(ConsoleLoggerInterface::class),
new Reference(FilesystemInterface::class),
]
)
;
$container
->register(TaskInformationHandler::class, TaskInformationHandler::class)
->setPublic(false)
->setArguments(
[
new Reference(Timezone::class),
new Reference(ConfigurationInterface::class),
new Reference(CollectionInterface::class),
new Reference(LoaderInterface::class),
new Reference(ScheduleFactory::class),
new Reference(CronExpressionFactoryInterface::class),
]
)
;
foreach ($simpleServices as $id => $simpleService) {
if (!\is_string($id)) {
$id = $simpleService;
}
$container
->register($id, $simpleService)
->setPublic(false)
;
}
================================================
FILE: crunz
================================================
#!/usr/bin/env php
<?php
/*
|--------------------------------------------------------------------------
| Crunz
|--------------------------------------------------------------------------
|
| This file is part of Crunz library.
| (c) Reza M. Lavaryan <mrl.8081@gmail.com>
| For the full copyright and license information, please view the LICENSE
| file that was distributed with this source code.
|
*/
use Composer\InstalledVersions;
use Crunz\Application;
if (!\defined('CRUNZ_BIN')) {
\define('CRUNZ_BIN', __FILE__);
}
$generatePath = static fn(string ...$parts): string => \implode(DIRECTORY_SEPARATOR, $parts);
$autoloadPaths = [
// Dependency
$generatePath(
\dirname(__DIR__, 2),
'autoload.php'
),
// Vendor/Bin
$generatePath(
\dirname(__DIR__),
'autoload.php'
),
// Local dev
$generatePath(
__DIR__,
'vendor',
'autoload.php'
),
];
$loadAutoloader = static function () use($autoloadPaths): void {
foreach ($autoloadPaths as $autoloadPath) {
if (\file_exists($autoloadPath) === true) {
require_once $autoloadPath;
return;
}
}
throw new RuntimeException(
\sprintf(
'Unable to find "vendor/autoload.php" in "%s" paths.',
\implode('", "', $autoloadPaths)
)
);
};
$loadAutoloader();
$application = new Application(
'Crunz Command Line Interface',
InstalledVersions::getPrettyVersion('crunzphp/crunz') ?? '1.0.x-dev',
);
$application->run();
================================================
FILE: docker/php82/Dockerfile
================================================
FROM php:8.2.30-cli-alpine
RUN apk add --no-cache \
shadow \
su-exec && \
usermod --non-unique --uid 1000 www-data && \
apk del \
shadow && \
docker-php-ext-install -j$(nproc) \
opcache \
sysvsem
RUN mkdir -p \
/var/log/php \
/var/www/.composer \
&& touch /var/log/php/error.log \
&& chown www-data:www-data \
/var/log/php/error.log \
/var/www/.composer
COPY --from=composer/composer:2.9.3-bin /composer /usr/bin/composer
ENV COMPOSER_HOME /var/www/.composer
================================================
FILE: docker/php82/php.ini
================================================
realpath_cache_size = 8192k
realpath_cache_ttl = 6000
expose_php = On
error_log = /var/log/php/error.log
error_reporting = E_ALL
display_errors = On
display_startup_errors = On
log_errors = On
report_memleaks = On
memory_limit = 80M
date.timezone = "UTC"
zend.assertions = 1
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=80
opcache.interned_strings_buffer=5
opcache.max_accelerated_files=3000
opcache.validate_timestamps=1
opcache.revalidate_freq=0
opcache.save_comments=1
opcache.fast_shutdown=1
opcache.huge_code_pages=1
================================================
FILE: docker-compose.yml
================================================
services:
php82:
build:
context: ./docker/php82
working_dir: /var/www/html
environment:
CRUNZ_CONTAINER_DEBUG: 1
command: >
sh -c "
chown -R www-data:www-data /var/www/.composer && \
echo 'Logs from /var/log/php/error.log:' && \
touch /var/log/php/error.log && \
tail -f /var/log/php/error.log
"
volumes:
- .:/var/www/html
- ./docker/php82/php.ini:/usr/local/etc/php/php.ini:ro
stop_grace_period: 1s
================================================
FILE: phpstan-baseline.neon
================================================
parameters:
ignoreErrors:
-
message: "#^Parameter \\#5 \\$timeZone of class Crunz\\\\Application\\\\Query\\\\TaskInformation\\\\TaskInformationView constructor expects DateTimeZone\\|null, mixed given\\.$#"
count: 1
path: src/Application/Query/TaskInformation/TaskInformationHandler.php
-
message: "#^Variable property access on \\$this\\(Crunz\\\\Application\\\\Query\\\\TaskInformation\\\\TaskInformationHandler\\)\\.$#"
count: 1
path: src/Application/Query/TaskInformation/TaskInformationHandler.php
-
message: "#^Parameter \\#1 \\$parts of static method Crunz\\\\Path\\\\Path\\:\\:create\\(\\) expects array\\<string\\>, array\\<int, mixed\\> given\\.$#"
count: 1
path: src/Configuration/Configuration.php
-
message: "#^Method Crunz\\\\Configuration\\\\ConfigurationParser\\:\\:parseConfig\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Configuration/ConfigurationParser.php
-
message: "#^Variable \\$cwd on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
path: src/Configuration/ConfigurationParser.php
-
message: "#^Method Crunz\\\\Configuration\\\\ConfigurationParserInterface\\:\\:parseConfig\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Configuration/ConfigurationParserInterface.php
-
message: "#^Method Crunz\\\\Configuration\\\\FileParser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Configuration/FileParser.php
-
message: "#^Method Crunz\\\\Configuration\\\\FileParser\\:\\:parse\\(\\) should return array\\<array\\> but returns array\\<int, mixed\\>\\.$#"
count: 1
path: src/Configuration/FileParser.php
-
message: "#^Property Crunz\\\\Console\\\\Command\\\\Command\\:\\:\\$arguments type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Console/Command/Command.php
-
message: "#^Property Crunz\\\\Console\\\\Command\\\\Command\\:\\:\\$options type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Console/Command/Command.php
-
message: "#^Method Crunz\\\\Console\\\\Command\\\\ConfigGeneratorCommand\\:\\:askForTimezone\\(\\) should return string but returns mixed\\.$#"
count: 1
path: src/Console/Command/ConfigGeneratorCommand.php
-
message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#"
count: 1
path: src/Console/Command/ConfigGeneratorCommand.php
-
message: "#^Only booleans are allowed in a negated boolean, int\\<0, max\\> given\\.$#"
count: 1
path: src/Console/Command/ScheduleListCommand.php
-
message: "#^Only booleans are allowed in a negated boolean, int\\<0, max\\> given\\.$#"
count: 2
path: src/Console/Command/ScheduleRunCommand.php
-
message: "#^Binary operation \"\\.\" between array\\|string\\|null and mixed results in an error\\.$#"
count: 1
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#"
count: 1
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Method Crunz\\\\Console\\\\Command\\\\TaskGeneratorCommand\\:\\:type\\(\\) should return string but returns array\\|bool\\|float\\|int\\|string\\|null\\.$#"
count: 1
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Only booleans are allowed in an if condition, string given\\.$#"
count: 1
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Parameter \\#1 \\$string of function rtrim expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#"
count: 2
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Parameter \\#2 \\$replace of function str_replace expects array\\|string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#"
count: 3
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Return type \\(int\\|null\\) of method Crunz\\\\Console\\\\Command\\\\TaskGeneratorCommand\\:\\:execute\\(\\) should be covariant with return type \\(int\\) of method Symfony\\\\Component\\\\Console\\\\Command\\\\Command\\:\\:execute\\(\\)$#"
count: 1
path: src/Console/Command/TaskGeneratorCommand.php
-
message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#"
count: 1
path: src/Event.php
-
message: "#^Cannot cast mixed to string\\.$#"
count: 1
path: src/Event.php
-
message: "#^Instanceof between Closure and Closure will always evaluate to true\\.$#"
count: 1
path: src/Event.php
-
message: "#^Instanceof between DateTimeZone and DateTimeZone will always evaluate to true\\.$#"
count: 1
path: src/Event.php
-
message: "#^Only booleans are allowed in a negated boolean, mixed given\\.$#"
count: 1
path: src/Event.php
-
message: "#^Only booleans are allowed in an if condition, DateTimeZone\\|string given\\.$#"
count: 1
path: src/Event.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Event.php
-
message: "#^Only booleans are allowed in an if condition, string given\\.$#"
count: 3
path: src/Event.php
-
message: "#^Property Crunz\\\\Event\\:\\:\\$lock \\(Symfony\\\\Component\\\\Lock\\\\Lock\\) does not accept Symfony\\\\Component\\\\Lock\\\\SharedLockInterface\\.$#"
count: 1
path: src/Event.php
-
message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#"
count: 1
path: src/Event.php
-
message: "#^Strict comparison using \\=\\=\\= between false and array\\<string, mixed\\> will always evaluate to false\\.$#"
count: 1
path: src/Event.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/EventRunner.php
-
message: "#^Only booleans are allowed in &&, mixed given on the left side\\.$#"
count: 2
path: src/EventRunner.php
-
message: "#^Only booleans are allowed in a negated boolean, int\\<0, max\\> given\\.$#"
count: 1
path: src/EventRunner.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/EventRunner.php
-
message: "#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#"
count: 1
path: src/EventRunner.php
-
message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(array\\)\\: SplFileInfo given\\.$#"
count: 1
path: src/Finder/Finder.php
-
message: "#^Method Crunz\\\\Infrastructure\\\\Laravel\\\\LaravelClosureSerializer\\:\\:extractWrapper\\(\\) should return Laravel\\\\SerializableClosure\\\\SerializableClosure but returns mixed\\.$#"
count: 1
path: src/Infrastructure/Laravel/LaravelClosureSerializer.php
-
message: "#^Method Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\EnabledLoggerDecorator\\:\\:log\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/EnabledLoggerDecorator.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLogger.php
-
message: "#^Method Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger\\:\\:log\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLogger.php
-
message: "#^Parameter \\#1 \\$message of method Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger\\:\\:replaceNewlines\\(\\) expects string, string\\|Stringable given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLogger.php
-
message: "#^Parameter \\#1 \\$string of function mb_strtoupper expects string, mixed given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLogger.php
-
message: "#^Parameter \\#3 \\$outputStreamPath of class Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger constructor expects string\\|null, mixed given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php
-
message: "#^Parameter \\#4 \\$errorStreamPath of class Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger constructor expects string\\|null, mixed given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php
-
message: "#^Parameter \\#5 \\$ignoreEmptyContext of class Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger constructor expects bool, mixed given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php
-
message: "#^Parameter \\#6 \\$timezoneLog of class Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger constructor expects bool, mixed given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php
-
message: "#^Parameter \\#7 \\$allowLineBreaks of class Crunz\\\\Infrastructure\\\\Psr\\\\Logger\\\\PsrStreamLogger constructor expects bool, mixed given\\.$#"
count: 1
path: src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php
-
message: "#^Method Crunz\\\\Logger\\\\LoggerFactory\\:\\:createLoggerFactory\\(\\) should return Crunz\\\\Application\\\\Service\\\\LoggerFactoryInterface but returns object\\.$#"
count: 1
path: src/Logger/LoggerFactory.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Logger/LoggerFactory.php
-
message: "#^Parameter \\#1 \\$class of function class_exists expects string, mixed given\\.$#"
count: 1
path: src/Logger/LoggerFactory.php
-
message: "#^Part \\$loggerFactoryClass \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 2
path: src/Logger/LoggerFactory.php
-
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Only booleans are allowed in an if condition, Symfony\\\\Component\\\\Mailer\\\\Mailer\\|null given\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Parameter \\#1 \\$address of class Symfony\\\\Component\\\\Mime\\\\Address constructor expects string, mixed given\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Parameter \\#1 \\.\\.\\.\\$addresses of method Symfony\\\\Component\\\\Mime\\\\Email\\:\\:addTo\\(\\) expects string\\|Symfony\\\\Component\\\\Mime\\\\Address, mixed given\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Parameter \\#2 \\$name of class Symfony\\\\Component\\\\Mime\\\\Address constructor expects string, mixed given\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Part \\$host \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Part \\$password \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Part \\$port \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Part \\$user \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/Mailer.php
-
message: "#^Only booleans are allowed in \\|\\|, mixed given on the right side\\.$#"
count: 1
path: src/Output/OutputFactory.php
-
message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#"
count: 1
path: src/Schedule.php
-
message: "#^Only booleans are allowed in &&, int\\<0, max\\> given on the right side\\.$#"
count: 1
path: src/Schedule.php
-
message: "#^Parameter \\#2 \\$suffix of method Crunz\\\\Finder\\\\FinderInterface\\:\\:find\\(\\) expects string, mixed given\\.$#"
count: 1
path: src/Task/Collection.php
-
message: "#^Part \\$suffix \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/Task/Collection.php
-
message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#"
count: 1
path: src/Task/TaskNumber.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1
path: src/Task/Timezone.php
-
message: "#^Parameter \\#1 \\$timezone of class DateTimeZone constructor expects string, mixed given\\.$#"
count: 1
path: src/Task/Timezone.php
-
message: "#^Part \\$newTimezone \\(mixed\\) of encapsed string cannot be cast to string\\.$#"
count: 2
path: src/Task/Timezone.php
-
message: "#^Return type \\(int\\|null\\) of method Crunz\\\\UserInterface\\\\Cli\\\\ClosureRunCommand\\:\\:execute\\(\\) should be covariant with return type \\(int\\) of method Symfony\\\\Component\\\\Console\\\\Command\\\\Command\\:\\:execute\\(\\)$#"
count: 1
path: src/UserInterface/Cli/ClosureRunCommand.php
-
message: "#^Method Crunz\\\\Tests\\\\EndToEnd\\\\WrongTaskTest\\:\\:scheduleInstanceProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/EndToEnd/WrongTaskTest.php
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:setInputStream\\(\\)\\.$#"
count: 1
path: tests/Functional/TaskGeneratorTest.php
-
message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Tester\\\\CommandTester and 'setInputs' will always evaluate to true\\.$#"
count: 1
path: tests/Functional/TaskGeneratorTest.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 2
path: tests/TestCase/EndToEnd/Environment/Environment.php
-
message: "#^Casting to string something that's already string\\.$#"
count: 1
path: tests/TestCase/EndToEndTestCase.php
-
message: "#^Cannot cast mixed to string\\.$#"
count: 1
path: tests/TestCase/FakeConfiguration.php
-
message: "#^Method Crunz\\\\Tests\\\\TestCase\\\\FakeConfiguration\\:\\:__construct\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
count: 1
path: tests/TestCase/FakeConfiguration.php
-
message: "#^Property Crunz\\\\Tests\\\\TestCase\\\\FakeConfiguration\\:\\:\\$config \\(array\\<int\\|string, array\\|bool\\|string\\|null\\>\\) does not accept array\\<int\\|string, mixed\\>\\.$#"
count: 1
path: tests/TestCase/FakeConfiguration.php
-
message: "#^Property Crunz\\\\Tests\\\\TestCase\\\\FakeConfiguration\\:\\:\\$config type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/TestCase/FakeConfiguration.php
-
message: "#^Parameter \\#1 \\$timezone of class DateTimeZone constructor expects string, mixed given\\.$#"
count: 1
path: tests/TestCase/Faker.php
-
message: "#^Method Crunz\\\\Tests\\\\TestCase\\\\Logger\\\\SpyPsrLogger\\:\\:getLogs\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/TestCase/Logger/SpyPsrLogger.php
-
message: "#^Method Crunz\\\\Tests\\\\TestCase\\\\Logger\\\\SpyPsrLogger\\:\\:log\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#"
count: 1
path: tests/TestCase/Logger/SpyPsrLogger.php
-
message: "#^Property Crunz\\\\Tests\\\\TestCase\\\\Logger\\\\SpyPsrLogger\\:\\:\\$logs type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/TestCase/Logger/SpyPsrLogger.php
-
message: "#^Offset 'uri' on array\\{timed_out\\: bool, blocked\\: bool, eof\\: bool, unread_bytes\\: int, stream_type\\: string, wrapper_type\\: string, wrapper_data\\: mixed, mode\\: string, \\.\\.\\.\\} on left side of \\?\\? always exists and is not nullable\\.$#"
count: 1
path: tests/TestCase/TemporaryFile.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Application\\\\Cron\\\\AbstractCronExpressionTest\\:\\:multipleRunDatesProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Application/Cron/AbstractCronExpressionTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Application\\\\Query\\\\TaskInformation\\\\TaskInformationHandlerTest\\:\\:taskInformationProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Application/Query/TaskInformation/TaskInformationHandlerTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Configuration\\\\ConfigurationTest\\:\\:createConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Configuration/ConfigurationTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\EnvFlags\\\\EnvFlagsTest\\:\\:containerDebugProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/EnvFlags/EnvFlagsTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\EnvFlags\\\\EnvFlagsTest\\:\\:statusProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/EnvFlags/EnvFlagsTest.php
-
message: "#^Call to method expects\\(\\) on an unknown class Symfony\\\\Component\\\\Lock\\\\StoreInterface\\.$#"
count: 1
path: tests/Unit/EventRunnerTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\EventTest\\:\\:deprecatedEveryProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/EventTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\EventTest\\:\\:everyMethodProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/EventTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\EventTest\\:\\:hourlyAtInvalidProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/EventTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Filesystem\\\\FilesystemTest\\:\\:fileExistsProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Filesystem/FilesystemTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Finder\\\\FinderTest\\:\\:tasksProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Finder/FinderTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Infrastructure\\\\Psr\\\\Logger\\\\EnabledLoggerDecoratorTest\\:\\:disabledChannelProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Infrastructure/Psr/Logger/EnabledLoggerDecoratorTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Infrastructure\\\\Psr\\\\Logger\\\\EnabledLoggerDecoratorTest\\:\\:enabledChannelProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Infrastructure/Psr/Logger/EnabledLoggerDecoratorTest.php
-
message: "#^Parameter \\#1 \\$config of class Crunz\\\\Tests\\\\TestCase\\\\FakeConfiguration constructor expects array\\<int\\|string, array\\|bool\\|string\\|null\\>, array\\<string, mixed\\> given\\.$#"
count: 1
path: tests/Unit/Logger/LoggerFactoryTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Output\\\\OutputFactoryTest\\:\\:inputProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Output/OutputFactoryTest.php
-
message: "#^Call to function is_string\\(\\) with string will always evaluate to true\\.$#"
count: 1
path: tests/Unit/Pingable.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Pinger\\\\PingableTest\\:\\:nonStringProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Pinger/PingableTest.php
-
message: "#^Parameter \\#1 \\$url of method Crunz\\\\Tests\\\\Unit\\\\Pingable\\:\\:pingBefore\\(\\) expects string, mixed given\\.$#"
count: 1
path: tests/Unit/Pinger/PingableTest.php
-
message: "#^Parameter \\#1 \\$url of method Crunz\\\\Tests\\\\Unit\\\\Pingable\\:\\:thenPing\\(\\) expects string, mixed given\\.$#"
count: 1
path: tests/Unit/Pinger/PingableTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Task\\\\TaskNumberTest\\:\\:nonNumericProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Task/TaskNumberTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Task\\\\TaskNumberTest\\:\\:nonStringValueProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Task/TaskNumberTest.php
-
message: "#^Method Crunz\\\\Tests\\\\Unit\\\\Task\\\\TaskNumberTest\\:\\:numericValueProvider\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: tests/Unit/Task/TaskNumberTest.php
-
message: "#^Parameter \\#1 \\$value of static method Crunz\\\\Task\\\\TaskNumber\\:\\:fromString\\(\\) expects string, mixed given\\.$#"
count: 1
path: tests/Unit/Task/TaskNumberTest.php
================================================
FILE: phpstan.neon
================================================
parameters:
level: 9
reportUnmatchedIgnoredErrors: false
inferPrivatePropertyTypeFromConstructor: true
ignoreErrors:
-
message: '#Variable \$container might not be defined#'
path: config/services.php
-
message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\)#'
path: src/Configuration/Definition.php
-
message: '#Variable \$configFile might not be defined#'
path: src/Configuration/ConfigurationParser.php
-
message: '#Call to an undefined method Crunz\\Event::DummyFrequency\(\)#'
path: src/Stubs/BasicTask.php
-
message: '#Parameter \#1 \$command of static method Symfony\\Component\\Process\\Process::fromShellCommandline\(\) expects string#'
path: src/Process/Process.php
-
message: '#Result of#'
path: src/Event.php
-
message: '#Parameter \#1 \$store of class#'
path: src/Event.php
-
message: '#CrunzContainer#'
path: src/Application.php
-
message: '#Parameter \#2 \$currentTime#'
path: src/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpression.php
includes:
- phpstan-baseline.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
================================================
FILE: phpunit.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
bootstrap="bootstrap.php"
backupGlobals="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutTodoAnnotatedTests="true"
verbose="true"
colors="true"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="EndToEnd">
<directory suffix="Test.php">tests/EndToEnd</directory>
</testsuite>
<testsuite name="Integration">
<directory suffix="Test.php">tests/Functional</directory>
</testsuite>
<testsuite name="Unit">
<directory suffix="Test.php">tests/Unit</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"/>
</listeners>
</phpunit>
================================================
FILE: resources/config/crunz.yml
================================================
# Crunz Configuration Settings
# This option defines where the task files and
# directories reside.
# The path is relative to this config file.
# Trailing slashes will be ignored.
source: tasks
# The suffix is meant to target the task files inside the ":source" directory.
# Please note if you change this value, you need
# to make sure all the existing tasks files are renamed accordingly.
suffix: Tasks.php
# Timezone is used to calculate task run time
# This option is very important and not setting it is deprecated
# and will result in exception in 2.0 version.
timezone: ~
# This option define which timezone should be used for log files
# If false, system default timezone will be used
# If true, the timezone in config file that is used to calculate task run time will be used
timezone_log: false
# By default the errors are not logged by Crunz
# You may set the value to true for logging the errors
log_errors: false
# This is the absolute path to the errors' log file
# You need to make sure you have the required permission to write to this file though.
errors_log_file: ~
# By default the output is not logged as they are redirected to the
# null output.
# Set this to true if you want to keep the outputs
log_output: false
# This is the absolute path to the global output log file
# The events which have dedicated log files (defined with them), won't be
# logged to this file though.
output_log_file: ~
# By default line breaks in logs aren't allowed.
# Set the value to true to allow them.
log_allow_line_breaks: false
# By default empty context arrays are shown in the log.
# Set the value to true to remove them.
log_ignore_empty_context: false
# This option determines whether the output should be emailed or not.
email_output: false
# This option determines whether the error messages should be emailed or not.
email_errors: false
# Global Swift Mailer settings
mailer:
# Possible values: smtp, mail, and sendmail
transport: smtp
recipients:
sender_name:
sender_email:
# SMTP settings
smtp:
host: ~
port: ~
username: ~
password: ~
encryption: ~
================================================
FILE: src/Application/Cron/CronExpressionFactoryInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Cron;
interface CronExpressionFactoryInterface
{
public function createFromString(string $cronExpression): CronExpressionInterface;
}
================================================
FILE: src/Application/Cron/CronExpressionInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Cron;
interface CronExpressionInterface
{
/** @return \DateTimeImmutable[] */
public function multipleRunDates(
int $total,
\DateTimeImmutable $now,
?\DateTimeZone $timeZone = null,
): array;
}
================================================
FILE: src/Application/Query/TaskInformation/TaskInformation.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Query\TaskInformation;
use Crunz\Task\TaskNumber;
final class TaskInformation
{
public function __construct(private readonly TaskNumber $taskNumber)
{
}
public function taskNumber(): TaskNumber
{
return $this->taskNumber;
}
}
================================================
FILE: src/Application/Query/TaskInformation/TaskInformationHandler.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Query\TaskInformation;
use Crunz\Application\Cron\CronExpressionFactoryInterface;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\Event;
use Crunz\Schedule\ScheduleFactory;
use Crunz\Task\CollectionInterface;
use Crunz\Task\LoaderInterface;
use Crunz\Task\Timezone;
final class TaskInformationHandler
{
public function __construct(
private readonly Timezone $timezone,
private readonly ConfigurationInterface $configuration,
private readonly CollectionInterface $taskCollection,
private readonly LoaderInterface $taskLoader,
private readonly ScheduleFactory $scheduleFactory,
private readonly CronExpressionFactoryInterface $cronExpressionFactory,
) {
}
public function handle(TaskInformation $taskInformation): TaskInformationView
{
$source = $this->configuration
->getSourcePath()
;
/** @var \SplFileInfo[] $files */
$files = $this->taskCollection
->all($source)
;
// List of schedules
$schedules = $this->taskLoader
->load(...\array_values($files))
;
$timezoneForComparisons = $this->timezone
->timezoneForComparisons()
;
$event = $this->scheduleFactory
->singleTask($taskInformation->taskNumber(), ...$schedules)
;
$cronExpression = $this->cronExpressionFactory
->createFromString($event->getExpression())
;
$nextRunTimezone = $timezoneForComparisons;
$eventProperties = $this->getEventProperties($event, ['timezone', 'preventOverlapping']);
$eventTimezone = $eventProperties['timezone'];
if (\is_string($eventTimezone)) {
$eventTimezone = new \DateTimeZone($eventTimezone);
$nextRunTimezone = $eventTimezone;
}
$nextRuns = $cronExpression->multipleRunDates(
5,
new \DateTimeImmutable(),
$nextRunTimezone
);
return new TaskInformationView(
$event->getCommand(),
$event->description ?? '',
$event->getExpression(),
\filter_var($eventProperties['preventOverlapping'] ?? false, FILTER_VALIDATE_BOOLEAN),
$eventTimezone,
$timezoneForComparisons,
...$nextRuns
);
}
/**
* @param string[] $properties
*
* @return array<string,mixed>
*/
private function getEventProperties(Event $event, array $properties): array
{
$propertiesExtractor = function () use ($properties, $event): array {
$values = [];
foreach ($properties as $property) {
if (!\property_exists($event, $property)) {
$class = $event::class;
throw new \RuntimeException("Property '{$property}' doesn't exists in '{$class}' class.");
}
$values[$property] = $this->{$property};
}
return $values;
};
return $propertiesExtractor->bindTo($event, Event::class)();
}
}
================================================
FILE: src/Application/Query/TaskInformation/TaskInformationView.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Query\TaskInformation;
final class TaskInformationView
{
/** @var \DateTimeImmutable[] */
private readonly array $nextRuns;
public function __construct(
private readonly string|object $command,
private readonly string $description,
private readonly string $cronExpression,
private readonly bool $preventOverlapping,
private readonly ?\DateTimeZone $timeZone,
private readonly \DateTimeZone $configTimeZone,
\DateTimeImmutable ...$nextRuns,
) {
$this->nextRuns = $nextRuns;
}
public function command(): string|object
{
return $this->command;
}
public function description(): string
{
return $this->description;
}
public function cronExpression(): string
{
return $this->cronExpression;
}
public function timeZone(): ?\DateTimeZone
{
return $this->timeZone;
}
public function configTimeZone(): \DateTimeZone
{
return $this->configTimeZone;
}
/** @return \DateTimeImmutable[] */
public function nextRuns(): array
{
return $this->nextRuns;
}
public function preventOverlapping(): bool
{
return $this->preventOverlapping;
}
}
================================================
FILE: src/Application/Service/ClosureSerializerInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Service;
interface ClosureSerializerInterface
{
public function serialize(\Closure $closure): string;
public function unserialize(string $serializedClosure): \Closure;
public function closureCode(\Closure $closure): string;
}
================================================
FILE: src/Application/Service/ConfigurationInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Service;
interface ConfigurationInterface
{
/**
* Return a parameter based on a key.
*/
public function get(string $key, mixed $default = null): mixed;
/**
* Set a parameter based on a key.
*/
public function withNewEntry(string $key, mixed $value): ConfigurationInterface;
public function getSourcePath(): string;
}
================================================
FILE: src/Application/Service/LoggerFactoryInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Application\Service;
use Psr\Log\LoggerInterface;
/**
* @experimental
*/
interface LoggerFactoryInterface
{
public function create(ConfigurationInterface $configuration): LoggerInterface;
}
================================================
FILE: src/Application.php
================================================
<?php
declare(strict_types=1);
namespace Crunz;
use Crunz\CacheDirectoryFactory\CacheDirectoryFactory;
use Crunz\Console\Command\ConfigGeneratorCommand;
use Crunz\Console\Command\ScheduleListCommand;
use Crunz\Console\Command\ScheduleRunCommand;
use Crunz\Console\Command\TaskGeneratorCommand;
use Crunz\EnvFlags\EnvFlags;
use Crunz\Path\Path;
use Crunz\UserInterface\Cli\DebugTaskCommand;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
class Application extends SymfonyApplication
{
/**
* List of commands to register.
*
* @var class-string[]
*/
private const COMMANDS = [
// This command starts the event runner (vendor/bin/crunz schedule:run)
// It takes an optional argument which is the source directory for tasks
// If the argument is not provided, the default in the configuratrion file
// will be considered as the source path
ScheduleRunCommand::class,
// This command (vendor/bin/schedule:list) lists the scheduled events in different task files
// Just like schedule:run it gets the :source argument
ScheduleListCommand::class,
// This command generates a task from the command-line
// This is often useful when you want to create a task file and start
// adding tasks to it.
TaskGeneratorCommand::class,
// The modify the configuration, the user's own copy should be modified
// This command creates a configuration file in Crunz installation directory
ConfigGeneratorCommand::class,
// This command is used by Crunz itself for running serialized closures
// It accepts an argument which is the serialized form of the closure to run.
UserInterface\Cli\ClosureRunCommand::class,
// Debug task command
DebugTaskCommand::class,
];
private Container $container;
private readonly EnvFlags $envFlags;
private readonly CacheDirectoryFactory $cacheDirectoryFactory;
public function __construct(string $appName, string $appVersion)
{
parent::__construct($appName, $appVersion);
$this->cacheDirectoryFactory = new CacheDirectoryFactory();
$this->envFlags = new EnvFlags();
$this->initializeContainer();
$this->registerDeprecationHandler();
foreach (self::COMMANDS as $commandClass) {
/** @var Command $command */
$command = $this->container
->get($commandClass)
;
// @phpstan-ignore function.alreadyNarrowedType (backward compatibility with Symfony < 7.4)
if (\method_exists($this, 'addCommand')) {
$this->addCommand($command);
} else {
$this->add($command);
}
}
}
public function run(?InputInterface $input = null, ?OutputInterface $output = null): int
{
if (null === $output) {
/** @var OutputInterface $outputObject */
$outputObject = $this->container
->get(OutputInterface::class);
$output = $outputObject;
}
if (null === $input) {
/** @var InputInterface $inputObject */
$inputObject = $this->container
->get(InputInterface::class);
$input = $inputObject;
}
return parent::run($input, $output);
}
private function initializeContainer(): void
{
$containerCacheDirWritable = $this->createBaseCacheDirectory();
$isContainerDebugEnabled = $this->envFlags
->isContainerDebugEnabled();
if ($containerCacheDirWritable) {
$class = 'CrunzContainer';
$baseClass = 'Container';
$cachePath = Path::create(
[
$this->getContainerCacheDir(),
"{$class}.php",
]
);
$cache = new ConfigCache($cachePath->toString(), $isContainerDebugEnabled);
if (!$cache->isFresh()) {
$containerBuilder = $this->buildContainer();
$containerBuilder->compile();
$this->dumpContainer(
$cache,
$containerBuilder,
$class,
$baseClass
);
}
require_once $cache->getPath();
$this->container = new $class();
return;
}
$containerBuilder = $this->buildContainer();
$containerBuilder->compile();
$this->container = $containerBuilder;
}
/**
* @return ContainerBuilder
*
* @throws \Exception
*/
private function buildContainer()
{
$containerBuilder = new ContainerBuilder();
$configDir = Path::create(
[
__DIR__,
'..',
'config',
]
);
$phpLoader = new PhpFileLoader($containerBuilder, new FileLocator($configDir->toString()));
$phpLoader->load('services.php');
return $containerBuilder;
}
private function dumpContainer(
ConfigCache $cache,
ContainerBuilder $container,
string $class,
string $baseClass,
): void {
$dumper = new PhpDumper($container);
/** @var string $content */
$content = $dumper->dump(
[
'class' => $class,
'base_class' => $baseClass,
'file' => $cache->getPath(),
]
);
$cache->write($content, $container->getResources());
}
/**
* @return bool
*/
private function createBaseCacheDirectory()
{
$baseCacheDir = $this->getBaseCacheDir();
if (!\is_dir($baseCacheDir)) {
$makeDirResult = \mkdir(
$this->getBaseCacheDir(),
0777,
true
);
return $makeDirResult
&& \is_dir($baseCacheDir)
&& \is_writable($baseCacheDir)
;
}
return \is_writable($baseCacheDir);
}
private function getBaseCacheDir(): string
{
return $this->cacheDirectoryFactory->generate()->toString();
}
/**
* @return string
*/
private function getContainerCacheDir()
{
$containerCacheDir = Path::create(
[
$this->getBaseCacheDir(),
\get_current_user(),
$this->getVersion(),
]
);
return $containerCacheDir->toString();
}
private function registerDeprecationHandler(): void
{
$isDeprecationHandlerEnabled = $this->envFlags
->isDeprecationHandlerEnabled();
if (!$isDeprecationHandlerEnabled) {
return;
}
/** @var SymfonyStyle $io */
$io = $this->container
->get(SymfonyStyle::class);
\set_error_handler(
static function (
int $errorNumber,
string $errorString,
string $file,
int $line,
) use ($io): bool {
$io->block(
"{$errorString} File {$file}, line {$line}",
'Deprecation',
'bg=yellow;fg=black',
' ',
true
);
return true;
},
E_USER_DEPRECATED
);
}
}
================================================
FILE: src/CacheDirectoryFactory/CacheDirectoryFactory.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\CacheDirectoryFactory;
use Crunz\Exception\CrunzException;
use Crunz\Path\Path;
final class CacheDirectoryFactory
{
public const CRUNZ_BASE_CACHE_DIR = 'CRUNZ_BASE_CACHE_DIR';
/**
* @throws CrunzException
*/
public function generate(): Path
{
$cacheDirectory = '.crunz';
/** @var false|string $basePath */
$basePath = \getenv(self::CRUNZ_BASE_CACHE_DIR, true);
if (false === $basePath) {
return Path::fromStrings(\sys_get_temp_dir(), $cacheDirectory);
}
$basePath = \ltrim($basePath);
if ('' === $basePath) {
throw new CrunzException('Crunz base cache directory path is empty.');
}
return Path::fromStrings($basePath, $cacheDirectory);
}
}
================================================
FILE: src/Clock/Clock.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Clock;
final class Clock implements ClockInterface
{
public function now(): \DateTimeImmutable
{
return new \DateTimeImmutable();
}
}
================================================
FILE: src/Clock/ClockInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Clock;
interface ClockInterface
{
public function now(): \DateTimeImmutable;
}
================================================
FILE: src/Configuration/ConfigFileNotExistsException.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
use Crunz\Exception\CrunzException;
final class ConfigFileNotExistsException extends CrunzException
{
public static function fromFilePath(string $filePath): self
{
return new self("Configuration file '{$filePath}' not exists.");
}
}
================================================
FILE: src/Configuration/ConfigFileNotReadableException.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
use Crunz\Exception\CrunzException;
final class ConfigFileNotReadableException extends CrunzException
{
public static function fromFilePath(string $filePath): self
{
return new self("Config file '{$filePath}' is not readable.");
}
}
================================================
FILE: src/Configuration/Configuration.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\Filesystem\FilesystemInterface;
use Crunz\Path\Path;
final class Configuration implements ConfigurationInterface
{
/** @var array<string,mixed> */
private $config;
public function __construct(
private readonly ConfigurationParserInterface $configurationParser,
private readonly FilesystemInterface $filesystem,
) {
}
/**
* Return a parameter based on a key.
*/
public function get(string $key, mixed $default = null): mixed
{
if (null === $this->config) {
$this->config = $this->configurationParser
->parseConfig();
}
if (\array_key_exists($key, $this->config)) {
return $this->config[$key];
}
$parts = \explode('.', $key);
$value = $this->config;
foreach ($parts as $part) {
if (!\is_array($value) || !\array_key_exists($part, $value)) {
return $default;
}
$value = $value[$part];
}
return $value;
}
/**
* Set a parameter based on key/value.
*/
public function withNewEntry(string $key, mixed $value): ConfigurationInterface
{
$newConfiguration = clone $this;
if (null === $newConfiguration->config) {
$newConfiguration->config = $newConfiguration->configurationParser
->parseConfig();
}
$parts = \explode('.', $key);
if (\count($parts) > 1) {
if (\array_key_exists($parts[0], $newConfiguration->config) && \is_array($newConfiguration->config[$parts[0]])) {
$newConfiguration->config[$parts[0]][$parts[1]] = $value;
} else {
$newConfiguration->config[$parts[0]] = [$parts[1] => $value];
}
} else {
$newConfiguration->config[$key] = $value;
}
return $newConfiguration;
}
public function getSourcePath(): string
{
$sourcePath = Path::create(
[
$this->filesystem
->getCwd(),
$this->get('source', 'tasks'),
]
);
return $sourcePath->toString();
}
}
================================================
FILE: src/Configuration/ConfigurationParser.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
use Crunz\Console\Command\ConfigGeneratorCommand;
use Crunz\Filesystem\FilesystemInterface;
use Crunz\Logger\ConsoleLoggerInterface;
use Crunz\Path\Path;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Processor;
final class ConfigurationParser implements ConfigurationParserInterface
{
public function __construct(
private readonly ConfigurationInterface $configurationDefinition,
private readonly Processor $definitionProcessor,
private readonly FileParser $fileParser,
private readonly ConsoleLoggerInterface $consoleLogger,
private readonly FilesystemInterface $filesystem,
) {
}
public function parseConfig(): array
{
$configFile = null;
$parsedConfig = [];
$configFileParsed = false;
try {
$configFile = $this->configFilePath();
$parsedConfig = $this->fileParser
->parse($configFile);
$configFileParsed = true;
} catch (ConfigFileNotExistsException $exception) {
$this->consoleLogger
->debug("Config file not found, exception message: '<error>{$exception->getMessage()}</error>'.");
} catch (ConfigFileNotReadableException $exception) {
$this->consoleLogger
->debug("Config file is not readable, exception message: '<error>{$exception->getMessage()}</error>'.");
}
if (false === $configFileParsed) {
$this->consoleLogger
->verbose('Unable to find/parse config file, fallback to default values.');
} else {
$this->consoleLogger
->verbose("Using config file <info>{$configFile}</info>.");
}
return $this->definitionProcessor
->processConfiguration(
$this->configurationDefinition,
$parsedConfig
);
}
/** @throws ConfigFileNotExistsException */
private function configFilePath(): string
{
$cwd = $this->filesystem
->getCwd();
$configPath = Path::fromStrings($cwd ?? '', ConfigGeneratorCommand::CONFIG_FILE_NAME)->toString();
$configExists = $this->filesystem
->fileExists($configPath);
if ($configExists) {
return $configPath;
}
throw new ConfigFileNotExistsException(
\sprintf(
'Unable to find config file "%s".',
$configPath
)
);
}
}
================================================
FILE: src/Configuration/ConfigurationParserInterface.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
interface ConfigurationParserInterface
{
/** @return array<string,int|string|array> */
public function parseConfig(): array;
}
================================================
FILE: src/Configuration/Definition.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
use Crunz\Infrastructure\Psr\Logger\PsrStreamLoggerFactory;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Definition implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('crunz');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()
->scalarNode('source')
->cannotBeEmpty()
->info('path to the tasks directory' . PHP_EOL)
->end()
->scalarNode('suffix')
->defaultValue('Tasks.php')
->info('The suffix for filenames' . PHP_EOL)
->end()
->scalarNode('timezone')
->info('Timezone used to calculate task run date')
->end()
->booleanNode('timezone_log')
->defaultFalse()
->info('Whether configured "timezone" will be used for logs')
->end()
->scalarNode('logger_factory')
->defaultValue(PsrStreamLoggerFactory::class)
->cannotBeEmpty()
->info("Class name implementing 'LoggerFactoryInterface'. Use it to provider your own logger.")
->end()
->booleanNode('log_errors')
->defaultFalse()
->info('Flag for logging errors' . PHP_EOL)
->end()
->scalarNode('errors_log_file')
->defaultValue('/dev/null')
->info('Path to errors log' . PHP_EOL)
->end()
->booleanNode('log_output')
->defaultFalse()
->info('Flag for logging output' . PHP_EOL)
->end()
->scalarNode('output_log_file')
->defaultValue('/dev/null')
->info('Path to output logs' . PHP_EOL)
->end()
->scalarNode('log_allow_line_breaks')
->defaultFalse()
->info('Flag for line breaks in logs' . PHP_EOL)
->end()
->scalarNode('log_ignore_empty_context')
->defaultFalse()
->info('Flag for empty context in logs' . PHP_EOL)
->end()
->scalarNode('email_output')
->defaultFalse()
->info('Email the event\'s output' . PHP_EOL)
->end()
->scalarNode('email_errors')
->defaultFalse()
->info('Notify by email in case of an error' . PHP_EOL)
->end()
->arrayNode('mailer')
->children()
->scalarNode('transport')
->info('The type the Swift transporter' . PHP_EOL)
->end()
->arrayNode('recipients')
->prototype('scalar')->end()
->info('List of the email recipients' . PHP_EOL)
->end()
->scalarNode('sender_name')
->info('The sender name' . PHP_EOL)
->end()
->scalarNode('sender_email')
->info('The sender email' . PHP_EOL)
->end()
->end()
->end()
->arrayNode('smtp')
->children()
->scalarNode('host')
->info('SMTP host' . PHP_EOL)
->end()
->scalarNode('port')
->info('SMTP port' . PHP_EOL)
->end()
->scalarNode('username')
->info('SMTP username' . PHP_EOL)
->end()
->scalarNode('password')
->info('SMTP password' . PHP_EOL)
->end()
->scalarNode('encryption')
->info('SMTP encryption' . PHP_EOL)
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}
}
================================================
FILE: src/Configuration/FileParser.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Configuration;
use Symfony\Component\Yaml\Yaml;
class FileParser
{
public function __construct(private readonly Yaml $yamlParser)
{
}
/**
* @return array<array>
*
* @throws ConfigFileNotExistsException
* @throws ConfigFileNotReadableException
*/
public function parse(string $configPath): array
{
if (!\file_exists($configPath)) {
throw ConfigFileNotExistsException::fromFilePath($configPath);
}
if (!\is_readable($configPath)) {
throw ConfigFileNotReadableException::fromFilePath($configPath);
}
$yamlParser = $this->yamlParser;
$configContent = \file_get_contents($configPath);
if (false === $configContent) {
throw ConfigFileNotReadableException::fromFilePath($configPath);
}
return [$yamlParser::parse($configContent)];
}
}
================================================
FILE: src/Console/Command/Command.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Console\Command;
use Symfony\Component\Console\Command\Command as BaseCommand;
class Command extends BaseCommand
{
/** @var array<string|bool|int|float|array|null> */
protected $arguments;
/** @var array<string|bool|int|float|array|null> */
protected $options;
/**
* Input object.
*
* @var \Symfony\Component\Console\Input\InputInterface
*/
protected $input;
/**
* output object.
*
* @var \Symfony\Component\Console\Output\OutputInterface
*/
protected $output;
}
================================================
FILE: src/Console/Command/ConfigGeneratorCommand.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Console\Command;
use Crunz\Filesystem\FilesystemInterface;
use Crunz\Path\Path;
use Crunz\Timezone\ProviderInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
final class ConfigGeneratorCommand extends Command
{
public const CONFIG_FILE_NAME = 'crunz.yml';
public function __construct(
private readonly ProviderInterface $timezoneProvider,
private readonly Filesystem $symfonyFilesystem,
private readonly FilesystemInterface $filesystem,
) {
parent::__construct();
}
/**
* Configures the current command.
*/
protected function configure(): void
{
$this
->setName('publish:config')
->setDescription("Generates a config file within the project's root directory.")
->setHelp("This generates a config file in YML format within the project's root directory.")
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$symfonyStyleIo = new SymfonyStyle($input, $output);
$cwd = $this->filesystem
->getCwd();
$path = Path::create([$cwd, self::CONFIG_FILE_NAME])->toString();
$destination = \realpath($path) ?: $path;
$configExists = $this->filesystem
->fileExists($destination)
;
$output->writeln(
"<info>Destination config file: '{$destination}'.</info>",
OutputInterface::VERBOSITY_VERBOSE
);
if ($configExists) {
$output->writeln(
"<comment>The configuration file already exists at '{$destination}'.</comment>"
);
return 0;
}
$projectRoot = $this->filesystem
->projectRootDirectory();
$srcPath = Path::fromStrings(
$projectRoot,
'resources',
'config',
self::CONFIG_FILE_NAME
);
$src = $srcPath->toString();
$output->writeln(
"<info>Source config file: '{$src}'.</info>",
OutputInterface::VERBOSITY_VERBOSE
);
$defaultTimezone = $this->askForTimezone($symfonyStyleIo);
$output->writeln(
"<info>Provided timezone: '{$defaultTimezone}'.</info>",
OutputInterface::VERBOSITY_VERBOSE
);
$this->updateTimezone(
$destination,
$src,
$defaultTimezone
);
$output->writeln('<info>The configuration file was generated successfully.</info>');
return 0;
}
/**
* @return string
*/
protected function askForTimezone(SymfonyStyle $symfonyStyleIo)
{
$defaultTimezone = $this->timezoneProvider
->defaultTimezone()
->getName()
;
$question = new Question(
'<question>Please provide default timezone for task run date calculations</question>',
$defaultTimezone
);
$question->setAutocompleterValues(\DateTimeZone::listIdentifiers());
$question->setValidator(
static function ($answer) {
try {
new \DateTimeZone($answer);
} catch (\Exception) {
throw new \Exception("Timezone '{$answer}' is not valid. Please provide valid timezone.");
}
return $answer;
}
);
return $symfonyStyleIo->askQuestion($question);
}
private function updateTimezone(
string $destination,
string $src,
string $timezone,
): void {
$this->symfonyFilesystem
->dumpFile(
$destination,
\str_replace(
'timezone: ~',
"timezone: {$timezone}",
$this->filesystem
->readContent($src)
)
)
;
}
}
================================================
FILE: src/Console/Command/ScheduleListCommand.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Console\Command;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\Exception\CrunzException;
use Crunz\Task\CollectionInterface;
use Crunz\Task\LoaderInterface;
use Crunz\Task\WrongTaskInstanceException;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ScheduleListCommand extends \Symfony\Component\Console\Command\Command
{
private const FORMAT_TEXT = 'text';
private const FORMAT_JSON = 'json';
private const FORMATS = [
self::FORMAT_TEXT,
self::FORMAT_JSON,
];
public function __construct(
private readonly ConfigurationInterface $configuration,
private readonly CollectionInterface $taskCollection,
private readonly LoaderInterface $taskLoader,
) {
parent::__construct();
}
/**
* Configures the current command.
*/
protected function configure(): void
{
$possibleFormats = \implode('", "', self::FORMATS);
$this
->setName('schedule:list')
->setDescription('Displays the list of scheduled tasks.')
->setDefinition(
[
new InputArgument(
'source',
InputArgument::OPTIONAL,
'The source directory for collecting the tasks.',
$this->configuration
->getSourcePath()
),
]
)
->addOption(
'format',
'f',
InputOption::VALUE_REQUIRED,
"Tasks list format, possible formats: \"{$possibleFormats}\".",
self::FORMAT_TEXT,
)
;
}
/**
* @throws WrongTaskInstanceException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
/** @var string $source */
$source = $input->getArgument('source');
$format = $this->resolveFormat($input);
$tasks = $this->tasks($source);
if (!\count($tasks)) {
$output->writeln('<comment>No task found!</comment>');
return 0;
}
$this->printList(
$output,
$tasks,
$format,
);
return 0;
}
/**
* @return array<
* int,
* array{
* number: int,
* task: string,
* expression: string,
* command: string,
* },
* >
*/
private function tasks(string $source): array
{
/** @var \SplFileInfo[] $tasks */
$tasks = $this->taskCollection
->all($source)
;
$schedules = $this->taskLoader
->load(...\array_values($tasks))
;
$tasksList = [];
$number = 0;
foreach ($schedules as $schedule) {
$events = $schedule->events();
foreach ($events as $event) {
$tasksList[] = [
'number' => ++$number,
'task' => $event->description ?? '',
'expression' => $event->getExpression(),
'command' => $event->getCommandForDisplay(),
];
}
}
return $tasksList;
}
private function resolveFormat(InputInterface $input): string
{
/** @var string $format */
$format = $input->getOption('format');
$isValidFormat = \in_array(
$format,
self::FORMATS,
true,
);
if (false === $isValidFormat) {
throw new CrunzException("Format '{$format}' is not supported.");
}
return $format;
}
/**
* @param array<
* int,
* array{
* number: int,
* task: string,
* expression: string,
* command: string,
* },
* > $tasks
*/
private function printList(
OutputInterface $output,
array $tasks,
string $format,
): void {
switch ($format) {
case self::FORMAT_TEXT:
$table = new Table($output);
$table->setHeaders(
[
'#',
'Task',
'Expression',
'Command to Run',
]
);
foreach ($tasks as $task) {
$table->addRow($task);
}
$table->render();
break;
case self::FORMAT_JSON:
$output->writeln(
\json_encode(
$tasks,
JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT,
),
);
break;
default:
throw new CrunzException("Unable to print list in format '{$format}'.");
}
}
}
================================================
FILE: src/Console/Command/ScheduleRunCommand.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Console\Command;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\EventRunner;
use Crunz\Schedule;
use Crunz\Task\CollectionInterface;
use Crunz\Task\LoaderInterface;
use Crunz\Task\TaskNumber;
use Crunz\Task\Timezone;
use Crunz\Task\WrongTaskInstanceException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ScheduleRunCommand extends Command
{
public function __construct(
private readonly CollectionInterface $taskCollection,
private readonly ConfigurationInterface $configuration,
private readonly EventRunner $eventRunner,
private readonly Timezone $taskTimezone,
private readonly Schedule\ScheduleFactory $scheduleFactory,
private readonly LoaderInterface $taskLoader,
) {
parent::__construct();
}
/**
* Configures the current command.
*/
protected function configure(): void
{
$this->setName('schedule:run')
->setDescription('Starts the event runner.')
->setDefinition(
[
new InputArgument(
'source',
InputArgument::OPTIONAL,
'The source directory for collecting the task files.',
$this->configuration
->getSourcePath()
),
]
)
->addOption(
'force',
'f',
InputOption::VALUE_NONE,
'Run all tasks regardless of configured run time.'
)
->addOption(
'task',
't',
InputOption::VALUE_REQUIRED,
'Which task to run. Provide task number from <info>schedule:list</info> command.',
null
)
->setHelp('This command starts the Crunz event runner.');
}
/**
* @throws WrongTaskInstanceException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->arguments = $input->getArguments();
$this->options = $input->getOptions();
$task = $this->options['task'];
/** @var string $source */
$source = $input->getArgument('source') ?? '';
/** @var \SplFileInfo[] $files */
$files = $this->taskCollection
->all($source)
;
if (!\count($files)) {
$output->writeln('<comment>No task found! Please check your source path.</comment>');
return 0;
}
// List of schedules
$schedules = $this->taskLoader
->load(...\array_values($files))
;
$tasksTimezone = $this->taskTimezone
->timezoneForComparisons()
;
// Is specified task should be invoked?
if (\is_string($task)) {
$schedules = $this->scheduleFactory
->singleTaskSchedule(TaskNumber::fromString($task), ...$schedules);
}
$forceRun = \filter_var($this->options['force'] ?? false, FILTER_VALIDATE_BOOLEAN);
$schedules = \array_map(
static function (Schedule $schedule) use ($tasksTimezone, $forceRun) {
if (false === $forceRun) {
// We keep the events which are due and dismiss the rest.
$schedule->events(
$schedule->dueEvents(
$tasksTimezone
)
);
}
return $schedule;
},
$schedules
);
$schedules = \array_filter(
$schedules,
static fn (Schedule $schedule): bool => \count($schedule->events()) > 0
);
if (!\count($schedules)) {
$output->writeln('<comment>No event is due!</comment>');
return 0;
}
// Running the events
$this->eventRunner
->handle($output, $schedules)
;
return 0;
}
}
================================================
FILE: src/Console/Command/TaskGeneratorCommand.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Console\Command;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\Filesystem\FilesystemInterface;
use Crunz\Path\Path;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
class TaskGeneratorCommand extends Command
{
/**
* Default option values.
*
* @var array<string,string>
*/
final public const DEFAULTS = [
'frequency' => 'everyThirtyMinutes',
'constraint' => 'weekdays',
'in' => 'path/to/your/command',
'run' => 'command/to/execute',
'description' => 'Task description',
'type' => 'basic',
];
/**
* Stub content.
*
* @var string
*/
protected $stub;
public function __construct(
private readonly ConfigurationInterface $config,
private readonly FilesystemInterface $filesystem,
) {
parent::__construct();
}
/**
* Configures the current command.
*/
protected function configure(): void
{
$this
->setName('make:task')
->setDescription('Generates a task file with one task.')
->setDefinition(
[
new InputArgument(
'taskfile',
InputArgument::REQUIRED,
'The task file name'
),
new InputOption(
'frequency',
'f',
InputOption::VALUE_OPTIONAL,
"The task's frequency",
self::DEFAULTS['frequency']
),
new InputOption(
'constraint',
'c',
InputOption::VALUE_OPTIONAL,
"The task's constraint",
self::DEFAULTS['constraint']
),
new InputOption(
'in',
'i',
InputOption::VALUE_OPTIONAL,
"The command's path",
self::DEFAULTS['in']
),
new InputOption(
'run',
'r',
InputOption::VALUE_OPTIONAL,
"The task's command",
self::DEFAULTS['run']
),
new InputOption(
'description',
'd',
InputOption::VALUE_OPTIONAL,
"The task's description",
self::DEFAULTS['description']
),
new InputOption(
'type',
't',
InputOption::VALUE_OPTIONAL,
'The task type',
self::DEFAULTS['type']
),
]
)
->setHelp('This command makes a task file skeleton.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->input = $input;
$this->output = $output;
$this->arguments = $input->getArguments();
$this->options = $input->getOptions();
$this->stub = $this->getStub();
if ($this->stub) {
$this
->replaceFrequency()
->replaceConstraint()
->replaceCommand()
->replacePath()
->replaceDescription()
;
}
if ($this->save()) {
$output->writeln('<info>The task file generated successfully</info>');
} else {
$output->writeln('<comment>There was a problem when generating the file. Please check your command.</comment>');
}
return 0;
}
/**
* Save the generate task skeleton into a file.
*
* @return bool
*/
protected function save()
{
$filename = Path::create([$this->outputPath(), $this->outputFile()]);
return (bool) \file_put_contents($filename->toString(), $this->stub);
}
/**
* Ask a question.
*
* @param string $question
*
* @return ?string
*/
protected function ask($question)
{
$helper = $this->getHelper('question');
$question = new Question("<question>{$question}</question>");
return $helper->ask($this->input, $this->output, $question);
}
/**
* Return the output path.
*
* @return string
*/
protected function outputPath()
{
$source = $this->config
->getSourcePath()
;
$destination = $this->ask('Where do you want to save the file? (Press enter for the current directory)');
$outputPath = $destination ?? $source;
if (!\file_exists($outputPath)) {
\mkdir($outputPath, 0744, true);
}
return $outputPath;
}
/**
* Populate the output filename.
*
* @return string
*/
protected function outputFile()
{
/** @var string $suffix */
$suffix = $this->config
->get('suffix')
;
/** @var string $taskFile */
$taskFile = $this->arguments['taskfile'];
return \preg_replace('/Tasks|\.php$/', '', $taskFile) . $suffix;
}
/**
* Get the task stub.
*
* @return string
*/
protected function getStub()
{
$projectRootDirectory = $this->filesystem
->projectRootDirectory();
$path = Path::fromStrings(
$projectRootDirectory,
'src',
'Stubs',
\ucfirst($this->type() . 'Task.php')
);
return $this->filesystem
->readContent($path->toString());
}
/**
* Get the task type.
*
* @return string
*/
protected function type()
{
return $this->options['type'];
}
/**
* Replace frequency.
*/
protected function replaceFrequency(): self
{
$this->stub = \str_replace('DummyFrequency', \rtrim($this->options['frequency'], '()'), $this->stub);
return $this;
}
/**
* Replace constraint.
*/
protected function replaceConstraint(): self
{
$this->stub = \str_replace('DummyConstraint', \rtrim($this->options['constraint'], '()'), $this->stub);
return $this;
}
protected function replaceCommand(): self
{
$run = $this->optionString('run');
$this->stub = \str_replace('DummyCommand', $run, $this->stub);
return $this;
}
protected function replacePath(): self
{
$in = $this->optionString('in');
$this->stub = \str_replace('DummyPath', $in, $this->stub);
return $this;
}
protected function replaceDescription(): self
{
$description = $this->optionString('description');
$this->stub = \str_replace('DummyDescription', $description, $this->stub);
return $this;
}
private function optionString(string $name): string
{
$option = $this->options[$name] ?? throw new \RuntimeException("Missing option '{$name}'.");
if (false === \is_string($option)) {
throw new \RuntimeException("Option must be of type 'string'.");
}
return $option;
}
}
================================================
FILE: src/EnvFlags/EnvFlags.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\EnvFlags;
use Crunz\Exception\CrunzException;
final class EnvFlags
{
public const DEPRECATION_HANDLER_FLAG = 'CRUNZ_DEPRECATION_HANDLER';
public const CONTAINER_DEBUG_FLAG = 'CRUNZ_CONTAINER_DEBUG';
/** @return bool */
public function isDeprecationHandlerEnabled()
{
$registerHandlerEnv = \getenv(self::DEPRECATION_HANDLER_FLAG, true);
$registerHandler = true;
if (false !== $registerHandlerEnv) {
$registerHandler = \filter_var($registerHandlerEnv, FILTER_VALIDATE_BOOLEAN);
}
return $registerHandler;
}
/** @return bool */
public function isContainerDebugEnabled()
{
$containerDebugEnv = \getenv(self::CONTAINER_DEBUG_FLAG, true);
$containerDebug = false;
if (false !== $containerDebugEnv) {
$containerDebug = \filter_var($containerDebugEnv, FILTER_VALIDATE_BOOLEAN);
}
return $containerDebug;
}
/** @throws CrunzException When disabling deprecation handler fails */
public function disableContainerDebug(): void
{
if (false === \putenv(self::CONTAINER_DEBUG_FLAG . '=0')) {
throw new CrunzException('Disabling container debug failed.');
}
}
/** @throws CrunzException When enabling deprecation handler fails */
public function enableContainerDebug(): void
{
if (false === \putenv(self::CONTAINER_DEBUG_FLAG . '=1')) {
throw new CrunzException('Enabling container debug failed.');
}
}
/** @throws CrunzException When enabling deprecation handler fails */
public function enableDeprecationHandler(): void
{
if (false === \putenv(self::DEPRECATION_HANDLER_FLAG . '=1')) {
throw new CrunzException('Enabling deprecation handler failed.');
}
}
/** @throws CrunzException When disabling deprecation handler fails */
public function disableDeprecationHandler(): void
{
if (false === \putenv(self::DEPRECATION_HANDLER_FLAG . '=0')) {
throw new CrunzException('Disabling deprecation handler failed.');
}
}
}
================================================
FILE: src/Event.php
================================================
<?php
declare(strict_types=1);
namespace Crunz;
use Closure;
use Cron\CronExpression;
use Crunz\Application\Service\ClosureSerializerInterface;
use Crunz\Clock\Clock;
use Crunz\Clock\ClockInterface;
use Crunz\Exception\CrunzException;
use Crunz\Exception\NotImplementedException;
use Crunz\Infrastructure\Laravel\LaravelClosureSerializer;
use Crunz\Logger\Logger;
use Crunz\Path\Path;
use Crunz\Pinger\PingableInterface;
use Crunz\Pinger\PingableTrait;
use Crunz\Process\Process;
use Crunz\Task\TaskException;
use Symfony\Component\Lock\Exception\InvalidArgumentException;
use Symfony\Component\Lock\Factory;
use Symfony\Component\Lock\Lock;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\PersistingStoreInterface;
use Symfony\Component\Lock\Store\FlockStore;
class Event implements PingableInterface
{
use PingableTrait;
private const LOCK_TTL = 120; // seconds
private const LOCK_REFRESH_THRESHOLD = 30; // seconds
/**
* The location that output should be sent to.
*
* @var string
*/
public $output = '/dev/null';
/**
* Indicates whether output should be appended.
*
* @var bool
*/
public $shouldAppendOutput = false;
/**
* The human readable description of the event.
*
* @var string|null
*/
public $description;
/**
* Event generated output.
*
* @var string|null
*/
public $outputStream;
/**
* Event personal logger instance.
*
* @var Logger
*/
public $logger;
/** @var string|\Closure */
protected $command;
/**
* Process that runs the event.
*
* @var Process
*/
protected $process;
/**
* The cron expression representing the event's frequency.
*
* @var string
*/
protected $expression = '* * * * *';
/**
* The timezone the date should be evaluated on.
*
* @var \DateTimeZone|string
*/
protected $timezone;
/**
* Datetime or time since the task is evaluated and possibly executed only for display purposes.
*/
protected \DateTime|string|null $from = null;
/**
* Datetime or time until the task is evaluated and possibly executed only for display purposes.
*/
protected \DateTime|string|null $to = null;
/**
* The user the command should run as.
*
* @var string
*/
protected $user;
/**
* The array of filter callbacks.
*
* @var \Closure[]
*/
protected $filters = [];
/**
* The array of reject callbacks.
*
* @var \Closure[]
*/
protected $rejects = [];
/**
* The array of callbacks to be run before the event is started.
*
* @var \Closure[]
*/
protected $beforeCallbacks = [];
/**
* The array of callbacks to be run after the event is finished.
*
* @var \Closure[]
*/
protected $afterCallbacks = [];
/**
* Current working directory.
*
* @var string
*/
protected $cwd;
/**
* Position of cron fields.
*
* @var array<string,int>
*/
protected $fieldsPosition = [
'minute' => 1,
'hour' => 2,
'day' => 3,
'month' => 4,
'week' => 5,
];
/**
* Indicates if the command should not overlap itself.
*/
private bool $preventOverlapping = false;
/** @var ClockInterface */
private static $clock;
private static ?ClosureSerializerInterface $closureSerializer = null;
/**
* The symfony lock factory that is used to acquire locks. If the value is null, but preventOverlapping = true
* crunz falls back to filesystem locks.
*/
private ?LockFactory $lockFactory = null;
/** @var string[] */
private array $wholeOutput = [];
/** @var Lock */
private $lock;
/** @var \Closure[] */
private array $errorCallbacks = [];
/**
* Create a new event instance.
*
* @param string|\Closure $command
* @param string|int $id
*/
public function __construct(protected $id, $command)
{
$this->command = $command;
$this->output = $this->getDefaultOutput();
}
/**
* Change the current working directory.
*
* @param string $directory
*
* @return self
*/
public function in($directory)
{
$this->cwd = $directory;
return $this;
}
/**
* Determine if the event's output is sent to null.
*
* @return bool
*/
public function nullOutput()
{
return 'NUL' === $this->output || '/dev/null' === $this->output;
}
/**
* Build the command string.
*
* @return string
*/
public function buildCommand()
{
$command = '';
if ($this->cwd) {
if ($this->user) {
$command .= $this->sudo($this->user);
}
// Support changing drives in Windows
$cdParameter = $this->isWindows() ? '/d ' : '';
$andSign = $this->isWindows() ? ' &' : ';';
$command .= "cd {$cdParameter}{$this->cwd}{$andSign} ";
}
if ($this->user) {
$command .= $this->sudo($this->user);
}
$command .= \is_string($this->command)
? $this->command
: $this->serializeClosure($this->command)
;
return \trim($command, '& ');
}
/**
* Determine whether the passed value is a closure or not.
*
* @return bool
*/
public function isClosure()
{
return \is_object($this->command) && ($this->command instanceof \Closure);
}
/**
* Determine if the given event should run based on the Cron expression.
*
* @return bool
*/
public function isDue(\DateTimeZone $timeZone)
{
return $this->expressionPasses($timeZone) && $this->filtersPass($timeZone);
}
/**
* Determine if the filters pass for the event.
*
* @return bool
*/
public function filtersPass(\DateTimeZone $timeZone)
{
$invoker = new Invoker();
foreach ($this->filters as $callback) {
if (!$invoker->call($callback)) {
return false;
}
}
foreach ($this->rejects as $callback) {
if ($invoker->call($callback, [$timeZone])) {
return false;
}
}
return true;
}
/** @return string */
public function wholeOutput()
{
return \implode('', $this->wholeOutput);
}
/**
* Start the event execution.
*
* @return int
*/
public function start()
{
$command = $this->buildCommand();
$process = Process::fromStringCommand($command);
$this->setProcess($process);
$this->getProcess()->start(
function ($type, $content): void {
$this->wholeOutput[] = $content;
}
);
if ($this->preventOverlapping) {
$this->lock();
}
/** @var int $pid */
$pid = $this->getProcess()
->getPid();
return $pid;
}
/**
* The Cron expression representing the event's frequency.
*
* @throws TaskException
*/
public function cron(string $expression): self
{
$parts = \preg_split(
'/\s/',
$expression,
-1,
PREG_SPLIT_NO_EMPTY
);
$parts = false === $parts
? []
: $parts
;
if (\count($parts) > 5) {
throw new TaskException("Expression '{$expression}' has more than five parts and this is not allowed.");
}
$this->expression = $expression;
return $this;
}
/**
* Schedule the event to run hourly.
*/
public function hourly(): self
{
return $this->hourlyAt(0);
}
public function hourlyAt(int $minute): self
{
if ($minute < 0) {
throw new CrunzException("Minute cannot be lower than '0'.");
}
if ($minute > 59) {
throw new CrunzException("Minute cannot be greater than '59'.");
}
return $this->cron("{$minute} * * * *");
}
/**
* Schedule the event to run daily.
*/
public function daily(): self
{
return $this->cron('0 0 * * *');
}
/**
* Schedule the event to run on a certain date.
*
* @param string $date
*
* @return $this
*/
public function on($date)
{
$parsedDate = \date_parse($date);
if (false === $parsedDate) {
$parsedDate = [];
}
$segments = \array_intersect_key($parsedDate, $this->fieldsPosition);
if ($parsedDate['year']) {
$this->skip(static fn () => (int) \date('Y') !== $parsedDate['year']);
}
foreach ($segments as $key => $value) {
if (false !== $value) {
$this->spliceIntoPosition($this->fieldsPosition[$key], (string) $value);
}
}
return $this;
}
/**
* Schedule the command at a given time.
*
* @param string $time
*/
public function at($time): self
{
return $this->dailyAt($time);
}
/**
* Schedule the event to run daily at a given time (10:00, 19:30, etc).
*
* @param string $time
*/
public function dailyAt($time): self
{
$segments = \explode(':', $time);
$firstSegment = (int) $segments[0];
$secondSegment = \count($segments) > 1
? (int) $segments[1]
: '0'
;
return $this
->spliceIntoPosition(2, (string) $firstSegment)
->spliceIntoPosition(1, (string) $secondSegment)
;
}
/**
* Set Working period.
*
* @param string $from
* @param string $to
*
* @return self
*/
public function between($from, $to)
{
return $this->from($from)
->to($to);
}
/**
* Check if event should be on.
*
* @param string $datetime
*
* @return self
*/
public function from($datetime)
{
$this->from = $datetime;
return $this->skip(
fn (\DateTimeZone $timeZone) => $this->notYet($datetime, $timeZone)
);
}
/**
* Check if event should be off.
*
* @param string $datetime
*
* @return self
*/
public function to($datetime)
{
$this->to = $datetime;
return $this->skip(
fn (\DateTimeZone $timeZone) => $this->past($datetime, $timeZone),
);
}
/**
* Schedule the event to run twice daily.
*
* @param int $first
* @param int $second
*/
public function twiceDaily($first = 1, $second = 13): self
{
$hours = $first . ',' . $second;
return $this
->spliceIntoPosition(1, '0')
->spliceIntoPosition(2, $hours)
;
}
/**
* Schedule the event to run only on weekdays.
*/
public function weekdays(): self
{
return $this->spliceIntoPosition(5, '1-5');
}
/**
* Schedule the event to run only on Mondays.
*/
public function mondays(): self
{
return $this->days(1);
}
/**
* Schedule the event to run only on Tuesdays.
*/
public function tuesdays(): self
{
return $this->days(2);
}
/**
* Schedule the event to run only on Wednesdays.
*/
public function wednesdays(): self
{
return $this->days(3);
}
/**
* Schedule the event to run only on Thursdays.
*/
public function thursdays(): self
{
return $this->days(4);
}
/**
* Schedule the event to run only on Fridays.
*/
public function fridays(): self
{
return $this->days(5);
}
/**
* Schedule the event to run only on Saturdays.
*/
public function saturdays(): self
{
return $this->days(6);
}
/**
* Schedule the event to run only on Sundays.
*/
public function sundays(): self
{
return $this->days(0);
}
/**
* Schedule the event to run weekly.
*/
public function weekly(): self
{
return $this->cron('0 0 * * 0');
}
/**
* Schedule the event to run weekly on a given day and time.
*
* @param string $time
*/
public function weeklyOn(int|string $day, $time = '0:0'): self
{
$this->dailyAt($time);
return $this->spliceIntoPosition(5, (string) $day);
}
/**
* Schedule the event to run monthly.
*/
public function monthly(): self
{
return $this->cron('0 0 1 * *');
}
/**
* Schedule the event to run quarterly.
*/
public function quarterly(): self
{
return $this->cron('0 0 1 */3 *');
}
/**
* Schedule the event to run yearly.
*/
public function yearly(): self
{
return $this->cron('0 0 1 1 *');
}
/**
* Set the days of the week the command should run on.
*/
public function days(mixed $days): self
{
$days = \is_array($days) ? $days : \func_get_args();
return $this->spliceIntoPosition(5, \implode(',', $days));
}
/**
* Set hour for the cron job.
*/
public function hour(mixed $value): self
{
$value = \is_array($value) ? $value : \func_get_args();
return $this->spliceIntoPosition(2, \implode(',', $value));
}
/**
* Set minute for the cron job.
*/
public function minute(mixed $value): self
{
$value = \is_array($value) ? $value : \func_get_args();
return $this->spliceIntoPosition(1, \implode(',', $value));
}
/**
* Set hour for the cron job.
*/
public function dayOfMonth(mixed $value): self
{
$value = \is_array($value) ? $value : \func_get_args();
return $this->spliceIntoPosition(3, \implode(',', $value));
}
/**
* Set hour for the cron job.
*/
public function month(mixed $value): self
{
$value = \is_array($value) ? $value : \func_get_args();
return $this->spliceIntoPosition(4, \implode(',', $value));
}
/**
* Set hour for the cron job.
*/
public function dayOfWeek(mixed $value): self
{
$value = \is_array($value) ? $value : \func_get_args();
return $this->spliceIntoPosition(5, \implode(',', $value));
}
/**
* Set the timezone the date should be evaluated on.
*
* @return $this
*/
public function timezone(\DateTimeZone|string $timezone)
{
$this->timezone = $timezone;
return $this;
}
/**
* Set which user the command should run as.
*
* @param string $user
*
* @return $this
*/
public function user($user)
{
if ($this->isWindows()) {
throw new NotImplementedException('Changing user on Windows is not implemented.');
}
$this->user = $user;
return $this;
}
/**
* Do not allow the event to overlap each other.
*
* By default, the lock is acquired through file system locks. Alternatively, you can pass a symfony lock store
* that will be responsible for the locking.
*
* @param PersistingStoreInterface|object $store
*
* @return $this
*/
public function preventOverlapping(?object $store = null)
{
if (null !== $store && !($store instanceof PersistingStoreInterface)) {
$expectedClass = PersistingStoreInterface::class;
$actualClass = $store::class;
throw new \RuntimeException(
"Instance of '{$expectedClass}' is expected, '{$actualClass}' provided"
);
}
$lockStore = $store ?: $this->createDefaultLockStore();
$this->preventOverlapping = true;
$this->lockFactory = new LockFactory($lockStore);
// Skip the event if it's locked (processing)
$this->skip(function () {
$lock = $this->createLockObject();
$lock->acquire();
return !$lock->isAcquired();
});
$releaseCallback = function (): void {
$this->releaseLock();
};
// Delete the lock file when the event is completed
$this->after($releaseCallback);
// Or on error
$this->addErrorCallback($releaseCallback);
return $this;
}
/**
* Register a callback to further filter the schedule.
*
* @return $this
*/
public function when(\Closure $callback)
{
$this->filters[] = $callback;
return $this;
}
/**
* Register a callback to further filter the schedule.
*
* @return $this
*/
public function skip(\Closure $callback)
{
$this->rejects[] = $callback;
return $this;
}
/**
* Send the output of the command to a given location.
*
* @param string $location
* @param bool $append
*
* @return $this
*/
public function sendOutputTo($location, $append = false)
{
$this->output = $location;
$this->shouldAppendOutput = $append;
return $this;
}
/**
* Append the output of the command to a given location.
*
* @param string $location
*
* @return $this
*/
public function appendOutputTo($location)
{
return $this->sendOutputTo($location, true);
}
/**
* Register a callback to be called before the operation.
*
* @return $this
*/
public function before(\Closure $callback)
{
$this->beforeCallbacks[] = $callback;
return $this;
}
/**
* Register a callback to be called after the operation.
*
* @return $this
*/
public function after(\Closure $callback)
{
return $this->then($callback);
}
/**
* Register a callback to be called after the operation.
*
* @return $this
*/
public function then(\Closure $callback)
{
$this->afterCallbacks[] = $callback;
return $this;
}
/**
* Set the human-friendly description of the event.
*
* @param string $description
*
* @return $this
*/
public function name($description)
{
return $this->description($description);
}
/**
* Return the event's process.
*
* @return Process $process
*/
public function getProcess()
{
return $this->process;
}
/**
* Set the human-friendly description of the event.
*
* @param string $description
*
* @return $this
*/
public function description($description)
{
$this->description = $description;
return $this;
}
/**
* Another way to the frequency of the cron job.
*
* @param string $unit
* @param float|int|null $value
*/
public function every($unit = null, $value = null): self
{
if (null === $unit || !isset($this->fieldsPosition[$unit])) {
return $this;
}
$value = (1 === (int) $value) ? '*' : '*/' . $value;
return $this->spliceIntoPosition($this->fieldsPosition[$unit], $value)
->applyMask($unit);
}
/**
* Return the event's command.
*/
public function getId(): string|int
{
return $this->id;
}
/**
* Get the summary of the event for display.
*
* @return string
*/
public function getSummaryForDisplay()
{
if (\is_string($this->description)) {
return $this->description;
}
return $this->buildCommand();
}
/**
* Get the command for display.
*
* @return string
*/
public function getCommandForDisplay()
{
return $this->isClosure() ? 'object(Closure)' : $this->buildCommand();
}
/**
* Get the Cron expression for the event.
*
* @return string
*/
public function getExpression()
{
return $this->expression;
}
/**
* Get the 'from' configuration for the event if present.
*/
public function getFrom(): \DateTime|string|null
{
return $this->from;
}
/**
* Get the 'to' configuration for the event if present.
*/
public function getTo(): \DateTime|string|null
{
return $this->to;
}
/**
* Set the event's command.
*
* @param string $command
*
* @return $this
*/
public function setCommand($command)
{
$this->command = $command;
return $this;
}
/**
* Return the event's command.
*/
public function getCommand(): string|\Closure
{
return $this->command;
}
/**
* Return the current working directory.
*
* @return string
*/
public function getWorkingDirectory()
{
return $this->cwd;
}
/**
* Return event's full output.
*
* @return string|null
*/
public function getOutputStream()
{
return $this->outputStream;
}
/**
* Return all registered before callbacks.
*
* @return \Closure[]
*/
public function beforeCallbacks()
{
return $this->beforeCallbacks;
}
/**
* Return all registered after callbacks.
*
* @return \Closure[]
*/
public function afterCallbacks()
{
return $this->afterCallbacks;
}
/** @return \Closure[] */
public function errorCallbacks()
{
return $this->errorCallbacks;
}
/**
* If this event is prevented from overlapping, this method should be called regularly to refresh the lock.
*/
public function refreshLock(): void
{
if (!$this->preventOverlapping) {
return;
}
$lock = $this->createLockObject();
$remainingLifetime = $lock->getRemainingLifetime();
// Lock will never expire
if (null === $remainingLifetime) {
return;
}
$lockRefreshNeeded = $remainingLifetime < self::LOCK_REFRESH_THRESHOLD;
if ($lockRefreshNeeded) {
$lock->refresh();
}
}
public function everyMinute(): self
{
return $this->cron('* * * * *');
}
public function everyTwoMinutes(): self
{
return $this->cron('*/2 * * * *');
}
public function everyThreeMinutes(): self
{
return $this->cron('*/3 * * * *');
}
public function everyFourMinutes(): self
{
return $this->cron('*/4 * * * *');
}
public function everyFiveMinutes(): self
{
return $this->cron('*/5 * * * *');
}
public function everyTenMinutes(): self
{
return $this->cron('*/10 * * * *');
}
public function everyFifteenMinutes(): self
{
return $this->cron('*/15 * * * *');
}
public function everyThirtyMinutes(): self
{
return $this->cron('*/30 * * * *');
}
public function everyTwoHours(): self
{
return $this->cron('0 */2 * * *');
}
public function everyThreeHours(): self
{
return $this->cron('0 */3 * * *');
}
public function everyFourHours(): self
{
return $this->cron('0 */4 * * *');
}
public function everySixHours(): self
{
return $this->cron('0 */6 * * *');
}
/**
* Get the symfony lock object for the task.
*
* @return Lock
*/
protected function createLockObject()
{
$this->checkLockFactory();
if (null === $this->lock && null !== $this->lockFactory) {
$this->lock = $this->lockFactory
->createLock($this->lockKey(), self::LOCK_TTL);
}
return $this->lock;
}
/**
* Release the lock after the command completed.
*/
protected function releaseLock(): void
{
$this->checkLockFactory();
$lock = $this->createLockObject();
$lock->release();
}
/**
* Get the default output depending on the OS.
*
* @return string
*/
protected function getDefaultOutput()
{
return (DIRECTORY_SEPARATOR === '\\') ? 'NUL' : '/dev/null';
}
/**
* Add sudo to the command.
*
* @param string $user
*
* @return string
*/
protected function sudo($user)
{
return "sudo -u {$user} ";
}
/**
* Convert closure to an executable command.
*
* @return string
*/
protected function serializeClosure(\Closure $closure)
{
$closure = $this->closureSerializer()
->serialize($closure)
;
$serializedClosure = \http_build_query([$closure]);
$crunzRoot = CRUNZ_BIN;
return \escapeshellarg(PHP_BINARY) . ' ' . \escapeshellarg($crunzRoot) . " closure:run {$serializedClosure}";
}
/**
* Determine if the Cron expression passes.
*
* @return bool
*/
protected function expressionPasses(\DateTimeZone $timeZone)
{
$now = $this->getClock()
->now();
$now = $now->setTimezone($timeZone);
if ($this->timezone) {
$taskTimeZone = \is_object($this->timezone) && $this->timezone instanceof \DateTimeZone
? $this->timezone
->getName()
: $this->timezone
;
$now = $now->setTimezone(
new \DateTimeZone(
$taskTimeZone
)
);
}
return CronExpression::factory($this->expression)->isDue($now->format('Y-m-d H:i:s'));
}
/**
* Check if time hasn't arrived.
*
* @param string $datetime
*/
protected function notYet($datetime, \DateTimeZone $timeZone): bool
{
$timeZonedNow = $this->timeZonedNow($timeZone);
$testedDateTime = new \DateTimeImmutable($datetime, $timeZone);
return $timeZonedNow < $testedDateTime;
}
/**
* Check if the time has passed.
*
* @param string $datetime
*/
protected function past($datetime, \DateTimeZone $timeZone): bool
{
$timeZonedNow = $this->timeZonedNow($timeZone);
$testedDateTime = new \DateTimeImmutable($datetime, $timeZone);
return $timeZonedNow > $testedDateTime;
}
/**
* Splice the given value into the given position of the expression.
*
* @param int $position
* @param string $value
*/
protected function spliceIntoPosition($position, $value): self
{
$segments = \explode(' ', $this->expression);
$segments[$position - 1] = $value;
return $this->cron(\implode(' ', $segments));
}
/**
* Mask a cron expression.
*
* @param string $unit
*
* @return self
*/
protected function applyMask($unit)
{
$cron = \explode(' ', $this->expression);
$mask = ['0', '0', '1', '1', '*', '*'];
$fpos = $this->fieldsPosition[$unit] - 1;
\array_splice($cron, 0, $fpos, \array_slice($mask, 0, $fpos));
return $this->cron(\implode(' ', $cron));
}
/**
* Lock the event.
*/
protected function lock(): void
{
$lock = $this->createLockObject();
$lock->acquire();
}
private function addErrorCallback(\Closure $closure): void
{
$this->errorCallbacks[] = $closure;
}
/**
* Set the event's process.
*/
private function setProcess(Process $process): void
{
$this->process = $process;
}
/**
* @return FlockStore
*
* @throws CrunzException
*/
private function createDefaultLockStore()
{
try {
$lockPath = Path::create(
[
\sys_get_temp_dir(),
'.crunz',
]
);
$store = new FlockStore($lockPath->toString());
} catch (InvalidArgumentException) {
// Fallback to system temp dir
$lockPath = Path::create([\sys_get_temp_dir()]);
$store = new FlockStore($lockPath->toString());
}
return $store;
}
private function lockKey(): string
{
if ($this->isClosure()) {
/** @var \Closure $closure */
$closure = $this->command;
$command = $this->closureSerializer()
->closureCode($closure)
;
} else {
$command = $this->buildCommand();
}
return 'crunz-' . \md5($command);
}
private function checkLockFactory(): void
{
if (null === $this->lockFactory) {
throw new \BadMethodCallException(
'No lock factory. Please call preventOverlapping() first.'
);
}
}
private function getClock(): ClockInterface
{
if (null === self::$clock) {
self::$clock = new Clock();
}
return self::$clock;
}
private function closureSerializer(): ClosureSerializerInterface
{
if (null === self::$closureSerializer) {
self::$closureSerializer = new LaravelClosureSerializer();
}
return self::$closureSerializer;
}
private function isWindows(): bool
{
$osCode = \mb_substr(
PHP_OS,
0,
3
);
return 'WIN' === $osCode;
}
private function timeZonedNow(\DateTimeZone $timeZone): \DateTimeImmutable
{
$clock = $this->getClock();
$now = $clock->now();
return $now->setTimezone($timeZone);
}
}
================================================
FILE: src/EventRunner.php
================================================
<?php
declare(strict_types=1);
namespace Crunz;
use Crunz\Application\Service\ConfigurationInterface;
use Crunz\HttpClient\HttpClientInterface;
use Crunz\Logger\ConsoleLoggerInterface;
use Crunz\Logger\Logger;
use Crunz\Logger\LoggerFactory;
use Crunz\Pinger\PingableInterface;
use Symfony\Component\Console\Output\OutputInterface;
class EventRunner
{
/** @var Schedule[] */
protected array $schedules = [];
/** @var Logger|null */
protected $logger;
private ?OutputInterface $output = null;
public function __construct(
protected Invoker $invoker,
private readonly ConfigurationInterface $configuration,
protected Mailer $mailer,
private readonly LoggerFactory $loggerFactory,
private readonly HttpClientInterface $httpClient,
private readonly ConsoleLoggerInterface $consoleLogger,
) {
}
/** @param Schedule[] $schedules */
public function handle(OutputInterface $output, array $schedules = []): void
{
$this->schedules = $schedules;
$this->output = $output;
foreach ($this->schedules as $schedule) {
$this->consoleLogger
->debug("Invoke Schedule's ping before");
$this->pingBefore($schedule);
// Running the before-callbacks of the current schedule
$this->invoke($schedule->beforeCallbacks());
$events = $schedule->events();
foreach ($events as $event) {
$this->start($event);
}
}
// Watch events until they are finished
$this->manageStartedEvents();
}
protected function start(Event $event): void
{
$this->logger = $this->loggerFactory
->create()
;
// if sendOutputTo or appendOutputTo have been specified
if (!$event->nullOutput()) {
// if sendOutputTo then truncate the log file if it exists
if (!$event->shouldAppendOutput) {
$f = @\fopen($event->output, 'r+');
if (false !== $f) {
\ftruncate($f, 0);
\fclose($f);
}
}
// Create an instance of the Logger specific to the event
$event->logger = $this->loggerFactory->createEvent($event->output);
}
$this->consoleLogger
->debug("Invoke Event's ping before.");
$this->pingBefore($event);
// Running the before-callbacks
$event->outputStream = $this->invoke($event->beforeCallbacks());
$event->start();
}
protected function manageStartedEvents(): void
{
while ($this->schedules) {
foreach ($this->schedules as $scheduleKey => $schedule) {
$events = $schedule->events();
// 10% chance that refresh will be called
$refreshLocks = (\random_int(1, 100) <= 10);
/** @var Event $event */
foreach ($events as $eventKey => $event) {
if ($refreshLocks) {
$event->refreshLock();
}
$proc = $event->getProcess();
if ($proc->isRunning()) {
continue;
}
$runStatus = '';
if ($proc->isSuccessful()) {
$this->consoleLogger
->debug("Invoke Event's ping after.");
$this->pingAfter($event);
$runStatus = '<info>success</info>';
$event->outputStream .= $event->wholeOutput();
$event->outputStream .= $this->invoke($event->afterCallbacks());
$this->handleOutput($event);
} else {
$runStatus = '<error>fail</error>';
// Invoke error callbacks
$this->invoke($event->errorCallbacks());
// Calling registered error callbacks with an instance of $event as argument
$this->invoke($schedule->errorCallbacks(), [$event]);
$this->handleError($event);
}
$id = $event->description ?: $event->getId();
$this->consoleLogger
->debug("Task <info>{$id}</info> status: {$runStatus}.");
// Dismiss the event if it's finished
$schedule->dismissEvent($eventKey);
}
// If there's no event left for the Schedule instance,
// run the schedule's after-callbacks and remove
// the Schedule from list of active schedules. zzzwwscxqqqAAAQ11
if (!\count($schedule->events())) {
$this->consoleLogger
->debug("Invoke Schedule's ping after.");
$this->pingAfter($schedule);
$this->invoke($schedule->afterCallbacks());
unset($this->schedules[$scheduleKey]);
}
}
\usleep(250000);
}
}
/**
* @param \Closure[] $callbacks
* @param array<mixed,mixed> $parameters
*
* @return string
*/
protected function invoke(array $callbacks = [], array $parameters = [])
{
$output = '';
foreach ($callbacks as $callback) {
/** @var string $callResult */
$callResult = $this->invoker->call($callback, $parameters, true);
// Invoke the callback with buffering enabled
$output .= $callResult;
}
return $output;
}
protected function handleOutput(Event $event): void
{
$logged = false;
$logOutput = $this->configuration
->get('log_output')
;
if (!$event->nullOutput()) {
$event->logger->info($this->formatEventOutput($event));
$logged = true;
}
if ($logOutput && !$logged) {
$this->logger()
->info($this->formatEventOutput($event))
;
$logged = true;
}
if (!$logged) {
$this->display($event->getOutputStream());
}
$emailOutput = $this->configuration
->get('email_output')
;
if ($emailOutput && !empty($event->getOutputStream())) {
$this->mailer->send(
'Crunz: output for event: ' . ($event->description ?? $event->getId()),
$this->formatEventOutput($event)
);
}
}
protected function handleError(Event $event): void
{
$logErrors = $this->configuration
->get('log_errors')
;
$emailErrors = $this->configuration
->get('email_errors')
;
if ($logErrors) {
$this->logger()
->error($this->formatEventError($event))
;
} else {
$output = $event->wholeOutput();
$this->output
?->write("<error>{$output}</error>")
;
}
// Send error as email as configured
if ($emailErrors) {
$this->mailer->send(
'Crunz: reporting error for event:' . ($event->description ?? $event->getId()),
$this->formatEventError($event)
);
}
}
/** @return string */
protected function formatEventOutput(Event $event)
{
return $event->description
. '('
. $event->getCommandForDisplay()
. ') '
. PHP_EOL
. PHP_EOL
. $event->outputStream
. PHP_EOL;
}
/** @return string */
protected function formatEventError(Event $event)
{
return $event->description
. '('
. $event->getCommandForDisplay()
. ') '
. PHP_EOL
. $event->wholeOutput()
. PHP_EOL;
}
/** @param string|null $output */
protected function display($output): void
{
$this->output
?->write(\is_string($output) ? $output : '')
;
}
private function pingBefore(PingableInterface $schedule): void
{
if (!$schedule->hasPingBefore()) {
$this->consoleLogger
->debug('There is no ping before url.');
return;
}
/** @var non-empty-string $pingBeforeUrl */
$pingBeforeUrl = $schedule->getPingBeforeUrl();
$this->httpClient
->ping($pingBeforeUrl)
;
}
private function pingAfter(PingableInterface $schedule): void
{
if (!$schedule->hasPingAfter()) {
$this->consoleLogger
->debug('There is no ping after url.');
return;
}
/** @var non-empty-string $pingAfterUrl */
$pingAfterUrl = $schedule->getPingAfterUrl();
$this->httpClient
->ping($pingAfterUrl)
;
}
private function logger(): Logger
{
if (null === $this->logger) {
$this->logger = $this->loggerFactory
->create()
;
}
return $this->logger;
}
}
================================================
FILE: src/Exception/CrunzException.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Exception;
class CrunzException extends \Exception
{
}
================================================
FILE: src/Exception/EmptyTimezoneException.php
================================================
<?php
declare(strict_types=1);
namespace Crunz\Ex
gitextract_lvj5ppvr/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1_Bug_report.md
│ │ └── 2_Feature_request.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── php.yaml
├── .gitignore
├── .php-cs-fixer.dist.php
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── UPGRADE.md
├── bootstrap.php
├── composer.json
├── config/
│ └── services.php
├── crunz
├── docker/
│ └── php82/
│ ├── Dockerfile
│ └── php.ini
├── docker-compose.yml
├── phpstan-baseline.neon
├── phpstan.neon
├── phpunit.xml
├── resources/
│ └── config/
│ └── crunz.yml
├── src/
│ ├── Application/
│ │ ├── Cron/
│ │ │ ├── CronExpressionFactoryInterface.php
│ │ │ └── CronExpressionInterface.php
│ │ ├── Query/
│ │ │ └── TaskInformation/
│ │ │ ├── TaskInformation.php
│ │ │ ├── TaskInformationHandler.php
│ │ │ └── TaskInformationView.php
│ │ └── Service/
│ │ ├── ClosureSerializerInterface.php
│ │ ├── ConfigurationInterface.php
│ │ └── LoggerFactoryInterface.php
│ ├── Application.php
│ ├── CacheDirectoryFactory/
│ │ └── CacheDirectoryFactory.php
│ ├── Clock/
│ │ ├── Clock.php
│ │ └── ClockInterface.php
│ ├── Configuration/
│ │ ├── ConfigFileNotExistsException.php
│ │ ├── ConfigFileNotReadableException.php
│ │ ├── Configuration.php
│ │ ├── ConfigurationParser.php
│ │ ├── ConfigurationParserInterface.php
│ │ ├── Definition.php
│ │ └── FileParser.php
│ ├── Console/
│ │ └── Command/
│ │ ├── Command.php
│ │ ├── ConfigGeneratorCommand.php
│ │ ├── ScheduleListCommand.php
│ │ ├── ScheduleRunCommand.php
│ │ └── TaskGeneratorCommand.php
│ ├── EnvFlags/
│ │ └── EnvFlags.php
│ ├── Event.php
│ ├── EventRunner.php
│ ├── Exception/
│ │ ├── CrunzException.php
│ │ ├── EmptyTimezoneException.php
│ │ ├── MailerException.php
│ │ ├── NotImplementedException.php
│ │ ├── TaskNotExistException.php
│ │ └── WrongTaskNumberException.php
│ ├── Filesystem/
│ │ ├── Filesystem.php
│ │ └── FilesystemInterface.php
│ ├── Finder/
│ │ ├── Finder.php
│ │ └── FinderInterface.php
│ ├── HttpClient/
│ │ ├── CurlHttpClient.php
│ │ ├── FallbackHttpClient.php
│ │ ├── HttpClientException.php
│ │ ├── HttpClientInterface.php
│ │ ├── HttpClientLoggerDecorator.php
│ │ └── StreamHttpClient.php
│ ├── Infrastructure/
│ │ ├── Dragonmantank/
│ │ │ └── CronExpression/
│ │ │ ├── DragonmantankCronExpression.php
│ │ │ └── DragonmantankCronExpressionFactory.php
│ │ ├── Laravel/
│ │ │ └── LaravelClosureSerializer.php
│ │ └── Psr/
│ │ └── Logger/
│ │ ├── EnabledLoggerDecorator.php
│ │ ├── PsrStreamLogger.php
│ │ └── PsrStreamLoggerFactory.php
│ ├── Invoker.php
│ ├── Logger/
│ │ ├── ConsoleLogger.php
│ │ ├── ConsoleLoggerInterface.php
│ │ ├── Logger.php
│ │ └── LoggerFactory.php
│ ├── Mailer.php
│ ├── Output/
│ │ └── OutputFactory.php
│ ├── Path/
│ │ └── Path.php
│ ├── Pinger/
│ │ ├── PingableException.php
│ │ ├── PingableInterface.php
│ │ └── PingableTrait.php
│ ├── Process/
│ │ └── Process.php
│ ├── Schedule/
│ │ └── ScheduleFactory.php
│ ├── Schedule.php
│ ├── Stubs/
│ │ └── BasicTask.php
│ ├── Task/
│ │ ├── Collection.php
│ │ ├── CollectionInterface.php
│ │ ├── Loader.php
│ │ ├── LoaderInterface.php
│ │ ├── TaskException.php
│ │ ├── TaskNumber.php
│ │ ├── Timezone.php
│ │ └── WrongTaskInstanceException.php
│ ├── Timezone/
│ │ ├── Provider.php
│ │ └── ProviderInterface.php
│ └── UserInterface/
│ └── Cli/
│ ├── ClosureRunCommand.php
│ └── DebugTaskCommand.php
└── tests/
├── EndToEnd/
│ ├── ClosureRunTest.php
│ ├── ConfigProviderTest.php
│ ├── ConfigRecognitionTest.php
│ ├── DebugTaskTest.php
│ ├── LoggerTest.php
│ ├── TasksSourceRecognitionTest.php
│ ├── VersionTest.php
│ └── WrongTaskTest.php
├── Functional/
│ ├── ConfigProviderTest.php
│ ├── DifferentBaseCacheDirTest.php
│ ├── ScheduleListTest.php
│ ├── ScheduleRunTest.php
│ └── TaskGeneratorTest.php
├── TestCase/
│ ├── EndToEnd/
│ │ └── Environment/
│ │ ├── Environment.php
│ │ └── EnvironmentBuilder.php
│ ├── EndToEndTestCase.php
│ ├── FakeConfiguration.php
│ ├── FakeLoader.php
│ ├── FakeTaskCollection.php
│ ├── Faker.php
│ ├── Logger/
│ │ ├── NullLogger.php
│ │ └── SpyPsrLogger.php
│ ├── SerializableTaskRunnerStub.php
│ ├── TaskRunnerStub.php
│ ├── TemporaryFile.php
│ ├── TestClock.php
│ └── UnitTestCase.php
├── Unit/
│ ├── Application/
│ │ ├── Cron/
│ │ │ └── AbstractCronExpressionTestCase.php
│ │ └── Query/
│ │ └── TaskInformation/
│ │ └── TaskInformationHandlerTest.php
│ ├── CacheDirectoryFactory/
│ │ └── CacheDirectoryFactoryTest.php
│ ├── Configuration/
│ │ ├── ConfigurationParserTest.php
│ │ ├── ConfigurationTest.php
│ │ └── FileParserTest.php
│ ├── Console/
│ │ └── Command/
│ │ ├── ScheduleListCommandTest.php
│ │ └── ScheduleRunCommandTest.php
│ ├── EnvFlags/
│ │ └── EnvFlagsTest.php
│ ├── EventRunnerTest.php
│ ├── EventTest.php
│ ├── Filesystem/
│ │ └── FilesystemTest.php
│ ├── Finder/
│ │ └── FinderTest.php
│ ├── HttpClient/
│ │ └── StreamHttpClientTest.php
│ ├── Infrastructure/
│ │ ├── Dragonmantank/
│ │ │ └── CronExpression/
│ │ │ └── DragonmantankCronExpressionTestCase.php
│ │ └── Psr/
│ │ └── Logger/
│ │ ├── EnabledLoggerDecoratorTest.php
│ │ ├── PsrStreamLoggerFactoryTest.php
│ │ └── PsrStreamLoggerTest.php
│ ├── InvokerTest.php
│ ├── Logger/
│ │ ├── ConsoleLoggerTest.php
│ │ └── LoggerFactoryTest.php
│ ├── MailerTest.php
│ ├── Output/
│ │ └── OutputFactoryTest.php
│ ├── Path/
│ │ └── PathTest.php
│ ├── Pingable.php
│ ├── Pinger/
│ │ └── PingableTest.php
│ ├── Process/
│ │ └── ProcessTest.php
│ ├── Schedule/
│ │ └── ScheduleFactoryTest.php
│ ├── ScheduleTest.php
│ ├── Service/
│ │ ├── AbstractClosureSerializerTestCase.php
│ │ ├── LaravelClosureSerializerTest.php
│ │ └── LaravelClosureSerializerTestCase.php
│ ├── Task/
│ │ ├── TaskNumberTest.php
│ │ └── TimezoneTest.php
│ ├── Timezone/
│ │ └── ProviderTest.php
│ └── UserInterface/
│ └── Cli/
│ └── ClosureRunCommandTest.php
├── crunz.yml
├── resources/
│ ├── fixtures/
│ │ └── finder/
│ │ ├── direct/
│ │ │ └── directHere.php
│ │ └── symlink/
│ │ └── symlinkHere.php
│ └── tasks/
│ ├── ClosureTasks.php
│ ├── CustomOutputTasks.php
│ ├── FailTasks.php
│ ├── NoOverlappingClosureTasks.php
│ ├── PhpVersionTasks.php
│ └── WrongTasks.php
└── tasks/
└── TestTasks.php
SYMBOL INDEX (758 symbols across 138 files)
FILE: src/Application.php
class Application (line 27) | class Application extends SymfonyApplication
method __construct (line 66) | public function __construct(string $appName, string $appVersion)
method run (line 91) | public function run(?InputInterface $input = null, ?OutputInterface $o...
method initializeContainer (line 112) | private function initializeContainer(): void
method buildContainer (line 159) | private function buildContainer()
method dumpContainer (line 176) | private function dumpContainer(
method createBaseCacheDirectory (line 199) | private function createBaseCacheDirectory()
method getBaseCacheDir (line 219) | private function getBaseCacheDir(): string
method getContainerCacheDir (line 227) | private function getContainerCacheDir()
method registerDeprecationHandler (line 240) | private function registerDeprecationHandler(): void
FILE: src/Application/Cron/CronExpressionFactoryInterface.php
type CronExpressionFactoryInterface (line 7) | interface CronExpressionFactoryInterface
method createFromString (line 9) | public function createFromString(string $cronExpression): CronExpressi...
FILE: src/Application/Cron/CronExpressionInterface.php
type CronExpressionInterface (line 7) | interface CronExpressionInterface
method multipleRunDates (line 10) | public function multipleRunDates(
FILE: src/Application/Query/TaskInformation/TaskInformation.php
class TaskInformation (line 9) | final class TaskInformation
method __construct (line 11) | public function __construct(private readonly TaskNumber $taskNumber)
method taskNumber (line 15) | public function taskNumber(): TaskNumber
FILE: src/Application/Query/TaskInformation/TaskInformationHandler.php
class TaskInformationHandler (line 15) | final class TaskInformationHandler
method __construct (line 17) | public function __construct(
method handle (line 27) | public function handle(TaskInformation $taskInformation): TaskInformat...
method getEventProperties (line 82) | private function getEventProperties(Event $event, array $properties): ...
FILE: src/Application/Query/TaskInformation/TaskInformationView.php
class TaskInformationView (line 7) | final class TaskInformationView
method __construct (line 12) | public function __construct(
method command (line 24) | public function command(): string|object
method description (line 29) | public function description(): string
method cronExpression (line 34) | public function cronExpression(): string
method timeZone (line 39) | public function timeZone(): ?\DateTimeZone
method configTimeZone (line 44) | public function configTimeZone(): \DateTimeZone
method nextRuns (line 50) | public function nextRuns(): array
method preventOverlapping (line 55) | public function preventOverlapping(): bool
FILE: src/Application/Service/ClosureSerializerInterface.php
type ClosureSerializerInterface (line 7) | interface ClosureSerializerInterface
method serialize (line 9) | public function serialize(\Closure $closure): string;
method unserialize (line 11) | public function unserialize(string $serializedClosure): \Closure;
method closureCode (line 13) | public function closureCode(\Closure $closure): string;
FILE: src/Application/Service/ConfigurationInterface.php
type ConfigurationInterface (line 7) | interface ConfigurationInterface
method get (line 12) | public function get(string $key, mixed $default = null): mixed;
method withNewEntry (line 17) | public function withNewEntry(string $key, mixed $value): Configuration...
method getSourcePath (line 19) | public function getSourcePath(): string;
FILE: src/Application/Service/LoggerFactoryInterface.php
type LoggerFactoryInterface (line 12) | interface LoggerFactoryInterface
method create (line 14) | public function create(ConfigurationInterface $configuration): LoggerI...
FILE: src/CacheDirectoryFactory/CacheDirectoryFactory.php
class CacheDirectoryFactory (line 10) | final class CacheDirectoryFactory
method generate (line 17) | public function generate(): Path
FILE: src/Clock/Clock.php
class Clock (line 7) | final class Clock implements ClockInterface
method now (line 9) | public function now(): \DateTimeImmutable
FILE: src/Clock/ClockInterface.php
type ClockInterface (line 7) | interface ClockInterface
method now (line 9) | public function now(): \DateTimeImmutable;
FILE: src/Configuration/ConfigFileNotExistsException.php
class ConfigFileNotExistsException (line 9) | final class ConfigFileNotExistsException extends CrunzException
method fromFilePath (line 11) | public static function fromFilePath(string $filePath): self
FILE: src/Configuration/ConfigFileNotReadableException.php
class ConfigFileNotReadableException (line 9) | final class ConfigFileNotReadableException extends CrunzException
method fromFilePath (line 11) | public static function fromFilePath(string $filePath): self
FILE: src/Configuration/Configuration.php
class Configuration (line 11) | final class Configuration implements ConfigurationInterface
method __construct (line 16) | public function __construct(
method get (line 25) | public function get(string $key, mixed $default = null): mixed
method withNewEntry (line 53) | public function withNewEntry(string $key, mixed $value): Configuration...
method getSourcePath (line 77) | public function getSourcePath(): string
FILE: src/Configuration/ConfigurationParser.php
class ConfigurationParser (line 14) | final class ConfigurationParser implements ConfigurationParserInterface
method __construct (line 16) | public function __construct(
method parseConfig (line 25) | public function parseConfig(): array
method configFilePath (line 61) | private function configFilePath(): string
FILE: src/Configuration/ConfigurationParserInterface.php
type ConfigurationParserInterface (line 7) | interface ConfigurationParserInterface
method parseConfig (line 10) | public function parseConfig(): array;
FILE: src/Configuration/Definition.php
class Definition (line 11) | class Definition implements ConfigurationInterface
method getConfigTreeBuilder (line 13) | public function getConfigTreeBuilder(): TreeBuilder
FILE: src/Configuration/FileParser.php
class FileParser (line 9) | class FileParser
method __construct (line 11) | public function __construct(private readonly Yaml $yamlParser)
method parse (line 21) | public function parse(string $configPath): array
FILE: src/Console/Command/Command.php
class Command (line 9) | class Command extends BaseCommand
FILE: src/Console/Command/ConfigGeneratorCommand.php
class ConfigGeneratorCommand (line 16) | final class ConfigGeneratorCommand extends Command
method __construct (line 20) | public function __construct(
method configure (line 31) | protected function configure(): void
method execute (line 40) | protected function execute(InputInterface $input, OutputInterface $out...
method askForTimezone (line 97) | protected function askForTimezone(SymfonyStyle $symfonyStyleIo)
method updateTimezone (line 123) | private function updateTimezone(
FILE: src/Console/Command/ScheduleListCommand.php
class ScheduleListCommand (line 18) | class ScheduleListCommand extends \Symfony\Component\Console\Command\Com...
method __construct (line 27) | public function __construct(
method configure (line 38) | protected function configure(): void
method execute (line 68) | protected function execute(InputInterface $input, OutputInterface $out...
method tasks (line 100) | private function tasks(string $source): array
method resolveFormat (line 128) | private function resolveFormat(InputInterface $input): string
method printList (line 156) | private function printList(
FILE: src/Console/Command/ScheduleRunCommand.php
class ScheduleRunCommand (line 20) | class ScheduleRunCommand extends Command
method __construct (line 22) | public function __construct(
method configure (line 36) | protected function configure(): void
method execute (line 70) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Console/Command/TaskGeneratorCommand.php
class TaskGeneratorCommand (line 16) | class TaskGeneratorCommand extends Command
method __construct (line 38) | public function __construct(
method configure (line 48) | protected function configure(): void
method execute (line 107) | protected function execute(InputInterface $input, OutputInterface $out...
method save (line 140) | protected function save()
method ask (line 154) | protected function ask($question)
method outputPath (line 167) | protected function outputPath()
method outputFile (line 187) | protected function outputFile()
method getStub (line 204) | protected function getStub()
method type (line 224) | protected function type()
method replaceFrequency (line 232) | protected function replaceFrequency(): self
method replaceConstraint (line 242) | protected function replaceConstraint(): self
method replaceCommand (line 249) | protected function replaceCommand(): self
method replacePath (line 257) | protected function replacePath(): self
method replaceDescription (line 265) | protected function replaceDescription(): self
method optionString (line 273) | private function optionString(string $name): string
FILE: src/EnvFlags/EnvFlags.php
class EnvFlags (line 9) | final class EnvFlags
method isDeprecationHandlerEnabled (line 15) | public function isDeprecationHandlerEnabled()
method isContainerDebugEnabled (line 28) | public function isContainerDebugEnabled()
method disableContainerDebug (line 41) | public function disableContainerDebug(): void
method enableContainerDebug (line 49) | public function enableContainerDebug(): void
method enableDeprecationHandler (line 57) | public function enableDeprecationHandler(): void
method disableDeprecationHandler (line 65) | public function disableDeprecationHandler(): void
FILE: src/Event.php
class Event (line 28) | class Event implements PingableInterface
method __construct (line 185) | public function __construct(protected $id, $command)
method in (line 198) | public function in($directory)
method nullOutput (line 210) | public function nullOutput()
method buildCommand (line 220) | public function buildCommand()
method isClosure (line 253) | public function isClosure()
method isDue (line 263) | public function isDue(\DateTimeZone $timeZone)
method filtersPass (line 273) | public function filtersPass(\DateTimeZone $timeZone)
method wholeOutput (line 293) | public function wholeOutput()
method start (line 303) | public function start()
method cron (line 331) | public function cron(string $expression): self
method hourly (line 356) | public function hourly(): self
method hourlyAt (line 361) | public function hourlyAt(int $minute): self
method daily (line 377) | public function daily(): self
method on (line 389) | public function on($date)
method at (line 416) | public function at($time): self
method dailyAt (line 426) | public function dailyAt($time): self
method between (line 449) | public function between($from, $to)
method from (line 462) | public function from($datetime)
method to (line 478) | public function to($datetime)
method twiceDaily (line 493) | public function twiceDaily($first = 1, $second = 13): self
method weekdays (line 506) | public function weekdays(): self
method mondays (line 514) | public function mondays(): self
method tuesdays (line 522) | public function tuesdays(): self
method wednesdays (line 530) | public function wednesdays(): self
method thursdays (line 538) | public function thursdays(): self
method fridays (line 546) | public function fridays(): self
method saturdays (line 554) | public function saturdays(): self
method sundays (line 562) | public function sundays(): self
method weekly (line 570) | public function weekly(): self
method weeklyOn (line 580) | public function weeklyOn(int|string $day, $time = '0:0'): self
method monthly (line 590) | public function monthly(): self
method quarterly (line 598) | public function quarterly(): self
method yearly (line 606) | public function yearly(): self
method days (line 614) | public function days(mixed $days): self
method hour (line 624) | public function hour(mixed $value): self
method minute (line 634) | public function minute(mixed $value): self
method dayOfMonth (line 644) | public function dayOfMonth(mixed $value): self
method month (line 654) | public function month(mixed $value): self
method dayOfWeek (line 664) | public function dayOfWeek(mixed $value): self
method timezone (line 676) | public function timezone(\DateTimeZone|string $timezone)
method user (line 690) | public function user($user)
method preventOverlapping (line 711) | public function preventOverlapping(?object $store = null)
method when (line 751) | public function when(\Closure $callback)
method skip (line 763) | public function skip(\Closure $callback)
method sendOutputTo (line 778) | public function sendOutputTo($location, $append = false)
method appendOutputTo (line 794) | public function appendOutputTo($location)
method before (line 804) | public function before(\Closure $callback)
method after (line 816) | public function after(\Closure $callback)
method then (line 826) | public function then(\Closure $callback)
method name (line 840) | public function name($description)
method getProcess (line 850) | public function getProcess()
method description (line 862) | public function description($description)
method every (line 875) | public function every($unit = null, $value = null): self
method getId (line 890) | public function getId(): string|int
method getSummaryForDisplay (line 900) | public function getSummaryForDisplay()
method getCommandForDisplay (line 914) | public function getCommandForDisplay()
method getExpression (line 924) | public function getExpression()
method getFrom (line 932) | public function getFrom(): \DateTime|string|null
method getTo (line 940) | public function getTo(): \DateTime|string|null
method setCommand (line 952) | public function setCommand($command)
method getCommand (line 962) | public function getCommand(): string|\Closure
method getWorkingDirectory (line 972) | public function getWorkingDirectory()
method getOutputStream (line 982) | public function getOutputStream()
method beforeCallbacks (line 992) | public function beforeCallbacks()
method afterCallbacks (line 1002) | public function afterCallbacks()
method errorCallbacks (line 1008) | public function errorCallbacks()
method refreshLock (line 1016) | public function refreshLock(): void
method everyMinute (line 1036) | public function everyMinute(): self
method everyTwoMinutes (line 1041) | public function everyTwoMinutes(): self
method everyThreeMinutes (line 1046) | public function everyThreeMinutes(): self
method everyFourMinutes (line 1051) | public function everyFourMinutes(): self
method everyFiveMinutes (line 1056) | public function everyFiveMinutes(): self
method everyTenMinutes (line 1061) | public function everyTenMinutes(): self
method everyFifteenMinutes (line 1066) | public function everyFifteenMinutes(): self
method everyThirtyMinutes (line 1071) | public function everyThirtyMinutes(): self
method everyTwoHours (line 1076) | public function everyTwoHours(): self
method everyThreeHours (line 1081) | public function everyThreeHours(): self
method everyFourHours (line 1086) | public function everyFourHours(): self
method everySixHours (line 1091) | public function everySixHours(): self
method createLockObject (line 1101) | protected function createLockObject()
method releaseLock (line 1116) | protected function releaseLock(): void
method getDefaultOutput (line 1129) | protected function getDefaultOutput()
method sudo (line 1141) | protected function sudo($user)
method serializeClosure (line 1151) | protected function serializeClosure(\Closure $closure)
method expressionPasses (line 1167) | protected function expressionPasses(\DateTimeZone $timeZone)
method notYet (line 1195) | protected function notYet($datetime, \DateTimeZone $timeZone): bool
method past (line 1208) | protected function past($datetime, \DateTimeZone $timeZone): bool
method spliceIntoPosition (line 1222) | protected function spliceIntoPosition($position, $value): self
method applyMask (line 1238) | protected function applyMask($unit)
method lock (line 1252) | protected function lock(): void
method addErrorCallback (line 1258) | private function addErrorCallback(\Closure $closure): void
method setProcess (line 1266) | private function setProcess(Process $process): void
method createDefaultLockStore (line 1276) | private function createDefaultLockStore()
method lockKey (line 1296) | private function lockKey(): string
method checkLockFactory (line 1311) | private function checkLockFactory(): void
method getClock (line 1320) | private function getClock(): ClockInterface
method closureSerializer (line 1329) | private function closureSerializer(): ClosureSerializerInterface
method isWindows (line 1338) | private function isWindows(): bool
method timeZonedNow (line 1349) | private function timeZonedNow(\DateTimeZone $timeZone): \DateTimeImmut...
FILE: src/EventRunner.php
class EventRunner (line 15) | class EventRunner
method __construct (line 23) | public function __construct(
method handle (line 34) | public function handle(OutputInterface $output, array $schedules = [])...
method start (line 58) | protected function start(Event $event): void
method manageStartedEvents (line 88) | protected function manageStartedEvents(): void
method invoke (line 162) | protected function invoke(array $callbacks = [], array $parameters = [])
method handleOutput (line 175) | protected function handleOutput(Event $event): void
method handleError (line 209) | protected function handleError(Event $event): void
method formatEventOutput (line 240) | protected function formatEventOutput(Event $event)
method formatEventError (line 253) | protected function formatEventError(Event $event)
method display (line 265) | protected function display($output): void
method pingBefore (line 272) | private function pingBefore(PingableInterface $schedule): void
method pingAfter (line 288) | private function pingAfter(PingableInterface $schedule): void
method logger (line 304) | private function logger(): Logger
FILE: src/Exception/CrunzException.php
class CrunzException (line 7) | class CrunzException extends \Exception
FILE: src/Exception/EmptyTimezoneException.php
class EmptyTimezoneException (line 7) | class EmptyTimezoneException extends CrunzException
FILE: src/Exception/MailerException.php
class MailerException (line 7) | final class MailerException extends CrunzException
FILE: src/Exception/NotImplementedException.php
class NotImplementedException (line 7) | class NotImplementedException extends CrunzException
FILE: src/Exception/TaskNotExistException.php
class TaskNotExistException (line 7) | class TaskNotExistException extends CrunzException
FILE: src/Exception/WrongTaskNumberException.php
class WrongTaskNumberException (line 7) | class WrongTaskNumberException extends CrunzException
FILE: src/Filesystem/Filesystem.php
class Filesystem (line 9) | final class Filesystem implements FilesystemInterface
method getCwd (line 13) | public function getCwd()
method fileExists (line 24) | public function fileExists($filePath)
method tempDir (line 29) | public function tempDir()
method removeDirectory (line 34) | public function removeDirectory($directoryPath, $ignoredPaths = []): void
method dumpFile (line 70) | public function dumpFile($filePath, $content): void
method createDirectory (line 78) | public function createDirectory($directoryPath): void
method copy (line 99) | public function copy($sourceFile, $targetFile): void
method projectRootDirectory (line 104) | public function projectRootDirectory()
method readContent (line 129) | public function readContent($filePath)
FILE: src/Filesystem/FilesystemInterface.php
type FilesystemInterface (line 9) | interface FilesystemInterface
method getCwd (line 12) | public function getCwd();
method fileExists (line 19) | public function fileExists($filePath);
method tempDir (line 22) | public function tempDir();
method removeDirectory (line 28) | public function removeDirectory($directoryPath, $ignoredPaths = []): v...
method dumpFile (line 34) | public function dumpFile($filePath, $content): void;
method createDirectory (line 37) | public function createDirectory($directoryPath): void;
method copy (line 43) | public function copy($sourceFile, $targetFile): void;
method projectRootDirectory (line 46) | public function projectRootDirectory();
method readContent (line 53) | public function readContent($filePath);
FILE: src/Finder/Finder.php
class Finder (line 9) | final class Finder implements FinderInterface
method find (line 11) | public function find(Path $directory, $suffix)
FILE: src/Finder/FinderInterface.php
type FinderInterface (line 9) | interface FinderInterface
method find (line 16) | public function find(Path $directory, $suffix);
FILE: src/HttpClient/CurlHttpClient.php
class CurlHttpClient (line 7) | final class CurlHttpClient implements HttpClientInterface
method ping (line 9) | public function ping($url): void
FILE: src/HttpClient/FallbackHttpClient.php
class FallbackHttpClient (line 9) | final class FallbackHttpClient implements HttpClientInterface
method __construct (line 14) | public function __construct(
method ping (line 21) | public function ping($url): void
method chooseHttpClient (line 28) | private function chooseHttpClient(): HttpClientInterface
FILE: src/HttpClient/HttpClientException.php
class HttpClientException (line 9) | class HttpClientException extends CrunzException
FILE: src/HttpClient/HttpClientInterface.php
type HttpClientInterface (line 7) | interface HttpClientInterface
method ping (line 14) | public function ping($url): void;
FILE: src/HttpClient/HttpClientLoggerDecorator.php
class HttpClientLoggerDecorator (line 9) | final class HttpClientLoggerDecorator implements HttpClientInterface
method __construct (line 11) | public function __construct(private readonly HttpClientInterface $http...
method ping (line 15) | public function ping($url): void
FILE: src/HttpClient/StreamHttpClient.php
class StreamHttpClient (line 7) | final class StreamHttpClient implements HttpClientInterface
method ping (line 14) | public function ping($url): void
FILE: src/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpression.php
class DragonmantankCronExpression (line 10) | final class DragonmantankCronExpression implements CronExpressionInterface
method __construct (line 12) | public function __construct(private readonly CronExpression $innerCron...
method multipleRunDates (line 16) | public function multipleRunDates(int $total, \DateTimeImmutable $now, ...
FILE: src/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpressionFactory.php
class DragonmantankCronExpressionFactory (line 11) | final class DragonmantankCronExpressionFactory implements CronExpression...
method createFromString (line 13) | public function createFromString(string $cronExpression): CronExpressi...
FILE: src/Infrastructure/Laravel/LaravelClosureSerializer.php
class LaravelClosureSerializer (line 11) | final class LaravelClosureSerializer implements ClosureSerializerInterface
method serialize (line 13) | public function serialize(\Closure $closure): string
method unserialize (line 22) | public function unserialize(string $serializedClosure): \Closure
method closureCode (line 29) | public function closureCode(\Closure $closure): string
method extractWrapper (line 36) | private function extractWrapper(string $serializedClosure): Serializab...
FILE: src/Infrastructure/Psr/Logger/EnabledLoggerDecorator.php
class EnabledLoggerDecorator (line 12) | final class EnabledLoggerDecorator extends AbstractLogger
method __construct (line 14) | public function __construct(
method log (line 20) | public function log($level, string|\Stringable $message, array $contex...
FILE: src/Infrastructure/Psr/Logger/PsrStreamLogger.php
class PsrStreamLogger (line 12) | final class PsrStreamLogger extends AbstractLogger
method __construct (line 23) | public function __construct(
method __destruct (line 36) | public function __destruct()
method log (line 42) | public function log(
method createInfoHandler (line 69) | private function createInfoHandler()
method createErrorHandler (line 79) | private function createErrorHandler()
method initializeHandler (line 89) | private function initializeHandler(string $path)
method closeStream (line 125) | private function closeStream($stream): void
method dirFromStream (line 134) | private function dirFromStream(string $stream): ?string
method formatContext (line 154) | private function formatContext(array $data): string
method formatDate (line 163) | private function formatDate(): string
method replaceNewlines (line 176) | private function replaceNewlines(string $message): string
FILE: src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php
class PsrStreamLoggerFactory (line 13) | final class PsrStreamLoggerFactory implements LoggerFactoryInterface
method __construct (line 15) | public function __construct(private readonly Timezone $timezoneProvide...
method create (line 19) | public function create(ConfigurationInterface $configuration): LoggerI...
FILE: src/Invoker.php
class Invoker (line 7) | class Invoker
method call (line 16) | public function call($closure, array $parameters = [], $buffer = false...
FILE: src/Logger/ConsoleLogger.php
class ConsoleLogger (line 9) | final class ConsoleLogger implements ConsoleLoggerInterface
method __construct (line 11) | public function __construct(private readonly SymfonyStyle $symfonyStyle)
method normal (line 18) | public function normal($message): void
method verbose (line 26) | public function verbose($message): void
method veryVerbose (line 34) | public function veryVerbose($message): void
method debug (line 44) | public function debug($message): void
method write (line 53) | private function write($message, $verbosity): void
FILE: src/Logger/ConsoleLoggerInterface.php
type ConsoleLoggerInterface (line 9) | interface ConsoleLoggerInterface
method normal (line 20) | public function normal($message): void;
method verbose (line 25) | public function verbose($message): void;
method veryVerbose (line 30) | public function veryVerbose($message): void;
method debug (line 37) | public function debug($message): void;
FILE: src/Logger/Logger.php
class Logger (line 9) | class Logger
method __construct (line 11) | public function __construct(private readonly LoggerInterface $psrLogger)
method info (line 18) | public function info(string $message): void
method error (line 26) | public function error(string $message): void
method log (line 31) | private function log(string $content, string $level): void
FILE: src/Logger/LoggerFactory.php
class LoggerFactory (line 14) | class LoggerFactory
method __construct (line 18) | public function __construct(
method create (line 26) | public function create(): Logger
method createEvent (line 35) | public function createEvent(string $output): Logger
method loggerFactory (line 44) | private function loggerFactory(): LoggerFactoryInterface
method initializeLoggerFactory (line 49) | private function initializeLoggerFactory(): LoggerFactoryInterface
method createLoggerFactory (line 74) | private function createLoggerFactory(
FILE: src/Mailer.php
class Mailer (line 14) | class Mailer
method __construct (line 19) | public function __construct(private readonly ConfigurationInterface $c...
method send (line 28) | public function send(string $subject, string $message): void
method getMailer (line 42) | private function getMailer(): SymfonyMailer
method getSmtpTransport (line 70) | private function getSmtpTransport(): Transport\TransportInterface
method getSendMailTransport (line 91) | private function getSendMailTransport(): Transport\TransportInterface
method getMessage (line 98) | private function getMessage(string $subject, string $message): Email
method config (line 114) | private function config(string $key): mixed
FILE: src/Output/OutputFactory.php
class OutputFactory (line 11) | final class OutputFactory
method __construct (line 13) | public function __construct(private readonly InputInterface $input)
method createOutput (line 17) | public function createOutput(): OutputInterface
FILE: src/Path/Path.php
class Path (line 9) | final class Path
method __construct (line 11) | private function __construct(private readonly string $path)
method create (line 20) | public static function create(array $parts): self
method fromStrings (line 38) | public static function fromStrings(string ...$parts): self
method toString (line 43) | public function toString(): string
FILE: src/Pinger/PingableException.php
class PingableException (line 9) | class PingableException extends CrunzException
FILE: src/Pinger/PingableInterface.php
type PingableInterface (line 7) | interface PingableInterface
method pingBefore (line 14) | public function pingBefore($url);
method hasPingBefore (line 21) | public function hasPingBefore();
method thenPing (line 28) | public function thenPing($url);
method hasPingAfter (line 35) | public function hasPingAfter();
method getPingBeforeUrl (line 42) | public function getPingBeforeUrl();
method getPingAfterUrl (line 49) | public function getPingAfterUrl();
FILE: src/Pinger/PingableTrait.php
type PingableTrait (line 7) | trait PingableTrait
method pingBefore (line 14) | public function pingBefore($url)
method hasPingBefore (line 23) | public function hasPingBefore()
method thenPing (line 28) | public function thenPing($url)
method hasPingAfter (line 37) | public function hasPingAfter()
method getPingBeforeUrl (line 42) | public function getPingBeforeUrl()
method getPingAfterUrl (line 51) | public function getPingAfterUrl()
method checkUrl (line 65) | private function checkUrl($url): void
FILE: src/Process/Process.php
class Process (line 10) | final class Process
method __construct (line 13) | private function __construct(private readonly SymfonyProcess $process)
method fromStringCommand (line 17) | public static function fromStringCommand(string $command, ?string $cwd...
method fromArrayCommand (line 25) | public static function fromArrayCommand(array $command): self
method start (line 33) | public function start($callback = null): void
method wait (line 39) | public function wait(): void
method startAndWait (line 45) | public function startAndWait(): void
method setEnv (line 54) | public function setEnv(array $env): void
method getPid (line 60) | public function getPid(): ?int
method isRunning (line 66) | public function isRunning(): bool
method isSuccessful (line 72) | public function isSuccessful(): bool
method getOutput (line 78) | public function getOutput(): string
method errorOutput (line 84) | public function errorOutput(): string
method commandLine (line 90) | public function commandLine(): string
FILE: src/Schedule.php
class Schedule (line 11) | class Schedule implements PingableInterface
method run (line 51) | public function run($command, array $parameters = [])
method before (line 67) | public function before(\Closure $callback)
method after (line 79) | public function after(\Closure $callback)
method then (line 89) | public function then(\Closure $callback)
method onError (line 101) | public function onError(\Closure $callback)
method beforeCallbacks (line 113) | public function beforeCallbacks()
method afterCallbacks (line 123) | public function afterCallbacks()
method errorCallbacks (line 133) | public function errorCallbacks()
method events (line 145) | public function events(?array $events = null)
method dueEvents (line 159) | public function dueEvents(\DateTimeZone $timeZone)
method dismissEvent (line 174) | public function dismissEvent($key)
method id (line 186) | protected function id()
method compileParameters (line 197) | protected function compileParameters(array $parameters): string
FILE: src/Schedule/ScheduleFactory.php
class ScheduleFactory (line 12) | class ScheduleFactory
method singleTaskSchedule (line 19) | public function singleTaskSchedule(TaskNumber $taskNumber, Schedule .....
method singleTask (line 30) | public function singleTask(TaskNumber $taskNumber, Schedule ...$schedu...
FILE: src/Task/Collection.php
class Collection (line 12) | class Collection implements CollectionInterface
method __construct (line 14) | public function __construct(
method all (line 21) | public function all(string $source): iterable
FILE: src/Task/CollectionInterface.php
type CollectionInterface (line 7) | interface CollectionInterface
method all (line 10) | public function all(string $source): iterable;
FILE: src/Task/Loader.php
class Loader (line 9) | final class Loader implements LoaderInterface
method load (line 12) | public function load(\SplFileInfo ...$files): array
method loadSchedule (line 33) | private function loadSchedule(\SplFileInfo $file)
FILE: src/Task/LoaderInterface.php
type LoaderInterface (line 9) | interface LoaderInterface
method load (line 12) | public function load(\SplFileInfo ...$files): array;
FILE: src/Task/TaskException.php
class TaskException (line 9) | class TaskException extends CrunzException
FILE: src/Task/TaskNumber.php
class TaskNumber (line 9) | class TaskNumber
method __construct (line 15) | private function __construct(int $number)
method fromString (line 31) | public static function fromString($value)
method asInt (line 46) | public function asInt(): int
method asArrayIndex (line 51) | public function asArrayIndex(): int
FILE: src/Task/Timezone.php
class Timezone (line 11) | class Timezone
method __construct (line 15) | public function __construct(
method timezoneForComparisons (line 22) | public function timezoneForComparisons(): \DateTimeZone
FILE: src/Task/WrongTaskInstanceException.php
class WrongTaskInstanceException (line 9) | final class WrongTaskInstanceException extends TaskException
method fromFilePath (line 11) | public static function fromFilePath(\SplFileInfo $filePath, mixed $sch...
FILE: src/Timezone/Provider.php
class Provider (line 7) | final class Provider implements ProviderInterface
method defaultTimezone (line 9) | public function defaultTimezone(): \DateTimeZone
FILE: src/Timezone/ProviderInterface.php
type ProviderInterface (line 7) | interface ProviderInterface
method defaultTimezone (line 12) | public function defaultTimezone();
FILE: src/UserInterface/Cli/ClosureRunCommand.php
class ClosureRunCommand (line 13) | class ClosureRunCommand extends SymfonyCommand
method __construct (line 15) | public function __construct(private readonly ClosureSerializerInterfac...
method configure (line 23) | protected function configure(): void
method execute (line 42) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/UserInterface/Cli/DebugTaskCommand.php
class DebugTaskCommand (line 19) | final class DebugTaskCommand extends Command
method __construct (line 21) | public function __construct(private readonly TaskInformationHandler $t...
method configure (line 26) | protected function configure(): void
method execute (line 38) | protected function execute(InputInterface $input, OutputInterface $out...
method createTable (line 53) | private function createTable(
FILE: tests/EndToEnd/ClosureRunTest.php
class ClosureRunTest (line 9) | final class ClosureRunTest extends EndToEndTestCase
method closure_tasks (line 12) | public function closure_tasks(): void
method test_prevent_overlapping_works_on_closures (line 34) | public function test_prevent_overlapping_works_on_closures(): void
FILE: tests/EndToEnd/ConfigProviderTest.php
class ConfigProviderTest (line 13) | final class ConfigProviderTest extends EndToEndTestCase
method test_config_can_be_published (line 15) | public function test_config_can_be_published(): void
FILE: tests/EndToEnd/ConfigRecognitionTest.php
class ConfigRecognitionTest (line 10) | final class ConfigRecognitionTest extends EndToEndTestCase
method search_config_in_cwd (line 13) | public function search_config_in_cwd(): void
method assertHasTask (line 44) | private function assertHasTask(string $output): void
FILE: tests/EndToEnd/DebugTaskTest.php
class DebugTaskTest (line 9) | final class DebugTaskTest extends EndToEndTestCase
method test_task_debug (line 11) | public function test_task_debug(): void
method extractContentLines (line 52) | private function extractContentLines(string $output): array
method assertHeader (line 91) | private function assertHeader(string $header, array $lines): void
FILE: tests/EndToEnd/LoggerTest.php
class LoggerTest (line 9) | final class LoggerTest extends EndToEndTestCase
method test_outputs_are_logged (line 11) | public function test_outputs_are_logged(): void
method test_event_logging_override (line 42) | public function test_event_logging_override(): void
method assertLogRecord (line 69) | private function assertLogRecord(
FILE: tests/EndToEnd/TasksSourceRecognitionTest.php
class TasksSourceRecognitionTest (line 10) | final class TasksSourceRecognitionTest extends EndToEndTestCase
method search_tasks_in_cwd (line 13) | public function search_tasks_in_cwd(): void
method search_tasks_in_cwd_with_config (line 30) | public function search_tasks_in_cwd_with_config(): void
method assertHasTask (line 51) | private function assertHasTask(string $output): void
FILE: tests/EndToEnd/VersionTest.php
class VersionTest (line 10) | final class VersionTest extends EndToEndTestCase
method test_version (line 12) | public function test_version(): void
FILE: tests/EndToEnd/WrongTaskTest.php
class WrongTaskTest (line 9) | final class WrongTaskTest extends EndToEndTestCase
method every_task_must_return_crunz_schedule_instance (line 16) | public function every_task_must_return_crunz_schedule_instance(string ...
method scheduleInstanceProvider (line 37) | public static function scheduleInstanceProvider(): iterable
FILE: tests/Functional/ConfigProviderTest.php
class ConfigProviderTest (line 11) | class ConfigProviderTest extends TestCase
method config_already_exists (line 14) | public function config_already_exists(): void
FILE: tests/Functional/DifferentBaseCacheDirTest.php
class DifferentBaseCacheDirTest (line 11) | final class DifferentBaseCacheDirTest extends TestCase
method different_base_cache_dir_is_used (line 18) | public function different_base_cache_dir_is_used(): void
FILE: tests/Functional/ScheduleListTest.php
class ScheduleListTest (line 11) | class ScheduleListTest extends TestCase
method show_list (line 14) | public function show_list(): void
FILE: tests/Functional/ScheduleRunTest.php
class ScheduleRunTest (line 11) | class ScheduleRunTest extends TestCase
method show_list (line 14) | public function show_list(): void
FILE: tests/Functional/TaskGeneratorTest.php
class TaskGeneratorTest (line 13) | class TaskGeneratorTest extends TestCase
method setUp (line 19) | public function setUp(): void
method tearDown (line 33) | public function tearDown(): void
method generate_task_file (line 39) | public function generate_task_file(): void
method getInputStream (line 61) | private function getInputStream(string $input)
method clearTask (line 75) | private function clearTask(): void
method provideAnswer (line 82) | private function provideAnswer(
FILE: tests/TestCase/EndToEnd/Environment/Environment.php
class Environment (line 14) | final class Environment
method __construct (line 31) | public function __construct(
method __destruct (line 43) | public function __destruct()
method setUp (line 60) | private function setUp(): void
method runCrunzCommand (line 69) | public function runCrunzCommand(
method rootDirectory (line 117) | public function rootDirectory(): string
method dumpConfig (line 130) | private function dumpConfig(): void
method copyTasks (line 147) | private function copyTasks(): void
method dumpComposerJson (line 182) | private function dumpComposerJson(): void
method composerInstall (line 212) | private function composerInstall(): void
method createRootDirectory (line 223) | private function createRootDirectory(): void
method createProcess (line 236) | private function createProcess(string $command, ?string $cwd = null): ...
FILE: tests/TestCase/EndToEnd/Environment/EnvironmentBuilder.php
class EnvironmentBuilder (line 10) | final class EnvironmentBuilder
method __construct (line 18) | public function __construct(private readonly FilesystemInterface $file...
method addTask (line 23) | public function addTask(string $taskName): self
method changeTaskDirectory (line 30) | public function changeTaskDirectory(Path $path): self
method withConfig (line 38) | public function withConfig(array $config): self
method createEnvironment (line 45) | public function createEnvironment(): Environment
FILE: tests/TestCase/EndToEndTestCase.php
class EndToEndTestCase (line 13) | abstract class EndToEndTestCase extends TestCase
method createEnvironmentBuilder (line 18) | public function createEnvironmentBuilder(): EnvironmentBuilder
method normalizeOutput (line 27) | protected function normalizeOutput(string $output): string
method normalizeProcessOutput (line 43) | protected function normalizeProcessOutput(Process $process): string
method normalizeProcessErrorOutput (line 48) | protected function normalizeProcessErrorOutput(Process $process): string
FILE: tests/TestCase/FakeConfiguration.php
class FakeConfiguration (line 10) | final class FakeConfiguration implements ConfigurationInterface
method __construct (line 32) | public function __construct(array $config = [])
method get (line 37) | public function get(string $key, mixed $default = null): mixed
method withNewEntry (line 56) | public function withNewEntry(string $key, mixed $value): Configuration...
method getSourcePath (line 75) | public function getSourcePath(): string
FILE: tests/TestCase/FakeLoader.php
class FakeLoader (line 10) | final class FakeLoader implements LoaderInterface
method __construct (line 13) | public function __construct(private readonly array $schedules = [])
method load (line 17) | public function load(\SplFileInfo ...$files): array
FILE: tests/TestCase/FakeTaskCollection.php
class FakeTaskCollection (line 9) | final class FakeTaskCollection implements CollectionInterface
method __construct (line 12) | public function __construct(private readonly iterable $tasks = [])
method all (line 16) | public function all(string $source): iterable
FILE: tests/TestCase/Faker.php
class Faker (line 9) | final class Faker
method timeZone (line 26) | public static function timeZone(): \DateTimeZone
method elementFromArray (line 38) | public static function elementFromArray(array $elements): mixed
method int (line 51) | public static function int(int $min = PHP_INT_MIN, int $max = PHP_INT_...
method dateTime (line 56) | public static function dateTime(string $start = '-20 years', string $e...
method words (line 70) | public static function words(int $count = 3): string
method word (line 82) | public static function word(): string
FILE: tests/TestCase/Logger/NullLogger.php
class NullLogger (line 9) | final class NullLogger implements ConsoleLoggerInterface
method normal (line 11) | public function normal($message): void
method verbose (line 16) | public function verbose($message): void
method veryVerbose (line 21) | public function veryVerbose($message): void
method debug (line 26) | public function debug($message): void
FILE: tests/TestCase/Logger/SpyPsrLogger.php
class SpyPsrLogger (line 9) | final class SpyPsrLogger extends AbstractLogger
method log (line 14) | public function log($level, string|\Stringable $message, array $contex...
method getLogs (line 24) | public function getLogs(): array
FILE: tests/TestCase/SerializableTaskRunnerStub.php
class SerializableTaskRunnerStub (line 7) | final class SerializableTaskRunnerStub
method __serialize (line 15) | public function __serialize(): array
method __unserialize (line 24) | public function __unserialize(array $data): void
method createTask (line 30) | public function createTask(): \Closure
FILE: tests/TestCase/TaskRunnerStub.php
class TaskRunnerStub (line 7) | final class TaskRunnerStub
method createTask (line 14) | public function createTask(): \Closure
FILE: tests/TestCase/TemporaryFile.php
class TemporaryFile (line 9) | final class TemporaryFile
method __construct (line 13) | public function __construct()
method __destruct (line 24) | public function __destruct()
method filePath (line 42) | public function filePath(): string
method changePermissions (line 48) | public function changePermissions($mode): void
method contents (line 55) | public function contents(): string
method checkFileExists (line 68) | private function checkFileExists(): void
FILE: tests/TestCase/TestClock.php
class TestClock (line 9) | final class TestClock implements ClockInterface
method __construct (line 11) | public function __construct(private readonly \DateTimeImmutable $now)
method now (line 15) | public function now(): \DateTimeImmutable
FILE: tests/TestCase/UnitTestCase.php
class UnitTestCase (line 11) | abstract class UnitTestCase extends TestCase
method createClosureSerializer (line 15) | public function createClosureSerializer(): ClosureSerializerInterface
method encodeJson (line 20) | protected static function encodeJson(mixed $data): string
FILE: tests/Unit/Application/Cron/AbstractCronExpressionTestCase.php
class AbstractCronExpressionTestCase (line 10) | abstract class AbstractCronExpressionTestCase extends TestCase
method multiple_run_dates (line 19) | public function multiple_run_dates(
method multipleRunDatesProvider (line 48) | public static function multipleRunDatesProvider(): iterable
method createExpression (line 88) | abstract protected function createExpression(string $cronExpression): ...
FILE: tests/Unit/Application/Query/TaskInformation/TaskInformationHandlerTest.php
class TaskInformationHandlerTest (line 19) | final class TaskInformationHandlerTest extends TestCase
method handle_returns_task_information (line 26) | public function handle_returns_task_information(
method taskInformationProvider (line 51) | public static function taskInformationProvider(): iterable
method createHandler (line 113) | private function createHandler(Event $event, \DateTimeZone $comparison...
FILE: tests/Unit/CacheDirectoryFactory/CacheDirectoryFactoryTest.php
class CacheDirectoryFactoryTest (line 12) | final class CacheDirectoryFactoryTest extends TestCase
method sys_temp_dir_is_default_directory (line 15) | public function sys_temp_dir_is_default_directory(): void
method change_cache_directory_through_environment_variable (line 26) | public function change_cache_directory_through_environment_variable():...
method throw_exception_when_environment_variable_is_empty (line 39) | public function throw_exception_when_environment_variable_is_empty(): ...
FILE: tests/Unit/Configuration/ConfigurationParserTest.php
class ConfigurationParserTest (line 16) | final class ConfigurationParserTest extends TestCase
method use_empty_config_when_config_file_not_exists (line 19) | public function use_empty_config_when_config_file_not_exists(): void
method use_parsed_config_when_config_file_exists (line 34) | public function use_parsed_config_when_config_file_exists(): void
method createConfigurationParser (line 51) | private function createConfigurationParser(
FILE: tests/Unit/Configuration/ConfigurationTest.php
class ConfigurationTest (line 13) | final class ConfigurationTest extends TestCase
method get_can_return_path_split_by_dot (line 16) | public function get_can_return_path_split_by_dot(): void
method get_return_default_value_if_path_not_exists (line 30) | public function get_return_default_value_if_path_not_exists(): void
method source_path_is_relative_to_cwd (line 39) | public function source_path_is_relative_to_cwd(): void
method source_path_fallback_to_tasks_directory (line 50) | public function source_path_fallback_to_tasks_directory(): void
method set_configuration_key_value (line 60) | public function set_configuration_key_value(): void
method set_configuration_key_array (line 75) | public function set_configuration_key_array(): void
method createConfiguration (line 94) | private function createConfiguration(array $config = [], string $cwd =...
FILE: tests/Unit/Configuration/FileParserTest.php
class FileParserTest (line 14) | class FileParserTest extends TestCase
method parse_throws_exception_on_non_existing_file (line 17) | public function parse_throws_exception_on_non_existing_file(): void
method parse_throws_exception_on_non_readable_file (line 29) | public function parse_throws_exception_on_non_readable_file(): void
method parse_returns_parsed_file_content (line 47) | public function parse_returns_parsed_file_content(): void
method createFileParser (line 65) | private function createFileParser()
method isWindows (line 73) | private function isWindows()
FILE: tests/Unit/Console/Command/ScheduleListCommandTest.php
class ScheduleListCommandTest (line 20) | final class ScheduleListCommandTest extends UnitTestCase
method test_passing_unsupported_format_fails (line 22) | public function test_passing_unsupported_format_fails(): void
method test_list_output_format (line 40) | public function test_list_output_format(\Closure $paramsGenerator): void
method formatProvider (line 75) | public static function formatProvider(): iterable
method createCommand (line 139) | private function createCommand(array $schedules = []): ScheduleListCom...
method createInput (line 148) | private function createInput(string $format): InputInterface
method createScheduleWithTask (line 157) | private static function createScheduleWithTask(
FILE: tests/Unit/Console/Command/ScheduleRunCommandTest.php
class ScheduleRunCommandTest (line 26) | class ScheduleRunCommandTest extends TestCase
method force_run_all_tasks (line 29) | public function force_run_all_tasks(): void
method run_specific_task (line 61) | public function run_specific_task(): void
method mockTimezoneProvider (line 94) | public static function mockTimezoneProvider(): MockObject&Timezone
method mockScheduleFactory (line 117) | private function mockScheduleFactory(): ScheduleFactory
method mockEventRunner (line 132) | private function mockEventRunner(OutputInterface $output): EventRunner
method mockInput (line 164) | private function mockInput(array $options, array $arguments = []): Inp...
method mockTaskCollection (line 179) | private function mockTaskCollection(string ...$taskFiles): CollectionI...
method createTaskFile (line 189) | private function createTaskFile(string $taskContent, TemporaryFile $fi...
method taskContent (line 200) | private function taskContent(): string
method phpVersionTaskContent (line 219) | private function phpVersionTaskContent(): string
method createTaskLoader (line 237) | private function createTaskLoader(): LoaderInterface
FILE: tests/Unit/EnvFlags/EnvFlagsTest.php
class EnvFlagsTest (line 10) | final class EnvFlagsTest extends TestCase
method deprecation_handler_status_is_correct (line 17) | public function deprecation_handler_status_is_correct(string $flagValu...
method deprecation_handler_can_be_disabled (line 26) | public function deprecation_handler_can_be_disabled(): void
method deprecation_handler_can_be_enabled (line 35) | public function deprecation_handler_can_be_enabled(): void
method container_debug_flag_is_correct (line 48) | public function container_debug_flag_is_correct(string $flagValue, boo...
method container_debug_can_be_disabled (line 57) | public function container_debug_can_be_disabled(): void
method container_debug_can_be_enabled (line 66) | public function container_debug_can_be_enabled(): void
method statusProvider (line 75) | public static function statusProvider(): iterable
method containerDebugProvider (line 89) | public static function containerDebugProvider(): iterable
method assertFlagValue (line 102) | private function assertFlagValue(string $flag, string $expectedValue):...
FILE: tests/Unit/EventRunnerTest.php
class EventRunnerTest (line 20) | final class EventRunnerTest extends TestCase
method url_is_pinged_before (line 23) | public function url_is_pinged_before(): void
method url_is_pinged_after (line 38) | public function url_is_pinged_after(): void
method test_event_logging_configuration (line 52) | public function test_event_logging_configuration(): void
method test_lock_is_released_on_error (line 82) | public function test_lock_is_released_on_error(): void
method createEventRunnerForPing (line 109) | private function createEventRunnerForPing($url)
method createEventRunner (line 133) | private function createEventRunner(bool $realInvoker = false): EventRu...
FILE: tests/Unit/EventTest.php
class EventTest (line 17) | final class EventTest extends UnitTestCase
method setUp (line 29) | public function setUp(): void
method tearDown (line 37) | public function tearDown(): void
method test_unit_methods (line 45) | public function test_unit_methods(): void
method test_low_level_methods (line 75) | public function test_low_level_methods(): void
method test_weekday_methods (line 108) | public function test_weekday_methods(): void
method test_cron_life_time (line 129) | public function test_cron_life_time(): void
method test_get_from (line 165) | public function test_get_from(\Closure $paramsGenerator): void
method test_get_to (line 183) | public function test_get_to(\Closure $paramsGenerator): void
method test_get_between (line 201) | public function test_get_between(\Closure $paramsGenerator): void
method test_cron_conditions (line 213) | public function test_cron_conditions(): void
method more_than_five_parts_in_cron_expression_results_in_exception (line 231) | public function more_than_five_parts_in_cron_expression_results_in_exc...
method test_build_command (line 240) | public function test_build_command(): void
method test_is_due (line 247) | public function test_is_due(): void
method test_name (line 264) | public function test_name(): void
method in_change_working_directory_in_build_command_on_windows (line 273) | public function in_change_working_directory_in_build_command_on_window...
method in_change_working_directory_in_build_command_on_unix (line 288) | public function in_change_working_directory_in_build_command_on_unix()...
method on_do_not_run_task_every_minute (line 302) | public function on_do_not_run_task_every_minute(): void
method setting_user_prepend_sudo_to_command (line 312) | public function setting_user_prepend_sudo_to_command(): void
method custom_user_and_cwd (line 326) | public function custom_user_and_cwd(): void
method not_implemented_user_change_on_windows (line 341) | public function not_implemented_user_change_on_windows(): void
method closure_command_have_full_binary_paths (line 360) | public function closure_command_have_full_binary_paths(): void
method whole_output_catches_stdout_and_stderr (line 380) | public function whole_output_catches_stdout_and_stderr(): void
method task_will_prevent_overlapping_with_default_store (line 406) | public function task_will_prevent_overlapping_with_default_store(): void
method task_will_prevent_overlapping_with_semaphore_store (line 412) | public function task_will_prevent_overlapping_with_semaphore_store(): ...
method test_every_methods (line 422) | public function test_every_methods(string $method, string $expectedCro...
method test_hourly_at_with_valid_minute (line 437) | public function test_hourly_at_with_valid_minute(): void
method test_hourly_at_with_invalid_minute (line 451) | public function test_hourly_at_with_invalid_minute(
method test_non_blocking_store_can_be_passed_to_prevent_overlapping (line 466) | public function test_non_blocking_store_can_be_passed_to_prevent_overl...
method test_from_respects_time_zone (line 489) | public function test_from_respects_time_zone(\Closure $paramsGenerator...
method test_to_respects_timezone (line 519) | public function test_to_respects_timezone(\Closure $paramsGenerator): ...
method deprecatedEveryProvider (line 540) | public static function deprecatedEveryProvider(): iterable
method everyMethodProvider (line 549) | public static function everyMethodProvider(): iterable
method fromTimeZoneProvider (line 566) | public static function fromTimeZoneProvider(): iterable
method toTimeZoneProvider (line 598) | public static function toTimeZoneProvider(): iterable
method hourlyAtInvalidProvider (line 630) | public static function hourlyAtInvalidProvider(): iterable
method dateFromToProvider (line 644) | public static function dateFromToProvider(): iterable
method assertPreventOverlapping (line 668) | private function assertPreventOverlapping(?PersistingStoreInterface $s...
method createPreventOverlappingEvent (line 678) | private function createPreventOverlappingEvent(?PersistingStoreInterfa...
method setClockNow (line 689) | private function setClockNow(\DateTimeImmutable $dateTime): void
method isWindows (line 697) | private function isWindows(): bool
method createEvent (line 702) | private function createEvent(): Event
FILE: tests/Unit/Filesystem/FilesystemTest.php
class FilesystemTest (line 12) | final class FilesystemTest extends UnitTestCase
method cwd_is_correct (line 15) | public function cwd_is_correct(): void
method file_exists_is_correct (line 27) | public function file_exists_is_correct(string $path, bool $expectedExi...
method temp_directory_return_system_temp_directory (line 35) | public function temp_directory_return_system_temp_directory(): void
method remove_directory_removes_directories_recursively (line 43) | public function remove_directory_removes_directories_recursively(): void
method dump_file_writes_content_to_file (line 65) | public function dump_file_writes_content_to_file(): void
method create_directory_creates_directory_recursive (line 80) | public function create_directory_creates_directory_recursive(): void
method copy_files (line 100) | public function copy_files(): void
method project_root_directory (line 120) | public function project_root_directory(): void
method read_content_return_file_content (line 128) | public function read_content_return_file_content(): void
method read_content_throws_exception_when_file_not_exists (line 137) | public function read_content_throws_exception_when_file_not_exists(): ...
method fileExistsProvider (line 153) | public static function fileExistsProvider(): iterable
method findProjectRootDirectory (line 170) | private function findProjectRootDirectory(): string
FILE: tests/Unit/Finder/FinderTest.php
class FinderTest (line 12) | final class FinderTest extends TestCase
method setUp (line 18) | public function setUp(): void
method tearDown (line 37) | public function tearDown(): void
method find_returns_spl_file_info_collection (line 49) | public function find_returns_spl_file_info_collection(string $suffix, ...
method tasksProvider (line 66) | public static function tasksProvider(): iterable
method find_files_in_symlinked_folder (line 94) | public function find_files_in_symlinked_folder(): void
method createFiles (line 114) | private function createFiles(Path ...$files): void
method isWindows (line 126) | private function isWindows(): bool
FILE: tests/Unit/HttpClient/StreamHttpClientTest.php
class StreamHttpClientTest (line 11) | final class StreamHttpClientTest extends TestCase
method ping_fail_with_invalid_address (line 14) | public function ping_fail_with_invalid_address(): void
FILE: tests/Unit/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpressionTestCase.php
class DragonmantankCronExpressionTestCase (line 12) | final class DragonmantankCronExpressionTestCase extends AbstractCronExpr...
method createExpression (line 14) | protected function createExpression(string $cronExpression): CronExpre...
FILE: tests/Unit/Infrastructure/Psr/Logger/EnabledLoggerDecoratorTest.php
class EnabledLoggerDecoratorTest (line 16) | final class EnabledLoggerDecoratorTest extends UnitTestCase
method test_disabled_channels_not_log (line 19) | public function test_disabled_channels_not_log(
method test_enabled_channels_log (line 35) | public function test_enabled_channels_log(
method disabledChannelProvider (line 51) | public static function disabledChannelProvider(): iterable
method enabledChannelProvider (line 65) | public static function enabledChannelProvider(): iterable
method createEnabledLoggerDecorator (line 78) | private function createEnabledLoggerDecorator(
FILE: tests/Unit/Infrastructure/Psr/Logger/PsrStreamLoggerFactoryTest.php
class PsrStreamLoggerFactoryTest (line 14) | final class PsrStreamLoggerFactoryTest extends UnitTestCase
method test_factory_returns_decorated_logger (line 16) | public function test_factory_returns_decorated_logger(): void
method createStreamLoggerFactory (line 28) | private function createStreamLoggerFactory(): PsrStreamLoggerFactory
FILE: tests/Unit/Infrastructure/Psr/Logger/PsrStreamLoggerTest.php
class PsrStreamLoggerTest (line 15) | final class PsrStreamLoggerTest extends TestCase
method test_supported_levels_are_logged (line 18) | public function test_supported_levels_are_logged(string $level): void
method test_unsupported_levels_are_ignored (line 38) | public function test_unsupported_levels_are_ignored(string $level): void
method test_empty_context_is_ignored (line 50) | public function test_empty_context_is_ignored(string $level): void
method test_date_use_passed_time_zone (line 76) | public function test_date_use_passed_time_zone(string $level): void
method test_logging_with_allowed_line_breaks (line 107) | public function test_logging_with_allowed_line_breaks(string $level): ...
method test_logging_with_disallowed_line_breaks (line 137) | public function test_logging_with_disallowed_line_breaks(string $level...
method supportedLevelsProvider (line 157) | public static function supportedLevelsProvider(): iterable
method unsupportedLevelsProvider (line 164) | public static function unsupportedLevelsProvider(): iterable
method createLogger (line 174) | private function createLogger(
method formatLine (line 195) | private function formatLine(
FILE: tests/Unit/InvokerTest.php
class InvokerTest (line 10) | class InvokerTest extends TestCase
method call_executes_closure (line 13) | public function call_executes_closure(): void
method call_executes_closure_with_params (line 29) | public function call_executes_closure_with_params(): void
method call_can_catch_output (line 45) | public function call_can_catch_output(): void
FILE: tests/Unit/Logger/ConsoleLoggerTest.php
class ConsoleLoggerTest (line 13) | final class ConsoleLoggerTest extends TestCase
method logger_writes_normal_only_with_suitable_verbosity (line 20) | public function logger_writes_normal_only_with_suitable_verbosity(int ...
method logger_writes_verbose_only_with_suitable_verbosity (line 38) | public function logger_writes_verbose_only_with_suitable_verbosity(int...
method logger_writes_very_verbose_only_with_suitable_verbosity (line 56) | public function logger_writes_very_verbose_only_with_suitable_verbosit...
method logger_writes_debug_only_with_suitable_verbosity (line 74) | public function logger_writes_debug_only_with_suitable_verbosity(int $...
method verbosityProvider (line 88) | public static function verbosityProvider(): iterable
method mockSymfonyStyle (line 98) | private function mockSymfonyStyle(int $ioVerbosity): object
FILE: tests/Unit/Logger/LoggerFactoryTest.php
class LoggerFactoryTest (line 17) | final class LoggerFactoryTest extends TestCase
method test_logger_factory_creates_logger (line 19) | public function test_logger_factory_creates_logger(): void
method test_logger_factory_creates_event_logger (line 28) | public function test_logger_factory_creates_event_logger(): void
method test_wrong_logger_class_throws_exception (line 42) | public function test_wrong_logger_class_throws_exception(): void
method createLoggerFactory (line 53) | private function createLoggerFactory(array $configuration = []): Logge...
FILE: tests/Unit/MailerTest.php
class MailerTest (line 12) | final class MailerTest extends TestCase
method using_mail_transport_will_result_in_exception (line 15) | public function using_mail_transport_will_result_in_exception(): void
method createMailer (line 24) | private function createMailer(string $transport): Mailer
FILE: tests/Unit/Output/OutputFactoryTest.php
class OutputFactoryTest (line 13) | final class OutputFactoryTest extends TestCase
method input_defines_output_verbosity (line 20) | public function input_defines_output_verbosity(InputInterface $input, ...
method inputProvider (line 30) | public static function inputProvider(): iterable
method createInput (line 63) | private static function createInput(string $option): InputInterface
FILE: tests/Unit/Path/PathTest.php
class PathTest (line 11) | final class PathTest extends TestCase
method create_requires_at_least_one_path (line 14) | public function create_requires_at_least_one_path(): void
method parts_are_delimited_by_directory_separator (line 23) | public function parts_are_delimited_by_directory_separator(): void
method path_can_be_created_from_strings (line 40) | public function path_can_be_created_from_strings(): void
method doubled_directory_separator_is_normalized (line 58) | public function doubled_directory_separator_is_normalized(): void
FILE: tests/Unit/Pingable.php
class Pingable (line 10) | class Pingable implements PingableInterface
FILE: tests/Unit/Pinger/PingableTest.php
class PingableTest (line 11) | final class PingableTest extends TestCase
method before_url_must_be_string (line 18) | public function before_url_must_be_string(mixed $url): void
method before_url_must_be_non_empty_string (line 31) | public function before_url_must_be_non_empty_string(): void
method after_url_must_be_non_empty_string (line 43) | public function after_url_must_be_non_empty_string(): void
method after_url_must_be_string (line 57) | public function after_url_must_be_string(mixed $url): void
method get_ping_before_without_url_fails (line 68) | public function get_ping_before_without_url_fails(): void
method get_ping_after_without_url_fails (line 78) | public function get_ping_after_without_url_fails(): void
method nonStringProvider (line 88) | public static function nonStringProvider(): iterable
FILE: tests/Unit/Process/ProcessTest.php
class ProcessTest (line 10) | final class ProcessTest extends UnitTestCase
method test_command_line_built_from_array (line 12) | public function test_command_line_built_from_array(): void
method assertCommand (line 32) | private function assertCommand(string $expectedCommand, Process $proce...
FILE: tests/Unit/Schedule/ScheduleFactoryTest.php
class ScheduleFactoryTest (line 14) | final class ScheduleFactoryTest extends TestCase
method single_task_schedule (line 17) | public function single_task_schedule(): void
method single_task (line 34) | public function single_task(): void
method single_task_schedule_throws_exception_on_wrong_task_number (line 49) | public function single_task_schedule_throws_exception_on_wrong_task_nu...
method single_task_throws_exception_on_wrong_task_number (line 64) | public function single_task_throws_exception_on_wrong_task_number(): void
FILE: tests/Unit/ScheduleTest.php
class ScheduleTest (line 12) | final class ScheduleTest extends UnitTestCase
method test_run (line 17) | public function test_run(\Closure $paramsGenerator): void
method test_run_with_non_string_parameters (line 44) | public function test_run_with_non_string_parameters(\Closure $paramsGe...
method runProvider (line 70) | public static function runProvider(): iterable
method nonStringParametersProvider (line 114) | public static function nonStringParametersProvider(): iterable
method assertCommand (line 149) | private function assertCommand(string $expectedCommand, Event $event):...
FILE: tests/Unit/Service/AbstractClosureSerializerTestCase.php
class AbstractClosureSerializerTestCase (line 10) | abstract class AbstractClosureSerializerTestCase extends UnitTestCase
method test_closure_code_can_be_extracted (line 12) | public function test_closure_code_can_be_extracted(): void
method createSerializer (line 25) | abstract protected function createSerializer(): ClosureSerializerInter...
FILE: tests/Unit/Service/LaravelClosureSerializerTest.php
class LaravelClosureSerializerTest (line 12) | final class LaravelClosureSerializerTest extends UnitTestCase
method setUp (line 16) | protected function setUp(): void
method test_serialize_simple_closure (line 22) | public function test_serialize_simple_closure(): void
method test_serialize_closure_with_use_variable (line 34) | public function test_serialize_closure_with_use_variable(): void
method test_serialize_closure_bound_to_object_with_closure_properties (line 47) | public function test_serialize_closure_bound_to_object_with_closure_pr...
method test_serialize_closure_bound_to_object_with_serialize_and_closure_properties (line 65) | public function test_serialize_closure_bound_to_object_with_serialize_...
method test_closure_code_can_be_extracted (line 76) | public function test_closure_code_can_be_extracted(): void
FILE: tests/Unit/Service/LaravelClosureSerializerTestCase.php
class LaravelClosureSerializerTestCase (line 9) | final class LaravelClosureSerializerTestCase extends AbstractClosureSeri...
method createSerializer (line 11) | protected function createSerializer(): LaravelClosureSerializer
FILE: tests/Unit/Task/TaskNumberTest.php
class TaskNumberTest (line 11) | class TaskNumberTest extends TestCase
method can_not_create_task_number_with_non_string_value_by_from_string (line 18) | public function can_not_create_task_number_with_non_string_value_by_fr...
method task_number_can_not_be_non_numeric_string (line 31) | public function task_number_can_not_be_non_numeric_string(string $valu...
method task_number_can_be_created_with_numeric_string_value (line 44) | public function task_number_can_be_created_with_numeric_string_value(s...
method array_index_is_one_step_lower (line 52) | public function array_index_is_one_step_lower(): void
method nonStringValueProvider (line 60) | public static function nonStringValueProvider(): iterable
method numericValueProvider (line 70) | public static function numericValueProvider(): iterable
method nonNumericProvider (line 83) | public static function nonNumericProvider(): iterable
FILE: tests/Unit/Task/TimezoneTest.php
class TimezoneTest (line 13) | final class TimezoneTest extends TestCase
method configured_timezone_cannot_be_empty (line 16) | public function configured_timezone_cannot_be_empty(): void
FILE: tests/Unit/Timezone/ProviderTest.php
class ProviderTest (line 10) | class ProviderTest extends TestCase
method default_timezone_is_returned (line 17) | public function default_timezone_is_returned(): void
FILE: tests/Unit/UserInterface/Cli/ClosureRunCommandTest.php
class ClosureRunCommandTest (line 12) | final class ClosureRunCommandTest extends UnitTestCase
method test_return_value_of_closure_is_omitted (line 15) | public function test_return_value_of_closure_is_omitted(int $returnVal...
method command_is_hidden (line 29) | public function command_is_hidden(): void
method closureValueProvider (line 37) | public static function closureValueProvider(): iterable
method createInput (line 43) | private function createInput(\Closure $closure): ArrayInput
method createCommand (line 58) | private function createCommand(): ClosureRunCommand
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (428K chars).
[
{
"path": ".editorconfig",
"chars": 115,
"preview": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ncharset = utf-8\n"
},
{
"path": ".gitattributes",
"chars": 567,
"preview": "* text=auto\n\n/tests export-ignore\n.gitattributes export-ignore\n.gitignore export-ignore\n.editorconfig export-ignore\nREAD"
},
{
"path": ".github/FUNDING.yml",
"chars": 23,
"preview": "github: PabloKowalczyk\n"
},
{
"path": ".github/ISSUE_TEMPLATE/1_Bug_report.md",
"chars": 540,
"preview": "---\nname: \"\\U0001F41B Bug Report\"\nabout: Report errors and problems\n\n---\n\n**Crunz version**: x.y.z\n\n**PHP version**: x.y"
},
{
"path": ".github/ISSUE_TEMPLATE/2_Feature_request.md",
"chars": 369,
"preview": "---\nname: \"\\U0001F680 Feature Request\"\nabout: RFC and ideas for new features and improvements\n\n---\n\n**Description** \n<!"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 290,
"preview": "| Q | A\n| ------------- | ---\n| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->\n\n<!--\nWri"
},
{
"path": ".github/workflows/php.yaml",
"chars": 4159,
"preview": "name: PHP\n\non:\n pull_request:\n branches:\n - '3.9'\n - '3.10'\n push: null\n\npermissions:"
},
{
"path": ".gitignore",
"chars": 138,
"preview": "/vendor\n/tasks\ncomposer.phar\n/composer.lock\n.DS_Store\n*.log\n.idea/\nvar/\n.php-cs-fixer.cache\n/crunz.phar\n/crunz.yml\n/.php"
},
{
"path": ".php-cs-fixer.dist.php",
"chars": 2259,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse PhpCsFixer\\Runner\\Parallel\\ParallelConfigFactory;\n\nreturn (new PhpCsFixer\\Config())"
},
{
"path": "CHANGELOG.md",
"chars": 19240,
"preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
},
{
"path": "LICENSE",
"chars": 1074,
"preview": "MIT License\n\nCopyright (c) 2018 Moe Reza Lavarian\n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "Makefile",
"chars": 54,
"preview": "sh-php:\n\tdocker compose exec --user=www-data php82 sh\n"
},
{
"path": "README.md",
"chars": 28031,
"preview": "# Crunz needs your funding 💲\n\n## Support further Crunz development by [GitHub](https://github.com/sponsors/PabloKowalczy"
},
{
"path": "UPGRADE.md",
"chars": 2219,
"preview": "# Upgrading from v3.2 to v3.3\n\n## Pass only string parameters to `\\Crunz\\Schedule::run`\n\nConvert this:\n```php\n$schedule "
},
{
"path": "bootstrap.php",
"chars": 536,
"preview": "<?php\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nif (!\\defined('IS_WINDOWS')) {\n \\define('IS_WINDOWS', PHP_OS_F"
},
{
"path": "composer.json",
"chars": 2975,
"preview": "{\n \"name\": \"crunzphp/crunz\",\n \"description\": \"Schedule your tasks right from the code.\",\n \"license\": \"MIT\",\n "
},
{
"path": "config/services.php",
"chars": 9444,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Crunz\\Application\\Cron\\CronExpressionFactoryInterface;\nuse Crunz\\Application\\Query\\"
},
{
"path": "crunz",
"chars": 1547,
"preview": "#!/usr/bin/env php\n<?php\n\n/*\n|--------------------------------------------------------------------------\n| Crunz\n|------"
},
{
"path": "docker/php82/Dockerfile",
"chars": 554,
"preview": "FROM php:8.2.30-cli-alpine\n\nRUN apk add --no-cache \\\n shadow \\\n su-exec && \\\n usermod --non-unique --ui"
},
{
"path": "docker/php82/php.ini",
"chars": 547,
"preview": "realpath_cache_size = 8192k\nrealpath_cache_ttl = 6000\n\nexpose_php = On\nerror_log = /var/log/php/error.log\nerror_reportin"
},
{
"path": "docker-compose.yml",
"chars": 588,
"preview": "services:\n php82:\n build:\n context: ./docker/php82\n working_dir: /var/www/html\n envir"
},
{
"path": "phpstan-baseline.neon",
"chars": 21920,
"preview": "parameters:\n\tignoreErrors:\n\t\t-\n\t\t\tmessage: \"#^Parameter \\\\#5 \\\\$timeZone of class Crunz\\\\\\\\Application\\\\\\\\Query\\\\\\\\TaskI"
},
{
"path": "phpstan.neon",
"chars": 1528,
"preview": "parameters:\n level: 9\n reportUnmatchedIgnoredErrors: false\n inferPrivatePropertyTypeFromConstructor: true\n i"
},
{
"path": "phpunit.xml",
"chars": 1131,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noName"
},
{
"path": "resources/config/crunz.yml",
"chars": 2119,
"preview": "# Crunz Configuration Settings\n\n# This option defines where the task files and\n# directories reside.\n# The path is relat"
},
{
"path": "src/Application/Cron/CronExpressionFactoryInterface.php",
"chars": 200,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Cron;\n\ninterface CronExpressionFactoryInterface\n{\n publi"
},
{
"path": "src/Application/Cron/CronExpressionInterface.php",
"chars": 292,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Cron;\n\ninterface CronExpressionInterface\n{\n /** @return "
},
{
"path": "src/Application/Query/TaskInformation/TaskInformation.php",
"chars": 322,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Query\\TaskInformation;\n\nuse Crunz\\Task\\TaskNumber;\n\nfinal c"
},
{
"path": "src/Application/Query/TaskInformation/TaskInformationHandler.php",
"chars": 3178,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Query\\TaskInformation;\n\nuse Crunz\\Application\\Cron\\CronExpr"
},
{
"path": "src/Application/Query/TaskInformation/TaskInformationView.php",
"chars": 1319,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Query\\TaskInformation;\n\nfinal class TaskInformationView\n{\n "
},
{
"path": "src/Application/Service/ClosureSerializerInterface.php",
"chars": 302,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Service;\n\ninterface ClosureSerializerInterface\n{\n public"
},
{
"path": "src/Application/Service/ConfigurationInterface.php",
"chars": 421,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Service;\n\ninterface ConfigurationInterface\n{\n /**\n *"
},
{
"path": "src/Application/Service/LoggerFactoryInterface.php",
"chars": 247,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Application\\Service;\n\nuse Psr\\Log\\LoggerInterface;\n\n/**\n * @experimenta"
},
{
"path": "src/Application.php",
"chars": 8051,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz;\n\nuse Crunz\\CacheDirectoryFactory\\CacheDirectoryFactory;\nuse Crunz\\Cons"
},
{
"path": "src/CacheDirectoryFactory/CacheDirectoryFactory.php",
"chars": 820,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\CacheDirectoryFactory;\n\nuse Crunz\\Exception\\CrunzException;\nuse Crunz\\P"
},
{
"path": "src/Clock/Clock.php",
"chars": 204,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Clock;\n\nfinal class Clock implements ClockInterface\n{\n public functi"
},
{
"path": "src/Clock/ClockInterface.php",
"chars": 133,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Clock;\n\ninterface ClockInterface\n{\n public function now(): \\DateTime"
},
{
"path": "src/Configuration/ConfigFileNotExistsException.php",
"chars": 319,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\nuse Crunz\\Exception\\CrunzException;\n\nfinal class Config"
},
{
"path": "src/Configuration/ConfigFileNotReadableException.php",
"chars": 319,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\nuse Crunz\\Exception\\CrunzException;\n\nfinal class Config"
},
{
"path": "src/Configuration/Configuration.php",
"chars": 2325,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\nu"
},
{
"path": "src/Configuration/ConfigurationParser.php",
"chars": 2605,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\nuse Crunz\\Console\\Command\\ConfigGeneratorCommand;\nuse C"
},
{
"path": "src/Configuration/ConfigurationParserInterface.php",
"chars": 200,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\ninterface ConfigurationParserInterface\n{\n /** @retur"
},
{
"path": "src/Configuration/Definition.php",
"chars": 4575,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\nuse Crunz\\Infrastructure\\Psr\\Logger\\PsrStreamLoggerFact"
},
{
"path": "src/Configuration/FileParser.php",
"chars": 945,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Configuration;\n\nuse Symfony\\Component\\Yaml\\Yaml;\n\nclass FileParser\n{\n "
},
{
"path": "src/Console/Command/Command.php",
"chars": 589,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Console\\Command;\n\nuse Symfony\\Component\\Console\\Command\\Command as Base"
},
{
"path": "src/Console/Command/ConfigGeneratorCommand.php",
"chars": 4171,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Console\\Command;\n\nuse Crunz\\Filesystem\\FilesystemInterface;\nuse Crunz\\P"
},
{
"path": "src/Console/Command/ScheduleListCommand.php",
"chars": 5232,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Console\\Command;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;"
},
{
"path": "src/Console/Command/ScheduleRunCommand.php",
"chars": 4252,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Console\\Command;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;"
},
{
"path": "src/Console/Command/TaskGeneratorCommand.php",
"chars": 7707,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Console\\Command;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;"
},
{
"path": "src/EnvFlags/EnvFlags.php",
"chars": 2183,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\EnvFlags;\n\nuse Crunz\\Exception\\CrunzException;\n\nfinal class EnvFlags\n{\n"
},
{
"path": "src/Event.php",
"chars": 30076,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz;\n\nuse Closure;\nuse Cron\\CronExpression;\nuse Crunz\\Application\\Service\\C"
},
{
"path": "src/EventRunner.php",
"chars": 9487,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\nuse Crunz\\HttpC"
},
{
"path": "src/Exception/CrunzException.php",
"chars": 105,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Exception;\n\nclass CrunzException extends \\Exception\n{\n}\n"
},
{
"path": "src/Exception/EmptyTimezoneException.php",
"chars": 117,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Exception;\n\nclass EmptyTimezoneException extends CrunzException\n{\n}\n"
},
{
"path": "src/Exception/MailerException.php",
"chars": 116,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Exception;\n\nfinal class MailerException extends CrunzException\n{\n}\n"
},
{
"path": "src/Exception/NotImplementedException.php",
"chars": 118,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Exception;\n\nclass NotImplementedException extends CrunzException\n{\n}\n"
},
{
"path": "src/Exception/TaskNotExistException.php",
"chars": 116,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Exception;\n\nclass TaskNotExistException extends CrunzException\n{\n}\n"
},
{
"path": "src/Exception/WrongTaskNumberException.php",
"chars": 119,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Exception;\n\nclass WrongTaskNumberException extends CrunzException\n{\n}\n"
},
{
"path": "src/Filesystem/Filesystem.php",
"chars": 3578,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Filesystem;\n\nuse Crunz\\Path\\Path;\n\nfinal class Filesystem implements Fi"
},
{
"path": "src/Filesystem/FilesystemInterface.php",
"chars": 1085,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Filesystem;\n\nuse Crunz\\Path\\Path;\n\ninterface FilesystemInterface\n{\n "
},
{
"path": "src/Finder/Finder.php",
"chars": 998,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Finder;\n\nuse Crunz\\Path\\Path;\n\nfinal class Finder implements FinderInte"
},
{
"path": "src/Finder/FinderInterface.php",
"chars": 244,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Finder;\n\nuse Crunz\\Path\\Path;\n\ninterface FinderInterface\n{\n /**\n "
},
{
"path": "src/HttpClient/CurlHttpClient.php",
"chars": 1158,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\HttpClient;\n\nfinal class CurlHttpClient implements HttpClientInterface\n"
},
{
"path": "src/HttpClient/FallbackHttpClient.php",
"chars": 1714,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\HttpClient;\n\nuse Crunz\\Logger\\ConsoleLoggerInterface;\n\nfinal class Fall"
},
{
"path": "src/HttpClient/HttpClientException.php",
"chars": 152,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\HttpClient;\n\nuse Crunz\\Exception\\CrunzException;\n\nclass HttpClientExcep"
},
{
"path": "src/HttpClient/HttpClientInterface.php",
"chars": 228,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\HttpClient;\n\ninterface HttpClientInterface\n{\n /**\n * @param non-"
},
{
"path": "src/HttpClient/HttpClientLoggerDecorator.php",
"chars": 618,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\HttpClient;\n\nuse Crunz\\Logger\\ConsoleLoggerInterface;\n\nfinal class Http"
},
{
"path": "src/HttpClient/StreamHttpClient.php",
"chars": 736,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\HttpClient;\n\nfinal class StreamHttpClient implements HttpClientInterfac"
},
{
"path": "src/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpression.php",
"chars": 875,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Infrastructure\\Dragonmantank\\CronExpression;\n\nuse Cron\\CronExpression;\n"
},
{
"path": "src/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpressionFactory.php",
"chars": 567,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Infrastructure\\Dragonmantank\\CronExpression;\n\nuse Cron\\CronExpression;\n"
},
{
"path": "src/Infrastructure/Laravel/LaravelClosureSerializer.php",
"chars": 1065,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Infrastructure\\Laravel;\n\nuse Crunz\\Application\\Service\\ClosureSerialize"
},
{
"path": "src/Infrastructure/Psr/Logger/EnabledLoggerDecorator.php",
"chars": 1175,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Infrastructure\\Psr\\Logger;\n\nuse Crunz\\Application\\Service\\Configuration"
},
{
"path": "src/Infrastructure/Psr/Logger/PsrStreamLogger.php",
"chars": 5224,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Infrastructure\\Psr\\Logger;\n\nuse Crunz\\Clock\\ClockInterface;\nuse Crunz\\E"
},
{
"path": "src/Infrastructure/Psr/Logger/PsrStreamLoggerFactory.php",
"chars": 1141,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Infrastructure\\Psr\\Logger;\n\nuse Crunz\\Application\\Service\\Configuration"
},
{
"path": "src/Invoker.php",
"chars": 583,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz;\n\nclass Invoker\n{\n /**\n * Call the given Closure with buffering "
},
{
"path": "src/Logger/ConsoleLogger.php",
"chars": 1288,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Logger;\n\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nfinal class"
},
{
"path": "src/Logger/ConsoleLoggerInterface.php",
"chars": 926,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Logger;\n\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n\ninterfa"
},
{
"path": "src/Logger/Logger.php",
"chars": 676,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Logger;\n\nuse Psr\\Log\\LoggerInterface;\n\nclass Logger\n{\n public functi"
},
{
"path": "src/Logger/LoggerFactory.php",
"chars": 2994,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Logger;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\nuse Crun"
},
{
"path": "src/Mailer.php",
"chars": 3146,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\nuse Crunz\\Excep"
},
{
"path": "src/Output/OutputFactory.php",
"chars": 1341,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Output;\n\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfon"
},
{
"path": "src/Path/Path.php",
"chars": 927,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Path;\n\nuse Crunz\\Exception\\CrunzException;\n\nfinal class Path\n{\n priv"
},
{
"path": "src/Pinger/PingableException.php",
"chars": 146,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Pinger;\n\nuse Crunz\\Exception\\CrunzException;\n\nclass PingableException e"
},
{
"path": "src/Pinger/PingableInterface.php",
"chars": 705,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Pinger;\n\ninterface PingableInterface\n{\n /**\n * @param string $ur"
},
{
"path": "src/Pinger/PingableTrait.php",
"chars": 1476,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Pinger;\n\ntrait PingableTrait\n{\n /** @var string */\n private $ping"
},
{
"path": "src/Process/Process.php",
"chars": 1933,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Process;\n\nuse Symfony\\Component\\Process\\Process as SymfonyProcess;\n\n/**"
},
{
"path": "src/Schedule/ScheduleFactory.php",
"chars": 1229,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Schedule;\n\nuse Crunz\\Event;\nuse Crunz\\Exception\\TaskNotExistException;\n"
},
{
"path": "src/Schedule.php",
"chars": 5200,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz;\n\nuse Crunz\\Pinger\\PingableInterface;\nuse Crunz\\Pinger\\PingableTrait;\nu"
},
{
"path": "src/Stubs/BasicTask.php",
"chars": 694,
"preview": "<?php\n\ndeclare(strict_types=1);\n/*\n|------------------------------------------------------------------------------------"
},
{
"path": "src/Task/Collection.php",
"chars": 1530,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\nuse Crunz\\"
},
{
"path": "src/Task/CollectionInterface.php",
"chars": 175,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\ninterface CollectionInterface\n{\n /** @return \\SplFileInfo[] *"
},
{
"path": "src/Task/Loader.php",
"chars": 960,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Schedule;\n\nfinal class Loader implements LoaderInterfa"
},
{
"path": "src/Task/LoaderInterface.php",
"chars": 194,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Schedule;\n\ninterface LoaderInterface\n{\n /** @return"
},
{
"path": "src/Task/TaskException.php",
"chars": 140,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Exception\\CrunzException;\n\nclass TaskException extends"
},
{
"path": "src/Task/TaskNumber.php",
"chars": 1166,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Exception\\WrongTaskNumberException;\n\nclass TaskNumber\n"
},
{
"path": "src/Task/Timezone.php",
"chars": 1262,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\nuse Crunz\\"
},
{
"path": "src/Task/WrongTaskInstanceException.php",
"chars": 514,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Task;\n\nuse Crunz\\Schedule;\n\nfinal class WrongTaskInstanceException exte"
},
{
"path": "src/Timezone/Provider.php",
"chars": 243,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Timezone;\n\nfinal class Provider implements ProviderInterface\n{\n publ"
},
{
"path": "src/Timezone/ProviderInterface.php",
"chars": 176,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Timezone;\n\ninterface ProviderInterface\n{\n /**\n * @return \\DateTi"
},
{
"path": "src/UserInterface/Cli/ClosureRunCommand.php",
"chars": 1710,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\UserInterface\\Cli;\n\nuse Crunz\\Application\\Service\\ClosureSerializerInte"
},
{
"path": "src/UserInterface/Cli/DebugTaskCommand.php",
"chars": 3827,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\UserInterface\\Cli;\n\nuse Crunz\\Application\\Query\\TaskInformation\\TaskInf"
},
{
"path": "tests/EndToEnd/ClosureRunTest.php",
"chars": 1633,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Tests\\TestCase\\EndToEndTestCase;\n\nfinal clas"
},
{
"path": "tests/EndToEnd/ConfigProviderTest.php",
"chars": 1153,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Console\\Command\\ConfigGeneratorCommand;\nuse "
},
{
"path": "tests/EndToEnd/ConfigRecognitionTest.php",
"chars": 1544,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Path\\Path;\nuse Crunz\\Tests\\TestCase\\EndToEnd"
},
{
"path": "tests/EndToEnd/DebugTaskTest.php",
"chars": 2795,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Tests\\TestCase\\EndToEndTestCase;\n\nfinal clas"
},
{
"path": "tests/EndToEnd/LoggerTest.php",
"chars": 2341,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Tests\\TestCase\\EndToEndTestCase;\n\nfinal clas"
},
{
"path": "tests/EndToEnd/TasksSourceRecognitionTest.php",
"chars": 1692,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Path\\Path;\nuse Crunz\\Tests\\TestCase\\EndToEnd"
},
{
"path": "tests/EndToEnd/VersionTest.php",
"chars": 923,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Composer\\InstalledVersions;\nuse Crunz\\Tests\\TestCa"
},
{
"path": "tests/EndToEnd/WrongTaskTest.php",
"chars": 1197,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\EndToEnd;\n\nuse Crunz\\Tests\\TestCase\\EndToEndTestCase;\n\nfinal clas"
},
{
"path": "tests/Functional/ConfigProviderTest.php",
"chars": 687,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Functional;\n\nuse Crunz\\Application;\nuse PHPUnit\\Framework\\TestCas"
},
{
"path": "tests/Functional/DifferentBaseCacheDirTest.php",
"chars": 641,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Functional;\n\nuse Crunz\\Application;\nuse Crunz\\CacheDirectoryFacto"
},
{
"path": "tests/Functional/ScheduleListTest.php",
"chars": 648,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Functional;\n\nuse Crunz\\Application;\nuse PHPUnit\\Framework\\TestCas"
},
{
"path": "tests/Functional/ScheduleRunTest.php",
"chars": 639,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Functional;\n\nuse Crunz\\Application;\nuse PHPUnit\\Framework\\TestCas"
},
{
"path": "tests/Functional/TaskGeneratorTest.php",
"chars": 2380,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Functional;\n\nuse Crunz\\Application;\nuse Crunz\\Path\\Path;\nuse PHPU"
},
{
"path": "tests/TestCase/EndToEnd/Environment/Environment.php",
"chars": 6706,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase\\EndToEnd\\Environment;\n\nuse Crunz\\Console\\Command\\ConfigG"
},
{
"path": "tests/TestCase/EndToEnd/Environment/EnvironmentBuilder.php",
"chars": 1151,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase\\EndToEnd\\Environment;\n\nuse Crunz\\Filesystem\\FilesystemIn"
},
{
"path": "tests/TestCase/EndToEndTestCase.php",
"chars": 1284,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Filesystem\\Filesystem;\nuse Crunz\\Filesystem\\"
},
{
"path": "tests/TestCase/FakeConfiguration.php",
"chars": 2284,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Application\\Service\\ConfigurationInterface;\n"
},
{
"path": "tests/TestCase/FakeLoader.php",
"chars": 399,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Schedule;\nuse Crunz\\Task\\LoaderInterface;\n\nf"
},
{
"path": "tests/TestCase/FakeTaskCollection.php",
"chars": 384,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Task\\CollectionInterface;\n\nfinal class FakeT"
},
{
"path": "tests/TestCase/Faker.php",
"chars": 2064,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Exception\\CrunzException;\n\nfinal class Faker"
},
{
"path": "tests/TestCase/Logger/NullLogger.php",
"chars": 472,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase\\Logger;\n\nuse Crunz\\Logger\\ConsoleLoggerInterface;\n\nfinal"
},
{
"path": "tests/TestCase/Logger/SpyPsrLogger.php",
"chars": 570,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase\\Logger;\n\nuse Psr\\Log\\AbstractLogger;\n\nfinal class SpyPsr"
},
{
"path": "tests/TestCase/SerializableTaskRunnerStub.php",
"chars": 921,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nfinal class SerializableTaskRunnerStub\n{\n public st"
},
{
"path": "tests/TestCase/TaskRunnerStub.php",
"chars": 446,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nfinal class TaskRunnerStub\n{\n public string $taskNa"
},
{
"path": "tests/TestCase/TemporaryFile.php",
"chars": 1654,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Exception\\CrunzException;\n\nfinal class Tempo"
},
{
"path": "tests/TestCase/TestClock.php",
"chars": 323,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Clock\\ClockInterface;\n\nfinal class TestClock"
},
{
"path": "tests/TestCase/UnitTestCase.php",
"chars": 627,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\TestCase;\n\nuse Crunz\\Application\\Service\\ClosureSerializerInterfa"
},
{
"path": "tests/Unit/Application/Cron/AbstractCronExpressionTestCase.php",
"chars": 2369,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Application\\Cron;\n\nuse Crunz\\Application\\Cron\\CronExpression"
},
{
"path": "tests/Unit/Application/Query/TaskInformation/TaskInformationHandlerTest.php",
"chars": 4335,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Application\\Query\\TaskInformation;\n\nuse Crunz\\Application\\Qu"
},
{
"path": "tests/Unit/CacheDirectoryFactory/CacheDirectoryFactoryTest.php",
"chars": 1515,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\CacheDirectoryFactory;\n\nuse Crunz\\CacheDirectoryFactory\\Cach"
},
{
"path": "tests/Unit/Configuration/ConfigurationParserTest.php",
"chars": 2340,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Configuration;\n\nuse Crunz\\Configuration\\ConfigFileNotExistsE"
},
{
"path": "tests/Unit/Configuration/ConfigurationTest.php",
"chars": 3561,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Configuration;\n\nuse Crunz\\Configuration\\Configuration;\nuse C"
},
{
"path": "tests/Unit/Configuration/FileParserTest.php",
"chars": 2051,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Configuration;\n\nuse Crunz\\Configuration\\ConfigFileNotExistsE"
},
{
"path": "tests/Unit/Console/Command/ScheduleListCommandTest.php",
"chars": 5444,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Console\\Command;\n\nuse Crunz\\Console\\Command\\ScheduleListComm"
},
{
"path": "tests/Unit/Console/Command/ScheduleRunCommandTest.php",
"chars": 7059,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Console\\Command;\n\nuse Crunz\\Console\\Command\\ScheduleRunComma"
},
{
"path": "tests/Unit/EnvFlags/EnvFlagsTest.php",
"chars": 2630,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\EnvFlags;\n\nuse Crunz\\EnvFlags\\EnvFlags;\nuse PHPUnit\\Framewor"
},
{
"path": "tests/Unit/EventRunnerTest.php",
"chars": 4528,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit;\n\nuse Crunz\\EventRunner;\nuse Crunz\\HttpClient\\HttpClientInte"
},
{
"path": "tests/Unit/EventTest.php",
"chars": 21730,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit;\n\nuse Crunz\\Event;\nuse Crunz\\Exception\\CrunzException;\nuse C"
},
{
"path": "tests/Unit/Filesystem/FilesystemTest.php",
"chars": 5327,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Filesystem;\n\nuse Crunz\\Filesystem\\Filesystem;\nuse Crunz\\Path"
},
{
"path": "tests/Unit/Finder/FinderTest.php",
"chars": 3717,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Finder;\n\nuse Crunz\\Filesystem\\Filesystem;\nuse Crunz\\Finder\\F"
},
{
"path": "tests/Unit/HttpClient/StreamHttpClientTest.php",
"chars": 679,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\HttpClient;\n\nuse Crunz\\HttpClient\\HttpClientException;\nuse C"
},
{
"path": "tests/Unit/Infrastructure/Dragonmantank/CronExpression/DragonmantankCronExpressionTestCase.php",
"chars": 673,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Infrastructure\\Dragonmantank\\CronExpression;\n\nuse Cron\\CronE"
},
{
"path": "tests/Unit/Infrastructure/Psr/Logger/EnabledLoggerDecoratorTest.php",
"chars": 2467,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Infrastructure\\Psr\\Logger;\n\nuse Crunz\\Application\\Service\\Co"
},
{
"path": "tests/Unit/Infrastructure/Psr/Logger/PsrStreamLoggerFactoryTest.php",
"chars": 1007,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Infrastructure\\Psr\\Logger;\n\nuse Crunz\\Infrastructure\\Psr\\Log"
},
{
"path": "tests/Unit/Infrastructure/Psr/Logger/PsrStreamLoggerTest.php",
"chars": 6300,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Infrastructure\\Psr\\Logger;\n\nuse Crunz\\Exception\\CrunzExcepti"
},
{
"path": "tests/Unit/InvokerTest.php",
"chars": 1158,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit;\n\nuse Crunz\\Invoker;\nuse PHPUnit\\Framework\\TestCase;\n\nclass "
},
{
"path": "tests/Unit/Logger/ConsoleLoggerTest.php",
"chars": 3413,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Logger;\n\nuse Crunz\\Logger\\ConsoleLogger;\nuse Crunz\\Logger\\Co"
},
{
"path": "tests/Unit/Logger/LoggerFactoryTest.php",
"chars": 1783,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Logger;\n\nuse Crunz\\Clock\\Clock;\nuse Crunz\\Event;\nuse Crunz\\E"
},
{
"path": "tests/Unit/MailerTest.php",
"chars": 914,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit;\n\nuse Crunz\\Exception\\MailerException;\nuse Crunz\\Mailer;\nuse"
},
{
"path": "tests/Unit/Output/OutputFactoryTest.php",
"chars": 1699,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Output;\n\nuse Crunz\\Output\\OutputFactory;\nuse PHPUnit\\Framewo"
},
{
"path": "tests/Unit/Path/PathTest.php",
"chars": 1529,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Path;\n\nuse Crunz\\Exception\\CrunzException;\nuse Crunz\\Path\\Pa"
},
{
"path": "tests/Unit/Pingable.php",
"chars": 202,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit;\n\nuse Crunz\\Pinger\\PingableInterface;\nuse Crunz\\Pinger\\Pinga"
},
{
"path": "tests/Unit/Pinger/PingableTest.php",
"chars": 2481,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Pinger;\n\nuse Crunz\\Pinger\\PingableException;\nuse Crunz\\Tests"
},
{
"path": "tests/Unit/Process/ProcessTest.php",
"chars": 1169,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Process;\n\nuse Crunz\\Process\\Process;\nuse Crunz\\Tests\\TestCas"
},
{
"path": "tests/Unit/Schedule/ScheduleFactoryTest.php",
"chars": 2246,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Schedule;\n\nuse Crunz\\Event;\nuse Crunz\\Exception\\TaskNotExist"
},
{
"path": "tests/Unit/ScheduleTest.php",
"chars": 4680,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit;\n\nuse Crunz\\Event;\nuse Crunz\\Schedule;\nuse Crunz\\Tests\\TestC"
},
{
"path": "tests/Unit/Service/AbstractClosureSerializerTestCase.php",
"chars": 703,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Service;\n\nuse Crunz\\Application\\Service\\ClosureSerializerInt"
},
{
"path": "tests/Unit/Service/LaravelClosureSerializerTest.php",
"chars": 2565,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Service;\n\nuse Crunz\\Infrastructure\\Laravel\\LaravelClosureSer"
},
{
"path": "tests/Unit/Service/LaravelClosureSerializerTestCase.php",
"chars": 348,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Service;\n\nuse Crunz\\Infrastructure\\Laravel\\LaravelClosureSer"
},
{
"path": "tests/Unit/Task/TaskNumberTest.php",
"chars": 2217,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Task;\n\nuse Crunz\\Exception\\WrongTaskNumberException;\nuse Cru"
},
{
"path": "tests/Unit/Task/TimezoneTest.php",
"chars": 644,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Task;\n\nuse Crunz\\Exception\\EmptyTimezoneException;\nuse Crunz"
},
{
"path": "tests/Unit/Timezone/ProviderTest.php",
"chars": 550,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\Timezone;\n\nuse Crunz\\Timezone\\Provider;\nuse PHPUnit\\Framewor"
},
{
"path": "tests/Unit/UserInterface/Cli/ClosureRunCommandTest.php",
"chars": 1609,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Crunz\\Tests\\Unit\\UserInterface\\Cli;\n\nuse Crunz\\Tests\\TestCase\\UnitTestCase;\nu"
},
{
"path": "tests/crunz.yml",
"chars": 401,
"preview": "source: tasks\nsuffix: Tasks.php\ntimezone: UTC\ntimezone_log: false\nlog_errors: false\nerrors_log_file: ~\nlog_output: false"
},
{
"path": "tests/resources/fixtures/finder/direct/directHere.php",
"chars": 0,
"preview": ""
},
{
"path": "tests/resources/fixtures/finder/symlink/symlinkHere.php",
"chars": 0,
"preview": ""
},
{
"path": "tests/resources/tasks/ClosureTasks.php",
"chars": 337,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Crunz\\Schedule;\n\n$x = 153;\n\n$scheduler = new Schedule();\n$scheduler\n ->run(\n "
},
{
"path": "tests/resources/tasks/CustomOutputTasks.php",
"chars": 237,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Crunz\\Schedule;\n\n$scheduler = new Schedule();\n\n$scheduler\n ->run('php --help')\n "
},
{
"path": "tests/resources/tasks/FailTasks.php",
"chars": 289,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Crunz\\Schedule;\n\n$scheduler = new Schedule();\n$scheduler\n ->run(\n functio"
},
{
"path": "tests/resources/tasks/NoOverlappingClosureTasks.php",
"chars": 380,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Crunz\\Schedule;\n\n$scheduler = new Schedule();\n$scheduler\n ->run(\n functio"
},
{
"path": "tests/resources/tasks/PhpVersionTasks.php",
"chars": 190,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nuse Crunz\\Schedule;\n\n$scheduler = new Schedule();\n\n$scheduler\n ->run('php -v')\n -"
},
{
"path": "tests/resources/tasks/WrongTasks.php",
"chars": 44,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nreturn [];\n"
},
{
"path": "tests/tasks/TestTasks.php",
"chars": 246,
"preview": "<?php\n\ndeclare(strict_types=1);\nuse Crunz\\Schedule;\n\n$schedule = new Schedule();\n\n$schedule->run(PHP_BINARY . ' -v')\n "
}
]
About this extraction
This page contains the full source code of the crunzphp/crunz GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (386.9 KB), approximately 99.8k tokens, and a symbol index with 758 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.