├── LICENSE.md ├── composer.json └── src ├── Console ├── ArgumentParser.php ├── BaseApplication.php ├── Command │ ├── Command.php │ └── Exception │ │ └── MissingCommandException.php ├── CommandRunner.php ├── ConsoleApplication.php ├── ConsoleHelpFormatter.php ├── ConsoleInput.php ├── ConsoleIo.php ├── ConsoleOutput.php ├── ErrorHandler.php ├── Exception │ ├── ConsoleException.php │ └── StopExecutionException.php ├── LICENSE.md └── composer.json ├── Core ├── .preloadignore ├── Autoloader.php ├── Bag.php ├── BaseObject.php ├── CallbackRegistrationTrait.php ├── CallbacksTrait.php ├── Config.php ├── Debugger.php ├── Dot.php ├── Exception │ ├── Exception.php │ ├── FileNotFoundException.php │ ├── InvalidArgumentException.php │ ├── MissingClassException.php │ ├── MissingPluginException.php │ └── RouterException.php ├── HookTrait.php ├── InitializerTrait.php ├── LICENSE.md ├── LazyLoadContainer.php ├── ModelTrait.php ├── ObjectRegistry.php ├── PhpFile.php ├── Plugin.php ├── Preloader.php ├── Resolver.php ├── bootstrap.php ├── composer.json └── functions.php ├── Http ├── BaseApplication.php ├── Controller │ ├── Component │ │ ├── AuthComponent.php │ │ ├── Component.php │ │ ├── ComponentRegistry.php │ │ ├── CookieComponent.php │ │ ├── Exception │ │ │ └── MissingComponentException.php │ │ ├── FlashComponent.php │ │ ├── PaginatorComponent.php │ │ └── SessionComponent.php │ ├── Controller.php │ └── Exception │ │ ├── MissingControllerException.php │ │ ├── MissingMethodException.php │ │ └── PrivateMethodException.php ├── Cookie.php ├── Dispatcher.php ├── ErrorHandler.php ├── Exception │ ├── BadRequestException.php │ ├── ForbiddenException.php │ ├── HttpException.php │ ├── InternalErrorException.php │ ├── MethodNotAllowedException.php │ ├── NotFoundException.php │ ├── NotImplementedException.php │ ├── ServiceUnavailableException.php │ └── UnauthorizedException.php ├── ExceptionRenderer.php ├── LICENSE.md ├── Middleware │ ├── AccessLogMiddleware.php │ ├── CsrfProtectionMiddleware.php │ ├── DispatcherMiddleware.php │ ├── Exception │ │ ├── InvalidCsrfTokenException.php │ │ └── MaintainenceModeException.php │ ├── FirewallMiddleware.php │ ├── IdsMiddleware.php │ ├── MaintenanceModeMiddleware.php │ ├── Middleware.php │ ├── MiddlewareRunner.php │ ├── MinifyMiddleware.php │ ├── ProfilerMiddleware.php │ ├── SessionMiddleware.php │ └── ThrottleMiddleware.php ├── Request.php ├── Response.php ├── Router.php ├── Session.php ├── Session │ ├── Engine │ │ ├── ArrayEngine.php │ │ ├── BaseEngine.php │ │ ├── PhpEngine.php │ │ └── RedisEngine.php │ └── SessionEngineInterface.php ├── View │ ├── Exception │ │ ├── MissingHelperException.php │ │ ├── MissingLayoutException.php │ │ ├── MissingSharedViewException.php │ │ ├── MissingViewException.php │ │ └── NotFoundException.php │ ├── Helper │ │ ├── BundleHelper.php │ │ ├── CookieHelper.php │ │ ├── DateHelper.php │ │ ├── FlashHelper.php │ │ ├── FormHelper.php │ │ ├── Helper.php │ │ ├── HelperRegistry.php │ │ ├── HtmlHelper.php │ │ ├── IntlHelper.php │ │ ├── NumberHelper.php │ │ ├── PaginatorHelper.php │ │ └── SessionHelper.php │ ├── JsonView.php │ ├── TemplateTrait.php │ ├── Templater.php │ ├── View.php │ └── XmlView.php └── composer.json ├── I18n ├── Date.php ├── Exception │ └── LocaleNotAvailableException.php ├── I18n.php ├── LICENSE.md ├── Locale │ └── empty ├── Number.php ├── composer.json └── functions.php ├── Job ├── Engine │ ├── BaseEngine.php │ ├── DatabaseEngine.php │ ├── RedisConnection.php │ └── RedisEngine.php ├── Job.php ├── LICENSE.md ├── Model │ └── Queue.php ├── Queue.php └── composer.json ├── Lock ├── LICENSE.md ├── Lock.php └── composer.json ├── Mailbox ├── .preloadignore ├── Job │ ├── MailboxCleanJob.php │ └── MailboxJob.php ├── LICENSE.md ├── Mail.php ├── MailFetcher.php ├── MailParser.php ├── Mailbox.php ├── Model │ ├── ImapMessage.php │ └── InboundEmail.php ├── Server.php ├── Service │ └── MailboxDownloadService.php ├── composer.json └── pipe.php ├── Mailer ├── EmailBuilder.php ├── Exception │ └── MissingTemplateException.php ├── LICENSE.md ├── Mailer.php ├── MailerJob.php ├── Message.php └── composer.json ├── Migration ├── Exception │ └── IrreversibleMigrationException.php ├── LICENSE.md ├── Migration.php ├── Sql.php └── composer.json ├── Model ├── Association.php ├── BaseEntity.php ├── Collection.php ├── Concern │ ├── Cacheable.php │ ├── CounterCacheable.php │ ├── Delocalizable.php │ └── Timestampable.php ├── Connection.php ├── ConnectionManager.php ├── Engine │ ├── MysqlEngine.php │ ├── PostgresEngine.php │ └── SqliteEngine.php ├── Entity.php ├── Exception │ ├── ConnectionException.php │ ├── DatasourceException.php │ ├── MissingDatasourceException.php │ ├── MissingModelException.php │ ├── QueryBuilderException.php │ ├── RecordNotFoundException.php │ ├── RecordSaveException.php │ └── ValidatorException.php ├── Finder.php ├── LICENSE.md ├── Marshaller.php ├── Model.php ├── ModelRegistry.php ├── ModelValidator.php ├── Query.php ├── Query │ ├── BatchInsertQuery.php │ └── QueryObject.php ├── QueryBuilder.php ├── Record.php ├── Repository │ └── Repository.php ├── Schema.php ├── Schema │ ├── BaseSchema.php │ ├── MysqlSchema.php │ ├── PostgresSchema.php │ ├── SqliteSchema.php │ └── TableSchema.php ├── Seed.php ├── ValidationRuleSet.php └── composer.json ├── Process ├── BackgroundProcess.php ├── BaseProcess.php ├── Exception │ └── TimeoutException.php ├── LICENSE.md ├── Process.php └── composer.json ├── Publisher ├── Exception │ └── PublisherException.php ├── LICENSE.md ├── Listener.php ├── ListenerJob.php ├── Publisher.php ├── PublisherTrait.php └── composer.json ├── Redis ├── Connection.php ├── LICENSE.md ├── Redis.php └── composer.json ├── Schedule ├── Command │ └── ScheduleRunCommand.php ├── CronExpression.php ├── Event.php ├── Exception │ └── ScheduleException.php ├── Schedule.php ├── Task.php ├── bin │ └── schedule:run └── composer.json ├── Service ├── LICENSE.md ├── Result.php ├── Service.php └── composer.json ├── Ssh ├── RemoteFile.php ├── Ssh.php └── composer.json ├── TestSuite ├── ConsoleIntegrationTestTrait.php ├── Exception │ └── ConsoleInputRequiredException.php ├── Fixture.php ├── FixtureManager.php ├── IntegrationTestTrait.php ├── JobTestTrait.php ├── LICENSE.md ├── OriginTestCase.php ├── OriginTestListener.php ├── Stub │ ├── ConsoleInput.php │ ├── ConsoleOutput.php │ └── Request.php ├── TestTrait.php └── composer.json └── Utility ├── Date.php ├── LICENSE.md ├── Number.php └── composer.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/framework", 3 | "description": "The OriginPHP framework", 4 | "type": "library", 5 | "keywords": [ 6 | "originPHP", 7 | "framework", 8 | "mvc", 9 | "easy", 10 | "simple", 11 | "convention over configuration", 12 | "orm", 13 | "docker", 14 | "rapid" 15 | ], 16 | "homepage": "https://www.originphp.com", 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "Jamiel Sharief", 21 | "email": "js@originphp.com" 22 | } 23 | ], 24 | "autoload": { 25 | "psr-4": { 26 | "Origin\\": "src/" 27 | }, 28 | "files": [ 29 | "src/Core/functions.php", 30 | "src/I18n/functions.php" 31 | ] 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Origin\\Test\\": "tests/" 36 | } 37 | }, 38 | "require": { 39 | "php": ">=7.3.0", 40 | "originphp/defer": "^3.0", 41 | "originphp/configurable": "^1.0|^2.0", 42 | "originphp/inflector": "^1.0|^2.0", 43 | "originphp/security": "^1.0|^2.0", 44 | "originphp/cache": "^1.0|^2.0", 45 | "originphp/email": "^1.0|^2.0", 46 | "originphp/html": "^1.0|^2.0", 47 | "originphp/xml": "^1.0|^2.0", 48 | "originphp/log": "^1.0|^2.0", 49 | "originphp/validation": "^1.0|^2.0", 50 | "originphp/dotenv": "^1.0|^2.0", 51 | "ext-json": "*", 52 | "ext-mbstring": "*", 53 | "ext-openssl": "*", 54 | "ext-intl": "*" 55 | }, 56 | "require-dev": { 57 | "phpunit/phpunit": "^9.2", 58 | "phpstan/phpstan": "^0.12.64" 59 | }, 60 | "replace": { 61 | "originphp/console": "self.version", 62 | "originphp/core": "self.version", 63 | "originphp/http": "self.version", 64 | "originphp/i18n": "self.version", 65 | "originphp/job": "self.version", 66 | "originphp/lock": "self.version", 67 | "originphp/mailbox": "self.version", 68 | "originphp/mailer": "self.version", 69 | "originphp/migration": "self.version", 70 | "originphp/model": "self.version", 71 | "originphp/process": "self.version", 72 | "originphp/publisher": "self.version", 73 | "originphp/redis": "self.version", 74 | "originphp/schedule": "self.version", 75 | "originphp/service": "self.version", 76 | "originphp/ssh": "self.version", 77 | "originphp/test-suite": "self.version", 78 | "originphp/utility": "self.version" 79 | }, 80 | "suggest": { 81 | "ext-redis": "Redis", 82 | "ext-imap": "Imap", 83 | "ext-mailparse": "Mailparse", 84 | "ext-ssh2": "SSH2 extension" 85 | }, 86 | "minimum-stability": "dev", 87 | "prefer-stable": true, 88 | "config": { 89 | "sort-packages": true 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Console/BaseApplication.php: -------------------------------------------------------------------------------- 1 | executeHook('initialize'); 27 | } 28 | 29 | /** 30 | * Dispatches the command 31 | * 32 | * @return int 33 | */ 34 | public function dispatch(array $arguments = []): int 35 | { 36 | $this->executeHook('startup'); 37 | $exitCode = (new CommandRunner())->run($arguments); 38 | $this->executeHook('shutdown'); 39 | 40 | return $exitCode; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Console/Command/Exception/MissingCommandException.php: -------------------------------------------------------------------------------- 1 | stream = fopen($stream, 'r'); 33 | } 34 | 35 | /** 36 | * Reads from the stream 37 | * 38 | * @return string|null 39 | */ 40 | public function read(): ?string 41 | { 42 | $data = fgets($this->stream); 43 | 44 | return $data ? trim($data) : null; 45 | } 46 | 47 | /** 48 | * Closes the stream 49 | * 50 | * @return void 51 | */ 52 | public function close(): void 53 | { 54 | if (is_resource($this->stream)) { 55 | fclose($this->stream); 56 | } 57 | } 58 | 59 | public function __destruct() 60 | { 61 | $this->close(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Console/Exception/ConsoleException.php: -------------------------------------------------------------------------------- 1 | =7.3.0", 24 | "originphp/core": "^3.0", 25 | "originphp/inflector": "^2.0", 26 | "originphp/log": "^2.0" 27 | }, 28 | "minimum-stability": "dev", 29 | "prefer-stable": true 30 | } 31 | -------------------------------------------------------------------------------- /src/Core/.preloadignore: -------------------------------------------------------------------------------- 1 | bootstrap.php -------------------------------------------------------------------------------- /src/Core/BaseObject.php: -------------------------------------------------------------------------------- 1 | executeHook('initialize', func_get_args()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Core/Exception/Exception.php: -------------------------------------------------------------------------------- 1 | template !== null) { 29 | if (! is_array($message)) { 30 | $message = [$message]; 31 | } 32 | $message = vsprintf($this->template, $message); 33 | } 34 | if ($this->defaultErrorCode !== null) { 35 | $code = $this->defaultErrorCode; 36 | } 37 | 38 | parent::__construct($message, $code); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Core/Exception/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | classUses() as $trait) { 40 | list($namespace, $className) = namespaceSplit($trait); 41 | 42 | $method = $className; 43 | if (strpos($className, 'Trait') !== false) { 44 | $method = substr($className, 0, -5); 45 | } 46 | 47 | $method = 'initialize' . $method; 48 | if (method_exists($this, $method)) { 49 | $this->$method(...$args); 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * Gets the traits used by this class 56 | * 57 | * @return array 58 | */ 59 | private function classUses(): array 60 | { 61 | $class = $this; 62 | 63 | $traits = []; 64 | while ($class) { 65 | $traits = array_merge(class_uses($class), $traits); 66 | $class = get_parent_class($class); 67 | } 68 | 69 | return array_unique($traits); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Core/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Core/LazyLoadContainer.php: -------------------------------------------------------------------------------- 1 | config[$alias] = ['className' => $className,'config' => $config]; 45 | } 46 | 47 | /** 48 | * Returns true if this container can return an item for the entry 49 | * 50 | * @param string $alias 51 | * @return boolean 52 | */ 53 | public function has(string $alias): bool 54 | { 55 | return isset($this->config[$alias]); 56 | } 57 | 58 | /** 59 | * Returns a list of items in this container 60 | * 61 | * @return array 62 | */ 63 | public function list(): array 64 | { 65 | return array_keys($this->config); 66 | } 67 | 68 | /** 69 | * Gets an item from this container 70 | * 71 | * @param string $alias 72 | * @return mixed Entry 73 | */ 74 | public function get(string $alias) 75 | { 76 | if ($this->has($alias)) { 77 | if (empty($this->objects[$alias])) { 78 | $className = $this->config[$alias]['className']; 79 | $this->objects[$alias] = new $className(...$this->config[$alias]['config']); 80 | } 81 | 82 | return $this->objects[$alias]; 83 | } 84 | throw new Exception(sprintf('%s was not found', $alias)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Core/ModelTrait.php: -------------------------------------------------------------------------------- 1 | $alias)) { 44 | return $this->$alias; 45 | } 46 | 47 | return $this->$alias = $this->modelRegistryGet($model, $options); 48 | } 49 | 50 | /** 51 | * Loads a model, uses from registry or creates a new one. 52 | * 53 | * @param string $model User, MyPlugin.User, User::class 54 | * @param array $options 55 | * @return \Origin\Model\Model 56 | */ 57 | private function modelRegistryGet(string $model, array $options = []): Model 58 | { 59 | $object = ModelRegistry::get($model, $options); 60 | if (! $object) { 61 | throw new MissingModelException($model); 62 | } 63 | 64 | return $object; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Core/PhpFile.php: -------------------------------------------------------------------------------- 1 | false, 'short' => false,'before' => null, 'after' => null]; 57 | $out = $options['short'] ? $this->varExport($data) : var_export($data, true); 58 | $out = ' \n"], 74 | ['[', "],\n", ' => '], 75 | $data 76 | ); 77 | $data = preg_replace('/=>\s\s+\[/i', '=> [', $data); 78 | $data = preg_replace("/=> \[\s\s+\]/m", '=> []', $data); 79 | 80 | return substr($data, 0, -1).']'; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Core/Resolver.php: -------------------------------------------------------------------------------- 1 | register(); 32 | } else { 33 | ( new Origin\Http\ErrorHandler())->register(); 34 | } 35 | 36 | /** 37 | * As of version 2.6 .env.php is the cached version of .env. Prior 38 | * to this config was set manually .env.php 39 | */ 40 | $configFile = ROOT . '/config/.env.php'; 41 | if (file_exists($configFile)) { 42 | $result = include $configFile; 43 | foreach ($result as $key => $value) { 44 | $_ENV[$key] = $value; 45 | } 46 | } elseif (file_exists(ROOT . '/config/.env')) { 47 | $vars = (new DotEnv())->load(ROOT. '/config'); 48 | $header = [ 49 | '# .env (cached version) - Do not edit, delete instead', 50 | '# Automatically generated ' . now(), 51 | ]; 52 | if (env('APP_DEBUG') === false) { 53 | (new PhpFile())->write($configFile, $vars, ['short' => true,'before' => implode("\n", $header)]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Core/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/core", 3 | "description": "OriginPHP Core", 4 | "type": "library", 5 | "keywords": [ 6 | "originPHP", 7 | "core" 8 | ], 9 | "homepage": "https://www.originphp.com/", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Jamiel Sharief", 14 | "email": "js@originphp.com" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-4": { 19 | "Origin\\Core\\": "." 20 | }, 21 | "files": [ 22 | "functions.php" 23 | ] 24 | }, 25 | "require": { 26 | "php": ">=7.3.0", 27 | "originphp/inflector": "^2.0" 28 | }, 29 | "suggest": { 30 | "originphp/dotenv": "If this package is used part of the framework and you will use bootstrap.php" 31 | }, 32 | "minimum-stability": "dev", 33 | "prefer-stable": true 34 | } 35 | -------------------------------------------------------------------------------- /src/Http/Controller/Component/Component.php: -------------------------------------------------------------------------------- 1 | controller = $controller; 46 | 47 | $this->config($config); 48 | $this->executeHook('initialize', [$config]); 49 | } 50 | 51 | /** 52 | * Loads a component, the component is not returned, but when you call it will be 53 | * lazy loaded 54 | * 55 | * examples: 56 | * 57 | * Session 58 | * MyPlugin.Session 59 | * 60 | * @param string $name 61 | * @param array $config 62 | * @return void 63 | */ 64 | public function loadComponent(string $name, array $config = []) 65 | { 66 | list($plugin, $component) = pluginSplit($name); 67 | if (! isset($this->components[$component])) { 68 | $this->components[$component] = array_merge(['className' => $name . 'Component','enable' => false], $config); 69 | } 70 | } 71 | 72 | /** 73 | * Lazy loading 74 | */ 75 | public function __get($name) 76 | { 77 | if (isset($this->components[$name])) { 78 | $this->$name = $this->controller->componentRegistry()->load($name, $this->components[$name]); 79 | 80 | if (isset($this->$name)) { 81 | return $this->$name; 82 | } 83 | } 84 | 85 | return null; 86 | } 87 | 88 | /** 89 | * Returns the controller 90 | * @return \Origin\Http\Controller\Controller 91 | */ 92 | public function controller(): Controller 93 | { 94 | return $this->controller; 95 | } 96 | 97 | /** 98 | * Returns the request object 99 | * 100 | * @return \Origin\Http\Request 101 | */ 102 | public function request(): Request 103 | { 104 | return $this->controller->request(); 105 | } 106 | 107 | /** 108 | * Returns the response object 109 | * 110 | * @return \Origin\Http\Response 111 | */ 112 | public function response(): Response 113 | { 114 | return $this->controller->response(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Http/Controller/Component/CookieComponent.php: -------------------------------------------------------------------------------- 1 | Cookie->doSomething 26 | * 27 | * Or you can read of the request and write on the response. 28 | */ 29 | 30 | class CookieComponent extends Component 31 | { 32 | 33 | /** 34 | * Reads a value of a cookie from request 35 | * 36 | * @param string $name 37 | * @return string|array|null 38 | */ 39 | public function read(string $name) 40 | { 41 | return $this->request()->cookies($name); 42 | } 43 | 44 | /** 45 | * Writes a cookie through response 46 | * 47 | * $cookie->write('key',$value); 48 | * $cookie->write('key',$value,['expires'=>'+1 month'); 49 | * 50 | * @param string $name cookie name 51 | * @param mixed $value 52 | * @param array $options The options keys are: 53 | * - expires: default:'+1 month'. a strtotime string e.g. +5 days, 2019-01-01 10:23:55 54 | * - encrypt: default:true. encrypt value 55 | * - path: default:'/' . Path on server 56 | * - domain: domains cookie will be available on 57 | * - secure: default:false. only send if through https 58 | * - httpOnly: default:false. only available to HTTP protocol not to javascript 59 | * - sameSite: default:none can be lax, strict or none (requires secure) 60 | * @return void 61 | */ 62 | public function write(string $name, $value, array $options = []): void 63 | { 64 | $this->response()->cookie($name, $value, $options); 65 | } 66 | 67 | /** 68 | * Deletes a cookie 69 | * 70 | * @param string $name 71 | * @return void 72 | */ 73 | public function delete(string $name): void 74 | { 75 | $this->response()->cookie($name, '', ['expires' => '-60 minutes']); 76 | } 77 | 78 | /** 79 | * Checks if a cookie exists 80 | * 81 | * @param string $name 82 | * @return bool 83 | */ 84 | public function exists(string $name): bool 85 | { 86 | $cookies = $this->request()->cookies(); 87 | 88 | return isset($cookies[$name]); 89 | } 90 | 91 | /** 92 | * Deletes all cookies 93 | * 94 | * @return void 95 | */ 96 | public function destroy(): void 97 | { 98 | unset($_COOKIE); 99 | $_COOKIE = []; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Http/Controller/Component/Exception/MissingComponentException.php: -------------------------------------------------------------------------------- 1 | loadComponent('Session'); 25 | } 26 | 27 | public function error(string $message): void 28 | { 29 | $this->addMessage('error', $message); 30 | } 31 | 32 | public function success(string $message): void 33 | { 34 | $this->addMessage('success', $message); 35 | } 36 | 37 | public function warning(string $message): void 38 | { 39 | $this->addMessage('warning', $message); 40 | } 41 | 42 | public function info(string $message): void 43 | { 44 | $this->addMessage('info', $message); 45 | } 46 | 47 | public function addMessage(string $type, string $message): void 48 | { 49 | $messages = []; 50 | 51 | if ($this->Session->exists('Flash')) { 52 | $messages = $this->Session->read('Flash'); 53 | } 54 | $messages[] = [ 55 | 'template' => $type, 56 | 'message' => $message 57 | ]; 58 | $this->Session->write('Flash', $messages); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Http/Controller/Component/SessionComponent.php: -------------------------------------------------------------------------------- 1 | session)) { 41 | $this->session = $this->request()->session(); 42 | } 43 | 44 | return $this->session; 45 | } 46 | /** 47 | * Reads a value of a session 48 | * 49 | * @param string $name 50 | * @return mixed 51 | */ 52 | public function read(string $name) 53 | { 54 | return $this->session()->read($name); 55 | } 56 | 57 | /** 58 | * Writes a session 59 | * 60 | * $session->write('key',$value); 61 | * $session->write('key',$value,strtotime('+1 day')); 62 | * 63 | * @param string $name 64 | * @param mixed $value 65 | * @return void 66 | */ 67 | public function write(string $name, $value): void 68 | { 69 | $this->session()->write($name, $value); 70 | } 71 | 72 | /** 73 | * Deletes a session 74 | * 75 | * @param string $name 76 | * @return void 77 | */ 78 | public function delete(string $name): void 79 | { 80 | $this->session()->delete($name); 81 | } 82 | 83 | /** 84 | * Checks if a session exists 85 | * 86 | * @param string $name 87 | * @return bool 88 | */ 89 | public function exists(string $name): bool 90 | { 91 | return $this->session()->exists($name); 92 | } 93 | 94 | /** 95 | * Clears the session 96 | * 97 | * @return void 98 | */ 99 | public function clear(): void 100 | { 101 | $this->session()->clear(); 102 | } 103 | 104 | /** 105 | * Deletes all sessions 106 | * 107 | * @return void 108 | */ 109 | public function destroy(): void 110 | { 111 | $this->session()->destroy(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Http/Controller/Exception/MissingControllerException.php: -------------------------------------------------------------------------------- 1 | params('controller')) { 84 | $class = $this->getClass($request->params('controller'), $request->params('plugin'), $request->params('prefix')); 85 | if (! class_exists($class)) { 86 | throw new MissingControllerException($request->params('controller')); 87 | } 88 | 89 | $this->controller = new $class($request, $response); 90 | 91 | return $this->controller->dispatch($request->params('action')); 92 | } 93 | throw new RouterException('No route found.', 404); 94 | } 95 | 96 | /** 97 | * Gets the controller 98 | * 99 | * @return \Origin\Http\Controller\Controller 100 | */ 101 | public function controller(): Controller 102 | { 103 | return $this->controller; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Http/Exception/BadRequestException.php: -------------------------------------------------------------------------------- 1 | LOGS . '/access.log' 35 | ]; 36 | 37 | /** 38 | * This PROCESSES the response. Use this to make changes to the response. 39 | * 40 | * @param \Origin\Http\Request $request 41 | * @param \Origin\Http\Response $response 42 | * @return void 43 | */ 44 | public function process(Request $request, Response $response): void 45 | { 46 | file_put_contents($this->config['file'], $this->commonFormat($request, $response) . "\n", FILE_APPEND); 47 | } 48 | 49 | /** 50 | * Creates an access log using the Apache Common LOG format 51 | * @see https://httpd.apache.org/docs/2.4/logs.html#accesslog. 52 | * 53 | * @param \Origin\Http\Request $request 54 | * @param \Origin\Http\Response $response 55 | * @return string 56 | */ 57 | private function commonFormat(Request $request, Response $response): string 58 | { 59 | return sprintf( 60 | '%s %s [%s] "%s %s %s" %d %d', 61 | $request->ip(), 62 | $request->session()->read('Auth.User.id') ?: '-', 63 | date('d/M/Y:H:i:s O'), 64 | $request->method(), 65 | $request->server('REQUEST_URI'), 66 | $request->server('SERVER_PROTOCOL'), 67 | $response->statusCode(), 68 | mb_strlen($response->body() ?? '') 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Http/Middleware/DispatcherMiddleware.php: -------------------------------------------------------------------------------- 1 | dispatch($request, $response); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Http/Middleware/Exception/InvalidCsrfTokenException.php: -------------------------------------------------------------------------------- 1 | ip(); 58 | if (! $ipAddress) { 59 | throw new ForbiddenException('Invalid IP address'); 60 | } 61 | 62 | if (file_exists(CONFIG . '/blacklist.php')) { 63 | $this->blacklist = (new PhpFile())->read(CONFIG . '/blacklist.php'); 64 | } 65 | 66 | if (file_exists(CONFIG . '/whitelist.php')) { 67 | $this->whitelist = (new PhpFile())->read(CONFIG . '/whitelist.php'); 68 | } 69 | 70 | $this->checkLists($ipAddress); 71 | 72 | // Free mem 73 | $this->blacklist = $this->whitelist = null; 74 | } 75 | 76 | /** 77 | * Checks an IP address against blacklist and whitelists 78 | * 79 | * @param string $ip 80 | * @return void 81 | */ 82 | protected function checkLists(string $ip): void 83 | { 84 | if ($this->whitelist) { 85 | if (! in_array($ip, $this->whitelist)) { 86 | throw new ForbiddenException('IP address is not allowed'); 87 | } 88 | } elseif (in_array($ip, $this->blacklist)) { 89 | throw new ForbiddenException('IP address is blacklisted'); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Http/Middleware/MaintenanceModeMiddleware.php: -------------------------------------------------------------------------------- 1 | false 24 | ]; 25 | 26 | /** 27 | * This HANDLES the request. Use this to make changes to the request. 28 | * 29 | * @param \Origin\Http\Request $request 30 | * @return void 31 | */ 32 | public function handle(Request $request): void 33 | { 34 | if ($this->maintenanceMode()) { 35 | $data = json_decode(file_get_contents(tmp_path('maintenance.json')), true); 36 | 37 | // Check IP to see if its in allowed list 38 | if ($data['allowed'] && in_array($request->ip(), $data['allowed'])) { 39 | return; 40 | } 41 | // Send headers 42 | $this->sendHeader('Maintenance-Started: ' . $data['time']); 43 | $this->sendHeader('Retry-After: ' . ($data['retry'] ? ($data['time'] + $data['retry']) : null)); 44 | 45 | if ($this->config('html')) { 46 | $this->sendHeader('Location: /maintenance.html'); 47 | $this->exit(); 48 | } else { 49 | throw new MaintainenceModeException( 50 | $data['message'] 51 | ); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * Wrapped for testing 58 | * 59 | * @codeCoverageIgnore 60 | * 61 | * @return void 62 | */ 63 | protected function exit(): void 64 | { 65 | exit(); 66 | } 67 | 68 | /** 69 | * Checks if maintenance is enabled 70 | * 71 | * @return boolean 72 | */ 73 | protected function maintenanceMode(): bool 74 | { 75 | return file_exists(tmp_path('maintenance.json')); 76 | } 77 | 78 | /** 79 | * Sends a header for the exception 80 | * 81 | * @codeCoverageIgnore 82 | * 83 | * @param string $header 84 | * @return void 85 | */ 86 | protected function sendHeader(string $header): void 87 | { 88 | header($header); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Http/Middleware/Middleware.php: -------------------------------------------------------------------------------- 1 | config($config); 33 | $this->executeHook('initialize', [$config]); 34 | } 35 | 36 | /** 37 | * This HANDLES the request. 38 | * 39 | * @param \Origin\Http\Request $request 40 | * @return void 41 | */ 42 | public function handle(Request $request): void 43 | { 44 | } 45 | /** 46 | * This PROCESSES the response after all middleware requests have 47 | * been handled 48 | * 49 | * @param \Origin\Http\Request $request 50 | * @param \Origin\Http\Response $response 51 | * @return void 52 | */ 53 | public function process(Request $request, Response $response): void 54 | { 55 | } 56 | 57 | /** 58 | * This is the magic method. 59 | * 60 | * @param \Origin\Http\Request $request 61 | * @param \Origin\Http\Response $response 62 | * @param callable $next 63 | * @return \Origin\Http\Response $response 64 | */ 65 | public function __invoke(Request $request, Response $response, callable $next = null): Response 66 | { 67 | $this->executeHook('startup'); 68 | $this->handle($request); 69 | if ($next) { 70 | $response = $next($request, $response); 71 | } 72 | $this->process($request, $response); 73 | $this->executeHook('shutdown'); 74 | 75 | return $response; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Http/Middleware/MiddlewareRunner.php: -------------------------------------------------------------------------------- 1 | middlewareStack[] = $object; 44 | } 45 | 46 | /** 47 | * Adds a Middleware to the start of the queue 48 | * 49 | * @param Middleware $object 50 | * @return void 51 | */ 52 | public function prepend(Middleware $object): void 53 | { 54 | array_unshift($this->middlewareStack, $object); 55 | } 56 | 57 | /** 58 | * Returns a list of the middleware added to the runner 59 | * @return array 60 | */ 61 | public function list(): array 62 | { 63 | $out = []; 64 | foreach ($this->middlewareStack as $object) { 65 | $out[] = get_class($object); 66 | } 67 | 68 | return $out; 69 | } 70 | 71 | /** 72 | * Runs the middleware 73 | * 74 | * @param \Origin\Http\Request $request 75 | * @param \Origin\Http\Response $response 76 | * @return \Origin\Http\Response $response 77 | */ 78 | public function run(Request $request, Response $response): Response 79 | { 80 | return $this->__invoke($request, $response); 81 | } 82 | /** 83 | * Magic Method 84 | * 85 | * @param \Origin\Http\Request $request 86 | * @param \Origin\Http\Response $response 87 | * @return \Origin\Http\Response $response 88 | */ 89 | public function __invoke(Request $request, Response $response): Response 90 | { 91 | if (isset($this->middlewareStack[$this->current])) { 92 | $next = $this->middlewareStack[$this->current]; 93 | if ($next) { 94 | $this->current ++; 95 | 96 | return $next($request, $response, $this); 97 | } 98 | } 99 | 100 | return $response; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Http/Middleware/MinifyMiddleware.php: -------------------------------------------------------------------------------- 1 | true, 33 | /** 34 | * Minifies inline Javascript 35 | */ 36 | 'minifyJs' => true, 37 | /** 38 | * Minifies inline Styles 39 | */ 40 | 'minifyCss' => true 41 | ]; 42 | 43 | /** 44 | * @param \Origin\Http\Request $request 45 | * @param \Origin\Http\Response $response 46 | * @return void 47 | */ 48 | public function process(Request $request, Response $response): void 49 | { 50 | if ($response->contentType() === 'text/html' && $response->body()) { 51 | $this->minifyBody($response); 52 | } 53 | } 54 | 55 | /** 56 | * Handles the minfication 57 | * 58 | * @param \Origin\Http\Response $response 59 | * @return void 60 | */ 61 | private function minifyBody(Response $response): void 62 | { 63 | $response->body( 64 | Html::minify($response->body(), $this->config()) 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Http/Middleware/ProfilerMiddleware.php: -------------------------------------------------------------------------------- 1 | LOGS . '/profile.log' 37 | ]; 38 | 39 | /** 40 | * @var int 41 | */ 42 | private $startTime; 43 | 44 | /** 45 | * @var \Origin\Http\Request 46 | */ 47 | private $request; 48 | 49 | protected function initialize(): void 50 | { 51 | $this->startTime = microtime(true); 52 | } 53 | 54 | /** 55 | * This PROCESSES the response. Use this to make changes to the response. 56 | * 57 | * @param \Origin\Http\Request $request 58 | * @param \Origin\Http\Response $response 59 | * @return void 60 | */ 61 | public function process(Request $request, Response $response): void 62 | { 63 | $this->request = $request; 64 | } 65 | 66 | /** 67 | * Using destruct to make sure that logging happens after all middlewares run 68 | * and processed. Always check request object exists first. 69 | */ 70 | public function __destruct() 71 | { 72 | if ($this->request) { 73 | $profile = $this->logRequest($this->request); 74 | file_put_contents($this->config['log'], $profile . "\n", FILE_APPEND); 75 | } 76 | } 77 | 78 | private function logRequest(Request $request): string 79 | { 80 | return sprintf( 81 | '[%s] %s %s %s %s', 82 | date('Y-m-d H:i:s'), 83 | $request->method(), 84 | $request->url(), 85 | number_format(microtime(true) - $this->startTime, 4) . 's', 86 | $this->humanReadable(memory_get_peak_usage()) 87 | ); 88 | } 89 | 90 | /** 91 | * Gets the human readable 92 | * 93 | * @param integer $bytes 94 | * @return string 95 | */ 96 | private function humanReadable(int $bytes): string 97 | { 98 | $size = ['b', 'kb', 'mb', 'gb']; 99 | $factor = floor((strlen((string) $bytes) - 1) / 3); 100 | 101 | return sprintf('%.2f', $bytes / pow(1024, $factor)) . $size[$factor]; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Http/Middleware/SessionMiddleware.php: -------------------------------------------------------------------------------- 1 | session = $request->session(); 44 | $this->name = $this->session->name(); 45 | 46 | // gets the id from the cookie 47 | $this->id = $this->getSessionId($request); 48 | 49 | $this->session->id($this->id); 50 | $this->session->start(); 51 | 52 | /** 53 | * This removes the cookie header set by PHP session extension, so we can write our own cookie 54 | * if this becomes problematic or confusing due to PHP.ini settings, then remove. Call here 55 | */ 56 | if (! headers_sent()) { 57 | header_remove('Set-Cookie'); 58 | } 59 | } 60 | 61 | /** 62 | * @param \Origin\Http\Request $request 63 | * @param \Origin\Http\Response $response 64 | * @return void 65 | */ 66 | public function process(Request $request, Response $response): void 67 | { 68 | if (! $request->cookies($this->name) || $this->id !== $this->session->id()) { 69 | $this->addCookieToResponse($request, $response); 70 | } 71 | 72 | $this->session->close(); 73 | } 74 | 75 | private function getSessionId(Request $request): ?string 76 | { 77 | return $request->cookies($this->name); 78 | } 79 | 80 | /** 81 | * PHP Session has its own settings and will add cookie to the header, so anything set here will 82 | * be ingored in that case. To get it work use header_remove('Set-Cookie') but this means PHP_INI settings 83 | * will be ignored as well, so I have to think about this. 84 | * 85 | * TODO: think if should ignore PHP session settings from PHP.ini 86 | * 87 | * @param \Origin\Http\Request $request 88 | * @param \Origin\Http\Response $response 89 | * @return void 90 | */ 91 | private function addCookieToResponse(Request $request, Response $response): void 92 | { 93 | $response->cookie($this->name, $this->session->id(), [ 94 | 'expires' => 0, // Cookie expires after browser is closed 95 | 'encrypt' => false, // Encryption helps identify system 96 | 'secure' => $request->server('HTTPS') !== null, 97 | 'httpOnly' => true, 98 | 'sameSite' => 'strict' // TODO: test in different senarios 99 | ]); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Http/Session/SessionEngineInterface.php: -------------------------------------------------------------------------------- 1 | request()->cookies($name); 33 | } 34 | 35 | /** 36 | * Writes a cookie through response 37 | * 38 | * $cookie->write('key',$value); 39 | * $cookie->write('key',$value,['expires'=>'+1 month'); 40 | * 41 | * @param string $name 42 | * @param mixed $value 43 | * @param array $options The options keys are: 44 | * - expires: default:'+1 month'. a strtotime string e.g. +5 days, 2019-01-01 10:23:55 45 | * - encrypt: default:true. encrypt value 46 | * - path: default:'/' . Path on server 47 | * - domain: domains cookie will be available on 48 | * - secure: default:false. only send if through https 49 | * - httpOnly: default:false. only available to HTTP protocol not to javascript 50 | * @return void 51 | */ 52 | public function write(string $name, $value, array $options = []): void 53 | { 54 | $this->response()->cookie($name, $value, $options); 55 | } 56 | 57 | /** 58 | * Deletes a cookie 59 | * 60 | * @param string $name 61 | * @return void 62 | */ 63 | public function delete(string $name): void 64 | { 65 | $this->response()->cookie($name, '', ['expires' => '-60 minutes']); 66 | } 67 | 68 | /** 69 | * Checks if a cookie exists 70 | * 71 | * @param string $name 72 | * @return bool 73 | */ 74 | public function exists(string $name): bool 75 | { 76 | $cookies = $this->request()->cookies(); 77 | 78 | return isset($cookies[$name]); 79 | } 80 | 81 | /** 82 | * Deletes all cookies 83 | * 84 | * @return void 85 | */ 86 | public function destroy(): void 87 | { 88 | unset($_COOKIE); 89 | $_COOKIE = []; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Http/View/Helper/DateHelper.php: -------------------------------------------------------------------------------- 1 | $now) { 63 | $isFuture = true; 64 | $startTime = $now; 65 | $endTime = $time; 66 | } 67 | 68 | $difference = $endTime - $startTime; 69 | 70 | if ($difference < 1) { 71 | return 'just now'; 72 | } 73 | 74 | $conditions = [ 75 | 31104000 => 'year', // 12 * 30 * 24 * 60 * 60 76 | 2592000 => 'month', // 30 * 24 * 60 * 60 77 | 86400 => 'day', // 24 * 60 * 60 78 | 3600 => 'hour', // 60 * 60 79 | 60 => 'minute', 80 | 1 => 'second', 81 | ]; 82 | 83 | $result = null; 84 | foreach ($conditions as $seconds => $type) { 85 | $result = $difference / $seconds; 86 | if ($result >= 1) { 87 | $final = round($result); 88 | if ($final > 1) { 89 | $type .= 's'; // inflect 90 | } 91 | $result = "{$final} {$type}" . ($isFuture === false ? ' ago' : ''); 92 | break; 93 | } 94 | } 95 | 96 | return $result; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Http/View/Helper/FlashHelper.php: -------------------------------------------------------------------------------- 1 | [ 28 | 'error' => '', 29 | 'success' => '', 30 | 'warning' => '', 31 | 'info' => '', 32 | ], 33 | ]; 34 | 35 | public function initialize(array $config): void 36 | { 37 | $this->loadHelper('Session'); 38 | } 39 | 40 | public function messages() 41 | { 42 | if (! $this->Session->exists('Flash')) { 43 | return null; 44 | } 45 | $output = ''; 46 | 47 | foreach ($this->Session->read('Flash') as $message) { 48 | $template = $message['template']; 49 | $message = $message['message']; 50 | if (isset($this->config['templates'][$template])) { 51 | $output .= sprintf($this->config['templates'][$template], $message); 52 | } 53 | } 54 | $this->Session->delete('Flash'); 55 | 56 | return $output; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Http/View/Helper/HelperRegistry.php: -------------------------------------------------------------------------------- 1 | view = $view; 39 | } 40 | 41 | /** 42 | * Resolves the class name of a helper 43 | * 44 | * @param string $class 45 | * @return string|null $namespacedClass 46 | */ 47 | protected function className(string $class): ?string 48 | { 49 | return Resolver::className($class, 'View/Helper', null, 'Http'); 50 | } 51 | 52 | /** 53 | * Creates the object 54 | * 55 | * @param string $class 56 | * @param array $options 57 | * @return \Origin\Http\View\Helper\Helper 58 | */ 59 | protected function createObject(string $class, array $options = []): Helper 60 | { 61 | return new $class($this->view, $options); 62 | } 63 | 64 | /** 65 | * Throws an exception 66 | * 67 | * @param string $object 68 | * @return void 69 | */ 70 | protected function throwException(string $object): void 71 | { 72 | throw new MissingHelperException($object); 73 | } 74 | 75 | /** 76 | * Returns a view object 77 | * 78 | * @return View 79 | */ 80 | public function view(): View 81 | { 82 | return $this->view; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Http/View/Helper/NumberHelper.php: -------------------------------------------------------------------------------- 1 | currency(1024,'USD'); // $1,024 25 | * $number->currency(1024.00,'USD'); // $1,024.00 26 | * 27 | * @param string|float|integer $value 1234567.43 28 | * @param string $currency USD|EUR|GBP|AUD etc. 29 | * @param array $options additional options 30 | * @return string 31 | */ 32 | public function currency($value, string $currency = null, array $options = []): string 33 | { 34 | return Number::currency($value, $currency, $options); 35 | } 36 | 37 | /** 38 | * Formats a percent number 39 | * 40 | * $number->percent(50.55); // 50.55% 41 | * 42 | * @param string|int|float $value 1234567.43 43 | * @param integer $precision 44 | * @param array $options additional options 45 | * @return string 46 | */ 47 | public function percent($value, int $precision = 2, array $options = []): string 48 | { 49 | return Number::percent($value, $precision, $options); 50 | } 51 | 52 | /** 53 | * Formats a number with a specified level of precision 54 | * 55 | * $number->precision(1024.10101010,4); // 1,024.1010 56 | * 57 | * @param string|int|float $value 1234567.43 58 | * @param int $precision max number of decimal places to show 59 | * @param array $options additional options 60 | * @return string 61 | */ 62 | public function precision($value, int $precision = 2, array $options = []): string 63 | { 64 | return Number::precision($value, $precision, $options); 65 | } 66 | /** 67 | * Formats a number 68 | * 69 | * $number->format(1024.512); // 1,024.51 70 | * 71 | * @param string|int|float $value 72 | * @param array $options 73 | * - before: something to be shown before 74 | * - after: something to be added after 75 | * - thousands: the thousands seperator 76 | * - decimals: the decimals seperator 77 | * - places: how many decimal points to show 78 | * - negative: default:() 79 | * @return string 80 | */ 81 | public function format($value, array $options = []): string 82 | { 83 | return Number::format($value, $options); 84 | } 85 | 86 | /** 87 | * Formats bytes to human readable size 88 | * 89 | * @param integer $bytes 90 | * @param array $options 91 | * - precision: default:2 the level of precision 92 | * @return string 93 | */ 94 | public function readableSize(int $bytes, array $options = []): string 95 | { 96 | return Number::readableSize($bytes, $options); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Http/View/Helper/SessionHelper.php: -------------------------------------------------------------------------------- 1 | request()->session(); 40 | } 41 | 42 | /** 43 | * Reads a value from the session 44 | * 45 | * @param string $name 46 | * @return mixed 47 | */ 48 | public function read(string $name) 49 | { 50 | return $this->session()->read($name); 51 | } 52 | 53 | /** 54 | * Writes a session 55 | * 56 | * $session->write('key',$value); 57 | * $session->write('key',$value,strtotime('+1 day')); 58 | * 59 | * @param string $name 60 | * @param mixed $value 61 | * @return void 62 | */ 63 | public function write(string $name, $value): void 64 | { 65 | $this->session()->write($name, $value); 66 | } 67 | 68 | /** 69 | * Deletes a session 70 | * 71 | * @param string $name 72 | * @return void 73 | */ 74 | public function delete(string $name): void 75 | { 76 | $this->session()->delete($name); 77 | } 78 | 79 | /** 80 | * Checks if an item exists in session 81 | * 82 | * @param string $name 83 | * @return bool 84 | */ 85 | public function exists(string $name): bool 86 | { 87 | return $this->session()->exists($name); 88 | } 89 | 90 | /** 91 | * Clears the session 92 | * 93 | * @return void 94 | */ 95 | public function clear(): void 96 | { 97 | $this->session()->clear(); 98 | } 99 | 100 | /** 101 | * Deletes all sessions 102 | * 103 | * @return void 104 | */ 105 | public function destroy(): void 106 | { 107 | $this->session()->destroy(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Http/View/JsonView.php: -------------------------------------------------------------------------------- 1 | request = $controller->request(); 51 | $this->response = $controller->response(); 52 | $this->viewVars = $controller->viewVars(); 53 | $this->serialize = $controller->serialize(); 54 | } 55 | 56 | /** 57 | * Does the rendering 58 | * 59 | * @param mixed $data 60 | * @return string 61 | */ 62 | public function render($data = null): string 63 | { 64 | /** 65 | * If user requests JSON and serialize is set then use that 66 | */ 67 | if ($data === null && $this->request->respondAs() === 'json' && ! empty($this->serialize)) { 68 | $data = $this->serialize($this->serialize); 69 | } 70 | 71 | if (is_object($data) && method_exists($data, 'toJson')) { 72 | return $data->toJson(); 73 | } 74 | 75 | return json_encode($data); 76 | } 77 | 78 | /** 79 | * Serializes the data 80 | * 81 | * @param string|array $serialize 82 | * @return array 83 | */ 84 | private function serialize($serialize): array 85 | { 86 | $result = []; 87 | 88 | if (is_string($serialize)) { 89 | if (isset($this->viewVars[$serialize])) { 90 | $result = $this->toArray($this->viewVars[$serialize]); 91 | } 92 | 93 | return $result; 94 | } 95 | 96 | foreach ($serialize as $key) { 97 | if (isset($this->viewVars[$key])) { 98 | $result[$key] = $this->toArray($this->viewVars[$key]); 99 | } 100 | } 101 | 102 | return $result; 103 | } 104 | 105 | /** 106 | * Converts an object to an array 107 | * 108 | * @param mixed $mixed 109 | * @return mixed 110 | */ 111 | private function toArray($mixed) 112 | { 113 | if (is_object($mixed) && method_exists($mixed, 'toArray')) { 114 | $mixed = $mixed->toArray(); 115 | } 116 | 117 | return $mixed; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/Http/View/TemplateTrait.php: -------------------------------------------------------------------------------- 1 | templates(); 36 | * $this->templates(['input'=>'']); 37 | * 38 | * @param array|null $templates 39 | * @return string|array 40 | */ 41 | public function templates($templates = null) 42 | { 43 | if (! is_array($templates)) { 44 | return $this->templater()->get($templates); 45 | } 46 | $this->templater()->set($templates); 47 | 48 | return $templates; 49 | } 50 | 51 | /** 52 | * Gets the templater object if it exists, or it will create one 53 | * if the templates key is a string, then it will fetch the default templates since they 54 | * were overwritten and load templates from file 55 | * 56 | * @return \Origin\Http\View\Templater 57 | */ 58 | private function templater(): Templater 59 | { 60 | if (! isset($this->templater)) { 61 | $this->templater = new Templater(); 62 | $templates = $this->config('templates'); 63 | if (is_array($templates)) { 64 | $this->templater->set($templates); 65 | } else { 66 | $this->templater->set($this->defaultConfig['templates']); 67 | $this->templater->load($templates); 68 | } 69 | } 70 | 71 | return $this->templater; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Http/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/http", 3 | "description": "OriginPHP Http Package, provides the Controller and View functionality.", 4 | "type": "library", 5 | "keywords": [], 6 | "homepage": "https://www.originphp.com/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Jamiel Sharief", 11 | "email": "js@originphp.com" 12 | } 13 | ], 14 | "autoload": { 15 | "psr-4": { 16 | "Origin\\Http\\": "." 17 | } 18 | }, 19 | "require": { 20 | "php": ">=7.3.0", 21 | "originphp/cache": "^2.0", 22 | "originphp/configurable": "^2.0", 23 | "originphp/core": "^3.0", 24 | "originphp/defer": "^3.0", 25 | "originphp/i18n": "^3.0", 26 | "originphp/inflector": "^2.0", 27 | "originphp/log": "^2.0", 28 | "originphp/model": "^3.0", 29 | "originphp/security": "^2.0", 30 | "originphp/utility": "^3.0", 31 | "originphp/xml": "^2.0", 32 | "originphp/redis": "^3.0" 33 | }, 34 | "suggest": { 35 | "originphp/model": "To use the Auth component" 36 | }, 37 | "minimum-stability": "dev", 38 | "prefer-stable": true 39 | } 40 | -------------------------------------------------------------------------------- /src/I18n/Exception/LocaleNotAvailableException.php: -------------------------------------------------------------------------------- 1 | =7.3.0", 27 | "originphp/core": "^3.0", 28 | "originphp/utility": "^3.0" 29 | }, 30 | "minimum-stability": "dev", 31 | "prefer-stable": true 32 | } 33 | -------------------------------------------------------------------------------- /src/I18n/functions.php: -------------------------------------------------------------------------------- 1 | $user->id,'name'=>$user->name]); 22 | * @param string $string 23 | * @param array $vars array of vars e.g ['id'=>$user->id,'name'=>$user->name] 24 | * @return string|null formatted 25 | */ 26 | function __(string $string = null, array $vars = []): ?string 27 | { 28 | if ($string) { 29 | return I18n::translate($string, $vars); 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Job/Engine/BaseEngine.php: -------------------------------------------------------------------------------- 1 | config($config); 33 | $this->initialize($config); 34 | } 35 | 36 | public function initialize(array $config): void 37 | { 38 | } 39 | /** 40 | * Add a job to the queue 41 | * 42 | * @param \Origin\Job\Job $job 43 | * @param string $strtotime 44 | * @return bool 45 | */ 46 | abstract public function add(Job $job, string $strtotime = 'now'): bool; 47 | 48 | /** 49 | * Get the next job from the queue 50 | * 51 | * @param string $queue 52 | * @return \Origin\Job\Job|null 53 | */ 54 | abstract public function fetch(string $queue = 'default'): ?Job; 55 | 56 | /** 57 | * Deletes a job 58 | * 59 | * @param \Origin\Job\Job $job 60 | * @return bool 61 | */ 62 | abstract public function delete(Job $job): bool; 63 | 64 | /** 65 | * Handles a failed job 66 | * 67 | * @param \Origin\Job\Job $job 68 | * @return bool 69 | */ 70 | abstract public function fail(Job $job): bool; 71 | 72 | /** 73 | * Handles a successful job 74 | * 75 | * @param \Origin\Job\Job $job 76 | * @return bool 77 | */ 78 | abstract public function success(Job $job): bool; 79 | 80 | /** 81 | * Retries a job 82 | * 83 | * @param \Origin\Job\Job $job 84 | * @param integer $tries 85 | * @param string $strtotime 86 | * @return bool 87 | */ 88 | abstract public function retry(Job $job, int $tries, $strtotime = 'now'): bool; 89 | 90 | /** 91 | * Serializes a job 92 | * 93 | * @return string 94 | */ 95 | public function serialize(Job $job): string 96 | { 97 | $serialized = json_encode($job->serialize()); 98 | 99 | if (json_last_error() !== JSON_ERROR_NONE) { 100 | throw new Exception('JSON encoding data Error: ' . json_last_error()); 101 | } 102 | 103 | return $serialized; 104 | } 105 | 106 | /** 107 | * Returns a new job instance using serialized data 108 | * 109 | * @param string $data 110 | * @return \Origin\Job\Job 111 | */ 112 | public function deserialize(string $data): Job 113 | { 114 | $unserialized = json_decode($data, true); 115 | 116 | if (json_last_error() !== JSON_ERROR_NONE) { 117 | throw new Exception('JSON decoding data Error: ' . json_last_error()); 118 | } 119 | 120 | $className = $unserialized['className']; 121 | $job = new $className(); 122 | $job->deserialize($unserialized); 123 | 124 | return $job; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Job/Engine/RedisConnection.php: -------------------------------------------------------------------------------- 1 | > /etc/php/7.2/cli/php.ini 27 | */ 28 | 29 | use Redis; 30 | use RedisException; 31 | use Origin\Core\Exception\Exception; 32 | 33 | class RedisConnection 34 | { 35 | /** 36 | * Connects to Redis 37 | * 38 | * @param array $config 39 | * @return Redis 40 | */ 41 | public static function connect(array $config): Redis 42 | { 43 | deprecationWarning('RedisConnection has been deprecated used Redis::connect instead'); 44 | $config += [ 45 | 'host' => '127.0.0.1','port' => 6379,'password' => null,'timeout' => 0,'persistent' => true, // Faster!!! 46 | 'path' => null]; 47 | 48 | if (! extension_loaded('redis')) { 49 | throw new Exception('Redis extension not loaded.'); 50 | } 51 | $redis = new Redis(); 52 | $result = false; 53 | try { 54 | if (! empty($config['path'])) { 55 | $result = $redis->connect($config['path']); 56 | } elseif (! empty($config['persistent'])) { 57 | $id = ($config['persistent'] === true)?'origin-php':(string)$config['persistent']; 58 | $result = $redis->pconnect($config['host'], $config['port'], $config['timeout'], $id); 59 | } else { 60 | $result = $redis->connect($config['host'], $config['port'], $config['timeout']); 61 | } 62 | if ($result && isset($config['password'])) { 63 | $result = $redis->auth($config['password']); 64 | } 65 | } catch (RedisException $e) { 66 | $result = false; 67 | } 68 | 69 | if (! $result) { 70 | throw new Exception('Error connecting to Redis server.'); 71 | } 72 | 73 | return $redis; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Job/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Job/Model/Queue.php: -------------------------------------------------------------------------------- 1 | =7.3.0", 26 | "originphp/configurable": "^2.0", 27 | "originphp/core": "^3.0", 28 | "originphp/log": "^2.0", 29 | "originphp/model": "^3.0", 30 | "originphp/redis": "^3.0", 31 | "originphp/security": "^2.0" 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true 35 | } 36 | -------------------------------------------------------------------------------- /src/Lock/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Lock/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/lock", 3 | "description": "OriginPHP Lock", 4 | "type": "library", 5 | "keywords": [ 6 | "lock" 7 | ], 8 | "homepage": "https://www.originphp.com/", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Jamiel Sharief", 13 | "email": "js@originphp.com" 14 | } 15 | ], 16 | "autoload": { 17 | "psr-4": { 18 | "Origin\\Lock\\": "." 19 | } 20 | }, 21 | "require": { 22 | "php": ">=7.3.0", 23 | "originphp/defer": "^3.0" 24 | }, 25 | "minimum-stability": "dev", 26 | "prefer-stable": true 27 | } 28 | -------------------------------------------------------------------------------- /src/Mailbox/.preloadignore: -------------------------------------------------------------------------------- 1 | pipe.php -------------------------------------------------------------------------------- /src/Mailbox/Job/MailboxCleanJob.php: -------------------------------------------------------------------------------- 1 | loadModel('InboundEmail', ['className' => InboundEmail::class]); 37 | } 38 | 39 | protected function execute(Entity $message): void 40 | { 41 | $this->InboundEmail->delete($message); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Mailbox/Job/MailboxJob.php: -------------------------------------------------------------------------------- 1 | loadModel('InboundEmail', ['className' => InboundEmail::class]); 39 | } 40 | 41 | protected function execute(Entity $inboundEmail): void 42 | { 43 | $mail = new Mail($inboundEmail->message); 44 | $mailbox = Mailbox::mailbox($mail->recipients()); 45 | 46 | if ($mailbox) { 47 | (new $mailbox($inboundEmail))->dispatch(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Mailbox/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Mailbox/Model/ImapMessage.php: -------------------------------------------------------------------------------- 1 | validate('account', 'notBlank'); 34 | $this->validate('message_id', 'notBlank'); 35 | } 36 | 37 | public function findByAccount(string $account) 38 | { 39 | return $this->select(['id','message_id']) 40 | ->where(['account' => $account]) 41 | ->first(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Mailbox/Model/InboundEmail.php: -------------------------------------------------------------------------------- 1 | validate('message_id', 'notBlank'); 36 | $this->validate('message', 'notBlank'); 37 | 38 | # Register callbacks 39 | $this->afterCreate('scheduleJobs'); 40 | } 41 | 42 | /** 43 | * Make sure the message has not already been saved 44 | * 45 | * @param \Origin\Model\Entity $inboundEmail 46 | * @return boolean 47 | */ 48 | public function existsInDb(Entity $inboundEmail): bool 49 | { 50 | return ! $this->isUnique($inboundEmail, ['message_id','checksum']); 51 | } 52 | 53 | /** 54 | * This is a model callback 55 | * 56 | * @param \Origin\Model\Entity $inboundEmail 57 | * @param ArrayObject $options 58 | * @return void 59 | */ 60 | protected function scheduleJobs(Entity $inboundEmail, ArrayObject $options): void 61 | { 62 | # Dispatch for Processing 63 | (new MailboxJob())->dispatch($inboundEmail); 64 | 65 | # Schedule the mailbox cleaner 66 | 67 | (new MailboxCleanJob())->schedule(Config::read('App.mailboxKeepEmails'))->dispatch(); 68 | } 69 | 70 | /** 71 | * Creates a new entity from a message 72 | * 73 | * @param string $message 74 | * @return \Origin\Model\Entity 75 | */ 76 | public function fromMessage(string $message): Entity 77 | { 78 | $mail = new Mail($message); 79 | 80 | return $this->new([ 81 | 'message_id' => $mail->messageId, 82 | 'checksum' => Security::hash($message, ['type' => 'sha1']), 83 | 'message' => $message, 84 | 'status' => 'pending' 85 | ]); 86 | } 87 | 88 | /** 89 | * Sets the status of an inbound email 90 | * 91 | * @param integer $id 92 | * @param string $status The following statuses : 93 | * - pending: this is newly added 94 | * - processing: this is currently being run in a job and processed 95 | * - delivered: everything went okay 96 | * - bounced: this message was bounced 97 | * - failed: an error occured when processing 98 | * @return boolean 99 | */ 100 | public function setStatus(int $id, string $status): bool 101 | { 102 | return $this->updateColumn($id, 'status', $status); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Mailbox/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/mailbox", 3 | "description": "OriginPHP Mailbox", 4 | "type": "library", 5 | "keywords": [ 6 | "mailbox", 7 | "imap", 8 | "pop3" 9 | ], 10 | "homepage": "https://www.originphp.com/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Jamiel Sharief", 15 | "email": "js@originphp.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Origin\\Mailbox\\": "." 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.3.0", 25 | "originphp/configurable": "^2.0", 26 | "originphp/core": "^3.0", 27 | "originphp/job": "^3.0", 28 | "originphp/log": "^2.0", 29 | "originphp/model": "^3.0", 30 | "originphp/security": "^2.0", 31 | "originphp/service-object": "^3.0" 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true 35 | } 36 | -------------------------------------------------------------------------------- /src/Mailbox/pipe.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -q 2 | dispatch(); 31 | -------------------------------------------------------------------------------- /src/Mailer/Exception/MissingTemplateException.php: -------------------------------------------------------------------------------- 1 | onError('errorHandler'); 26 | } 27 | 28 | /** 29 | * Executes the MailerJob, the first param is class or object and then after that are arguments 30 | * 31 | * @return void 32 | */ 33 | public function execute(): void 34 | { 35 | $arguments = func_get_args(); 36 | 37 | /** 38 | * Temporary backwards comptability to prevent queued jobs from breaking 39 | * @deprecated this will be depcreated 40 | */ 41 | if (isset($arguments[0]) && is_array($arguments[0])) { 42 | $mailer = $arguments['mailer']; 43 | $arguments = $arguments['arguments']; 44 | } else { 45 | $mailer = array_shift($arguments); 46 | if (! is_object($mailer)) { 47 | $mailer = new $mailer(); 48 | } 49 | } 50 | 51 | $mailer->dispatch(...$arguments); 52 | } 53 | 54 | public function errorHandler(\Exception $exception): void 55 | { 56 | $this->retry(['wait' => '+30 minutes','limit' => 3]); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Mailer/Message.php: -------------------------------------------------------------------------------- 1 | header = $header; 25 | $this->body = $body; 26 | } 27 | 28 | /** 29 | * Gets the message header 30 | * 31 | * @return string 32 | */ 33 | public function header(): string 34 | { 35 | return $this->header; 36 | } 37 | 38 | /** 39 | * Gets the message body 40 | * 41 | * @return string 42 | */ 43 | public function body(): string 44 | { 45 | return $this->body; 46 | } 47 | 48 | /** 49 | * Returns the full message (header and body) 50 | * 51 | * @return string 52 | */ 53 | public function message(): string 54 | { 55 | return $this->header . "\r\n\r\n" . $this->body; 56 | } 57 | 58 | public function __toString() 59 | { 60 | return $this->message(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Mailer/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/mailer", 3 | "description": "OriginPHP Mailer", 4 | "type": "library", 5 | "keywords": [ 6 | "mailer", 7 | "smtp", 8 | "email" 9 | ], 10 | "homepage": "https://www.originphp.com/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Jamiel Sharief", 15 | "email": "js@originphp.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Origin\\Mailer\\": "." 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.3.0", 25 | "originphp/core": "^3.0", 26 | "originphp/email": "^2.0", 27 | "originphp/html": "^2.0", 28 | "originphp/inflector": "^2.0", 29 | "originphp/job": "^3.0" 30 | }, 31 | "minimum-stability": "dev", 32 | "prefer-stable": true 33 | } 34 | -------------------------------------------------------------------------------- /src/Migration/Exception/IrreversibleMigrationException.php: -------------------------------------------------------------------------------- 1 | statements = array_filter((array) $mixed); 29 | } 30 | 31 | /** 32 | * Gets the statements for this 33 | * 34 | * @return array 35 | */ 36 | public function statements(): array 37 | { 38 | return $this->statements; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Migration/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/migration", 3 | "description": "OriginPHP Migration", 4 | "type": "library", 5 | "keywords": [ 6 | "migration" 7 | ], 8 | "homepage": "https://www.originphp.com/", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Jamiel Sharief", 13 | "email": "js@originphp.com" 14 | } 15 | ], 16 | "autoload": { 17 | "psr-4": { 18 | "Origin\\Migration\\": "." 19 | } 20 | }, 21 | "require": { 22 | "php": ">=7.3.0", 23 | "originphp/core": "^3.0", 24 | "originphp/inflector": "^2.0", 25 | "originphp/model": "^3.0" 26 | }, 27 | "minimum-stability": "dev", 28 | "prefer-stable": true 29 | } 30 | -------------------------------------------------------------------------------- /src/Model/Concern/Delocalizable.php: -------------------------------------------------------------------------------- 1 | beforeValidate('delocalize'); 31 | } 32 | 33 | /** 34 | * Delocalize the values in the entity 35 | * This is a beforeValidate callback, so it must return true; 36 | * 37 | * @param \Origin\Model\Entity $entity 38 | * @return bool 39 | */ 40 | public function delocalize(Entity $entity): bool 41 | { 42 | $columns = $this->schema()['columns']; 43 | foreach ($entity->dirty() as $field) { 44 | $value = $entity->get($field); 45 | if ($value && isset($columns[$field])) { 46 | $value = $this->processField($columns[$field]['type'], $value); 47 | 48 | // Restore value incase of invalid value etc 49 | if ($value === null) { 50 | $value = $entity->get($field); 51 | } 52 | $entity->set($field, $value); 53 | } 54 | } 55 | 56 | return true; 57 | } 58 | 59 | /** 60 | * Parses values 61 | * 62 | * @param string $type 63 | * @param string $value 64 | * @return mixed 65 | */ 66 | private function processField(string $type, $value) 67 | { 68 | if ($type === 'date') { 69 | return Date::parseDate($value); 70 | } 71 | if ($type === 'datetime') { 72 | return Date::parseDateTime($value); 73 | } 74 | if ($type === 'time') { 75 | $value = Date::parseTime($value); 76 | } 77 | if (in_array($type, ['decimal','integer','float','bigint'])) { 78 | return Number::parse($value); 79 | } 80 | 81 | return $value; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Model/Concern/Timestampable.php: -------------------------------------------------------------------------------- 1 | timestampConfig([ 27 | * 'created' => 'created_at', 28 | * 'modified' => 'modified_at' 29 | * ]); 30 | */ 31 | trait Timestampable 32 | { 33 | /** 34 | * @var string 35 | */ 36 | private $timestamp; 37 | 38 | /** 39 | * @var string 40 | */ 41 | private $createdField; 42 | 43 | /** 44 | * @var string 45 | */ 46 | private $modifiedField; 47 | 48 | public function initializeTimestampable() 49 | { 50 | if (! isset($this->createdField)) { 51 | $this->createdField = 'created'; 52 | } 53 | if (! isset($this->modifiedField)) { 54 | $this->modifiedField = 'modified'; 55 | } 56 | $this->timestamp = date('Y-m-d H:i:s'); 57 | $this->beforeCreate('timestambleBeforeCreate'); 58 | $this->beforeSave('timestambleBeforeSave'); 59 | } 60 | 61 | /** 62 | * Before create callback 63 | * 64 | * @param \Origin\Model\Entity $entity 65 | * @param ArrayObject $options 66 | * @return void 67 | */ 68 | protected function timestambleBeforeCreate(Entity $entity, ArrayObject $options): void 69 | { 70 | $this->setTimestamp($entity, $this->createdField); 71 | } 72 | 73 | /** 74 | * Before save callback 75 | * 76 | * @param \Origin\Model\Entity $entity 77 | * @param ArrayObject $options 78 | * @return void 79 | */ 80 | protected function timestambleBeforeSave(Entity $entity, ArrayObject $options): void 81 | { 82 | $this->setTimestamp($entity, $this->modifiedField); 83 | } 84 | 85 | /** 86 | * Sets the timestamp 87 | * 88 | * @param \Origin\Model\Entity $entity 89 | * @param string $field 90 | * @return void 91 | */ 92 | private function setTimestamp(Entity $entity, string $field): void 93 | { 94 | if (! $this->hasField($field)) { 95 | return; 96 | } 97 | if (empty($entity->$field) || ! in_array($field, $entity->dirty())) { 98 | $entity->set($field, $this->timestamp); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Model/Engine/MysqlEngine.php: -------------------------------------------------------------------------------- 1 | execute('SHOW TABLES;'); 55 | $results = $this->fetchAll(); 56 | if ($results) { 57 | foreach ($results as $value) { 58 | $tables[] = current($value); 59 | } 60 | } 61 | 62 | return $tables; 63 | } 64 | 65 | /** 66 | * Gets a list of tables 67 | * 68 | * @return array 69 | */ 70 | public function databases(): array 71 | { 72 | $out = []; 73 | $this->execute('SHOW DATABASES;'); 74 | $results = $this->fetchAll(); 75 | if ($results) { 76 | foreach ($results as $value) { 77 | $out[] = current($value); 78 | } 79 | } 80 | 81 | return $out; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Model/Engine/PostgresEngine.php: -------------------------------------------------------------------------------- 1 | execute($sql)) { 60 | $list = $this->fetchList(); 61 | if ($list) { 62 | $out = $list; 63 | } 64 | sort($out); // why sort with db server 65 | } 66 | 67 | return $out; 68 | } 69 | 70 | /** 71 | * Returns a list of databases 72 | * 73 | * @return array 74 | */ 75 | public function databases(): array 76 | { 77 | $sql = 'SELECT datname FROM pg_database WHERE datistemplate = false;'; 78 | $out = []; 79 | if ($this->execute($sql)) { 80 | $list = $this->fetchList(); 81 | if ($list) { 82 | $out = $list; 83 | } 84 | } 85 | 86 | return $out; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Model/Engine/SqliteEngine.php: -------------------------------------------------------------------------------- 1 | execute('SELECT name from sqlite_master WHERE type = "table"'); 57 | $results = $this->fetchList(); 58 | if ($results) { 59 | sort($results); 60 | $key = array_search('sqlite_sequence', $results); 61 | unset($results[$key]); 62 | $results = array_values($results); 63 | } 64 | 65 | return $results ?? $tables; 66 | } 67 | 68 | /** 69 | * Gets a list of databases 70 | * 71 | * @return array 72 | */ 73 | public function databases(): array 74 | { 75 | return []; 76 | } 77 | 78 | /** 79 | * Creates and handles a DB transaction with the option to disable foreign key constraints. 80 | * 81 | * @example 82 | * 83 | * $connection->transaction(function ($connection) use ($statements) { 84 | * $this->processStatements($connection,$statements); 85 | * }); 86 | * 87 | * @param callable $callback 88 | * @param boolean $disbleForeignKeyConstraints 89 | * @return mixed 90 | */ 91 | public function transaction(callable $callback, bool $disbleForeignKeyConstraints = false) 92 | { 93 | if ($disbleForeignKeyConstraints) { 94 | $this->disableForeignKeyConstraints(); 95 | } 96 | 97 | $this->begin(); 98 | 99 | try { 100 | $result = $callback($this); 101 | } catch (Exception $exception) { 102 | $this->rollback(); 103 | if ($disbleForeignKeyConstraints) { 104 | $this->enableForeignKeyConstraints(); 105 | } 106 | 107 | throw $exception; 108 | } 109 | 110 | if ($result === false) { 111 | $this->rollback(); 112 | } else { 113 | $this->commit(); 114 | } 115 | 116 | if ($disbleForeignKeyConstraints) { 117 | $this->enableForeignKeyConstraints(); 118 | } 119 | 120 | return $result; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Model/Exception/ConnectionException.php: -------------------------------------------------------------------------------- 1 | entity = $entity; 30 | 31 | $message = $this->formatMessage($entity, $message); 32 | 33 | parent::__construct($message, $code); 34 | } 35 | 36 | /** 37 | * @return \Origin\Model\Entity 38 | */ 39 | public function getEntity(): Entity 40 | { 41 | return $this->entity; 42 | } 43 | 44 | /** 45 | * @param \Origin\Model\Entity $entity 46 | * @param string $message 47 | * @return string 48 | */ 49 | private function formatMessage(Entity $entity, string $message): string 50 | { 51 | $out = []; 52 | foreach ($entity->errors() as $field => $errors) { 53 | foreach ($errors as $error) { 54 | $out[] = "{$field}: {$error}"; 55 | } 56 | } 57 | 58 | return sprintf( 59 | '%s %s failure. The following errors were found (%s).', 60 | $entity->name(), 61 | $message, 62 | implode(', ', $out), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Model/Exception/ValidatorException.php: -------------------------------------------------------------------------------- 1 | Model = $model; 32 | } 33 | 34 | /** 35 | * Runs a batch insert query. Note there is a limit on number of placeholderes. 36 | * 37 | * @param array $records 38 | * @param array $options The following options keys are supported 39 | * - transactions: default true. To wrap insert in a transaction begin/commit 40 | * - table: default is the Model table. 41 | * @return bool|array|null 42 | */ 43 | 44 | public function execute(array $records, array $options = []) 45 | { 46 | $options += ['transaction' => true,'table' => $this->Model->table()]; 47 | $fields = $questionMarks = $values = $buffer = []; 48 | 49 | if (empty($records)) { 50 | throw new InvalidArgumentException('No records'); 51 | } 52 | 53 | $firstKey = array_key_first($records); 54 | 55 | $fields = array_keys($records[$firstKey]); 56 | $questionMarks = array_fill(0, count($fields), '?'); 57 | 58 | foreach ($records as $record) { 59 | $values = array_merge($values, array_values($record)); 60 | $buffer[] = '(' . implode(', ', $questionMarks) . ')'; 61 | } 62 | 63 | $fields = implode(', ', $fields); 64 | $buffer = implode(', ', $buffer); 65 | 66 | if ($options['transaction']) { 67 | $this->Model->beginTransaction(); 68 | } 69 | 70 | $this->executeQuery("INSERT INTO {$options['table']} ({$fields}) VALUES {$buffer}", $values); 71 | 72 | if ($options['transaction']) { 73 | $this->Model->commitTransaction(); 74 | } 75 | 76 | return true; 77 | } 78 | 79 | protected function executeQuery(string $query, array $values = []) 80 | { 81 | return $this->Model->query($query, $values); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Model/Query/QueryObject.php: -------------------------------------------------------------------------------- 1 | Article = $Article; 30 | * } 31 | * 32 | * public function execute() : Collection 33 | * { 34 | * .... 35 | * } 36 | * } 37 | * 38 | * Example 39 | * 40 | * $result = (new BooleanSearchQuery($this->Article))->execute('how to'); 41 | * 42 | * @see https://www.martinfowler.com/eaaCatalog/queryObject.html 43 | */ 44 | class QueryObject 45 | { 46 | use HookTrait; 47 | 48 | public function __construct() 49 | { 50 | $this->executeHook('initialize', func_get_args()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Model/Repository/Repository.php: -------------------------------------------------------------------------------- 1 | modelClass === null) { 41 | list($namespace, $class) = namespaceSplit(get_class($this)); 42 | $this->modelClass = Inflector::singular(substr($class, 0, -10)); 43 | } 44 | /** 45 | * Models are dependcies and should not be lazyloaded. 46 | */ 47 | $this->loadModel($this->modelClass); 48 | $this->executeHook('initialize', func_get_args()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Model/Seed.php: -------------------------------------------------------------------------------- 1 | executeHook('initialize'); 26 | } 27 | /** 28 | * Creates the SQL statements for inserting 29 | * 30 | * @param \Origin\Model\Connection $connection 31 | * @return array 32 | */ 33 | public function insertSql(Connection $connection): array 34 | { 35 | $out = []; 36 | $properties = get_object_vars($this); 37 | foreach (array_keys($properties) as $table) { 38 | foreach ($this->$table as $record) { 39 | $builder = $connection->queryBuilder($table); 40 | $sql = $builder->insert($record)->write(); 41 | $out[] = [$sql,$builder->getValues()]; 42 | } 43 | } 44 | 45 | return $out; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Model/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/model", 3 | "description": "OriginPHP ORM", 4 | "type": "library", 5 | "keywords": [ 6 | "orm" 7 | ], 8 | "homepage": "https://www.originphp.com/", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Jamiel Sharief", 13 | "email": "js@originphp.com" 14 | } 15 | ], 16 | "autoload": { 17 | "psr-4": { 18 | "Origin\\Model\\": "." 19 | } 20 | }, 21 | "require": { 22 | "php": ">=7.3.0", 23 | "originphp/cache": "^2.0", 24 | "originphp/configurable": "^2.0", 25 | "originphp/core": "^3.0", 26 | "originphp/inflector": "^2.0", 27 | "originphp/log": "^2.0", 28 | "originphp/utility": "^3.0", 29 | "originphp/validation": "^2.0", 30 | "originphp/xml": "^2.0" 31 | }, 32 | "minimum-stability": "dev", 33 | "prefer-stable": true 34 | } 35 | -------------------------------------------------------------------------------- /src/Process/BaseProcess.php: -------------------------------------------------------------------------------- 1 | =7.3.0" 24 | }, 25 | "minimum-stability": "dev", 26 | "prefer-stable": true 27 | } 28 | -------------------------------------------------------------------------------- /src/Publisher/Exception/PublisherException.php: -------------------------------------------------------------------------------- 1 | executeHook('initialize'); 27 | } 28 | 29 | /** 30 | * Dispatches a method 31 | * 32 | * @param string $method 33 | * @param array $arguments 34 | * @return boolean 35 | */ 36 | public function dispatch(string $method, array $arguments = []): bool 37 | { 38 | $this->executeHook('startup'); 39 | if ($this->executeHook($method, $arguments) === false) { 40 | return false; 41 | } 42 | $this->executeHook('shutdown'); 43 | 44 | return true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Publisher/ListenerJob.php: -------------------------------------------------------------------------------- 1 | onError('errorHandler'); 26 | } 27 | 28 | protected function execute(string $className, string $method, array $args = []) 29 | { 30 | ( new Publisher())->dispatch(new $className(), $method, $args); 31 | } 32 | 33 | protected function errorHandler(\Exception $exception): void 34 | { 35 | $this->retry(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Publisher/PublisherTrait.php: -------------------------------------------------------------------------------- 1 | publisherInstance)) { 32 | $this->publisherInstance = new Publisher(); 33 | } 34 | 35 | return $this->publisherInstance; 36 | } 37 | 38 | /** 39 | * Subscribes an object 40 | * 41 | * @param object|string $object 42 | * @param array $options You can pass the following option keys 43 | * - on: an array of methods that this object that will listen to, by default it will listen to all 44 | * - queue: true or name of queue connection. All will go into 45 | * @return bool 46 | */ 47 | public function subscribe($object, array $options = []): bool 48 | { 49 | return $this->getPublisher()->subscribe($object, $options); 50 | } 51 | 52 | /** 53 | * Publish an event with any number of arguments 54 | * Example: 55 | * 56 | * $this->publish('cancelCustomerOrder',$order, $user); 57 | * 58 | * @param string $event e.g. 'cancelCustomerOrder' 59 | * @param mixed ...$args argument or multiple arguments that will be passed to event 60 | * @return void 61 | */ 62 | public function publish(string $event, ...$args): void 63 | { 64 | $this->getPublisher()->publish($event, ...$args); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Publisher/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/publisher", 3 | "description": "OriginPHP Publisher", 4 | "type": "library", 5 | "keywords": [ 6 | "publisher", 7 | "subscribe", 8 | "pub-sub" 9 | ], 10 | "homepage": "https://www.originphp.com/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Jamiel Sharief", 15 | "email": "js@originphp.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Origin\\Publisher\\": "." 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.3.0", 25 | "originphp/core": "^3.0" 26 | }, 27 | "suggest": { 28 | "originphp/job": "If you are going to queue events" 29 | }, 30 | "minimum-stability": "dev", 31 | "prefer-stable": true 32 | } 33 | -------------------------------------------------------------------------------- /src/Redis/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Redis/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/redis", 3 | "description": "OriginPHP Redis", 4 | "type": "library", 5 | "keywords": [], 6 | "homepage": "https://www.originphp.com/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Jamiel Sharief", 11 | "email": "js@originphp.com" 12 | } 13 | ], 14 | "autoload": { 15 | "psr-4": { 16 | "Origin\\Redis\\": "." 17 | } 18 | }, 19 | "require": { 20 | "php": ">=7.3.0" 21 | }, 22 | "minimum-stability": "dev", 23 | "prefer-stable": true 24 | } 25 | -------------------------------------------------------------------------------- /src/Schedule/Command/ScheduleRunCommand.php: -------------------------------------------------------------------------------- 1 | addOption('directory', [ 29 | 'description' => 'The directory where the tasks files are' 30 | ]); 31 | 32 | $this->addOption('id', [ 33 | 'description' => 'A specific event ID that should be run' 34 | ]); 35 | } 36 | 37 | /** 38 | * @return void 39 | */ 40 | protected function execute(): void 41 | { 42 | $path = $this->options('directory') ?: Schedule::config('path'); 43 | 44 | if (is_null($path)) { 45 | $path = (defined('ROOT') ? ROOT : getcwd()) . '/app/Task'; 46 | } 47 | 48 | try { 49 | Schedule::run($path, $this->options('id')); 50 | } catch (ScheduleException $exception) { 51 | $this->throwError($exception->getMessage()); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Schedule/Exception/ScheduleException.php: -------------------------------------------------------------------------------- 1 | schedule = new Schedule($this); 45 | 46 | list($namespace, $name) = namespaceSplit(get_class($this)); 47 | $this->name = $this->name ?? $name; 48 | 49 | $this->executeHook('initialize', func_get_args()); 50 | } 51 | 52 | /** 53 | * Schedule your task here 54 | * 55 | * @param \Origin\Schedule\Schedule $schedule 56 | * @return void 57 | */ 58 | abstract protected function handle(Schedule $schedule): void; 59 | 60 | /** 61 | * Invokes this task 62 | * 63 | * @return void 64 | */ 65 | public function __invoke(): void 66 | { 67 | $this->handle($this->schedule); 68 | } 69 | 70 | /** 71 | * Dispatches the Task 72 | * 73 | * @return void 74 | */ 75 | public function dispatch(): void 76 | { 77 | $this->executeHook('startup'); 78 | $this->handle($this->schedule); 79 | $this->schedule->dispatch(); 80 | $this->executeHook('shutdown'); 81 | } 82 | 83 | /** 84 | * Gets the schedule object for this task 85 | * 86 | * @return \Origin\Schedule\Schedule 87 | */ 88 | public function schedule(): Schedule 89 | { 90 | return $this->schedule; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Schedule/bin/schedule:run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | name('run'); 37 | $app->description([ 38 | 'Runs scheduled tasks' 39 | ]); 40 | $app->addCommand('run', ScheduleRunCommand::class); 41 | exit($app->run()); 42 | -------------------------------------------------------------------------------- /src/Schedule/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/schedule", 3 | "description": "OriginPHP Task Scheduler", 4 | "type": "library", 5 | "keywords": [ 6 | "cron", 7 | "task", 8 | "schedule" 9 | ], 10 | "homepage": "https://www.originphp.com/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Jamiel Sharief", 15 | "email": "js@originphp.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Origin\\Schedule\\": "." 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.3.0", 25 | "originphp/core": "^3.20", 26 | "originphp/configurable": "^2.0", 27 | "originphp/console": "^3.20", 28 | "originphp/process": "^3.20" 29 | }, 30 | "suggest": { 31 | "originphp/job": "If you are going to queue events" 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true, 35 | "bin": [ 36 | "bin/schedule:run" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/Service/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Service/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/service-object", 3 | "description": "OriginPHP Service Objects", 4 | "type": "library", 5 | "keywords": [ 6 | "service", 7 | "services", 8 | "service object" 9 | ], 10 | "homepage": "https://www.originphp.com/docs/services/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Jamiel Sharief", 15 | "email": "js@originphp.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Origin\\Service\\": "." 21 | } 22 | }, 23 | "require": { 24 | "php": ">=7.3.0", 25 | "originphp/core": "^3.0" 26 | }, 27 | "minimum-stability": "dev", 28 | "prefer-stable": true 29 | } 30 | -------------------------------------------------------------------------------- /src/Ssh/RemoteFile.php: -------------------------------------------------------------------------------- 1 | =7.3.0", 24 | "ext-ssh2": "*" 25 | }, 26 | "minimum-stability": "dev", 27 | "prefer-stable": true 28 | } 29 | -------------------------------------------------------------------------------- /src/TestSuite/Exception/ConsoleInputRequiredException.php: -------------------------------------------------------------------------------- 1 | input = $input; 29 | } 30 | 31 | public function read(): ?string 32 | { 33 | $index = $this->currentIndex(); 34 | 35 | if (! isset($this->input[$index])) { 36 | throw new ConsoleInputRequiredException('Console input is requesting more input that what was provided'); 37 | } 38 | 39 | return $this->input[$index]; 40 | } 41 | 42 | private function currentIndex(): int 43 | { 44 | $this->current ++; 45 | 46 | return $this->current; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/TestSuite/Stub/ConsoleOutput.php: -------------------------------------------------------------------------------- 1 | level === self::QUIET || ($level === self::VERBOSE && $this->level === self::NORMAL)) { 32 | return strlen($buffer); 33 | } 34 | 35 | $this->buffer .= $buffer; 36 | 37 | return strlen($buffer); 38 | } 39 | 40 | public function read() 41 | { 42 | return $this->buffer; 43 | } 44 | } 45 | 46 | /* 47 | class ConsoleOutput extends BaseConsoleOutput 48 | { 49 | protected $mode = SELF::RAW; 50 | 51 | protected $buffer = ''; 52 | 53 | protected function fwrite(string $data) : int 54 | { 55 | $this->buffer .= $data; 56 | 57 | return strlen($data); 58 | } 59 | 60 | public function read() 61 | { 62 | return $this->buffer; 63 | } 64 | } 65 | 66 | */ 67 | -------------------------------------------------------------------------------- /src/TestSuite/Stub/Request.php: -------------------------------------------------------------------------------- 1 | session = $session; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/TestSuite/TestTrait.php: -------------------------------------------------------------------------------- 1 | $method(); 32 | } 33 | 34 | return call_user_func_array([$this, $method], $args); 35 | } 36 | 37 | public function getProperty(string $property) 38 | { 39 | if (isset($this->$property)) { 40 | return $this->$property; 41 | } 42 | } 43 | 44 | public function setProperty(string $property, $value) 45 | { 46 | $this->$property = $value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/TestSuite/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/test-suite", 3 | "description": "OriginPHP Test Suite", 4 | "type": "library", 5 | "keywords": [ 6 | "test suite", 7 | "integration testing" 8 | ], 9 | "homepage": "https://www.originphp.com/", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Jamiel Sharief", 14 | "email": "js@originphp.com" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-4": { 19 | "Origin\\TestSuite\\": "." 20 | } 21 | }, 22 | "require": { 23 | "php": ">=7.3.0", 24 | "originphp/core": "^3.0", 25 | "originphp/inflector": "^2.0", 26 | "originphp/model": "^3.0", 27 | "phpunit/phpunit": "^9.2" 28 | }, 29 | "suggest": { 30 | "originphp/http": "If you are going to use integration testing for web based applications", 31 | "originphp/console": "If you are going to use integration testing for console based commands", 32 | "originphp/job": "If you want to test queued jobs" 33 | }, 34 | "minimum-stability": "dev", 35 | "prefer-stable": true 36 | } 37 | -------------------------------------------------------------------------------- /src/Utility/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2021 Jamiel Sharief. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/Utility/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "originphp/utility", 3 | "description": "OriginPHP Utility", 4 | "type": "library", 5 | "keywords": [ 6 | "date", 7 | "time" 8 | ], 9 | "homepage": "https://www.originphp.com/", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Jamiel Sharief", 14 | "email": "js@originphp.com" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-4": { 19 | "Origin\\Utility\\": "." 20 | } 21 | }, 22 | "require": { 23 | "php": ">=7.3.0" 24 | }, 25 | "minimum-stability": "dev", 26 | "prefer-stable": true 27 | } 28 | --------------------------------------------------------------------------------