├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── workflows │ ├── coding-standards.yml │ ├── issues.yml │ ├── pull-requests.yml │ ├── tests.yml │ └── update-changelog.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── composer.json ├── docker-compose.yml ├── php.dist.ini ├── phpunit.dist.xml ├── readme.md ├── src ├── Application │ ├── Application.php │ └── Exception │ │ ├── ApplicationAbortException.php │ │ ├── ApplicationException.php │ │ └── BaseErrorHandler.php ├── Auth │ ├── Auth.php │ ├── Authentication.php │ ├── AuthenticationConfiguration.php │ ├── Exception │ │ └── AuthenticationException.php │ ├── Guards │ │ ├── GuardContract.php │ │ ├── JwtGuard.php │ │ └── SessionGuard.php │ ├── README.md │ └── Traits │ │ └── LoginUserTrait.php ├── Cache │ ├── Adapters │ │ ├── CacheAdapterInterface.php │ │ ├── DatabaseAdapter.php │ │ ├── FilesystemAdapter.php │ │ └── RedisAdapter.php │ ├── Cache.php │ ├── CacheConfiguration.php │ └── README.md ├── Configuration │ ├── Configuration.php │ ├── EnvConfiguration.php │ ├── Loader.php │ ├── LoggerConfiguration.php │ └── README.md ├── Console │ ├── AbstractCommand.php │ ├── Argument.php │ ├── Color.php │ ├── Command.php │ ├── Command │ │ ├── ClearCommand.php │ │ ├── Generator │ │ │ ├── GenerateAppEventCommand.php │ │ │ ├── GenerateCacheCommand.php │ │ │ ├── GenerateConfigurationCommand.php │ │ │ ├── GenerateConsoleCommand.php │ │ │ ├── GenerateControllerCommand.php │ │ │ ├── GenerateEventListenerCommand.php │ │ │ ├── GenerateExceptionCommand.php │ │ │ ├── GenerateKeyCommand.php │ │ │ ├── GenerateMessagingCommand.php │ │ │ ├── GenerateMiddlewareCommand.php │ │ │ ├── GenerateMigrationCommand.php │ │ │ ├── GenerateModelCommand.php │ │ │ ├── GenerateNotificationCommand.php │ │ │ ├── GenerateProducerCommand.php │ │ │ ├── GenerateQueueCommand.php │ │ │ ├── GenerateRouterResourceCommand.php │ │ │ ├── GenerateSeederCommand.php │ │ │ ├── GenerateServiceCommand.php │ │ │ ├── GenerateSessionCommand.php │ │ │ └── GenerateValidationCommand.php │ │ ├── MigrationCommand.php │ │ ├── ReplCommand.php │ │ ├── SeederCommand.php │ │ ├── ServerCommand.php │ │ └── WorkerCommand.php │ ├── Console.php │ ├── Exception │ │ └── ConsoleException.php │ ├── Generator.php │ ├── README.md │ ├── Setting.php │ ├── Traits │ │ └── ConsoleTrait.php │ └── stubs │ │ ├── command.stub │ │ ├── configuration.stub │ │ ├── controller │ │ ├── controller.stub │ │ ├── no-plain.stub │ │ ├── rest.stub │ │ └── service.stub │ │ ├── event.stub │ │ ├── exception.stub │ │ ├── listener.stub │ │ ├── messaging.stub │ │ ├── middleware.stub │ │ ├── model │ │ ├── cache.stub │ │ ├── create.stub │ │ ├── model.stub │ │ ├── notification.stub │ │ ├── queue.stub │ │ ├── session.stub │ │ ├── standard.stub │ │ └── table.stub │ │ ├── producer.stub │ │ ├── seeder.stub │ │ ├── service.stub │ │ └── validation.stub ├── Container │ ├── Capsule.php │ ├── Compass.php │ ├── ContainerConfiguration.php │ ├── MiddlewareDispatcher.php │ └── README.md ├── Contracts │ ├── CollectionInterface.php │ └── ResponseInterface.php ├── Database │ ├── Barry │ │ ├── Builder.php │ │ ├── Concerns │ │ │ └── Relationship.php │ │ ├── Model.php │ │ ├── Relation.php │ │ ├── Relations │ │ │ ├── BelongsTo.php │ │ │ ├── BelongsToMany.php │ │ │ ├── HasMany.php │ │ │ └── HasOne.php │ │ └── Traits │ │ │ ├── ArrayAccessTrait.php │ │ │ ├── CanSerialized.php │ │ │ ├── EventTrait.php │ │ │ └── SerializableTrait.php │ ├── Collection.php │ ├── Connection │ │ ├── AbstractConnection.php │ │ ├── Adapters │ │ │ ├── MysqlAdapter.php │ │ │ ├── PostgreSQLAdapter.php │ │ │ └── SqliteAdapter.php │ │ └── Connection.php │ ├── Database.php │ ├── DatabaseConfiguration.php │ ├── Exception │ │ ├── ConnectionException.php │ │ ├── DatabaseException.php │ │ ├── MigrationException.php │ │ ├── ModelException.php │ │ ├── NotFoundException.php │ │ ├── QueryBuilderException.php │ │ └── SQLGeneratorException.php │ ├── Migration │ │ ├── Compose │ │ │ ├── MysqlCompose.php │ │ │ ├── PgsqlCompose.php │ │ │ └── SqliteCompose.php │ │ ├── Migration.php │ │ ├── Shortcut │ │ │ ├── ConstraintColumn.php │ │ │ ├── DateColumn.php │ │ │ ├── MixedColumn.php │ │ │ ├── NumberColumn.php │ │ │ └── TextColumn.php │ │ └── Table.php │ ├── Notification │ │ ├── DatabaseNotification.php │ │ └── WithNotification.php │ ├── Pagination.php │ ├── QueryBuilder.php │ ├── README.md │ └── Redis.php ├── Event │ ├── Contracts │ │ ├── AppEvent.php │ │ ├── EventListener.php │ │ └── EventShouldQueue.php │ ├── Dispatchable.php │ ├── Event.php │ ├── EventException.php │ ├── EventProducer.php │ ├── Listener.php │ └── README.md ├── Http │ ├── Client │ │ ├── HttpClient.php │ │ ├── HttpClientException.php │ │ └── Response.php │ ├── Exception │ │ ├── BadRequestException.php │ │ ├── CreatedException.php │ │ ├── ForbiddenException.php │ │ ├── HttpException.php │ │ ├── InternalServerErrorException.php │ │ ├── MethodNotAllowedException.php │ │ ├── NoContentException.php │ │ ├── NotFoundException.php │ │ ├── RequestException.php │ │ ├── ResponseException.php │ │ ├── ServerAccessControlException.php │ │ ├── UnauthorizedException.php │ │ └── UploadedFileException.php │ ├── HttpStatus.php │ ├── README.md │ ├── Redirect.php │ ├── Request.php │ ├── Response.php │ ├── ServerAccessControl.php │ └── UploadedFile.php ├── Mail │ ├── Adapters │ │ ├── LogAdapter.php │ │ ├── NativeAdapter.php │ │ ├── SesAdapter.php │ │ └── SmtpAdapter.php │ ├── Contracts │ │ └── MailAdapterInterface.php │ ├── Envelop.php │ ├── Exception │ │ ├── MailException.php │ │ ├── SmtpException.php │ │ └── SocketException.php │ ├── Mail.php │ ├── MailConfiguration.php │ ├── MailQueueProducer.php │ ├── README.md │ └── Security │ │ ├── DkimSigner.php │ │ └── SpfChecker.php ├── Messaging │ ├── Adapters │ │ ├── DatabaseChannelAdapter.php │ │ ├── MailChannelAdapter.php │ │ ├── SlackChannelAdapter.php │ │ ├── SmsChannelAdapter.php │ │ └── TelegramChannelAdapter.php │ ├── CanSendMessage.php │ ├── Contracts │ │ └── ChannelAdapterInterface.php │ ├── Messaging.php │ ├── MessagingQueueProducer.php │ └── README.md ├── Middleware │ ├── AuthMiddleware.php │ ├── BaseMiddleware.php │ └── CsrfMiddleware.php ├── Queue │ ├── Adapters │ │ ├── BeanstalkdAdapter.php │ │ ├── DatabaseAdapter.php │ │ ├── QueueAdapter.php │ │ ├── SQSAdapter.php │ │ └── SyncAdapter.php │ ├── Connection.php │ ├── ProducerService.php │ ├── QueueConfiguration.php │ ├── README.md │ └── WorkerService.php ├── Router │ ├── Exception │ │ └── RouterException.php │ ├── README.md │ ├── Resource.php │ ├── Route.php │ └── Router.php ├── Security │ ├── Crypto.php │ ├── CryptoConfiguration.php │ ├── Exception │ │ └── TokenMismatch.php │ ├── Hash.php │ ├── README.md │ ├── Sanitize.php │ └── Tokenize.php ├── Session │ ├── Adapters │ │ ├── ArrayAdapter.php │ │ ├── DatabaseAdapter.php │ │ ├── DurationTrait.php │ │ └── FilesystemAdapter.php │ ├── Cookie.php │ ├── Exception │ │ └── SessionException.php │ ├── README.md │ ├── Session.php │ └── SessionConfiguration.php ├── Storage │ ├── Contracts │ │ ├── FilesystemInterface.php │ │ └── ServiceInterface.php │ ├── Exception │ │ ├── DiskNotFoundException.php │ │ ├── ResourceException.php │ │ ├── ServiceConfigurationNotFoundException.php │ │ └── ServiceNotFoundException.php │ ├── README.md │ ├── Service │ │ ├── DiskFilesystemService.php │ │ ├── FTPService.php │ │ └── S3Service.php │ ├── Storage.php │ ├── StorageConfiguration.php │ └── Temporary.php ├── Support │ ├── Arraydotify.php │ ├── Collection.php │ ├── Env.php │ ├── Log.php │ ├── LoggerService.php │ ├── Serializes.php │ ├── Str.php │ ├── Util.php │ └── helpers.php ├── Testing │ ├── Assert.php │ ├── Features │ │ ├── FeatureHelper.php │ │ ├── Migration.php │ │ └── SeedingHelper.php │ ├── KernelTesting.php │ ├── README.md │ ├── Response.php │ └── TestCase.php ├── Translate │ ├── README.md │ ├── Translator.php │ └── TranslatorConfiguration.php ├── Validation │ ├── Exception │ │ ├── AuthorizationException.php │ │ └── ValidationException.php │ ├── FieldLexical.php │ ├── README.md │ ├── RequestValidation.php │ ├── Rules │ │ ├── DatabaseRule.php │ │ ├── DatetimeRule.php │ │ ├── EmailRule.php │ │ ├── NumericRule.php │ │ ├── RegexRule.php │ │ └── StringRule.php │ ├── Validate.php │ ├── Validator.php │ └── stubs │ │ └── lexical.php └── View │ ├── Engine │ ├── PHPEngine.php │ └── TwigEngine.php │ ├── EngineAbstract.php │ ├── Exception │ └── ViewException.php │ ├── README.md │ ├── View.php │ └── ViewConfiguration.php └── tests ├── .gitkeep ├── Application └── ApplicationTest.php ├── Auth ├── AuthenticationTest.php └── Stubs │ └── UserModelStub.php ├── Cache ├── CacheDatabaseTest.php ├── CacheFilesystemTest.php └── CacheRedisTest.php ├── Config ├── ConfigurationTest.php ├── TestingConfiguration.php └── stubs │ ├── .key │ ├── app.php │ ├── auth.php │ ├── cache.php │ ├── database.php │ ├── mail.php │ ├── policier.php │ ├── queue.php │ ├── security.php │ ├── session.php │ ├── storage.php │ ├── stub.php │ ├── translate.php │ └── view.php ├── Console ├── ArgumentTest.php ├── CustomCommandTest.php ├── GeneratorBasicTest.php ├── GeneratorDeepTest.php ├── SettingTest.php ├── Stubs │ └── CustomCommand.php └── __snapshots__ │ ├── GeneratorDeepTest__test_generate_cache_migration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_command_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_configuration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_controller_no_plain_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_controller_rest_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_controller_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_create_migration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_event_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_exception_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_listener_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_messaging_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_middleware_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_cache_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_create_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_migration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_session_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_standard_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_model_table_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_producer_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_queue_migration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_seeder_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_seeder_stubs__2.txt │ ├── GeneratorDeepTest__test_generate_service_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_session_migration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_standard_migration_stubs__1.txt │ ├── GeneratorDeepTest__test_generate_table_migration_stubs__1.txt │ └── GeneratorDeepTest__test_generate_validation_stubs__1.txt ├── Container ├── CapsuleTest.php └── Stubs │ └── MyClass.php ├── Database ├── ConnectionTest.php ├── Migration │ ├── MigrationTest.php │ ├── Mysql │ │ ├── SQLGeneratorTest.php │ │ └── SQLGenetorHelpersTest.php │ ├── Pgsql │ │ ├── SQLGeneratorTest.php │ │ └── SQLGenetorHelpersTest.php │ └── SQLite │ │ ├── SQLGeneratorTest.php │ │ └── SQLGenetorHelpersTest.php ├── PaginationTest.php ├── Query │ ├── DatabaseQueryTest.php │ ├── ModelQueryTest.php │ ├── PaginationTest.php │ └── QueryBuilderTest.php ├── RedisTest.php ├── Relation │ └── BelongsToRelationQueryTest.php └── Stubs │ ├── MigrationExtendedStub.php │ ├── PetMasterModelStub.php │ ├── PetModelStub.php │ └── PetWithMasterModelStub.php ├── Events ├── EventTest.php └── Stubs │ ├── EventModelStub.php │ ├── UserEventListenerStub.php │ └── UserEventStub.php ├── Filesystem ├── DiskFilesystemTest.php ├── FTPServiceTest.php ├── S3ServiceTest.php └── TemporaryTest.php ├── Hashing ├── SecurityTest.php └── stubs │ └── .key ├── Mail └── MailServiceTest.php ├── Messaging ├── MessagingTest.php └── Stubs │ ├── TestMessage.php │ └── TestNotifiableModel.php ├── Notification └── NotificationDatabaseTest.php ├── Queue ├── EventQueueTest.php ├── MailQueueTest.php ├── MessagingQueueTest.php ├── QueueTest.php └── Stubs │ ├── BasicProducerStubs.php │ ├── MixedProducerStub.php │ ├── ModelProducerStub.php │ ├── PetModelStub.php │ └── ServiceStub.php ├── Routing └── RouteTest.php ├── Support ├── ArraydotifyTest.php ├── CollectionTest.php ├── EnvTest.php ├── HttpClientTest.php ├── StrTest.php ├── TestingTest.php └── stubs │ └── env.json ├── Translate ├── TranslationTest.php └── stubs │ ├── en │ └── welcome.php │ └── fr │ └── welcome.php ├── Validation └── ValidationTest.php ├── View ├── ViewTest.php └── stubs │ ├── 404.twig │ ├── email.twig │ ├── mail.twig │ ├── php.php │ ├── tintin.tintin.php │ └── twig.twig └── bootstrap.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ papac ] 4 | open_collective: bowphp 5 | custom: [ "https://www.buymeacoffee.com/iOLqZ3h" ] 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | - Version: #.#.# 6 | - Tintin Version: #.#.# 7 | - PHP Version: #.#.# 8 | - Database Adapters & Version: Mysql|Sqlite 9 | 10 | ### Description 11 | 12 | ### Steps To Reproduce 13 | -------------------------------------------------------------------------------- /.github/workflows/coding-standards.yml: -------------------------------------------------------------------------------- 1 | name: fix code styling 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | php: 7 | default: "8.1" 8 | type: string 9 | message: 10 | default: Fix code styling 11 | type: string 12 | fix: 13 | default: true 14 | type: boolean 15 | 16 | jobs: 17 | lint: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | 24 | - name: Setup PHP 25 | uses: shivammathur/setup-php@v2 26 | with: 27 | php-version: ${{ inputs.php }} 28 | extensions: json, dom, curl, libxml, mbstring 29 | coverage: none 30 | 31 | - name: Install PHP CS 32 | run: composer global require squizlabs/php_codesniffer 33 | 34 | - name: Run Phpcbf 35 | run: phpcbf --standard=psr11 --tab-width=4 --severity=4 36 | 37 | - name: Commit linted files 38 | if: ${{ inputs.fix }} 39 | uses: stefanzweifel/git-auto-commit-action@v4 40 | with: 41 | commit_message: ${{ inputs.message }} 42 | -------------------------------------------------------------------------------- /.github/workflows/issues.yml: -------------------------------------------------------------------------------- 1 | name: issues 2 | 3 | on: 4 | issues: 5 | types: [labeled] 6 | 7 | permissions: 8 | issues: write 9 | 10 | jobs: 11 | help-wanted: 12 | uses: bowphp/.github/.github/workflows/issues.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/pull-requests.yml: -------------------------------------------------------------------------------- 1 | name: pull requests 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened] 6 | 7 | permissions: 8 | pull-requests: write 9 | 10 | jobs: 11 | uneditable: 12 | uses: bowphp/.github/.github/workflows/pull-requests.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: update changelog 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | update: 9 | uses: bowphp/.github/.github/workflows/update-changelog.yml@main 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/data/database.sqlite 2 | tests/data/cache/** 3 | vendor 4 | phpunit.xml 5 | .idea 6 | .DS_Store 7 | .env.json 8 | composer.lock 9 | .phpunit.result.cache 10 | bob -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.schemas": { 3 | "https://www.artillery.io/schema.json": [] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Bow's code of conduct is derived from the Ruby Code of Conduct. Any breach of the code of conduct may be reported to 2 | Franck DAKIA (dakiafranck@gmail.com). 3 | 4 | - Participants will be tolerant of opposing points of view. 5 | - Participants must ensure that their language and actions are free from personal attacks and derogatory personal 6 | remarks. 7 | - By interpreting the words and actions of others, participants must always assume good intentions. 8 | - Behavior that can reasonably be considered harassment will not be tolerated. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Franck DAKIA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /php.dist.ini: -------------------------------------------------------------------------------- 1 | sendmail_path = /tmp/sendmail -t -i 2 | -------------------------------------------------------------------------------- /phpunit.dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | tests/ 18 | ./tests/SessionTest.php 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | src 39 | 40 | 41 | vendor 42 | tests 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/Application/Exception/ApplicationAbortException.php: -------------------------------------------------------------------------------- 1 | attributes[$this->primary_key]; 19 | } 20 | 21 | /** 22 | * Define the additional values 23 | * 24 | * @return array 25 | */ 26 | public function customJwtAttributes(): array 27 | { 28 | return []; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Auth/AuthenticationConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'auth', 19 | function () use ($config) { 20 | return Auth::configure($config['auth']); 21 | } 22 | ); 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function run(): void 29 | { 30 | $this->container->make('auth'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Auth/Exception/AuthenticationException.php: -------------------------------------------------------------------------------- 1 | attempts(["username" => "name@example.com", "password" => "password"]); 11 | 12 | if (!$logged) { 13 | throw new UnauthorizedException("Access denied"); 14 | } 15 | 16 | $user = $auth->user(); 17 | ``` 18 | 19 | Enjoy! 20 | -------------------------------------------------------------------------------- /src/Auth/Traits/LoginUserTrait.php: -------------------------------------------------------------------------------- 1 | provider['model']; 23 | $fields = $this->provider['credentials']; 24 | 25 | if (!isset($credentials[$fields['username']])) { 26 | throw new AuthenticationException( 27 | "Please check your passed variable for make attemps login." 28 | . "The 'credentials.{$fields['username']}' key not found." 29 | ); 30 | } 31 | 32 | if (!isset($credentials[$fields['password']])) { 33 | throw new AuthenticationException( 34 | "Please check your passed variable for make attemps login." 35 | . "The 'credentials.{$fields['password']}' key not found." 36 | ); 37 | } 38 | 39 | $column = $fields['username']; 40 | $value = $credentials[$fields['username']]; 41 | 42 | return $model::where($column, $value)->first(); 43 | } 44 | 45 | /** 46 | * Get user by key 47 | * 48 | * @param string $key 49 | * @param float|int|string $value 50 | * @return Model|null 51 | */ 52 | private function getUserBy(string $key, float|int|string $value): ?Authentication 53 | { 54 | $model = $this->provider['model']; 55 | 56 | return $model::where($key, $value)->first(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Cache/CacheConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'cache', 19 | function () use ($config) { 20 | return Cache::configure($config['cache']); 21 | } 22 | ); 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function run(): void 29 | { 30 | $this->container->make('cache'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Cache/README.md: -------------------------------------------------------------------------------- 1 | # Bow Cache 2 | 3 | Bow Framework's cache system is very simple cache manager 4 | 5 | - Filesystem 6 | - Database 7 | - Redis 8 | - With extended driver interface 9 | 10 | Let's show a little exemple: 11 | 12 | ```php 13 | $content = cache("name"); 14 | ``` 15 | 16 | By specifying the driver: 17 | 18 | ``` 19 | $content = Cache::store('redis')->get('name'); 20 | ``` 21 | 22 | Is very enjoyful api 23 | -------------------------------------------------------------------------------- /src/Configuration/Configuration.php: -------------------------------------------------------------------------------- 1 | container = $container; 24 | } 25 | 26 | /** 27 | * Get the container instance 28 | * 29 | * @return Container 30 | */ 31 | public function getContainer(): Container 32 | { 33 | return $this->container; 34 | } 35 | 36 | /** 37 | * Get la service class name 38 | * 39 | * @return string 40 | */ 41 | public function getName(): string 42 | { 43 | return get_called_class(); 44 | } 45 | 46 | /** 47 | * Create and configure the server or package 48 | * 49 | * @param Loader $config 50 | * @return void 51 | */ 52 | abstract public function create(Loader $config): void; 53 | 54 | /** 55 | * Start the configured package 56 | * 57 | * @return void 58 | */ 59 | abstract public function run(): void; 60 | } 61 | -------------------------------------------------------------------------------- /src/Configuration/EnvConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'env', 19 | function () use ($config) { 20 | $path = $config['app.env_file']; 21 | if ($path === false) { 22 | throw new InvalidArgumentException( 23 | "The application environment file [.env.json] is not exists. " 24 | . "Copy the .env.example.json file to .env.json" 25 | ); 26 | } 27 | 28 | Env::load($config['app.env_file']); 29 | } 30 | ); 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function run(): void 37 | { 38 | $this->container->make('env'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Configuration/README.md: -------------------------------------------------------------------------------- 1 | # Bow Configuration 2 | 3 | Bow Framework's configuration system is very simple 4 | -------------------------------------------------------------------------------- /src/Console/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | setting = $setting; 44 | $this->arg = $arg; 45 | $this->namespaces = $setting->getNamespaces(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateAppEventCommand.php: -------------------------------------------------------------------------------- 1 | setting->getEventDirectory(), 22 | $event 23 | ); 24 | 25 | if ($generator->fileExists()) { 26 | echo "\033[0;31mThe event already exists.\033[00m\n"; 27 | 28 | exit(1); 29 | } 30 | 31 | $generator->write('event', [ 32 | 'baseNamespace' => $this->namespaces['event'] ?? 'App\\Events' 33 | ]); 34 | 35 | echo "\033[0;32mThe event has been well created.\033[00m\n"; 36 | 37 | exit(0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateCacheCommand.php: -------------------------------------------------------------------------------- 1 | setting->getMigrationDirectory(), 26 | $filename 27 | ); 28 | 29 | $generator->write('model/cache', [ 30 | 'className' => $filename 31 | ]); 32 | 33 | echo Color::green('Cache migration created.'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateConfigurationCommand.php: -------------------------------------------------------------------------------- 1 | setting->getPackageDirectory(), 23 | $configuration 24 | ); 25 | 26 | if ($generator->fileExists()) { 27 | echo Color::red('The configuration already exists.'); 28 | 29 | exit(0); 30 | } 31 | 32 | $generator->write('configuration', [ 33 | 'baseNamespace' => $this->namespaces['configuration'] 34 | ]); 35 | 36 | echo Color::green('The configuration was well created.'); 37 | 38 | exit(0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateConsoleCommand.php: -------------------------------------------------------------------------------- 1 | setting->getCommandDirectory(), 22 | $service 23 | ); 24 | 25 | if ($generator->fileExists()) { 26 | echo "\033[0;31mThe command already exists.\033[00m\n"; 27 | 28 | exit(1); 29 | } 30 | 31 | $generator->write('command', [ 32 | 'baseNamespace' => $this->namespaces['command'] ?? 'App\\Commands' 33 | ]); 34 | 35 | echo "\033[0;32mThe command has been well created.\033[00m\n"; 36 | 37 | exit(0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateControllerCommand.php: -------------------------------------------------------------------------------- 1 | setting->getControllerDirectory(), 22 | $controller 23 | ); 24 | 25 | if ($generator->fileExists()) { 26 | echo "\033[0;31mThe controller already exists.\033[00m\n"; 27 | 28 | exit(1); 29 | } 30 | 31 | if ($this->arg->getParameter('--no-plain')) { 32 | $generator->write('controller/no-plain', [ 33 | 'baseNamespace' => $this->namespaces['controller'] 34 | ]); 35 | } else { 36 | $generator->write('controller/controller', [ 37 | 'baseNamespace' => $this->namespaces['controller'] 38 | ]); 39 | } 40 | 41 | echo "\033[0;32mThe controller was well created.\033[00m\n"; 42 | 43 | exit(0); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateEventListenerCommand.php: -------------------------------------------------------------------------------- 1 | setting->getEventListenerDirectory(), 22 | $event 23 | ); 24 | 25 | if ($generator->fileExists()) { 26 | echo "\033[0;31mThe event already exists.\033[00m\n"; 27 | 28 | exit(1); 29 | } 30 | 31 | $generator->write('listener', [ 32 | 'baseNamespace' => $this->namespaces['listener'] ?? 'App\\Listeners' 33 | ]); 34 | 35 | echo "\033[0;32mThe event has been well created.\033[00m\n"; 36 | 37 | exit(0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateExceptionCommand.php: -------------------------------------------------------------------------------- 1 | setting->getExceptionDirectory(), 22 | $exception 23 | ); 24 | 25 | if ($generator->fileExists()) { 26 | echo "\033[0;31mThe exception already exists.\033[00m\n"; 27 | 28 | exit(1); 29 | } 30 | 31 | $generator->write('exception', [ 32 | 'baseNamespace' => $this->namespaces['exception'] ?? 'App\\Exceptions' 33 | ]); 34 | 35 | echo "\033[0;32mThe exception has been well created.\033[00m\n"; 36 | 37 | exit(0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateKeyCommand.php: -------------------------------------------------------------------------------- 1 | %s\n", Color::green($key)); 35 | 36 | exit; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateMessagingCommand.php: -------------------------------------------------------------------------------- 1 | setting->getMessagingDirectory(), 23 | $messaging 24 | ); 25 | 26 | if ($generator->fileExists()) { 27 | echo Color::red("The messaging already exists"); 28 | 29 | exit(1); 30 | } 31 | 32 | $generator->write('messaging', [ 33 | 'baseNamespace' => $this->namespaces['messaging'] ?? "App\\Messaging", 34 | ]); 35 | 36 | echo Color::green("The messaging has been well created."); 37 | 38 | exit(0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateMiddlewareCommand.php: -------------------------------------------------------------------------------- 1 | setting->getMiddlewareDirectory(), 23 | $middleware 24 | ); 25 | 26 | if ($generator->fileExists()) { 27 | echo Color::red("The middleware already exists"); 28 | 29 | exit(1); 30 | } 31 | 32 | $generator->write('middleware', [ 33 | 'baseNamespace' => $this->namespaces['middleware'] ?? "App\\Middlewares" 34 | ]); 35 | 36 | echo Color::green("The middleware has been well created."); 37 | 38 | exit(0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateModelCommand.php: -------------------------------------------------------------------------------- 1 | setting->getModelDirectory(), 23 | $model 24 | ); 25 | 26 | if ($generator->fileExists()) { 27 | echo Color::red('The model already exists.'); 28 | 29 | exit(1); 30 | } 31 | 32 | $generator->write('model/model', [ 33 | 'baseNamespace' => $this->namespaces['model'] 34 | ]); 35 | 36 | echo Color::green("The model was well created."); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateNotificationCommand.php: -------------------------------------------------------------------------------- 1 | setting->getMigrationDirectory(), 26 | $filename 27 | ); 28 | 29 | $generator->write('model/notification', [ 30 | 'className' => $filename 31 | ]); 32 | 33 | echo Color::green('Notification migration created.'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateProducerCommand.php: -------------------------------------------------------------------------------- 1 | setting->getProducerDirectory(), 23 | $producer 24 | ); 25 | 26 | if ($generator->fileExists()) { 27 | echo Color::red("The producer already exists"); 28 | exit(1); 29 | } 30 | 31 | $generator->write('producer', [ 32 | 'baseNamespace' => $this->namespaces['producer'] ?? 'App\\Producers' 33 | ]); 34 | 35 | echo Color::green("The producer has been well created."); 36 | exit(0); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateQueueCommand.php: -------------------------------------------------------------------------------- 1 | setting->getMigrationDirectory(), 26 | $filename 27 | ); 28 | 29 | $generator->write('model/queue', [ 30 | 'className' => $filename 31 | ]); 32 | 33 | echo Color::green('Queue migration created.'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateSeederCommand.php: -------------------------------------------------------------------------------- 1 | setting->getSeederDirectory(), 27 | $seeder 28 | ); 29 | 30 | if ($generator->fileExists()) { 31 | echo "\033[0;31mThe seeder already exists.\033[00m"; 32 | 33 | exit(1); 34 | } 35 | 36 | $generator->write('seeder', ['name' => $seeder]); 37 | 38 | echo "\033[0;32mThe seeder has been created.\033[00m\n"; 39 | 40 | exit(0); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateServiceCommand.php: -------------------------------------------------------------------------------- 1 | setting->getServiceDirectory(), 22 | $service 23 | ); 24 | 25 | if ($generator->fileExists()) { 26 | echo "\033[0;31mThe service already exists.\033[00m\n"; 27 | 28 | exit(1); 29 | } 30 | 31 | $generator->write('service', [ 32 | 'baseNamespace' => $this->namespaces['service'] ?? 'App\\Services' 33 | ]); 34 | 35 | echo "\033[0;32mThe service has been well created.\033[00m\n"; 36 | 37 | exit(0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateSessionCommand.php: -------------------------------------------------------------------------------- 1 | setting->getMigrationDirectory(), 26 | $filename 27 | ); 28 | 29 | $generator->write('model/session', [ 30 | 'className' => $filename 31 | ]); 32 | 33 | echo Color::green('Session migration created.'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Console/Command/Generator/GenerateValidationCommand.php: -------------------------------------------------------------------------------- 1 | setting->getValidationDirectory(), 23 | $validation 24 | ); 25 | 26 | if ($generator->fileExists()) { 27 | echo Color::red('The validation already exists.'); 28 | 29 | exit(0); 30 | } 31 | 32 | $generator->write('validation', [ 33 | 'baseNamespace' => $this->namespaces['validation'] 34 | ]); 35 | 36 | echo Color::green('The validation was created well.'); 37 | 38 | exit(0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Console/Command/ReplCommand.php: -------------------------------------------------------------------------------- 1 | arg->getParameter('--include'); 23 | 24 | if (is_string($include)) { 25 | $bootstraps = array_merge( 26 | $this->setting->getBootstrap(), 27 | (array) $include 28 | ); 29 | 30 | $this->setting->setBootstrap($bootstraps); 31 | } 32 | 33 | if (!class_exists('\Psy\Shell')) { 34 | echo Color::red('Please, install psy/psysh:@stable'); 35 | 36 | return; 37 | } 38 | 39 | $config = new Configuration(); 40 | $config->setUpdateCheck(Checker::NEVER); 41 | 42 | // Load the custom prompt 43 | $prompt = $this->arg->getParameter('--prompt', '(bow) >>'); 44 | $prompt = trim($prompt) . ' '; 45 | 46 | $config->theme()->setPrompt($prompt); 47 | 48 | $shell = new Shell($config); 49 | 50 | $shell->setIncludes($this->setting->getBootstrap()); 51 | $shell->run(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Console/Command/ServerCommand.php: -------------------------------------------------------------------------------- 1 | arg->getParameter('--port', 8080); 19 | $hostname = $this->arg->getParameter('--host', 'localhost'); 20 | $settings = $this->arg->getParameter('--php-settings', false); 21 | 22 | if (is_bool($settings)) { 23 | $settings = ''; 24 | } else { 25 | $settings = '-d ' . $settings; 26 | } 27 | 28 | $filename = $this->setting->getServerFilename(); 29 | $public_directory = $this->setting->getPublicDirectory(); 30 | 31 | // Launch the dev server. 32 | shell_exec( 33 | "php -S $hostname:$port -t {$public_directory} " . $filename . " $settings" 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Console/Command/WorkerCommand.php: -------------------------------------------------------------------------------- 1 | arg->getParameter('--tries', 3); 21 | $default = $this->arg->getParameter('--queue', "default"); 22 | $memory = (int)$this->arg->getParameter('--memory', 126); 23 | $timout = (int)$this->arg->getParameter('--timout', 60); 24 | $sleep = (int)$this->arg->getParameter('--sleep', 60); 25 | 26 | $queue = app("queue"); 27 | 28 | if (!is_null($connection)) { 29 | $queue->setConnection($connection); 30 | } 31 | 32 | $worker = $this->getWorderService(); 33 | $worker->setConnection($queue->getAdapter()); 34 | $worker->run($default, $tries, $sleep, $timout, $memory); 35 | } 36 | 37 | /** 38 | * Get the worker service 39 | * 40 | * @return WorkerService 41 | */ 42 | private function getWorderService() 43 | { 44 | return new WorkerService(); 45 | } 46 | 47 | /** 48 | * Flush the queue 49 | * 50 | * @param ?string $connection 51 | * @return void 52 | */ 53 | public function flush(?string $connection = null) 54 | { 55 | $connection_queue = $this->arg->getParameter('--queue'); 56 | 57 | $queue = app("queue"); 58 | 59 | if (!is_null($connection)) { 60 | $queue->setConnection($connection); 61 | } 62 | 63 | $adapter = $queue->getAdapter(); 64 | $adapter->flush($connection_queue); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Console/Exception/ConsoleException.php: -------------------------------------------------------------------------------- 1 | create("caches", function (Table $table) { 14 | $table->addString('key_name', ['primary' => true, 'size' => 500]); 15 | $table->addText('data'); 16 | $table->addDatetime('expire', ['nullable' => true]); 17 | $table->addTimestamps(); 18 | }); 19 | } 20 | 21 | /** 22 | * Rollback migration 23 | */ 24 | public function rollback(): void 25 | { 26 | $this->dropIfExists("caches"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Console/stubs/model/create.stub: -------------------------------------------------------------------------------- 1 | create("{table}", function (Table $table) { 14 | $table->addIncrement('id'); 15 | $table->addTimestamps(); 16 | }); 17 | } 18 | 19 | /** 20 | * Rollback migration 21 | */ 22 | public function rollback(): void 23 | { 24 | $this->dropIfExists("{table}"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Console/stubs/model/model.stub: -------------------------------------------------------------------------------- 1 | create("notifications", function (Table $table) { 14 | $table->addString('id', ["primary" => true]); 15 | $table->addString('type'); 16 | $table->addString('concern_id'); 17 | $table->addString('concern_type'); 18 | $table->addText('data'); 19 | $table->addDatetime('read_at', ['nullable' => true]); 20 | $table->addTimestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Rollback migration 26 | */ 27 | public function rollback(): void 28 | { 29 | $this->dropIfExists("notifications"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Console/stubs/model/queue.stub: -------------------------------------------------------------------------------- 1 | create("queues", function (Table $table) { 14 | $table->addString('id', ["primary" => true]); 15 | $table->addString('queue'); 16 | $table->addText('payload'); 17 | $table->addInteger('attempts', ["default" => 3]); 18 | $table->addEnum('status', [ 19 | "size" => ["waiting", "processing", "reserved", "failed", "done"], 20 | "default" => "waiting", 21 | ]); 22 | $table->addDatetime('available_at'); 23 | $table->addDatetime('reserved_at', ["nullable" => true, "default" => null]); 24 | $table->addDatetime('created_at'); 25 | }); 26 | } 27 | 28 | /** 29 | * Rollback migration 30 | */ 31 | public function rollback(): void 32 | { 33 | $this->dropIfExists("queues"); 34 | 35 | if ($this->getAdapterName() === 'pgsql') { 36 | $this->addSql("DROP TYPE IF EXISTS queue_status"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Console/stubs/model/session.stub: -------------------------------------------------------------------------------- 1 | create("sessions", function (Table $table) { 14 | $table->addColumn('id', 'string', ['primary' => true]); 15 | $table->addColumn('time', 'timestamp'); 16 | $table->addColumn('data', 'text'); 17 | $table->addColumn('ip', 'string'); 18 | }); 19 | } 20 | 21 | /** 22 | * Rollback migration 23 | */ 24 | public function rollback(): void 25 | { 26 | $this->dropIfExists("sessions"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Console/stubs/model/standard.stub: -------------------------------------------------------------------------------- 1 | create("{table}", function (Table $table) { 14 | // 15 | }); 16 | } 17 | 18 | /** 19 | * Rollback migration 20 | */ 21 | public function rollback(): void 22 | { 23 | $this->dropIfExists("{table}"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Console/stubs/model/table.stub: -------------------------------------------------------------------------------- 1 | alter("{table}", function (Table $table) { 14 | // 15 | }); 16 | } 17 | 18 | /** 19 | * Rollback migration 20 | */ 21 | public function rollback(): void 22 | { 23 | $this->alter("{table}", function (SQLGenerator $table) { 24 | // 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Console/stubs/producer.stub: -------------------------------------------------------------------------------- 1 | $faker->name(), 14 | 'created_at' => date('Y-m-d H:i:s'), 15 | 'updated_at' => date('Y-m-d H:i:s') 16 | ]; 17 | 18 | return ['{name}' => $seed]; 19 | -------------------------------------------------------------------------------- /src/Console/stubs/service.stub: -------------------------------------------------------------------------------- 1 | container->bind( 25 | 'action', 26 | function () use ($config) { 27 | $middlewares = array_merge($config->getMiddlewares(), $this->middlewares); 28 | 29 | return Compass::configure($config->namespaces(), $middlewares); 30 | } 31 | ); 32 | } 33 | 34 | /** 35 | * @inheritdoc 36 | */ 37 | public function run(): void 38 | { 39 | $this->container->make('action'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Container/README.md: -------------------------------------------------------------------------------- 1 | # Bow Container 2 | 3 | Bow Framework's container system is very simple and powerful class dependences building: 4 | 5 | Let's show a little exemple: 6 | 7 | ```php 8 | use App\Application; 9 | use Bow\Containers\Capsule; 10 | 11 | $capsule = Capsule::getInstance(); 12 | 13 | $capsule->bind(Application::class, function ($config) { 14 | return Application::make($config); 15 | }); 16 | 17 | $app = $capsule->make(Application::class); 18 | $app->run(); 19 | ``` 20 | 21 | Is very enjoyful api 22 | -------------------------------------------------------------------------------- /src/Contracts/CollectionInterface.php: -------------------------------------------------------------------------------- 1 | local_key = $local_key; 25 | $this->foreign_key = $foreign_key; 26 | 27 | parent::__construct($related, $parent); 28 | } 29 | 30 | /** 31 | * Get the results of the relationship. 32 | * 33 | * @return Collection 34 | */ 35 | public function getResults(): Collection 36 | { 37 | return $this->query->get(); 38 | } 39 | 40 | /** 41 | * Set the base constraints on the relation query. 42 | * 43 | * @return void 44 | * @throws QueryBuilderException 45 | */ 46 | public function addConstraints(): void 47 | { 48 | if (!static::$has_constraints) { 49 | return; 50 | } 51 | 52 | // For belongs to relationships, which are essentially the inverse of has one 53 | // or has many relationships, we need to actually query on the primary key 54 | // of the related models matching on the foreign key that's on a parent. 55 | $foreign_key_value = $this->parent->getAttribute($this->foreign_key); 56 | $this->query->where($this->local_key, '=', $foreign_key_value); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Database/Barry/Relations/HasMany.php: -------------------------------------------------------------------------------- 1 | local_key = $local_key; 28 | $this->foreign_key = $foreign_key; 29 | 30 | $this->query = $this->query->where($this->foreign_key, $this->parent->getKeyValue()); 31 | } 32 | 33 | /** 34 | * Get the results of the relationship. 35 | * 36 | * @return Collection 37 | */ 38 | public function getResults(): Collection 39 | { 40 | return $this->query->get(); 41 | } 42 | 43 | /** 44 | * Set the base constraints on the relation query. 45 | * 46 | * @return void 47 | */ 48 | public function addConstraints(): void 49 | { 50 | // 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Database/Barry/Traits/ArrayAccessTrait.php: -------------------------------------------------------------------------------- 1 | attributes[] = $value; 21 | } else { 22 | $this->attributes[$offset] = $value; 23 | } 24 | } 25 | 26 | /** 27 | * _offsetExists 28 | * 29 | * @param mixed $offset 30 | * @return bool 31 | * @see http://php.net/manual/fr/class.arrayaccess.php 32 | */ 33 | public function offsetExists(mixed $offset): bool 34 | { 35 | return isset($this->attributes[$offset]); 36 | } 37 | 38 | /** 39 | * _offsetUnset 40 | * 41 | * @param mixed $offset 42 | * 43 | * @see http://php.net/manual/fr/class.arrayaccess.php 44 | */ 45 | public function offsetUnset(mixed $offset): void 46 | { 47 | unset($this->attributes[$offset]); 48 | } 49 | 50 | /** 51 | * _offsetGet 52 | * 53 | * @param mixed $offset 54 | * @return mixed|null 55 | * 56 | * @see http://php.net/manual/fr/class.arrayaccess.php 57 | */ 58 | public function offsetGet(mixed $offset): mixed 59 | { 60 | return $this->attributes[$offset] ?? null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Database/Barry/Traits/CanSerialized.php: -------------------------------------------------------------------------------- 1 | $this->attributes]; 23 | } 24 | 25 | return ['attributes' => $this->toArray()]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Database/Barry/Traits/EventTrait.php: -------------------------------------------------------------------------------- 1 | bound($env)) { 21 | event()->emit($env, $this); 22 | } 23 | } 24 | 25 | /** 26 | * Get event name 27 | * 28 | * @param string $event 29 | * @return string 30 | */ 31 | private static function formatEventName(string $event): string 32 | { 33 | $class_name = str_replace('\\', '', strtolower(Str::snake(static::class))); 34 | 35 | return sprintf("%s.%s", $class_name, strtolower($event)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Database/Barry/Traits/SerializableTrait.php: -------------------------------------------------------------------------------- 1 | attributes ?? []; 17 | } 18 | 19 | /** 20 | * Unserialize 21 | * 22 | * @param array $attributes 23 | */ 24 | public function __unserialize(array $attributes): void 25 | { 26 | $this->setAttributes($attributes); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Database/Collection.php: -------------------------------------------------------------------------------- 1 | toArray() as $model) { 40 | $data[] = $model->toArray(); 41 | } 42 | 43 | return json_encode($data, $option = 0); 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function toArray(): array 50 | { 51 | $arr = []; 52 | 53 | foreach ($this->storage as $value) { 54 | $arr[] = $value->toArray(); 55 | } 56 | 57 | return $arr; 58 | } 59 | 60 | /** 61 | * Allows you to delete all the selected recordings 62 | * 63 | * @return void 64 | */ 65 | public function dropAll(): void 66 | { 67 | $this->each( 68 | function (Model $model) { 69 | $model->delete(); 70 | } 71 | ); 72 | } 73 | 74 | /** 75 | * @inheritdoc 76 | */ 77 | public function __toString(): string 78 | { 79 | return json_encode($this->all()); 80 | } 81 | 82 | /** 83 | * @inheritdoc 84 | */ 85 | public function jsonSerialize(): array 86 | { 87 | return $this->all(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Database/Connection/Adapters/SqliteAdapter.php: -------------------------------------------------------------------------------- 1 | config = $config; 28 | 29 | $this->connection(); 30 | } 31 | 32 | /** 33 | * @inheritDoc 34 | */ 35 | public function connection(): void 36 | { 37 | if (!isset($this->config['driver'])) { 38 | throw new InvalidArgumentException("Please select the right sqlite driver"); 39 | } 40 | 41 | if (!isset($this->config['database'])) { 42 | throw new InvalidArgumentException('The database is not defined'); 43 | } 44 | 45 | // Build the PDO connection 46 | $this->pdo = new PDO('sqlite:' . $this->config['database']); 47 | 48 | $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 49 | $this->pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); 50 | $this->pdo->setAttribute( 51 | PDO::ATTR_DEFAULT_FETCH_MODE, 52 | $this->config['fetch'] ?? $this->fetch 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Database/Connection/Connection.php: -------------------------------------------------------------------------------- 1 | adapter = $adapter; 24 | } 25 | 26 | /** 27 | * Returns the connection to a database. 28 | * 29 | * @return AbstractConnection 30 | */ 31 | public function getAdapter(): AbstractConnection 32 | { 33 | return $this->adapter; 34 | } 35 | 36 | /** 37 | * Set the adaptor 38 | * 39 | * @param AbstractConnection $adapter 40 | */ 41 | public function setAdapter(AbstractConnection $adapter): void 42 | { 43 | $this->adapter = $adapter; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Database/DatabaseConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'db', 19 | function () use ($config) { 20 | return Database::configure($config['database'] ?? $config['db']); 21 | } 22 | ); 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function run(): void 29 | { 30 | $this->container->make('db'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Database/Exception/ConnectionException.php: -------------------------------------------------------------------------------- 1 | 'array', 17 | ]; 18 | 19 | /** 20 | * The table name 21 | * 22 | * @var string 23 | */ 24 | protected string $table = "notifications"; 25 | 26 | public function __construct(array $attributes = []) 27 | { 28 | parent::__construct($attributes); 29 | 30 | $this->table = config('notification.table') ?: 'notifications'; 31 | } 32 | 33 | /** 34 | * Mark notification as read 35 | * 36 | * @return bool|int 37 | */ 38 | public function markAsRead(): bool|int 39 | { 40 | return $this->update(['read_at' => app_now()]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Database/Notification/WithNotification.php: -------------------------------------------------------------------------------- 1 | where('concern_id', $this->getKeyValue()) 17 | ->where('concern_type', get_class($this)); 18 | } 19 | 20 | /** 21 | * @throws ConnectionException|Exception\QueryBuilderException 22 | */ 23 | public function unreadNotifications() 24 | { 25 | return $this->notifications()->whereNull('read_at'); 26 | } 27 | 28 | /** 29 | * @throws ConnectionException|Exception\QueryBuilderException 30 | */ 31 | public function markAsRead(string $notification_id) 32 | { 33 | return $this->notifications()->where('id', $notification_id)->update(['read_at' => app_now()]); 34 | } 35 | 36 | /** 37 | * @throws ConnectionException|Exception\QueryBuilderException 38 | */ 39 | public function markAllAsRead() 40 | { 41 | return $this->notifications()->update(['read_at' => app_now()]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Event/Contracts/AppEvent.php: -------------------------------------------------------------------------------- 1 | event->process($this->payload); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Event/README.md: -------------------------------------------------------------------------------- 1 | # Bow Event 2 | 3 | Bow Framework's event driver system is very simple event subscription and interpage page event: 4 | 5 | Let's show a little exemple: 6 | 7 | ```php 8 | use Bow\Event\Event; 9 | 10 | // Add listener on send.mail event 11 | Event::on("email.sent", function (array $payload) { 12 | $name = $payload["name"]; 13 | echo "An email was sent to $name"; 14 | }); 15 | 16 | // Emit the send.mail event 17 | Event::emit("email.sent", ["name" => "Franck DAKIA"]); 18 | ``` 19 | 20 | NB: Is recommanded to write all event listener into simple class and define the event to the app Kernel file in boot 21 | method. 22 | 23 | ```php 24 | use App\Models\Activity; 25 | 26 | use Bow\Event\EventListener; 27 | 28 | class ActivityEvent extends EventListener 29 | { 30 | /** 31 | * Listener method 32 | * 33 | * @param array payload 34 | * @return mixed 35 | */ 36 | public function process($payload) 37 | { 38 | Activity::create($payload); 39 | } 40 | } 41 | ``` 42 | 43 | Into Kernel file 44 | 45 | ```php 46 | public function events() 47 | { 48 | return [ 49 | "user.activity" => [ 50 | ActivityEvent::class 51 | ] 52 | ] 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /src/Http/Client/HttpClientException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/CreatedException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/ForbiddenException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/HttpException.php: -------------------------------------------------------------------------------- 1 | status($code); 34 | 35 | parent::__construct($message, $code); 36 | } 37 | 38 | /** 39 | * Get the define user error code 40 | * 41 | * @return string 42 | */ 43 | public function getStatus(): string 44 | { 45 | return $this->status; 46 | } 47 | 48 | /** 49 | * Get the status code 50 | * 51 | * @return int 52 | */ 53 | public function getStatusCode(): int 54 | { 55 | return $this->getCode(); 56 | } 57 | 58 | /** 59 | * Get the errors bags 60 | * 61 | * @return array 62 | */ 63 | public function getErrorBags(): array 64 | { 65 | return $this->error_bags; 66 | } 67 | 68 | /** 69 | * Set the errors bags 70 | * 71 | * @param array $errors 72 | */ 73 | public function setErrorBags(array $errors) 74 | { 75 | $this->error_bags = $errors; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Http/Exception/InternalServerErrorException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/MethodNotAllowedException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/NoContentException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/NotFoundException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/RequestException.php: -------------------------------------------------------------------------------- 1 | code = $code; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Http/Exception/ResponseException.php: -------------------------------------------------------------------------------- 1 | code = $code; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Http/Exception/ServerAccessControlException.php: -------------------------------------------------------------------------------- 1 | status = $status; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Http/Exception/UploadedFileException.php: -------------------------------------------------------------------------------- 1 | post('/', function (Request $request) { 16 | $name = $request->get('name'); 17 | response()->addHeader("X-Custom-Header", "Bow Framework"); 18 | return response()->json(["data" => "Hello $name!"]); 19 | }); 20 | ``` 21 | 22 | Is very enjoyful api 23 | -------------------------------------------------------------------------------- /src/Mail/Adapters/LogAdapter.php: -------------------------------------------------------------------------------- 1 | config = $config; 35 | $this->path = $config['path']; 36 | 37 | if (!is_dir($this->path)) { 38 | mkdir($this->path, 0755, true); 39 | } 40 | } 41 | 42 | /** 43 | * Implement send email 44 | * 45 | * @param Envelop $envelop 46 | * @return bool 47 | */ 48 | public function send(Envelop $envelop): bool 49 | { 50 | $filename = date('Y-m-d_H-i-s') . '_' . Str::random(6) . '.eml'; 51 | $filepath = $this->path . '/' . $filename; 52 | 53 | $content = "Date: " . date('r') . "\n"; 54 | $content .= $envelop->compileHeaders(); 55 | 56 | $content .= "To: " . implode( 57 | ', ', 58 | array_map( 59 | function ($to) { 60 | return $to[0] ? "{$to[0]} <{$to[1]}>" : $to[1]; 61 | }, 62 | $envelop->getTo() 63 | ) 64 | ) . "\n"; 65 | 66 | $content .= "Subject: " . $envelop->getSubject() . "\n"; 67 | $content .= $envelop->getMessage(); 68 | 69 | return (bool)file_put_contents($filepath, $content); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Mail/Contracts/MailAdapterInterface.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'mail', 19 | function () use ($config) { 20 | return Mail::configure($config['mail']); 21 | } 22 | ); 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function run(): void 29 | { 30 | $this->container->make('mail'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Mail/MailQueueProducer.php: -------------------------------------------------------------------------------- 1 | bags = [ 33 | "view" => $view, 34 | "data" => $data, 35 | "envelop" => $envelop, 36 | ]; 37 | } 38 | 39 | /** 40 | * Process mail 41 | * 42 | * @return void 43 | */ 44 | public function process(): void 45 | { 46 | $envelop = $this->bags["envelop"]; 47 | 48 | $envelop->setMessage( 49 | View::parse($this->bags["view"], $this->bags["data"])->getContent() 50 | ); 51 | 52 | Mail::getInstance()->send($envelop); 53 | } 54 | 55 | /** 56 | * Send the processing exception 57 | * 58 | * @param Throwable $e 59 | * @return void 60 | */ 61 | public function onException(Throwable $e): void 62 | { 63 | $this->deleteJob(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Mail/README.md: -------------------------------------------------------------------------------- 1 | # Bow Mail 2 | 3 | Bow Framework's mail system is very simple email delivery system with support: 4 | 5 | - Native PHP mail api 6 | - SMTP setting 7 | - SES (Simple Email System) 8 | - With extended driver interface 9 | 10 | Let's show a little exemple: 11 | 12 | ```php 13 | use Bow\Mail\Envelop; 14 | 15 | email('view.template', function (Message $message) { 16 | $message->to("papac@bowphp.com"); 17 | $message->subject("Hello Franck DAKIA"); 18 | }); 19 | ``` 20 | 21 | Is very enjoyful api 22 | -------------------------------------------------------------------------------- /src/Messaging/Adapters/DatabaseChannelAdapter.php: -------------------------------------------------------------------------------- 1 | toDatabase($context); 25 | 26 | Database::table(config('messaging.notification.table') ?? 'notifications')->insert( 27 | [ 28 | 'id' => str_uuid(), 29 | 'data' => $database['data'], 30 | 'concern_id' => $context->getKey(), 31 | 'concern_type' => get_class($context), 32 | 'type' => $database['type'] ?? 'notification', 33 | ] 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Messaging/Adapters/MailChannelAdapter.php: -------------------------------------------------------------------------------- 1 | toMail($context); 26 | 27 | Mail::getInstance()->send($envelop); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Messaging/Adapters/SlackChannelAdapter.php: -------------------------------------------------------------------------------- 1 | toSlack($context); 28 | 29 | if (!isset($data['content'])) { 30 | throw new \InvalidArgumentException('The content are required for Slack'); 31 | } 32 | 33 | $webhook_url = $data['webhook_url'] ?? config('messaging.slack.webhook_url'); 34 | 35 | if (empty($webhook_url)) { 36 | throw new \InvalidArgumentException('The webhook URL is required for Slack'); 37 | } 38 | 39 | $client = new Client(); 40 | 41 | try { 42 | $client->post( 43 | $webhook_url, 44 | [ 45 | 'json' => $data['content'], 46 | 'headers' => [ 47 | 'Content-Type' => 'application/json' 48 | ] 49 | ] 50 | ); 51 | } catch (\Exception $e) { 52 | throw new \RuntimeException('Error while sending Slack message: ' . $e->getMessage()); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Messaging/Contracts/ChannelAdapterInterface.php: -------------------------------------------------------------------------------- 1 | bags = [ 31 | "message" => $message, 32 | "context" => $context, 33 | ]; 34 | } 35 | 36 | /** 37 | * Process mail 38 | * 39 | * @return void 40 | */ 41 | public function process(): void 42 | { 43 | $message = $this->bags['message']; 44 | $message->process($this->bags['context']); 45 | } 46 | 47 | /** 48 | * Send the processing exception 49 | * 50 | * @param Throwable $e 51 | * @return void 52 | */ 53 | public function onException(Throwable $e): void 54 | { 55 | $this->deleteJob(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Middleware/AuthMiddleware.php: -------------------------------------------------------------------------------- 1 | check()) { 24 | return $next($request); 25 | } 26 | 27 | return redirect($this->redirectTo()); 28 | } 29 | 30 | /** 31 | * Redirect to 32 | * 33 | * @return string 34 | */ 35 | public function redirectTo(): string 36 | { 37 | return '/'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Middleware/BaseMiddleware.php: -------------------------------------------------------------------------------- 1 | preventOn() as $url) { 23 | if ($request->is($url)) { 24 | return $next($request); 25 | } 26 | } 27 | 28 | if ($request->isAjax()) { 29 | if ($request->getHeader('x-csrf-token') === session('_token')) { 30 | return $next($request); 31 | } 32 | 33 | response()->status(401); 34 | 35 | throw new TokenMismatch( 36 | 'The request csrf token mismatch' 37 | ); 38 | } 39 | 40 | if ($request->get('_token') == $request->session()->get('_token')) { 41 | return $next($request); 42 | } 43 | 44 | throw new TokenMismatch( 45 | 'The request csrf token mismatch' 46 | ); 47 | } 48 | 49 | /** 50 | * Prevent csrf action on urls 51 | * 52 | * @return array 53 | */ 54 | public function preventOn(): array 55 | { 56 | return [ 57 | 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Queue/Adapters/SyncAdapter.php: -------------------------------------------------------------------------------- 1 | config = $config; 27 | 28 | return $this; 29 | } 30 | 31 | /** 32 | * Queue a job 33 | * 34 | * @param ProducerService $producer 35 | * @return void 36 | */ 37 | public function push(ProducerService $producer): void 38 | { 39 | $producer->process(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Queue/QueueConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 19 | 'queue', 20 | function () use ($config) { 21 | return new QueueConnection($config["worker"] ?? $config["queue"]); 22 | } 23 | ); 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public function run(): void 30 | { 31 | $this->container->make('queue'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Queue/README.md: -------------------------------------------------------------------------------- 1 | # Bow Queue 2 | 3 | Bow Framework's queue system help you to make a simple and powerful queue/job (consumer/producer) for your process whish 4 | take a low of time. 5 | 6 | ```php 7 | use App\Producers\EmailProducer; 8 | 9 | queue(new EmailProducer($email)); 10 | ``` 11 | 12 | Launch the worker/consumer. 13 | 14 | ```bash 15 | php bow run:worker --retry=3 --queue=mailing 16 | ``` 17 | -------------------------------------------------------------------------------- /src/Queue/WorkerService.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 27 | } 28 | 29 | /** 30 | * Start the consumer 31 | * 32 | * @param string $queue 33 | * @param int $tries 34 | * @param int $sleep 35 | * @param int $timeout 36 | * @param int $memory 37 | * @return void 38 | */ 39 | public function run( 40 | string $queue = "default", 41 | int $tries = 3, 42 | int $sleep = 5, 43 | int $timeout = 60, 44 | int $memory = 128 45 | ): void { 46 | $this->connection->setQueue($queue); 47 | $this->connection->setTries($tries); 48 | $this->connection->setSleep($sleep); 49 | $this->connection->work($timeout, $memory); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Router/Exception/RouterException.php: -------------------------------------------------------------------------------- 1 | status = $status; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Router/README.md: -------------------------------------------------------------------------------- 1 | # Bow Router 2 | 3 | Bow Framework's routing system is very simple with: 4 | 5 | - Route naming support 6 | - Route prefix support 7 | - Route parameter catcher support 8 | 9 | Let's show a little exemple: 10 | 11 | ```php 12 | $app->get('/', function () { 13 | return "Hello guy!"; 14 | }); 15 | ``` 16 | 17 | ## Diagramme de flux du routage 18 | 19 | ```mermaid 20 | sequenceDiagram 21 | participant Client as Client HTTP 22 | participant Router as Router 23 | participant Route as Route 24 | participant Middleware as Middleware 25 | participant Controller as Controller/Callback 26 | participant Response as Response 27 | 28 | Note over Client,Response: Traitement d'une requête HTTP 29 | 30 | Client->>Router: Requête HTTP (GET /users) 31 | 32 | Router->>Router: match(uri) 33 | 34 | alt Route trouvée 35 | Router->>Route: match(uri) 36 | Route->>Route: checkRequestUri() 37 | 38 | alt Avec Middleware 39 | Route->>Middleware: process(request) 40 | Middleware-->>Route: next(request) 41 | end 42 | 43 | Route->>Route: getParameters() 44 | Route->>Controller: call(parameters) 45 | Controller-->>Response: return response 46 | Response-->>Client: Envoie réponse HTTP 47 | else Route non trouvée 48 | Router-->>Response: 404 Not Found 49 | Response-->>Client: Erreur 404 50 | end 51 | 52 | Note over Client,Response: Exemple de définition de route 53 | 54 | Note right of Router: $app->get('/users/:id', function($id) { ... }) 55 | Note right of Router: $app->post('/users', [UserController::class, 'store']) 56 | ``` 57 | 58 | Is very joyful api 59 | -------------------------------------------------------------------------------- /src/Security/Crypto.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'security', 19 | function () use ($config) { 20 | Crypto::setkey($config['security.key'], $config['security.cipher']); 21 | 22 | return Crypto::class; 23 | } 24 | ); 25 | } 26 | 27 | /** 28 | * @inheritdoc 29 | */ 30 | public function run(): void 31 | { 32 | $this->container->make('security'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Security/Exception/TokenMismatch.php: -------------------------------------------------------------------------------- 1 | status = $status; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Security/README.md: -------------------------------------------------------------------------------- 1 | # Bow Security 2 | 3 | Bow Framework's security system protect you to CSRF, XSS and add the powerful data encryption system where you can 4 | change easily the encryption algorithm. 5 | 6 | Create the new hash. Usualy, it's use for make user password 7 | 8 | ```php 9 | Hash::make("hash content"); 10 | ``` 11 | 12 | Encrypt data 13 | 14 | ```php 15 | $encoded = encrypt("value"); 16 | // out it's the encrypted value 17 | ``` 18 | -------------------------------------------------------------------------------- /src/Session/Adapters/DurationTrait.php: -------------------------------------------------------------------------------- 1 | container->bind( 19 | 'session', 20 | function () use ($config) { 21 | $session = Session::configure((array)$config['session']); 22 | 23 | Tokenize::makeCsrfToken((int)$config['session.lifetime']); 24 | 25 | // Reboot the old request values 26 | Session::getInstance()->add('__bow.old', []); 27 | 28 | return $session; 29 | } 30 | ); 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function run(): void 37 | { 38 | $this->container->make('session'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Storage/Contracts/ServiceInterface.php: -------------------------------------------------------------------------------- 1 | service_name = $service_name; 27 | 28 | return $this; 29 | } 30 | 31 | /** 32 | * Get the service name 33 | * 34 | * @return string 35 | */ 36 | public function getService() 37 | { 38 | return $this->service_name; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Storage/README.md: -------------------------------------------------------------------------------- 1 | # Bow Storage 2 | 3 | Bow Framework's storage system is beautiful interface to manage file access. He support: 4 | 5 | - File System 6 | - FTP 7 | - Simple Storage System (S3) 8 | 9 | ```php 10 | // Get the content of code.js file 11 | mount("public")->get("code.js"); 12 | ``` 13 | 14 | Load some service for work on. 15 | 16 | ```php 17 | // Get the content of code.js file 18 | ftp()->get("code.js"); 19 | ``` 20 | -------------------------------------------------------------------------------- /src/Storage/StorageConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'storage', 19 | function () use ($config) { 20 | return Storage::configure($config['storage']); 21 | } 22 | ); 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function run(): void 29 | { 30 | $this->container->make('storage'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Support/Log.php: -------------------------------------------------------------------------------- 1 | error($message, $context); 17 | } 18 | 19 | /** 20 | * Logger service 21 | * 22 | * @param string $message 23 | * @param array $context 24 | * @return void 25 | */ 26 | public function info(string $message, array $context = []): void 27 | { 28 | app('logger')->info($message, $context); 29 | } 30 | 31 | /** 32 | * Logger service 33 | * 34 | * @param string $message 35 | * @param array $context 36 | * @return void 37 | */ 38 | public function warning(string $message, array $context = []): void 39 | { 40 | app('logger')->warning($message, $context); 41 | } 42 | 43 | /** 44 | * Logger service 45 | * 46 | * @param string $message 47 | * @param array $context 48 | * @return void 49 | */ 50 | public function alert(string $message, array $context = []): void 51 | { 52 | app('logger')->alert($message, $context); 53 | } 54 | 55 | /** 56 | * Logger service 57 | * 58 | * @param string $message 59 | * @param array $context 60 | * @return void 61 | */ 62 | public function critical(string $message, array $context = []): void 63 | { 64 | app('logger')->critical($message, $context); 65 | } 66 | 67 | /** 68 | * Logger service 69 | * 70 | * @param string $message 71 | * @param array $context 72 | * @return void 73 | */ 74 | public function emergency(string $message, array $context = []): void 75 | { 76 | app('logger')->emergency($message, $context); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Testing/Assert.php: -------------------------------------------------------------------------------- 1 | get('/landing'); 15 | 16 | $response->assertStatus(200); 17 | $response->assertContentType('text/html'); 18 | } 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /src/Translate/README.md: -------------------------------------------------------------------------------- 1 | # Bow Translate 2 | 3 | Bow Framework's translation system is a very simple translation API. Also supports pluralization. 4 | 5 | This is the sample configuration 6 | 7 | ```php 8 | // frontend/lang/en/messages.php 9 | return [ 10 | 'welcome' => 'Welcome to our application' 11 | ]; 12 | ``` 13 | 14 | Let's show a little example: 15 | 16 | ```php 17 | use Bow\Translate\Translator; 18 | 19 | echo Translator::translate('messages.welcome'); 20 | // Or 21 | echo app_trans('messages.welcome'); 22 | ``` 23 | -------------------------------------------------------------------------------- /src/Translate/TranslatorConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'translate', 19 | function () use ($config) { 20 | $auto_detected = $config['translate.auto_detected'] ?? false; 21 | $lang = $config['translate.lang']; 22 | $dictionary = $config['translate.dictionary']; 23 | 24 | if ($auto_detected) { 25 | $lang = app("request")->lang(); 26 | if (is_string($lang)) { 27 | $lang = strtolower($lang); 28 | } else { 29 | $lang = $config['translate.lang']; 30 | } 31 | } 32 | 33 | return Translator::configure($lang, $dictionary); 34 | } 35 | ); 36 | } 37 | 38 | /** 39 | * @inheritdoc 40 | */ 41 | public function run(): void 42 | { 43 | $this->container->make('translate'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Validation/Exception/AuthorizationException.php: -------------------------------------------------------------------------------- 1 | errors = $errors; 32 | $this->status = $status; 33 | } 34 | 35 | /** 36 | * Get the errors 37 | * 38 | * @return array 39 | */ 40 | public function getErrors() 41 | { 42 | return $this->errors; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Validation/README.md: -------------------------------------------------------------------------------- 1 | # Bow Validator 2 | 3 | Bow Framework's validator system help developer to make data validation delightful. 4 | 5 | Let's show a little example: 6 | 7 | ```php 8 | $data = ["name" => "Franck DAKIA"]; 9 | 10 | $validation = validator($data, [ 11 | "name" => "required|max:50" 12 | ]); 13 | 14 | if ($validation->fails()) { 15 | doSomething(); 16 | $validation->getMessages(); 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /src/Validation/Rules/DatetimeRule.php: -------------------------------------------------------------------------------- 1 | inputs[$key])) { 25 | return; 26 | } 27 | 28 | $this->fails = true; 29 | 30 | $this->last_message = $this->lexical('date', $key); 31 | 32 | $this->errors[$key][] = [ 33 | "masque" => $masque, 34 | "message" => $this->last_message 35 | ]; 36 | } 37 | 38 | /** 39 | * Compile Date Time Rule 40 | * 41 | * [datetime] Check that the contents of the field is a valid date time 42 | * 43 | * @param string $key 44 | * @param string $masque 45 | * @return void 46 | */ 47 | protected function compileDateTime(string $key, string $masque): void 48 | { 49 | if (!preg_match("/^datetime$/", $masque, $match)) { 50 | return; 51 | } 52 | 53 | if ( 54 | !preg_match( 55 | '/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/i', 56 | $this->inputs[$key] 57 | ) 58 | ) { 59 | return; 60 | } 61 | 62 | $this->last_message = $this->lexical('datetime', $key); 63 | 64 | $this->fails = true; 65 | 66 | $this->errors[$key][] = [ 67 | "masque" => $masque, 68 | "message" => $this->last_message 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Validation/Rules/EmailRule.php: -------------------------------------------------------------------------------- 1 | inputs[$key])) { 27 | return; 28 | } 29 | 30 | $this->last_message = $this->lexical('email', $key); 31 | 32 | $this->fails = true; 33 | 34 | $this->errors[$key][] = [ 35 | "masque" => $masque, 36 | "message" => $this->last_message 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Validation/Rules/RegexRule.php: -------------------------------------------------------------------------------- 1 | inputs[$key])) { 27 | return; 28 | } 29 | 30 | $this->fails = true; 31 | 32 | $this->last_message = $this->lexical('regex', $key); 33 | 34 | $this->errors[$key][] = [ 35 | "masque" => $masque, 36 | "message" => $this->last_message 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Validation/stubs/lexical.php: -------------------------------------------------------------------------------- 1 | 'The {attribute} field must be an email.', 5 | 'required' => 'The {attribute} field is required.', 6 | 'empty' => 'The {attribute} field is missing in the fields to be validated.', 7 | 'min' => 'The {attribute} field must be at least {length} characters long.', 8 | 'max' => 'The {attribute} field must not exceed {length} characters.', 9 | 'same' => 'The {attribute} field must be the same as {value}.', 10 | 'number' => 'The {attribute} field must be a number.', 11 | 'int' => 'The {attribute} field must be an integer.', 12 | 'float' => 'The {attribute} field must be a decimal.', 13 | 'alphanum' => 'Only alphanumeric characters are allowed for field {attribute}.', 14 | 'in' => 'The {attribute} field must be one of the following {value}.', 15 | 'size' => 'The {attribute} field must be {length} characters long.', 16 | 'lower' => 'Only lowercase letters are allowed for field {attribute}.', 17 | 'upper' => 'Only uppercase letters are allowed for field {attribute}.', 18 | 'alpha' => 'Only alphabetic characters are allowed for field {attribute}.', 19 | 'exists' => 'The {attribute} field does not exists.', 20 | 'not_exists' => 'The {attribute} field already exists.', 21 | 'unique' => 'The {attribute} field must be unique.', 22 | 'date' => 'The {attribute} field must use the format: yyyy-mm-dd', 23 | 'datetime' => 'The {attribute} field must use the format: yyyy-mm-dd hh:mm:ss', 24 | 'regex' => 'The {attribute} field does not match the pattern', 25 | ]; 26 | -------------------------------------------------------------------------------- /src/View/Exception/ViewException.php: -------------------------------------------------------------------------------- 1 | Hello {{name}} 10 | ``` 11 | 12 | Rendre the template file: 13 | 14 | ```php 15 | use Bow\View\View; 16 | 17 | $content = View::parse("template", ['name' => 'papac']); 18 | ``` 19 | 20 | The output 21 | 22 | ```html 23 | Show the legend message of programming learn 24 |

Hello papac

25 | ``` 26 | -------------------------------------------------------------------------------- /src/View/ViewConfiguration.php: -------------------------------------------------------------------------------- 1 | container->bind( 18 | 'view', 19 | function () use ($config) { 20 | View::configure($config["view"]); 21 | 22 | return View::getInstance(); 23 | } 24 | ); 25 | } 26 | 27 | /** 28 | * @inheritdoc 29 | */ 30 | public function run(): void 31 | { 32 | $this->container->make('view'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/Auth/Stubs/UserModelStub.php: -------------------------------------------------------------------------------- 1 | config = ConfigurationLoader::configure(__DIR__ . '/stubs'); 14 | } 15 | 16 | public function test_instance_of_loader() 17 | { 18 | $this->assertInstanceOf(ConfigurationLoader::class, $this->config); 19 | } 20 | 21 | public function test_access_to_values() 22 | { 23 | $this->assertIsArray($this->config["stub"]); 24 | $this->assertNull($this->config["key_not_found"]); 25 | $this->assertEquals($this->config["stub"]["name"], "papac"); 26 | $this->assertEquals($this->config["stub"]["sub"]["framework"], "bowphp"); 27 | 28 | $this->assertEquals($this->config["stub.name"], "papac"); 29 | $this->assertIsArray($this->config["stub.sub"]); 30 | $this->assertEquals($this->config["stub.sub.framework"], "bowphp"); 31 | } 32 | 33 | // public function test_set_config_values() 34 | // { 35 | // $this->config["stub"]["name"] = "franck"; 36 | // $this->config["stub"]["sub"] = [ 37 | // "job" => "dev" 38 | // ]; 39 | // $this->assertIsArray($this->config["stub"]); 40 | // $this->assertNull($this->config["key_not_found"]); 41 | // $this->assertEquals($this->config["stub"]["name"], "franck"); 42 | // $this->assertEquals($this->config["stub"]["sub"]["framework"], "bowphp"); 43 | // $this->assertEquals($this->config["stub"]["sub"]["job"], "dev"); 44 | // } 45 | } 46 | // I want to rewrite the internal dotnotion for config loader 47 | -------------------------------------------------------------------------------- /tests/Config/TestingConfiguration.php: -------------------------------------------------------------------------------- 1 | "", 5 | "auto_csrf" => false, 6 | "env_file" => realpath(__DIR__ . "/../../Support/stubs/env.json"), 7 | ]; 8 | -------------------------------------------------------------------------------- /tests/Config/stubs/auth.php: -------------------------------------------------------------------------------- 1 | "web", 10 | 11 | /** 12 | * Default authentication branch 13 | */ 14 | "web" => [ 15 | "type" => "session", 16 | 'model' => UserModelStub::class, 17 | 'credentials' => [ 18 | 'username' => 'username', 19 | 'password' => 'password' 20 | ] 21 | ], 22 | 23 | /** 24 | * Default authentication branch 25 | */ 26 | "api" => [ 27 | "type" => "jwt", 28 | 'model' => UserModelStub::class, 29 | 'credentials' => [ 30 | 'username' => 'username', 31 | 'password' => 'password' 32 | ] 33 | ], 34 | ]; 35 | -------------------------------------------------------------------------------- /tests/Config/stubs/cache.php: -------------------------------------------------------------------------------- 1 | "file", 5 | 6 | "stores" => [ 7 | "file" => [ 8 | "driver" => "file", 9 | "path" => TESTING_RESOURCE_BASE_DIRECTORY 10 | ], 11 | 12 | "database" => [ 13 | "driver" => "database", 14 | "connection" => "mysql", 15 | "table" => "caches", 16 | ], 17 | 18 | // The redis connection 19 | "redis" => [ 20 | 'driver' => 'redis', 21 | 'database' => app_env('REDIS_CACHE_DB', 5), 22 | "prefix" => "__app__", 23 | ] 24 | ] 25 | ]; 26 | -------------------------------------------------------------------------------- /tests/Config/stubs/mail.php: -------------------------------------------------------------------------------- 1 | 'smtp', 5 | 'charset' => 'utf8', 6 | 7 | 'smtp' => [ 8 | 'hostname' => 'localhost', 9 | 'username' => 'test@test.dev', 10 | 'password' => null, 11 | 'port' => 1025, 12 | 'tls' => false, 13 | 'ssl' => false, 14 | 'timeout' => 150, 15 | ], 16 | 17 | 'mail' => [ 18 | 'default' => 'contact', 19 | 'from' => [ 20 | 'contact' => [ 21 | 'address' => app_env('MAIL_FROM_EMAIL'), 22 | 'name' => app_env('MAIL_FROM_NAME') 23 | ], 24 | 'info' => [ 25 | 'address' => 'info@exemple.com', 26 | 'username' => 'Address information' 27 | ] 28 | ] 29 | ] 30 | ]; 31 | -------------------------------------------------------------------------------- /tests/Config/stubs/policier.php: -------------------------------------------------------------------------------- 1 | "FivwuTmpJlwfXB/WMjAyMS0wMS0yNCAyMDozMTozMTE2MTE1MjAyOTEuMDEwOA==", 5 | 6 | /** 7 | * Token expiration time 8 | */ 9 | "exp" => 3600 * 24 * 3, 10 | 11 | /** 12 | * Configures the issuer 13 | */ 14 | "iss" => app_env("APP_JWT_ISSUER", "app.example.com"), 15 | 16 | /** 17 | * Configures the audience 18 | */ 19 | "aud" => app_env("APP_JWT_AUD", "app.example.com"), 20 | 21 | /** 22 | * Token is usable after this time 23 | */ 24 | "nbf" => 60, 25 | 26 | /** 27 | * Token was issue 28 | */ 29 | "iat" => 60, 30 | 31 | /** 32 | * The type of the token, which is JWT 33 | */ 34 | "typ" => "JWT", 35 | 36 | /** 37 | * Hashing algorithm being used 38 | * 39 | * HS256, HS384, HS512, ES256, ES384, ES512, 40 | */ 41 | "alg" => "HS512", 42 | ]; 43 | -------------------------------------------------------------------------------- /tests/Config/stubs/queue.php: -------------------------------------------------------------------------------- 1 | "sync", 8 | 9 | /** 10 | * The queue drive connection 11 | */ 12 | "connections" => [ 13 | /** 14 | * The sync connexion 15 | */ 16 | "sync" => [ 17 | "directory" => TESTING_RESOURCE_BASE_DIRECTORY . "/queue" 18 | ], 19 | 20 | /** 21 | * The beanstalkd connexion 22 | */ 23 | "beanstalkd" => [ 24 | "hostname" => "127.0.0.1", 25 | "port" => 11300, 26 | "timeout" => 10, 27 | ], 28 | 29 | /** 30 | * The sqs connexion 31 | */ 32 | "sqs" => [ 33 | 'profile' => 'default', 34 | 'region' => 'ap-south-1', 35 | 'version' => 'latest', 36 | 'url' => getenv("AWS_SQS_URL"), 37 | 'credentials' => [ 38 | 'key' => getenv('AWS_KEY'), 39 | 'secret' => getenv('AWS_SECRET'), 40 | ], 41 | ], 42 | 43 | /** 44 | * The sqs connexion 45 | */ 46 | "database" => [ 47 | 'table' => "queues", 48 | ] 49 | ] 50 | ]; 51 | -------------------------------------------------------------------------------- /tests/Config/stubs/security.php: -------------------------------------------------------------------------------- 1 | `php bow generate:key` 8 | */ 9 | 'key' => file_get_contents(__DIR__ . '/.key'), 10 | 11 | /** 12 | * The Encrypt method 13 | */ 14 | 'cipher' => 'AES-256-CBC', 15 | 16 | /** 17 | * The Hash method 18 | * 19 | * @see https://github.com/bowphp/framework/issues/55 20 | */ 21 | 'hash_method' => PASSWORD_BCRYPT, 22 | 23 | /** 24 | * The Hash method options 25 | * 26 | * @see https://www.php.net/manual/fr/password.constants.php 27 | */ 28 | 'hash_options' => [ 29 | 'cost' => 10 30 | ], 31 | 32 | /** 33 | * When using token. This is the life time of a token. 34 | * It is strongly advised to program with tokens. 35 | */ 36 | 'token_expirate_time' => 50000 37 | ]; 38 | -------------------------------------------------------------------------------- /tests/Config/stubs/storage.php: -------------------------------------------------------------------------------- 1 | TESTING_RESOURCE_BASE_DIRECTORY, 8 | 9 | /** 10 | * Store location 11 | */ 12 | 'disk' => [ 13 | 'mount' => 'storage', 14 | 'path' => [ 15 | 'storage' => TESTING_RESOURCE_BASE_DIRECTORY, 16 | 'public' => TESTING_RESOURCE_BASE_DIRECTORY, 17 | ] 18 | ], 19 | 20 | 'services' => [ 21 | /** 22 | * FTP configuration 23 | */ 24 | 'ftp' => [ 25 | "driver" => "ftp", 26 | 'hostname' => app_env('FTP_HOST', 'localhost'), 27 | 'password' => app_env('FTP_PASSWORD', 'password'), 28 | 'username' => app_env('FTP_USERNAME', 'username'), 29 | 'port' => app_env('FTP_PORT', 21), 30 | 'root' => app_env('FTP_ROOT', '/tmp'), // Start directory 31 | 'tls' => app_env('FTP_SSL', false), // `true` enable the secure connexion. 32 | 'timeout' => app_env('FTP_TIMEOUT', 90) // Temps d'attente de connection 33 | ], 34 | 35 | /** 36 | * S3 configuration 37 | */ 38 | 's3' => [ 39 | "driver" => "s3", 40 | 'credentials' => [ 41 | 'key' => getenv('AWS_KEY'), 42 | 'secret' => getenv('AWS_SECRET'), 43 | ], 44 | 'bucket' => getenv('AWS_S3_BUCKET'), 45 | 'region' => 'us-east-1', 46 | 'version' => 'latest' 47 | ] 48 | ], 49 | ]; 50 | -------------------------------------------------------------------------------- /tests/Config/stubs/stub.php: -------------------------------------------------------------------------------- 1 | "papac", 5 | "sub" => [ 6 | "framework" => "bowphp" 7 | ], 8 | ]; 9 | -------------------------------------------------------------------------------- /tests/Config/stubs/translate.php: -------------------------------------------------------------------------------- 1 | 'fr', 8 | 9 | /** 10 | * When the value is true, the translation system 11 | * will detect the language of the client and will translate according to 12 | */ 13 | 'auto_detected' => false, 14 | 15 | /** 16 | * Path to the language repeater 17 | */ 18 | 'dictionary' => __DIR__ . '/../../Translate/stubs', 19 | ]; 20 | -------------------------------------------------------------------------------- /tests/Config/stubs/view.php: -------------------------------------------------------------------------------- 1 | 'twig', 6 | 7 | // Extension des pages de vues 8 | 'extension' => '.twig', 9 | 10 | // Le repertoire de cache. 11 | 'cache' => TESTING_RESOURCE_BASE_DIRECTORY . '/cache', 12 | 13 | // Le repertoire des vues. 14 | 'path' => realpath(__DIR__ . '/../../View/stubs'), 15 | 16 | 'additionnal_options' => [ 17 | 'auto_reload' => true 18 | ] 19 | ]; 20 | -------------------------------------------------------------------------------- /tests/Console/CustomCommandTest.php: -------------------------------------------------------------------------------- 1 | call("command"); 25 | 26 | $content = $this->getFileContent(); 27 | $this->assertEquals($content, 'ok'); 28 | 29 | $this->clearFile(); 30 | } 31 | 32 | public function test_create_the_custom_command_from_instance_calling() 33 | { 34 | static::$console->addCommand("command", CustomCommand::class); 35 | static::$console->call("command"); 36 | 37 | $content = $this->getFileContent(); 38 | $this->assertEquals($content, 'ok'); 39 | 40 | $this->clearFile(); 41 | } 42 | 43 | protected function getFileContent() 44 | { 45 | return file_get_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/test_custom_command.txt'); 46 | } 47 | 48 | protected function clearFile() 49 | { 50 | file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . '/test_custom_command.txt', ''); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Console/GeneratorBasicTest.php: -------------------------------------------------------------------------------- 1 | makeStubContent('command', [ 13 | "namespace" => "", 14 | "className" => "CreateUserCommand", 15 | "baseNamespace" => "Generator\Testing", 16 | ]); 17 | 18 | $this->assertNotNull($content); 19 | $this->assertMatchesRegularExpression("@\nnamespace\sGenerator\\\Testing;\n@", $content); 20 | $this->assertMatchesRegularExpression("@\nclass\sCreateUserCommand\sextends\sConsoleCommand\n@", $content); 21 | } 22 | 23 | public function test_generate_stub_without_data() 24 | { 25 | $generator = new Generator(TESTING_RESOURCE_BASE_DIRECTORY, 'CreateUserCommand'); 26 | $content = $generator->makeStubContent('command'); 27 | 28 | $this->assertNotNull($content); 29 | $this->assertMatchesRegularExpression("@\nnamespace\s\{baseNamespace\}\{namespace\};\n@", $content); 30 | $this->assertMatchesRegularExpression("@\nclass\s\{className\}\sextends\sConsoleCommand\n@", $content); 31 | } 32 | 33 | public function test_generate_by_writing_file() 34 | { 35 | $generator = new Generator(TESTING_RESOURCE_BASE_DIRECTORY, 'CreateUserCommand'); 36 | $result = $generator->write('command', [ 37 | "baseNamespace" => "Generator\Testing", 38 | ]); 39 | 40 | $this->assertTrue($result); 41 | $this->assertTrue(file_exists($generator->getPath())); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Console/Stubs/CustomCommand.php: -------------------------------------------------------------------------------- 1 | create("caches", function (Table $table) { 14 | $table->addString('key_name', ['primary' => true, 'size' => 500]); 15 | $table->addText('data'); 16 | $table->addDatetime('expire', ['nullable' => true]); 17 | $table->addTimestamps(); 18 | }); 19 | } 20 | 21 | /** 22 | * Rollback migration 23 | */ 24 | public function rollback(): void 25 | { 26 | $this->dropIfExists("caches"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_command_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("fakers", function (Table $table) { 14 | $table->addIncrement('id'); 15 | $table->addTimestamps(); 16 | }); 17 | } 18 | 19 | /** 20 | * Rollback migration 21 | */ 22 | public function rollback(): void 23 | { 24 | $this->dropIfExists("fakers"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_event_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("caches", function (SQLGenerator $table) { 14 | $table->addString('key_name', ['primary' => true, 'size' => 500]); 15 | $table->addText('data'); 16 | $table->addDatetime('expire', ['nullable' => true]); 17 | $table->addTimestamps(); 18 | }); 19 | } 20 | 21 | /** 22 | * Rollback migration 23 | */ 24 | public function rollback(): void 25 | { 26 | $this->dropIfExists("caches"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_model_create_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("fakers", function (SQLGenerator $table) { 14 | $table->addIncrement('id'); 15 | $table->addTimestamps(); 16 | }); 17 | } 18 | 19 | /** 20 | * Rollback migration 21 | */ 22 | public function rollback(): void 23 | { 24 | $this->dropIfExists("fakers"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_model_migration_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("sessions", function (SQLGenerator $table) { 14 | $table->addColumn('id', 'string', ['primary' => true]); 15 | $table->addColumn('time', 'timestamp'); 16 | $table->addColumn('data', 'text'); 17 | $table->addColumn('ip', 'string'); 18 | }); 19 | } 20 | 21 | /** 22 | * Rollback migration 23 | */ 24 | public function rollback(): void 25 | { 26 | $this->dropIfExists("sessions"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_model_standard_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("fakers", function (SQLGenerator $table) { 14 | // 15 | }); 16 | } 17 | 18 | /** 19 | * Rollback migration 20 | */ 21 | public function rollback(): void 22 | { 23 | $this->dropIfExists("fakers"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_model_stubs__1.txt: -------------------------------------------------------------------------------- 1 | alter("fakers", function (SQLGenerator $table) { 14 | // 15 | }); 16 | } 17 | 18 | /** 19 | * Rollback migration 20 | */ 21 | public function rollback(): void 22 | { 23 | $this->alter("fakers", function (SQLGenerator $table) { 24 | // 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_producer_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("queues", function (Table $table) { 14 | $table->addString('id', ["primary" => true]); 15 | $table->addString('queue'); 16 | $table->addText('payload'); 17 | $table->addInteger('attempts', ["default" => 3]); 18 | $table->addEnum('status', [ 19 | "size" => ["waiting", "processing", "reserved", "failed", "done"], 20 | "default" => "waiting", 21 | ]); 22 | $table->addDatetime('available_at'); 23 | $table->addDatetime('reserved_at', ["nullable" => true, "default" => null]); 24 | $table->addDatetime('created_at'); 25 | }); 26 | } 27 | 28 | /** 29 | * Rollback migration 30 | */ 31 | public function rollback(): void 32 | { 33 | $this->dropIfExists("queues"); 34 | 35 | if ($this->getAdapterName() === 'pgsql') { 36 | $this->addSql("DROP TYPE IF EXISTS queue_status"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_seeder_stubs__1.txt: -------------------------------------------------------------------------------- 1 | $faker->name(), 14 | 'created_at' => date('Y-m-d H:i:s'), 15 | 'updated_at' => date('Y-m-d H:i:s') 16 | ]; 17 | 18 | return ['fakes' => $seed]; 19 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_seeder_stubs__2.txt: -------------------------------------------------------------------------------- 1 | $faker->name(), 14 | 'created_at' => date('Y-m-d H:i:s'), 15 | 'updated_at' => date('Y-m-d H:i:s') 16 | ]; 17 | 18 | return ['fakes' => $seed]; 19 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_service_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("sessions", function (Table $table) { 14 | $table->addColumn('id', 'string', ['primary' => true]); 15 | $table->addColumn('time', 'timestamp'); 16 | $table->addColumn('data', 'text'); 17 | $table->addColumn('ip', 'string'); 18 | }); 19 | } 20 | 21 | /** 22 | * Rollback migration 23 | */ 24 | public function rollback(): void 25 | { 26 | $this->dropIfExists("sessions"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_standard_migration_stubs__1.txt: -------------------------------------------------------------------------------- 1 | create("fakers", function (Table $table) { 14 | // 15 | }); 16 | } 17 | 18 | /** 19 | * Rollback migration 20 | */ 21 | public function rollback(): void 22 | { 23 | $this->dropIfExists("fakers"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_table_migration_stubs__1.txt: -------------------------------------------------------------------------------- 1 | alter("fakers", function (Table $table) { 14 | // 15 | }); 16 | } 17 | 18 | /** 19 | * Rollback migration 20 | */ 21 | public function rollback(): void 22 | { 23 | $this->alter("fakers", function (SQLGenerator $table) { 24 | // 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Console/__snapshots__/GeneratorDeepTest__test_generate_validation_stubs__1.txt: -------------------------------------------------------------------------------- 1 | factory('\Bow\Support\Collection', fn() => new \Bow\Support\Collection()); 20 | static::$capsule->bind('std-class', fn() => new StdClass()); 21 | static::$capsule->bind('my-class', fn(Capsule $container) => new MyClass($container['\Bow\Support\Collection'])); 22 | static::$capsule->instance("my-class-instance", new MyClass(new \Bow\Support\Collection())); 23 | } 24 | 25 | public function test_make_simple_class_instance_from_container() 26 | { 27 | $this->assertInstanceOf(StdClass::class, static::$capsule->make('std-class')); 28 | } 29 | 30 | public function test_factory() 31 | { 32 | $this->assertNotInstanceOf(StdClass::class, static::$capsule->make('\Bow\Support\Collection')); 33 | $this->assertInstanceOf(\Bow\Support\Collection::class, static::$capsule->make('\Bow\Support\Collection')); 34 | } 35 | 36 | public function test_make_my_class_container() 37 | { 38 | $my_class = static::$capsule->make('my-class'); 39 | 40 | $this->assertInstanceOf(MyClass::class, $my_class); 41 | $this->assertInstanceOf(\Bow\Support\Collection::class, $my_class->getCollection()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Container/Stubs/MyClass.php: -------------------------------------------------------------------------------- 1 | collection = $collection; 12 | } 13 | 14 | public function getCollection() 15 | { 16 | return $this->collection; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Database/PaginationTest.php: -------------------------------------------------------------------------------- 1 | assertSame(2, $this->pagination->next()); 17 | } 18 | 19 | public function test_previous(): void 20 | { 21 | $this->assertSame(0, $this->pagination->previous()); 22 | } 23 | 24 | public function test_current(): void 25 | { 26 | $this->assertSame(1, $this->pagination->current()); 27 | } 28 | 29 | public function test_items(): void 30 | { 31 | $this->assertSame(['item1', 'item2', 'item3'], $this->pagination->items()->toArray()); 32 | } 33 | 34 | public function test_total(): void 35 | { 36 | $this->assertSame(3, $this->pagination->total()); 37 | } 38 | 39 | protected function setUp(): void 40 | { 41 | $this->pagination = new Pagination( 42 | next: 2, 43 | previous: 0, 44 | total: 3, 45 | perPage: 10, 46 | current: 1, 47 | data: collect(['item1', 'item2', 'item3']) 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Database/RedisTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($result, true); 15 | } 16 | 17 | public function test_get_cache() 18 | { 19 | Redis::set('lastname', 'papac'); 20 | 21 | $this->assertNull(Redis::get('name')); 22 | $this->assertEquals(Redis::get('lastname'), "papac"); 23 | } 24 | 25 | protected function setUp(): void 26 | { 27 | parent::setUp(); 28 | $config = TestingConfiguration::getConfig(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Database/Stubs/MigrationExtendedStub.php: -------------------------------------------------------------------------------- 1 | hasMany(PetModelStub::class); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Database/Stubs/PetModelStub.php: -------------------------------------------------------------------------------- 1 | belongsTo(PetMasterModelStub::class, "master_id"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Database/Stubs/PetWithMasterModelStub.php: -------------------------------------------------------------------------------- 1 | belongsTo(PetMasterModelStub::class, "master_id"); 32 | } 33 | 34 | /** 35 | * Build the relation with pet class 36 | * 37 | * @return BelongsTo 38 | */ 39 | public function pet(): BelongsTo 40 | { 41 | return $this->belongsTo(PetModelStub::class, "pet_id"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Events/Stubs/EventModelStub.php: -------------------------------------------------------------------------------- 1 | cache_filename = TESTING_RESOURCE_BASE_DIRECTORY . '/event.txt'; 14 | } 15 | 16 | /** 17 | * Process the event emited 18 | * 19 | * @param mixed $event 20 | * @return void 21 | */ 22 | public function process($event): void 23 | { 24 | file_put_contents($this->cache_filename, ''); 25 | file_put_contents($this->cache_filename, $event->getName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Events/Stubs/UserEventStub.php: -------------------------------------------------------------------------------- 1 | name = $name; 17 | } 18 | 19 | public function getName(): string 20 | { 21 | return $this->name; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Filesystem/S3ServiceTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped(); 22 | $s3 = Storage::service('s3'); 23 | 24 | $this->assertInstanceOf(S3Service::class, $s3); 25 | } 26 | 27 | public function test_put_file() 28 | { 29 | $this->markTestSkipped(); 30 | $s3 = Storage::service('s3'); 31 | 32 | $result = $s3->put("my-file.txt", "Content", ['visibility' => 'public']); 33 | 34 | $this->assertTrue($result); 35 | } 36 | 37 | public function test_get_file() 38 | { 39 | $this->markTestSkipped(); 40 | $s3 = Storage::service('s3'); 41 | 42 | $content = $s3->get("my-file.txt"); 43 | 44 | $this->assertEquals($content, 'Content'); 45 | } 46 | 47 | public function test_copy_file() 48 | { 49 | $this->markTestSkipped(); 50 | $s3 = Storage::service('s3'); 51 | 52 | $result = $s3->copy("my-file.txt", "the-copy-file.txt"); 53 | $first_file_content = $s3->get("my-file.txt"); 54 | $second_file_content = $s3->get("the-copy-file.txt"); 55 | 56 | $this->assertTrue($result); 57 | $this->assertEquals($first_file_content, $second_file_content); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Filesystem/TemporaryTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($temp->isOpen()); 14 | 15 | $temp->close(); 16 | $this->assertFalse($temp->isOpen()); 17 | } 18 | 19 | public function test_write() 20 | { 21 | $temp = new Temporary(sys_get_temp_dir() . '/temp'); 22 | 23 | $temp->write('hello bow'); 24 | 25 | $this->assertEquals('hello bow', $temp->read()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Hashing/SecurityTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(Crypto::decrypt($encrypted), 'bow'); 23 | } 24 | 25 | public function test_should_check_hash_value() 26 | { 27 | $hashed = Hash::create('bow'); 28 | 29 | $this->assertTrue(Hash::check('bow', $hashed)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Hashing/stubs/.key: -------------------------------------------------------------------------------- 1 | f3G8fdrUxgud43j3MjAxNy0wNS0yMiAxNzowMjozNDE0OTU0NzI1NTQuNjkzNw== -------------------------------------------------------------------------------- /tests/Messaging/Stubs/TestMessage.php: -------------------------------------------------------------------------------- 1 | to('test@example.com') 20 | ->subject('Test Message') 21 | ->view('email'); 22 | } 23 | 24 | public function toDatabase(Model $context): array 25 | { 26 | return [ 27 | 'type' => 'test_message', 28 | 'data' => [ 29 | 'message' => 'Test message content' 30 | ] 31 | ]; 32 | } 33 | 34 | public function toSlack(Model $context): array 35 | { 36 | return [ 37 | 'webhook_url' => 'https://hooks.slack.com/services/test', 38 | 'content' => [ 39 | 'text' => 'Test message for Slack' 40 | ] 41 | ]; 42 | } 43 | 44 | public function toSms(Model $context): array 45 | { 46 | return [ 47 | 'to' => '+1234567890', 48 | 'message' => 'Test SMS message' 49 | ]; 50 | } 51 | 52 | public function toTelegram(Model $context): array 53 | { 54 | return [ 55 | 'chat_id' => '123456789', 56 | 'message' => 'Test Telegram message', 57 | 'parse_mode' => 'HTML' 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Messaging/Stubs/TestNotifiableModel.php: -------------------------------------------------------------------------------- 1 | boot(); 37 | 38 | static::$connection = new Connection($config["queue"]); 39 | } 40 | 41 | /** 42 | * @test 43 | */ 44 | public function it_should_queue_event() 45 | { 46 | $adapter = static::$connection->setConnection("beanstalkd")->getAdapter(); 47 | $producer = new EventProducer(new UserEventListenerStub(), new UserEventStub("bowphp")); 48 | $cache_filename = TESTING_RESOURCE_BASE_DIRECTORY . '/event.txt'; 49 | 50 | $adapter->push($producer); 51 | $adapter->run(); 52 | 53 | $this->assertEquals("bowphp", file_get_contents($cache_filename)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/Queue/MailQueueTest.php: -------------------------------------------------------------------------------- 1 | boot(); 35 | 36 | static::$connection = new QueueConnection($config["queue"]); 37 | } 38 | 39 | public function testQueueMail() 40 | { 41 | $envelop = new Envelop(); 42 | $envelop->to("bow@bow.org"); 43 | $envelop->subject("hello from bow"); 44 | $producer = new MailQueueProducer("email", [], $envelop); 45 | 46 | $adapter = static::$connection->setConnection("beanstalkd")->getAdapter(); 47 | 48 | $adapter->push($producer); 49 | 50 | $adapter->run(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Queue/Stubs/BasicProducerStubs.php: -------------------------------------------------------------------------------- 1 | connection}_producer.txt", BasicProducerStubs::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Queue/Stubs/MixedProducerStub.php: -------------------------------------------------------------------------------- 1 | service->fire($this->connection); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Queue/Stubs/ModelProducerStub.php: -------------------------------------------------------------------------------- 1 | pet = $pet; 14 | $this->connection = $connection; 15 | } 16 | 17 | public function process(): void 18 | { 19 | $this->pet->persist(); 20 | 21 | file_put_contents(TESTING_RESOURCE_BASE_DIRECTORY . "/{$this->connection}_queue_pet_model_stub.txt", $this->pet->toJson()); 22 | 23 | $this->deleteJob(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Queue/Stubs/PetModelStub.php: -------------------------------------------------------------------------------- 1 | 'papac'])); 15 | } 16 | 17 | Env::load($env_filename); 18 | } 19 | 20 | public function test_is_loaded() 21 | { 22 | $this->assertEquals(Env::isLoaded(), true); 23 | } 24 | 25 | public function test_get() 26 | { 27 | $this->assertEquals(Env::get('APP_NAME'), 'papac'); 28 | $this->assertNull(Env::get('LAST_NAME')); 29 | $this->assertEquals(Env::get('SINCE', date('Y')), date('Y')); 30 | } 31 | 32 | public function test_set() 33 | { 34 | Env::set('APP_NAME', 'bow framework'); 35 | 36 | $this->assertNotEquals(Env::get('APP_NAME'), 'papac'); 37 | $this->assertEquals(Env::get('APP_NAME'), 'bow framework'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Support/HttpClientTest.php: -------------------------------------------------------------------------------- 1 | get("https://www.oogle.com"); 15 | 16 | $this->assertEquals($response->statusCode(), 503); 17 | } 18 | 19 | public function test_get_method_with_custom_headers() 20 | { 21 | $http = new HttpClient(); 22 | 23 | $http->addHeaders(["X-Api-Key" => "Fake-Key"]); 24 | $response = $http->get("https://www.google.com"); 25 | 26 | $this->assertEquals($response->statusCode(), 200); 27 | } 28 | 29 | public function test_should_be_fail_with_get_method() 30 | { 31 | $http = new HttpClient("https://www.google.com"); 32 | 33 | $http->addHeaders(["X-Api-Key" => "Fake-Key"]); 34 | $response = $http->get("/the-fake-url"); 35 | 36 | $this->assertEquals($response->statusCode(), 404); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Support/TestingTest.php: -------------------------------------------------------------------------------- 1 | get("/"); 19 | 20 | $response->assertStatus(200); 21 | } 22 | 23 | public function test_get_method_with_custom_headers() 24 | { 25 | $this->withHeaders(["X-Api-Key" => "Fake-Key"]); 26 | 27 | $response = $this->get("/"); 28 | 29 | $response->assertStatus(200); 30 | } 31 | 32 | public function test_should_be_fail_with_get_method() 33 | { 34 | $this->withHeaders(["X-Api-Key" => "Fake-Key"]); 35 | 36 | $response = $this->get("/the-fake-url-for-my-testing-please-do-not-block-this"); 37 | 38 | $response->assertStatus(404); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Support/stubs/env.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_NAME": "papac", 3 | "API": { 4 | "URL": "https://localhost:8000", 5 | "KEY": "key" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/Translate/stubs/en/welcome.php: -------------------------------------------------------------------------------- 1 | 'Bow framework', 5 | 'user' => [ 6 | 'name' => 'Franck' 7 | ], 8 | 'plurial' => 'User|Users', 9 | 'hello' => 'Hello {name}' 10 | ]; 11 | -------------------------------------------------------------------------------- /tests/Translate/stubs/fr/welcome.php: -------------------------------------------------------------------------------- 1 | 'Bow framework', 5 | 'user' => [ 6 | 'name' => 'Franck' 7 | ], 8 | 'plurial' => 'Utilisateur|Utilisateurs', 9 | 'hello' => 'Bonjour {name}' 10 | ]; 11 | -------------------------------------------------------------------------------- /tests/View/ViewTest.php: -------------------------------------------------------------------------------- 1 | 'bow', 'engine' => 'twig']); 34 | 35 | $this->assertEquals(trim($result), '

bow see hello world by twig

'); 36 | } 37 | 38 | public function test_tintin_compilation() 39 | { 40 | View::getInstance()->setEngine('tintin')->setExtension('.tintin.php'); 41 | 42 | $result = View::parse('tintin', ['name' => 'bow', 'engine' => 'tintin']); 43 | 44 | $this->assertEquals(trim($result), '

bow see hello world by tintin

'); 45 | } 46 | 47 | public function test_php_compilation() 48 | { 49 | View::getInstance()->setEngine('php')->setExtension('.php'); 50 | 51 | $result = View::parse('php', ['name' => 'bow', 'engine' => 'php']); 52 | 53 | $this->assertEquals(trim($result), '

bow see hello world by php

'); 54 | } 55 | 56 | public function test_file_exists() 57 | { 58 | View::getInstance()->fileExists('php'); 59 | 60 | $this->assertTrue(View::getInstance()->fileExists('php')); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/View/stubs/404.twig: -------------------------------------------------------------------------------- 1 | Not found 2 | -------------------------------------------------------------------------------- /tests/View/stubs/email.twig: -------------------------------------------------------------------------------- 1 | Hello, 2 | 3 | Bow framework is awesome 4 | 5 | Best, 6 | -------------------------------------------------------------------------------- /tests/View/stubs/mail.twig: -------------------------------------------------------------------------------- 1 | Hello {{ name }} 2 | 3 | The mail content 4 | -------------------------------------------------------------------------------- /tests/View/stubs/php.php: -------------------------------------------------------------------------------- 1 |

see hello world by

-------------------------------------------------------------------------------- /tests/View/stubs/tintin.tintin.php: -------------------------------------------------------------------------------- 1 |

{{ $name }} see hello world by {{ $engine }}

-------------------------------------------------------------------------------- /tests/View/stubs/twig.twig: -------------------------------------------------------------------------------- 1 |

{{ name }} see hello world by {{ engine }}

-------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 |