├── tests ├── Yaml │ ├── Fixtures │ │ ├── embededPhp.yml │ │ ├── sfObjects.yml │ │ ├── index.yml │ │ ├── YtsErrorTests.yml │ │ ├── sfQuotes.yml │ │ ├── YtsNullsAndEmpties.yml │ │ ├── YtsAnchorAlias.yml │ │ ├── YtsBlockMapping.yml │ │ ├── sfMergeKey.yml │ │ ├── unindentedCollections.yml │ │ ├── sfComments.yml │ │ ├── YtsFlowCollections.yml │ │ ├── YtsDocumentSeparator.yml │ │ ├── escapedCharacters.yml │ │ ├── sfTests.yml │ │ ├── sfCompact.yml │ │ ├── YtsBasicTests.yml │ │ ├── YtsFoldedScalars.yml │ │ └── YtsTypeTransfers.yml │ ├── LICENSE-Apache │ ├── LICENSE-MIT │ ├── DummyClass.php │ └── ParseExceptionTest.php └── Security │ └── HashTest.php ├── contributors.md ├── .gitignore ├── .travis.yml ├── sonar-project.properties ├── src ├── Router │ ├── Notes.txt │ ├── LICENSE-Apache │ ├── LICENSE-Flask │ └── Router.php ├── Loader │ ├── LICENSE-Apache │ ├── Notes.txt │ └── LICENSE-MIT ├── Yaml │ ├── LICENSE-Apache │ ├── Notes.txt │ ├── LICENSE-MIT │ ├── ParseException.php │ └── Dumper.php ├── Views │ ├── ViewGenerator.php │ ├── ViewEngineBase.php │ └── Views.php ├── Facades │ └── FacadeBase.php ├── Interfaces │ ├── IInterface.php │ ├── WebPage.php │ └── Console.php ├── Tasks │ ├── TaskBase.php │ └── Tasks.php ├── Containers │ ├── SingletonContainer.php │ ├── IocContainer.php │ └── BindableContainer.php ├── Framework │ ├── Tasks │ │ ├── HelpTask.php │ │ ├── ConsoleTask.php │ │ ├── CleanTask.php │ │ └── ServeTask.php │ └── ApplicationBase.php ├── Generators │ ├── GeneratorBase.php │ ├── GeneratorRegistry.php │ └── GenerateTask.php ├── Events │ ├── Events.php │ ├── EventGenerator.php │ └── Delegate.php ├── Helpers │ └── Runtime.php ├── Config │ ├── ConfigGenerator.php │ └── Config.php ├── Mvc │ └── Controller.php ├── Testing │ ├── TestsTask.php │ └── Testing.php ├── Documentor │ └── DocumentGenerator.php ├── Code │ ├── CompileGenerator.php │ ├── Minifier.php │ ├── AnnotationManager.php │ └── TokenStream.php ├── Objects │ ├── FormHandler.php │ ├── Binder.php │ ├── BufferedUpdateCollection.php │ └── CommandInterpreter.php └── Cli │ └── Application.php ├── LICENSE ├── composer.json ├── unittests.php └── README.md /tests/Yaml/Fixtures/embededPhp.yml: -------------------------------------------------------------------------------- 1 | value: 2 | -------------------------------------------------------------------------------- /contributors.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * Eser Ozvataf (Framework and Lead Developer, http://eser.ozvataf.com/) 4 | * Savas Koc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## PHPStorm 2 | .idea/ 3 | 4 | ## Windows 5 | Thumbs.db 6 | Desktop.ini 7 | 8 | ## Mac OS X 9 | .DS_Store 10 | 11 | ## Vim 12 | .*.sw? 13 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/sfObjects.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Objects 3 | brief: > 4 | Comments at the end of a line 5 | yaml: | 6 | ex1: "foo # bar" 7 | ex2: "foo # bar" # comment 8 | ex3: 'foo # bar' # comment 9 | ex4: foo # comment 10 | php: | 11 | array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') 12 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/index.yml: -------------------------------------------------------------------------------- 1 | - escapedCharacters 2 | - sfComments 3 | - sfCompact 4 | - sfTests 5 | - sfObjects 6 | - sfMergeKey 7 | - sfQuotes 8 | - YtsAnchorAlias 9 | - YtsBasicTests 10 | - YtsBlockMapping 11 | - YtsDocumentSeparator 12 | - YtsErrorTests 13 | - YtsFlowCollections 14 | - YtsFoldedScalars 15 | - YtsNullsAndEmpties 16 | - YtsSpecificationExamples 17 | - YtsTypeTransfers 18 | - unindentedCollections 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | - hhvm 6 | 7 | before_install: 8 | - pyrus install pear/PHP_CodeSniffer 9 | - phpenv rehash 10 | 11 | script: 12 | - php unittests.php 13 | # - phpcs --standard=PSR2 --report=info . 14 | - output=$(phpcs --standard=PSR2 --report=emacs .); if [[ $output ]]; then while read -r line; do echo -e "\e[00;31m$line\e[00m"; done <<< "$output"; false; fi; 15 | 16 | matrix: 17 | allow_failures: 18 | - php: hhvm 19 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # Required metadata 2 | sonar.projectKey=org.codehaus.sonar:scabbiafw 3 | sonar.projectName=Scabbia2 PHP Framework 4 | sonar.projectVersion=2.0 5 | 6 | # Comma-separated paths to directories with sources (required) 7 | sonar.sources=src 8 | 9 | # Language 10 | sonar.language=php 11 | sonar.phpCodesniffer.standardArgument=psr2 12 | 13 | # To deactivate features related to unit tests execution and coverage 14 | sonar.dynamicAnalysis=false 15 | 16 | # Encoding of the source files 17 | sonar.sourceEncoding=UTF-8 18 | -------------------------------------------------------------------------------- /src/Router/Notes.txt: -------------------------------------------------------------------------------- 1 | Some parts of this code are taken from FastRoute under the Flask license (modified version of BSD-3-Clause). 2 | 3 | (c) Nikita Popov 4 | 5 | For the full copyright and license information, please view the LICENSE-Flask 6 | file that was distributed with this source code. 7 | 8 | Modifications made: 9 | - Scabbia Framework code styles applied. 10 | - Completely implemented to Scabbia2's own generator-based system. 11 | 12 | Latest commit applied: 13 | https://github.com/nikic/FastRoute/commit/7dc9a43cb856debc22fe158a1144018e16f7a637 14 | 7dc9a43cb856debc22fe158a1144018e16f7a637 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2014 Scabbia Framework Organization 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /src/Loader/LICENSE-Apache: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2013 Scabbia Framework Organization 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /src/Router/LICENSE-Apache: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2013 Scabbia Framework Organization 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /src/Yaml/LICENSE-Apache: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2013 Scabbia Framework Organization 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/Yaml/LICENSE-Apache: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2013 Scabbia Framework Organization 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /src/Yaml/Notes.txt: -------------------------------------------------------------------------------- 1 | Some parts of this code are taken from Symfony YAML Component under the MIT license. 2 | 3 | (c) Fabien Potencier 4 | 5 | For the full copyright and license information, please view the LICENSE-MIT 6 | file that was distributed with this source code. 7 | 8 | Modifications made: 9 | - Scabbia Framework code styles applied. 10 | - All dump methods are moved under Dumper class. 11 | - Redundant classes removed. 12 | - Namespace changed. 13 | - Tests ported to Scabbia2. 14 | - Encoding checks removed. 15 | 16 | Latest commit applied: 17 | https://github.com/symfony/Yaml/commit/6db64aa92ce2b2aa35a72a0d0508de4d60c70b20 18 | 6db64aa92ce2b2aa35a72a0d0508de4d60c70b20 19 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsErrorTests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | test: Missing value for hash item 3 | todo: true 4 | brief: | 5 | Third item in this hash doesn't have a value 6 | yaml: | 7 | okay: value 8 | also okay: ~ 9 | causes error because no value specified 10 | last key: value okay here too 11 | python-error: causes error because no value specified 12 | 13 | --- 14 | test: Not indenting enough 15 | brief: | 16 | There was a bug in PyYaml where it was off by one 17 | in the indentation check. It was allowing the YAML 18 | below. 19 | # This is actually valid YAML now. Someone should tell showell. 20 | yaml: | 21 | foo: 22 | firstline: 1 23 | secondline: 2 24 | php: | 25 | array('foo' => null, 'firstline' => 1, 'secondline' => 2) 26 | -------------------------------------------------------------------------------- /src/Loader/Notes.txt: -------------------------------------------------------------------------------- 1 | Some parts of this code are taken from Composer under the MIT license. 2 | 3 | (c) Nils Adermann 4 | (c) Jordi Boggiano 5 | 6 | For the full copyright and license information, please view the LICENSE-MIT 7 | file that was distributed with this source code. 8 | 9 | Modifications made: 10 | - Scabbia Framework code styles applied. 11 | - Added push/pop configuration. 12 | - Loading/Importing composer files are now optional. 13 | - Finding resources with extensions is now possible. 14 | - Added prepended paths for overriding vendor resources. 15 | 16 | Latest commit applied: 17 | https://github.com/composer/composer/commit/d79f2b0fd33ee9b89f3d9f1969f43dc3d570a33a 18 | d79f2b0fd33ee9b89f3d9f1969f43dc3d570a33a 19 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/sfQuotes.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Some characters at the beginning of a string must be escaped 3 | brief: > 4 | Some characters at the beginning of a string must be escaped 5 | yaml: | 6 | foo: | bar 7 | php: | 8 | array('foo' => '| bar') 9 | --- 10 | test: A key can be a quoted string 11 | brief: > 12 | A key can be a quoted string 13 | yaml: | 14 | "foo1": bar 15 | 'foo2': bar 16 | "foo \" bar": bar 17 | 'foo '' bar': bar 18 | 'foo3: ': bar 19 | "foo4: ": bar 20 | foo5: { "foo \" bar: ": bar, 'foo '' bar: ': bar } 21 | php: | 22 | array( 23 | 'foo1' => 'bar', 24 | 'foo2' => 'bar', 25 | 'foo " bar' => 'bar', 26 | 'foo \' bar' => 'bar', 27 | 'foo3: ' => 'bar', 28 | 'foo4: ' => 'bar', 29 | 'foo5' => array( 30 | 'foo " bar: ' => 'bar', 31 | 'foo \' bar: ' => 'bar', 32 | ), 33 | ) 34 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsNullsAndEmpties.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Empty Sequence 3 | brief: > 4 | You can represent the empty sequence 5 | with an empty inline sequence. 6 | yaml: | 7 | empty: [] 8 | php: | 9 | array('empty' => array()) 10 | --- 11 | test: Empty Mapping 12 | brief: > 13 | You can represent the empty mapping 14 | with an empty inline mapping. 15 | yaml: | 16 | empty: {} 17 | php: | 18 | array('empty' => array()) 19 | --- 20 | test: Empty Sequence as Entire Document 21 | yaml: | 22 | [] 23 | php: | 24 | array() 25 | --- 26 | test: Empty Mapping as Entire Document 27 | yaml: | 28 | {} 29 | php: | 30 | array() 31 | --- 32 | test: Null as Document 33 | yaml: | 34 | ~ 35 | php: | 36 | null 37 | --- 38 | test: Empty String 39 | brief: > 40 | You can represent an empty string 41 | with a pair of quotes. 42 | yaml: | 43 | '' 44 | php: | 45 | '' 46 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsAnchorAlias.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Simple Alias Example 3 | brief: > 4 | If you need to refer to the same item of data twice, 5 | you can give that item an alias. The alias is a plain 6 | string, starting with an ampersand. The item may then 7 | be referred to by the alias throughout your document 8 | by using an asterisk before the name of the alias. 9 | This is called an anchor. 10 | yaml: | 11 | - &showell Steve 12 | - Clark 13 | - Brian 14 | - Oren 15 | - *showell 16 | php: | 17 | array('Steve', 'Clark', 'Brian', 'Oren', 'Steve') 18 | 19 | --- 20 | test: Alias of a Mapping 21 | brief: > 22 | An alias can be used on any item of data, including 23 | sequences, mappings, and other complex data types. 24 | yaml: | 25 | - &hello 26 | Meat: pork 27 | Starch: potato 28 | - banana 29 | - *hello 30 | php: | 31 | array(array('Meat'=>'pork', 'Starch'=>'potato'), 'banana', array('Meat'=>'pork', 'Starch'=>'potato')) 32 | -------------------------------------------------------------------------------- /src/Yaml/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2014 Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /tests/Yaml/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004-2013 Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/Loader/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Nils Adermann, Jordi Boggiano 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsBlockMapping.yml: -------------------------------------------------------------------------------- 1 | --- 2 | test: One Element Mapping 3 | brief: | 4 | A mapping with one key/value pair 5 | yaml: | 6 | foo: bar 7 | php: | 8 | array('foo' => 'bar') 9 | --- 10 | test: Multi Element Mapping 11 | brief: | 12 | More than one key/value pair 13 | yaml: | 14 | red: baron 15 | white: walls 16 | blue: berries 17 | php: | 18 | array( 19 | 'red' => 'baron', 20 | 'white' => 'walls', 21 | 'blue' => 'berries', 22 | ) 23 | --- 24 | test: Values aligned 25 | brief: | 26 | Often times human editors of documents will align the values even 27 | though YAML emitters generally don't. 28 | yaml: | 29 | red: baron 30 | white: walls 31 | blue: berries 32 | php: | 33 | array( 34 | 'red' => 'baron', 35 | 'white' => 'walls', 36 | 'blue' => 'berries', 37 | ) 38 | --- 39 | test: Colons aligned 40 | brief: | 41 | Spaces can come before the ': ' key/value separator. 42 | yaml: | 43 | red : baron 44 | white : walls 45 | blue : berries 46 | php: | 47 | array( 48 | 'red' => 'baron', 49 | 'white' => 'walls', 50 | 'blue' => 'berries', 51 | ) 52 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/sfMergeKey.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Simple In Place Substitution 3 | brief: > 4 | If you want to reuse an entire alias, only overwriting what is different 5 | you can use a << in place substitution. This is not part of the official 6 | YAML spec, but a widely implemented extension. See the following URL for 7 | details: http://yaml.org/type/merge.html 8 | yaml: | 9 | foo: &foo 10 | a: Steve 11 | b: Clark 12 | c: Brian 13 | bar: &bar 14 | <<: *foo 15 | x: Oren 16 | foo2: &foo2 17 | a: Ballmer 18 | ding: &dong [ fi, fei, fo, fam] 19 | check: 20 | <<: 21 | - *foo 22 | - *dong 23 | isit: tested 24 | head: 25 | <<: [ *foo , *dong , *foo2 ] 26 | php: | 27 | array('foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), 'bar' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'x' => 'Oren'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'head' => array('a' => 'Ballmer', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam')) 28 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scabbiafw/scabbia2-fw", 3 | "description": "Scabbia2 PHP Framework Code", 4 | "type": "library", 5 | "minimum-stability": "dev", 6 | "prefer-stable": true, 7 | "keywords": [ 8 | "larukedi", 9 | "scabbia", 10 | "scabbia2", 11 | "scabbiafw", 12 | "framework", 13 | "lightstack", 14 | "stack" 15 | ], 16 | "homepage": "http://www.scabbiafw.com/", 17 | "license": "Apache-2.0", 18 | "authors": [ 19 | { 20 | "name": "Scabbia Framework Organization", 21 | "email": "contact@scabbiafw.com" 22 | } 23 | ], 24 | "require": { 25 | "php": ">=5.6.0", 26 | "psr/log": "1.0.0", 27 | "scabbiafw/scabbia2-lightstack": "dev-master" 28 | }, 29 | "suggest": { 30 | "ext-curl": "ext/curl for http communication", 31 | "ext-mbstring": "ext/mbstring for multibyte string manipulation", 32 | "ext-tokenizer": "ext/tokenizer for lexical analysis" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Scabbia\\": [ "src/" ] 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Scabbia\\Tests\\": [ "tests/" ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /unittests.php: -------------------------------------------------------------------------------- 1 | register(true); 20 | 21 | // register the base directories for the namespace prefix 22 | $tLoader->addPsr4("Scabbia\\", __DIR__ . "/src/"); 23 | $tLoader->addPsr4("Scabbia\\Tests\\", __DIR__ . "/tests/"); 24 | 25 | use Scabbia\Interfaces\Console; 26 | use Scabbia\Testing\TestsTask; 27 | 28 | $tConfig = [ 29 | "fixtures" => [ 30 | "Scabbia\\Tests\\Yaml\\ParserTest", 31 | "Scabbia\\Tests\\Yaml\\InlineTest", 32 | "Scabbia\\Tests\\Security\\HashTest" 33 | ] 34 | ]; 35 | 36 | $tInterface = new Console(); 37 | $tTestTask = new TestsTask($tConfig, $tInterface); 38 | $tExitCode = $tTestTask->executeTask([]); 39 | 40 | exit($tExitCode); 41 | -------------------------------------------------------------------------------- /tests/Yaml/DummyClass.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * For the full copyright and license information, please view the LICENSE-MIT 19 | * file that was distributed with this source code. 20 | * 21 | * Modifications made: 22 | * - Scabbia Framework code styles applied. 23 | * - All dump methods are moved under Dumper class. 24 | * - Redundant classes removed. 25 | * - Namespace changed. 26 | * - Tests ported to Scabbia2. 27 | * - Encoding checks removed. 28 | */ 29 | 30 | namespace Scabbia\Tests\Yaml; 31 | 32 | /** 33 | * A dummy class for serialization tests 34 | * 35 | * @package Scabbia\Tests\Yaml 36 | * @since 2.0.0 37 | */ 38 | class DummyClass 39 | { 40 | /** @type string $b a dummy member */ 41 | public $b = "foo"; 42 | } 43 | -------------------------------------------------------------------------------- /src/Router/LICENSE-Flask: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 by Nikita Popov. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/unindentedCollections.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Unindented collection 3 | brief: > 4 | Unindented collection 5 | yaml: | 6 | collection: 7 | - item1 8 | - item2 9 | - item3 10 | php: | 11 | array('collection' => array('item1', 'item2', 'item3')) 12 | --- 13 | test: Nested unindented collection (two levels) 14 | brief: > 15 | Nested unindented collection 16 | yaml: | 17 | collection: 18 | key: 19 | - a 20 | - b 21 | - c 22 | php: | 23 | array('collection' => array('key' => array('a', 'b', 'c'))) 24 | --- 25 | test: Nested unindented collection (three levels) 26 | brief: > 27 | Nested unindented collection 28 | yaml: | 29 | collection: 30 | key: 31 | subkey: 32 | - one 33 | - two 34 | - three 35 | php: | 36 | array('collection' => array('key' => array('subkey' => array('one', 'two', 'three')))) 37 | --- 38 | test: Key/value after unindented collection (1) 39 | brief: > 40 | Key/value after unindented collection (1) 41 | yaml: | 42 | collection: 43 | key: 44 | - a 45 | - b 46 | - c 47 | foo: bar 48 | php: | 49 | array('collection' => array('key' => array('a', 'b', 'c')), 'foo' => 'bar') 50 | --- 51 | test: Key/value after unindented collection (at the same level) 52 | brief: > 53 | Key/value after unindented collection 54 | yaml: | 55 | collection: 56 | key: 57 | - a 58 | - b 59 | - c 60 | foo: bar 61 | php: | 62 | array('collection' => array('key' => array('a', 'b', 'c'), 'foo' => 'bar')) 63 | -------------------------------------------------------------------------------- /src/Views/ViewGenerator.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 2.0.0 26 | * 27 | * @scabbia-generator 28 | */ 29 | class ViewGenerator extends GeneratorBase 30 | { 31 | /** 32 | * Processes a file 33 | * 34 | * @param string $uPath file path 35 | * @param string $uFileContents contents of file 36 | * @param TokenStream $uTokenStream extracted tokens wrapped with tokenstream 37 | * 38 | * @return void 39 | */ 40 | public function processFile($uPath, $uFileContents, TokenStream $uTokenStream) 41 | { 42 | $tViewEngine = Views::findViewEngine($uPath); 43 | 44 | if ($tViewEngine === null) { 45 | return; 46 | } 47 | 48 | // TODO compile view 49 | $tViewEngineClass = get_class($tViewEngine); 50 | echo sprintf("View %s => %s\n", $uPath, $tViewEngineClass); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Facades/FacadeBase.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 2.0.0 25 | */ 26 | abstract class FacadeBase 27 | { 28 | /** @type array $callbackMap map for callbacks */ 29 | public static $callbackMap; 30 | 31 | 32 | /** 33 | * Handles static method calls to the class definition 34 | * 35 | * @param string $uMethod method name 36 | * @param array $uParameters parameters 37 | * 38 | * @throws UnexpectedValueException if mapped method not found 39 | * @return mixed 40 | */ 41 | public static function __callStatic($uMethod, $uParameters) 42 | { 43 | if (isset(static::$callbackMap[$uMethod])) { 44 | return call_user_func( 45 | Runtime::callbacks(static::$callbackMap[$uMethod]), 46 | ...$uParameters 47 | ); 48 | } 49 | 50 | // TODO exception 51 | throw new UnexpectedValueException(""); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Interfaces/IInterface.php: -------------------------------------------------------------------------------- 1 | 21 | * @since 2.0.0 22 | */ 23 | interface IInterface 24 | { 25 | /** 26 | * Writes given message in header format 27 | * 28 | * @param int $uHeading size 29 | * @param string $uMessage message 30 | * 31 | * @return void 32 | */ 33 | public function writeHeader($uHeading, $uMessage); 34 | 35 | /** 36 | * Writes given message in specified color 37 | * 38 | * @param string $uColor color 39 | * @param string $uMessage message 40 | * 41 | * @return void 42 | */ 43 | public function writeColor($uColor, $uMessage); 44 | 45 | /** 46 | * Writes given message 47 | * 48 | * @param string $uMessage message 49 | * 50 | * @return void 51 | */ 52 | public function write($uMessage); 53 | 54 | /** 55 | * Outputs the array in specified representation 56 | * 57 | * @param array $uArray Target array will be printed 58 | * 59 | * @return void 60 | */ 61 | public function writeArray(array $uArray); 62 | } 63 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/sfComments.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Comments at the end of a line 3 | brief: > 4 | Comments at the end of a line 5 | yaml: | 6 | ex1: "foo # bar" 7 | ex2: "foo # bar" # comment 8 | ex3: 'foo # bar' # comment 9 | ex4: foo # comment 10 | php: | 11 | array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') 12 | --- 13 | test: Comments in the middle 14 | brief: > 15 | Comments in the middle 16 | yaml: | 17 | foo: 18 | # some comment 19 | # some comment 20 | bar: foo 21 | # some comment 22 | # some comment 23 | php: | 24 | array('foo' => array('bar' => 'foo')) 25 | --- 26 | test: Comments on a hash line 27 | brief: > 28 | Comments on a hash line 29 | yaml: | 30 | foo: # a comment 31 | foo: bar # a comment 32 | php: | 33 | array('foo' => array('foo' => 'bar')) 34 | --- 35 | test: 'Value starting with a #' 36 | brief: > 37 | 'Value starting with a #' 38 | yaml: | 39 | foo: '#bar' 40 | php: | 41 | array('foo' => '#bar') 42 | --- 43 | test: Document starting with a comment and a separator 44 | brief: > 45 | Commenting before document start is allowed 46 | yaml: | 47 | # document comment 48 | --- 49 | foo: bar # a comment 50 | php: | 51 | array('foo' => 'bar') 52 | --- 53 | test: Comment containing a colon on a hash line 54 | brief: > 55 | Comment containing a colon on a scalar line 56 | yaml: 'foo # comment: this is also part of the comment' 57 | php: | 58 | 'foo' 59 | --- 60 | test: 'Hash key containing a #' 61 | brief: > 62 | 'Hash key containing a #' 63 | yaml: 'foo#bar: baz' 64 | php: | 65 | array('foo#bar' => 'baz') 66 | --- 67 | test: 'Hash key ending with a space and a #' 68 | brief: > 69 | 'Hash key ending with a space and a #' 70 | yaml: | 71 | 'foo #': baz 72 | php: | 73 | array('foo #' => 'baz') -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsFlowCollections.yml: -------------------------------------------------------------------------------- 1 | --- 2 | test: Simple Inline Array 3 | brief: > 4 | Sequences can be contained on a 5 | single line, using the inline syntax. 6 | Separate each entry with commas and 7 | enclose in square brackets. 8 | yaml: | 9 | seq: [ a, b, c ] 10 | php: | 11 | array('seq' => array('a', 'b', 'c')) 12 | --- 13 | test: Simple Inline Hash 14 | brief: > 15 | Mapping can also be contained on 16 | a single line, using the inline 17 | syntax. Each key-value pair is 18 | separated by a colon, with a comma 19 | between each entry in the mapping. 20 | Enclose with curly braces. 21 | yaml: | 22 | hash: { name: Steve, foo: bar } 23 | php: | 24 | array('hash' => array('name' => 'Steve', 'foo' => 'bar')) 25 | --- 26 | test: Multi-line Inline Collections 27 | todo: true 28 | brief: > 29 | Both inline sequences and inline mappings 30 | can span multiple lines, provided that you 31 | indent the additional lines. 32 | yaml: | 33 | languages: [ Ruby, 34 | Perl, 35 | Python ] 36 | websites: { YAML: yaml.org, 37 | Ruby: ruby-lang.org, 38 | Python: python.org, 39 | Perl: use.perl.org } 40 | php: | 41 | array( 42 | 'languages' => array('Ruby', 'Perl', 'Python'), 43 | 'websites' => array( 44 | 'YAML' => 'yaml.org', 45 | 'Ruby' => 'ruby-lang.org', 46 | 'Python' => 'python.org', 47 | 'Perl' => 'use.perl.org' 48 | ) 49 | ) 50 | --- 51 | test: Commas in Values (not in the spec!) 52 | todo: true 53 | brief: > 54 | List items in collections are delimited by commas, but 55 | there must be a space after each comma. This allows you 56 | to add numbers without quoting. 57 | yaml: | 58 | attendances: [ 45,123, 70,000, 17,222 ] 59 | php: | 60 | array('attendances' => array(45123, 70000, 17222)) 61 | -------------------------------------------------------------------------------- /tests/Yaml/ParseExceptionTest.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * For the full copyright and license information, please view the LICENSE-MIT 19 | * file that was distributed with this source code. 20 | * 21 | * Modifications made: 22 | * - Scabbia Framework code styles applied. 23 | * - All dump methods are moved under Dumper class. 24 | * - Redundant classes removed. 25 | * - Namespace changed. 26 | * - Tests ported to Scabbia2. 27 | * - Encoding checks removed. 28 | */ 29 | 30 | namespace Scabbia\Tests\Yaml; 31 | 32 | use Scabbia\Testing\UnitTestFixture; 33 | use Scabbia\Yaml\ParseException; 34 | 35 | /** 36 | * Tests of ParseException class 37 | * 38 | * @package Scabbia\Tests\Yaml 39 | * @since 2.0.0 40 | */ 41 | class ParseExceptionTest extends UnitTestFixture 42 | { 43 | /** 44 | * Gets data form specifications 45 | * 46 | * @return array 47 | */ 48 | public function getDataFormSpecifications() 49 | { 50 | $tException = new ParseException("Error message", 42, "foo: bar", "/var/www/app/config.yml"); 51 | 52 | $this->assertEquals( 53 | "Error message in \"/var/www/app/config.yml\" at line 42 (near \"foo: bar\")", 54 | $tException->getMessage() 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsDocumentSeparator.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Trailing Document Separator 3 | todo: true 4 | brief: > 5 | You can separate YAML documents 6 | with a string of three dashes. 7 | yaml: | 8 | - foo: 1 9 | bar: 2 10 | --- 11 | more: stuff 12 | python: | 13 | [ 14 | [ { 'foo': 1, 'bar': 2 } ], 15 | { 'more': 'stuff' } 16 | ] 17 | ruby: | 18 | [ { 'foo' => 1, 'bar' => 2 } ] 19 | 20 | --- 21 | test: Leading Document Separator 22 | todo: true 23 | brief: > 24 | You can explicity give an opening 25 | document separator to your YAML stream. 26 | yaml: | 27 | --- 28 | - foo: 1 29 | bar: 2 30 | --- 31 | more: stuff 32 | python: | 33 | [ 34 | [ {'foo': 1, 'bar': 2}], 35 | {'more': 'stuff'} 36 | ] 37 | ruby: | 38 | [ { 'foo' => 1, 'bar' => 2 } ] 39 | 40 | --- 41 | test: YAML Header 42 | todo: true 43 | brief: > 44 | The opening separator can contain directives 45 | to the YAML parser, such as the version 46 | number. 47 | yaml: | 48 | --- %YAML:1.0 49 | foo: 1 50 | bar: 2 51 | php: | 52 | array('foo' => 1, 'bar' => 2) 53 | documents: 1 54 | 55 | --- 56 | test: Red Herring Document Separator 57 | brief: > 58 | Separators included in blocks or strings 59 | are treated as blocks or strings, as the 60 | document separator should have no indentation 61 | preceding it. 62 | yaml: | 63 | foo: | 64 | --- 65 | php: | 66 | array('foo' => "---\n") 67 | 68 | --- 69 | test: Multiple Document Separators in Block 70 | brief: > 71 | This technique allows you to embed other YAML 72 | documents within literal blocks. 73 | yaml: | 74 | foo: | 75 | --- 76 | foo: bar 77 | --- 78 | yo: baz 79 | bar: | 80 | fooness 81 | php: | 82 | array( 83 | 'foo' => "---\nfoo: bar\n---\nyo: baz\n", 84 | 'bar' => "fooness\n" 85 | ) 86 | -------------------------------------------------------------------------------- /src/Views/ViewEngineBase.php: -------------------------------------------------------------------------------- 1 | 21 | * @since 2.0.0 22 | * 23 | * @todo compile 24 | * @todo sections 25 | */ 26 | class ViewEngineBase 27 | { 28 | /** @type mixed $model model for the current view */ 29 | public $model; 30 | /** @type object|null $controller controller for the current view */ 31 | public $controller; 32 | 33 | /** 34 | * Initializes a view engine 35 | * 36 | * @return ViewEngineBase 37 | */ 38 | public function __construct() 39 | { 40 | } 41 | 42 | /** 43 | * Renders plain PHP file for using them as a template format 44 | * 45 | * @param string $tTemplatePath path of the template file 46 | * @param string $tTemplateFile filename of the template file 47 | * @param mixed $uModel model object 48 | * @param mixed $uController controller instance 49 | * 50 | * @return void 51 | */ 52 | public function render($tTemplatePath, $tTemplateFile, $uModel = null, $uController = null) 53 | { 54 | $this->model = $uModel; 55 | $this->controller = $uController; 56 | 57 | if ($uModel !== null && is_array($uModel)) { 58 | extract($uModel, EXTR_SKIP | EXTR_REFS); 59 | } 60 | 61 | include "{$tTemplatePath}{$tTemplateFile}"; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Tasks/TaskBase.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 2.0.0 24 | * 25 | * @todo "scabbia help " task 26 | */ 27 | abstract class TaskBase 28 | { 29 | /** @type mixed $config task configuration */ 30 | public $config; 31 | /** @type IInterface $interface output class */ 32 | public $interface; 33 | 34 | 35 | /** 36 | * Registers the tasks itself to a command interpreter instance 37 | * 38 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 39 | * 40 | * @return void 41 | */ 42 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 43 | { 44 | } 45 | 46 | /** 47 | * Initializes a task 48 | * 49 | * @param mixed $uConfig configuration 50 | * @param IInterface $uInterface interface class 51 | * 52 | * @return TaskBase 53 | */ 54 | public function __construct($uConfig, $uInterface) 55 | { 56 | $this->config = $uConfig; 57 | $this->interface = $uInterface; 58 | } 59 | 60 | /** 61 | * Executes the task 62 | * 63 | * @param array $uParameters parameters 64 | * 65 | * @return int exit code 66 | */ 67 | abstract public function executeTask(array $uParameters); 68 | } 69 | -------------------------------------------------------------------------------- /src/Containers/SingletonContainer.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 2.0.0 24 | */ 25 | trait SingletonContainer 26 | { 27 | /** @type object $instance singleton object */ 28 | public static $instance = null; 29 | 30 | 31 | /** 32 | * Gets the singleton object 33 | * 34 | * @return object class instance 35 | */ 36 | public static function getInstance() 37 | { 38 | if (static::$instance === null) { 39 | static::$instance = new static(); 40 | } 41 | 42 | return static::$instance; 43 | } 44 | 45 | /** 46 | * Sets the class instance as singleton object 47 | * 48 | * @return void 49 | */ 50 | public function setDefaultInstance() 51 | { 52 | static::$instance = $this; 53 | } 54 | 55 | /** 56 | * Constructor to prevent new instances of this class 57 | * 58 | * @return SingletonContainer 59 | */ 60 | final private function __construct() 61 | { 62 | } 63 | 64 | /** 65 | * Clone method to prevent duplication of this class 66 | * 67 | * @return SingletonContainer 68 | */ 69 | final private function __clone() 70 | { 71 | } 72 | 73 | /** 74 | * Unserialization method to prevent restoration of this class 75 | * 76 | * @return SingletonContainer 77 | */ 78 | final private function __wakeup() 79 | { 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Framework/Tasks/HelpTask.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 2.0.0 27 | * 28 | * @scabbia-task help 29 | */ 30 | class HelpTask extends TaskBase 31 | { 32 | /** 33 | * Registers the tasks itself to a command interpreter instance 34 | * 35 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 36 | * 37 | * @return void 38 | */ 39 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 40 | { 41 | $uCommandInterpreter->addCommand( 42 | "help", 43 | "Displays this help", 44 | [] 45 | ); 46 | } 47 | 48 | /** 49 | * Initializes the serve task 50 | * 51 | * @param mixed $uConfig configuration 52 | * @param IInterface $uInterface interface class 53 | * 54 | * @return HelpTask 55 | */ 56 | public function __construct($uConfig, $uInterface) 57 | { 58 | parent::__construct($uConfig, $uInterface); 59 | } 60 | 61 | /** 62 | * Executes the task 63 | * 64 | * @param array $uParameters parameters 65 | * 66 | * @return int exit code 67 | */ 68 | public function executeTask(array $uParameters) 69 | { 70 | // TODO call interpreter->help(); 71 | return 0; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Generators/GeneratorBase.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 2.0.0 26 | */ 27 | abstract class GeneratorBase 28 | { 29 | /** @type GeneratorRegistry $generatorRegistry generator registry */ 30 | public $generatorRegistry; 31 | 32 | 33 | /** 34 | * Initializes a generator 35 | * 36 | * @param GeneratorRegistry $uGeneratorRegistry generator registry 37 | * 38 | * @return GeneratorBase 39 | */ 40 | public function __construct(GeneratorRegistry $uGeneratorRegistry) 41 | { 42 | $this->generatorRegistry = $uGeneratorRegistry; 43 | } 44 | 45 | /** 46 | * Processes a file 47 | * 48 | * @param string $uPath file path 49 | * @param string $uFileContents contents of file 50 | * @param TokenStream $uTokenStream extracted tokens wrapped with tokenstream 51 | * 52 | * @return void 53 | */ 54 | public function processFile($uPath, $uFileContents, TokenStream $uTokenStream) 55 | { 56 | } 57 | 58 | /** 59 | * Processes set of annotations 60 | * 61 | * @return void 62 | */ 63 | public function processAnnotations() 64 | { 65 | } 66 | 67 | /** 68 | * Finalizes generator process 69 | * 70 | * @return void 71 | */ 72 | public function finalize() 73 | { 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Framework/Tasks/ConsoleTask.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 2.0.0 26 | * 27 | * @scabbia-task console 28 | * @todo only pass annotations requested by generator 29 | */ 30 | class ConsoleTask extends TaskBase 31 | { 32 | /** 33 | * Registers the tasks itself to a command interpreter instance 34 | * 35 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 36 | * 37 | * @return void 38 | */ 39 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 40 | { 41 | $uCommandInterpreter->addCommand( 42 | "console", 43 | "Launches a REPL command interface", 44 | [] 45 | ); 46 | } 47 | 48 | /** 49 | * Initializes the console task 50 | * 51 | * @param mixed $uConfig configuration 52 | * @param IInterface $uInterface interface class 53 | * 54 | * @return ConsoleTask 55 | */ 56 | public function __construct($uConfig, $uInterface) 57 | { 58 | parent::__construct($uConfig, $uInterface); 59 | } 60 | 61 | /** 62 | * Executes the task 63 | * 64 | * @param array $uParameters parameters 65 | * 66 | * @return int exit code 67 | */ 68 | public function executeTask(array $uParameters) 69 | { 70 | $tBoris = new Boris("scabbia> "); 71 | $tBoris->start(); 72 | 73 | return 0; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Framework/Tasks/CleanTask.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 2.0.0 27 | * 28 | * @scabbia-task clean 29 | */ 30 | class CleanTask extends TaskBase 31 | { 32 | /** 33 | * Registers the tasks itself to a command interpreter instance 34 | * 35 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 36 | * 37 | * @return void 38 | */ 39 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 40 | { 41 | $uCommandInterpreter->addCommand( 42 | "clean", 43 | "Cleans the writable cache", 44 | [] 45 | ); 46 | } 47 | 48 | /** 49 | * Initializes the clean task 50 | * 51 | * @param mixed $uConfig configuration 52 | * @param IInterface $uInterface interface class 53 | * 54 | * @return CleanTask 55 | */ 56 | public function __construct($uConfig, $uInterface) 57 | { 58 | parent::__construct($uConfig, $uInterface); 59 | } 60 | 61 | /** 62 | * Executes the task 63 | * 64 | * @param array $uParameters parameters 65 | * 66 | * @return int exit code 67 | */ 68 | public function executeTask(array $uParameters) 69 | { 70 | $tPath = Core::$loader->basepath . "/var/cache"; 71 | FileSystem::garbageCollect($tPath, ["dotFiles" => false]); 72 | 73 | $this->interface->writeColor("yellow", "done."); 74 | 75 | return 0; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Events/Events.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 1.0.0 24 | */ 25 | class Events 26 | { 27 | /** @type array event subscribers */ 28 | public $events = []; 29 | /** @type array event depth */ 30 | public $eventDepth = []; 31 | /** @type bool indicates the event manager is currently disabled or not */ 32 | public $disabled = false; 33 | 34 | 35 | /** 36 | * Invokes an event 37 | * 38 | * @param string $uEvent name of the event 39 | * @param null|array $uEventArgs arguments for the event 40 | * 41 | * @return bool whether the event is invoked or not 42 | */ 43 | public function invoke($uEvent, $uEventArgs = null) 44 | { 45 | if ($this->disabled) { 46 | return null; 47 | } 48 | 49 | if (!isset($this->events[$uEvent])) { 50 | return null; 51 | } 52 | 53 | $this->eventDepth[] = [$uEvent, $uEventArgs]; 54 | $tReturn = $this->events[$uEvent]->invoke($uEventArgs); 55 | array_pop($this->eventDepth); 56 | 57 | return $tReturn; 58 | } 59 | 60 | /** 61 | * Makes a callback method subscribed to specified event 62 | * 63 | * @param string $uEvent event 64 | * @param callable $uCallback callback 65 | * @param mixed $uState state object 66 | * @param null|int $uPriority priority 67 | * 68 | * @return void 69 | */ 70 | public function register($uEvent, $uCallback, $uState, $uPriority = null) 71 | { 72 | if (!isset($this->events[$uEvent])) { 73 | $this->events[$uEvent] = new Delegate(); 74 | } 75 | 76 | $this->events[$uEvent]->add($uCallback, $uState, $uPriority); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Helpers/Runtime.php: -------------------------------------------------------------------------------- 1 | 21 | * @since 2.0.0 22 | * 23 | * @scabbia-compile 24 | */ 25 | class Runtime 26 | { 27 | /** 28 | * Default variables for runtime hacks 29 | * 30 | * @type array $defaults array of default variables 31 | */ 32 | public static $defaults = [ 33 | ]; 34 | 35 | 36 | /** 37 | * Constructor to prevent new instances of Runtime class 38 | * 39 | * @return Runtime 40 | */ 41 | final private function __construct() 42 | { 43 | } 44 | 45 | /** 46 | * Clone method to prevent duplication of Runtime class 47 | * 48 | * @return Runtime 49 | */ 50 | final private function __clone() 51 | { 52 | } 53 | 54 | /** 55 | * Unserialization method to prevent restoration of Runtime class 56 | * 57 | * @return Runtime 58 | */ 59 | final private function __wakeup() 60 | { 61 | } 62 | 63 | /** 64 | * Sets the default variables 65 | * 66 | * @param array $uDefaults variables to be set 67 | * 68 | * @return void 69 | */ 70 | public static function setDefaults($uDefaults) 71 | { 72 | self::$defaults = $uDefaults + self::$defaults; 73 | } 74 | 75 | /** 76 | * Allows on-the-fly construction of classes 77 | * 78 | * @param mixed $uCallback callback 79 | * 80 | * @return mixed callback 81 | */ 82 | public static function callbacks($uCallback) 83 | { 84 | if (is_string($uCallback) && ($tPos = strrpos($uCallback, "@")) !== false) { 85 | $tClassName = substr($uCallback, 0, $tPos); 86 | $uCallback = [new $tClassName (), substr($uCallback, $tPos + 1)]; 87 | } 88 | 89 | return $uCallback; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Config/ConfigGenerator.php: -------------------------------------------------------------------------------- 1 | 28 | * @since 2.0.0 29 | * 30 | * @scabbia-generator 31 | * 32 | * @todo FIXME include application configuration from project.yml ? 33 | */ 34 | class ConfigGenerator extends GeneratorBase 35 | { 36 | /** @type Config $unifiedConfig unified configuration */ 37 | public $unifiedConfig; 38 | 39 | 40 | /** 41 | * Initializes a generator 42 | * 43 | * @param GeneratorRegistry $uGeneratorRegistry generator registry 44 | * 45 | * @return GeneratorBase 46 | */ 47 | public function __construct(GeneratorRegistry $uGeneratorRegistry) 48 | { 49 | parent::__construct($uGeneratorRegistry); 50 | 51 | $this->unifiedConfig = new Config(); 52 | } 53 | 54 | /** 55 | * Processes a file 56 | * 57 | * @param string $uPath file path 58 | * @param string $uFileContents contents of file 59 | * @param TokenStream $uTokenStream extracted tokens wrapped with tokenstream 60 | * 61 | * @return void 62 | */ 63 | public function processFile($uPath, $uFileContents, TokenStream $uTokenStream) 64 | { 65 | if (substr($uPath, -11) !== ".config.yml") { 66 | return; 67 | } 68 | 69 | $this->unifiedConfig->add($uPath); 70 | echo "Config {$uPath}\n"; 71 | } 72 | 73 | /** 74 | * Finalizes generator process 75 | * 76 | * @return void 77 | */ 78 | public function finalize() 79 | { 80 | $this->generatorRegistry->saveFile( 81 | "unified-config.php", 82 | $this->unifiedConfig->get(), 83 | true 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Mvc/Controller.php: -------------------------------------------------------------------------------- 1 | 27 | * @since 1.0.0 28 | */ 29 | abstract class Controller 30 | { 31 | use BindableContainer; 32 | 33 | /** @type array $routeInfo routing information */ 34 | public $routeInfo; 35 | /** @type ApplicationBase $application application */ 36 | public $application; 37 | /** @type mixed $moduleConfig module configuration */ 38 | public $moduleConfig; 39 | /** @type Collection $vars variables */ 40 | public $vars; 41 | /** @type Delegate $prerender prerender hook */ 42 | public $prerender; 43 | /** @type Delegate $postrender postrender hook */ 44 | public $postrender; 45 | 46 | 47 | /** 48 | * Initializes a Controller class instance 49 | * 50 | * @return Controller 51 | */ 52 | public function __construct() 53 | { 54 | $this->vars = new Collection(); 55 | $this->prerender = new Delegate(); 56 | $this->postrender = new Delegate(); 57 | } 58 | 59 | /** 60 | * Renders a view 61 | * 62 | * @param string $uView view file 63 | * @param mixed $uModel view model 64 | * @param mixed $uController controller instance 65 | * 66 | * @return void 67 | */ 68 | public function view($uView, $uModel = null, $uController = null) 69 | { 70 | if ($uModel === null) { 71 | $uModel = $this->vars->toArray(); 72 | } 73 | 74 | if (strncmp($uView, "\\", 1) === 0) { 75 | Views::viewFile($uView, $uModel, $this); 76 | } else { 77 | $tNamespace = $this->application->config["modules"][$this->routeInfo["module"]]["namespace"]; 78 | Views::viewFile("{$tNamespace}\\Views\\{$uView}", $uModel, $this); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Testing/TestsTask.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 2.0.0 26 | * 27 | * @scabbia-task tests 28 | */ 29 | class TestsTask extends TaskBase 30 | { 31 | /** 32 | * Registers the tasks itself to a command interpreter instance 33 | * 34 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 35 | * 36 | * @return void 37 | */ 38 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 39 | { 40 | $uCommandInterpreter->addCommand( 41 | "tests", 42 | "Starts unit tests", 43 | [] 44 | ); 45 | } 46 | 47 | /** 48 | * Initializes the tests task 49 | * 50 | * @param mixed $uConfig configuration 51 | * @param IInterface $uInterface interface class 52 | * 53 | * @return TestsTask 54 | */ 55 | public function __construct($uConfig, $uInterface) 56 | { 57 | parent::__construct($uConfig, $uInterface); 58 | } 59 | 60 | /** 61 | * Executes the task 62 | * 63 | * @param array $uParameters parameters 64 | * 65 | * @return int exit code 66 | */ 67 | public function executeTask(array $uParameters) 68 | { 69 | Testing::coverageStart(); 70 | $tExitCode = Testing::runUnitTests($this->config["fixtures"], $this->interface); 71 | $tCoverageReport = Testing::coverageStop(); 72 | 73 | if ($tCoverageReport !== null) { 74 | $tCoverage = round($tCoverageReport["total"]["percentage"], 2) . "%"; 75 | } else { 76 | $tCoverage = "unknown"; 77 | } 78 | 79 | $this->interface->writeColor("green", sprintf("Code Coverage = %s", $tCoverage)); 80 | $this->interface->writeColor("yellow", "done."); 81 | 82 | return $tExitCode; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Events/EventGenerator.php: -------------------------------------------------------------------------------- 1 | 29 | * @since 2.0.0 30 | * 31 | * @scabbia-generator 32 | */ 33 | class EventGenerator extends GeneratorBase 34 | { 35 | /** @type Events $events set of events */ 36 | public $events; 37 | 38 | 39 | /** 40 | * Initializes a generator 41 | * 42 | * @param GeneratorRegistry $uGeneratorRegistry generator registry 43 | * 44 | * @return GeneratorBase 45 | */ 46 | public function __construct(GeneratorRegistry $uGeneratorRegistry) 47 | { 48 | parent::__construct($uGeneratorRegistry); 49 | 50 | $this->events = new Events(); 51 | } 52 | 53 | /** 54 | * Processes set of annotations 55 | * 56 | * @return void 57 | */ 58 | public function processAnnotations() 59 | { 60 | foreach ($this->generatorRegistry->annotationManager->get("event", true) as $tScanResult) { 61 | if ($tScanResult[AnnotationManager::LEVEL] !== "staticMethods") { 62 | continue; 63 | } 64 | 65 | foreach ($tScanResult[AnnotationManager::VALUE] as $tEvent) { 66 | $this->events->register( 67 | $tEvent["on"], 68 | [$tScanResult[AnnotationManager::SOURCE], $tScanResult[AnnotationManager::MEMBER]], 69 | null, 70 | isset($tEvent["priority"]) ? $tEvent["priority"] : null 71 | ); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * Finalizes generator process 78 | * 79 | * @return void 80 | */ 81 | public function finalize() 82 | { 83 | $this->generatorRegistry->saveFile( 84 | "events.php", 85 | $this->events->events, 86 | true 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Framework/Tasks/ServeTask.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 2.0.0 26 | * 27 | * @scabbia-task serve 28 | */ 29 | class ServeTask extends TaskBase 30 | { 31 | /** 32 | * Registers the tasks itself to a command interpreter instance 33 | * 34 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 35 | * 36 | * @return void 37 | */ 38 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 39 | { 40 | $uCommandInterpreter->addCommand( 41 | "serve", 42 | "Runs built-in PHP server", 43 | [ 44 | // type, name, description 45 | [Console::OPTION, "--host", "Binding host address"], 46 | [Console::OPTION, "--port", "Binding port number"] 47 | ] 48 | ); 49 | } 50 | 51 | /** 52 | * Initializes the serve task 53 | * 54 | * @param mixed $uConfig configuration 55 | * @param IInterface $uInterface interface class 56 | * 57 | * @return ServeTask 58 | */ 59 | public function __construct($uConfig, $uInterface) 60 | { 61 | parent::__construct($uConfig, $uInterface); 62 | } 63 | 64 | /** 65 | * Executes the task 66 | * 67 | * @param array $uParameters parameters 68 | * 69 | * @return int exit code 70 | */ 71 | public function executeTask(array $uParameters) 72 | { 73 | $tPort = 1984; 74 | 75 | $this->interface->writeColor("yellow", sprintf("Built-in server started on port %d.", $tPort)); 76 | $this->interface->writeColor("yellow", sprintf("Navigate to http://localhost:%d/\n", $tPort)); 77 | $this->interface->write("Ctrl-C to stop."); 78 | passthru("\"" . PHP_BINARY . "\" -S localhost:{$tPort} -t \"" . Core::$loader->basepath . "\" index.php"); 79 | 80 | return 0; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Documentor/DocumentGenerator.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 2.0.0 27 | * 28 | * @scabbia-generator 29 | */ 30 | class DocumentGenerator extends GeneratorBase 31 | { 32 | /** @type array $files set of files */ 33 | public $files = []; 34 | 35 | 36 | /** 37 | * Processes a file 38 | * 39 | * @param string $uPath file path 40 | * @param string $uFileContents contents of file 41 | * @param TokenStream $uTokenStream extracted tokens wrapped with tokenstream 42 | * 43 | * @return void 44 | */ 45 | public function processFile($uPath, $uFileContents, TokenStream $uTokenStream) 46 | { 47 | $tDocLines = []; 48 | $tRelativePath = substr($uPath, strlen(Core::$loader->basepath) + 1); 49 | $tDocTitle = $tRelativePath; 50 | 51 | foreach ($uTokenStream as $tToken) { 52 | if ($tToken[0] === T_COMMENT) { 53 | if (strncmp($tToken[1], "// MD-TITLE ", 12) === 0) { 54 | $tDocTitle = substr($tToken[1], 12); 55 | continue; 56 | } 57 | 58 | if (strncmp($tToken[1], "// MD ", 6) === 0) { 59 | $tDocLines[] = substr($tToken[1], 6); 60 | continue; 61 | } 62 | } 63 | } 64 | 65 | if (count($tDocLines) > 0) { 66 | $this->files[$tRelativePath] = [$tDocTitle, $tDocLines]; 67 | } 68 | } 69 | 70 | /** 71 | * Finalizes generator process 72 | * 73 | * @return void 74 | */ 75 | public function finalize() 76 | { 77 | $tContent = ""; 78 | foreach ($this->files as $tFileKey => $tFileContent) { 79 | $tContent .= "# {$tFileContent[0]}\n"; 80 | 81 | foreach ($tFileContent[1] as $tLine) { 82 | $tContent .= $tLine; 83 | } 84 | 85 | $tContent .= "\n\n"; 86 | } 87 | 88 | $this->generatorRegistry->saveFile("documentor.md", $tContent); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/escapedCharacters.yml: -------------------------------------------------------------------------------- 1 | test: outside double quotes 2 | yaml: | 3 | \0 \ \a \b \n 4 | php: | 5 | "\\0 \\ \\a \\b \\n" 6 | --- 7 | test: null 8 | yaml: | 9 | "\0" 10 | php: | 11 | "\x00" 12 | --- 13 | test: bell 14 | yaml: | 15 | "\a" 16 | php: | 17 | "\x07" 18 | --- 19 | test: backspace 20 | yaml: | 21 | "\b" 22 | php: | 23 | "\x08" 24 | --- 25 | test: horizontal tab (1) 26 | yaml: | 27 | "\t" 28 | php: | 29 | "\x09" 30 | --- 31 | test: horizontal tab (2) 32 | yaml: | 33 | "\ " 34 | php: | 35 | "\x09" 36 | --- 37 | test: line feed 38 | yaml: | 39 | "\n" 40 | php: | 41 | "\x0a" 42 | --- 43 | test: vertical tab 44 | yaml: | 45 | "\v" 46 | php: | 47 | "\x0b" 48 | --- 49 | test: form feed 50 | yaml: | 51 | "\f" 52 | php: | 53 | "\x0c" 54 | --- 55 | test: carriage return 56 | yaml: | 57 | "\r" 58 | php: | 59 | "\x0d" 60 | --- 61 | test: escape 62 | yaml: | 63 | "\e" 64 | php: | 65 | "\x1b" 66 | --- 67 | test: space 68 | yaml: | 69 | "\ " 70 | php: | 71 | "\x20" 72 | --- 73 | test: slash 74 | yaml: | 75 | "\/" 76 | php: | 77 | "\x2f" 78 | --- 79 | test: backslash 80 | yaml: | 81 | "\\" 82 | php: | 83 | "\\" 84 | --- 85 | test: Unicode next line 86 | yaml: | 87 | "\N" 88 | php: | 89 | "\xc2\x85" 90 | --- 91 | test: Unicode non-breaking space 92 | yaml: | 93 | "\_" 94 | php: | 95 | "\xc2\xa0" 96 | --- 97 | test: Unicode line separator 98 | yaml: | 99 | "\L" 100 | php: | 101 | "\xe2\x80\xa8" 102 | --- 103 | test: Unicode paragraph separator 104 | yaml: | 105 | "\P" 106 | php: | 107 | "\xe2\x80\xa9" 108 | --- 109 | test: Escaped 8-bit Unicode 110 | yaml: | 111 | "\x42" 112 | php: | 113 | "B" 114 | --- 115 | test: Escaped 16-bit Unicode 116 | yaml: | 117 | "\u20ac" 118 | php: | 119 | "\xe2\x82\xac" 120 | --- 121 | test: Escaped 32-bit Unicode 122 | yaml: | 123 | "\U00000043" 124 | php: | 125 | "C" 126 | --- 127 | test: Example 5.13 Escaped Characters 128 | note: | 129 | Currently throws an error parsing first line. Maybe Symfony Yaml doesn't support 130 | continuation of string across multiple lines? Keeping test here but disabled. 131 | todo: true 132 | yaml: | 133 | "Fun with \\ 134 | \" \a \b \e \f \ 135 | \n \r \t \v \0 \ 136 | \ \_ \N \L \P \ 137 | \x41 \u0041 \U00000041" 138 | php: | 139 | "Fun with \x5C\n\x22 \x07 \x08 \x1B \x0C\n\x0A \x0D \x09 \x0B \x00\n\x20 \xA0 \x85 \xe2\x80\xa8 \xe2\x80\xa9\nA A A" 140 | --- 141 | test: Double quotes with a line feed 142 | yaml: | 143 | { double: "some value\n \"some quoted string\" and 'some single quotes one'" } 144 | php: | 145 | array( 146 | 'double' => "some value\n \"some quoted string\" and 'some single quotes one'" 147 | ) 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scabbia2 PHP Framework Code 2 | 3 | **Scabbia2** is an open source PHP framework project which is currently on planning stage. Keep visiting [project homepage](http://scabbiafw.com/) and [repositories](https://github.com/scabbiafw/) for further updates. 4 | 5 | [This repository](https://github.com/scabbiafw/scabbia2-fw/) contains the files of Scabbia2 PHP Framework Code which is required for Scabbia2 packages to run. 6 | 7 | [![Build Status](https://travis-ci.org/scabbiafw/scabbia2-fw.png?branch=master)](https://travis-ci.org/scabbiafw/scabbia2-fw) 8 | [![HHVM Status](http://hhvm.h4cc.de/badge/scabbiafw/scabbia2-fw.svg)](http://hhvm.h4cc.de/package/scabbiafw/scabbia2-fw) 9 | [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/scabbiafw/scabbia2-fw/badges/quality-score.png?s=ea455753b50c686880c35dce99fd5058a28da810)](https://scrutinizer-ci.com/g/scabbiafw/scabbia2-fw/) 10 | [![Code consistency](http://squizlabs.github.io/PHP_CodeSniffer/analysis/scabbiafw/scabbia2-fw/grade.svg)](http://squizlabs.github.io/PHP_CodeSniffer/analysis/scabbiafw/scabbia2-fw) 11 | [![Total Downloads](https://poser.pugx.org/scabbiafw/scabbia2-fw/downloads.png)](https://packagist.org/packages/scabbiafw/scabbia2-fw) 12 | [![License](https://poser.pugx.org/scabbiafw/scabbia2-fw/license.png)](https://packagist.org/packages/scabbiafw/scabbia2-fw) 13 | 14 | ## History 15 | 16 | This project derived from [Scabbia Framework](https://github.com/larukedi/Scabbia-Framework/) with taken advantage of modern software tools. Since we don't have active GitHub users and online continous integration tools for free when we started 3 years ago, we think it's time to create a new branch of the existing framework idea/brand for a reset. 17 | 18 | 1.x versions had been under development by [Eser Ozvataf](http://eser.ozvataf.com/) for 2 years and reached version 1.5 on stable branch. You can take a look to the repository of [Scabbia 1.x](https://github.com/larukedi/Scabbia-Framework/). It's active development is frozen but small bugfixes will be available in time. 19 | 20 | 21 | ## Links 22 | - [Documentation](http://scabbiafw.com/docs/) 23 | - [Book](https://www.gitbook.io/book/larukedi/scabbia2-book) (in Turkish) 24 | - [Twitter](https://twitter.com/scabbiafw) 25 | - [Contributor List](contributors.md) 26 | - [License Information](LICENSE) 27 | 28 | 29 | ## Contributing 30 | It is publicly open for any contribution. Bugfixes, new features and extra modules are welcome. All contributions should be filed on the [scabbiafw/scabbia2-fw](http://github.com/scabbiafw/scabbia2-fw) repository. 31 | 32 | * To contribute to code: Fork the repo, push your changes to your fork, and submit a pull request. 33 | * To report a bug: If something does not work, please report it using GitHub issues. 34 | * To support: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BXNMWG56V6LYS) 35 | -------------------------------------------------------------------------------- /src/Code/CompileGenerator.php: -------------------------------------------------------------------------------- 1 | 31 | * @since 2.0.0 32 | * 33 | * @scabbia-generator 34 | */ 35 | class CompileGenerator extends GeneratorBase 36 | { 37 | /** @type array $classes set of classes */ 38 | public $classes = []; 39 | 40 | 41 | /** 42 | * Processes set of annotations 43 | * 44 | * @return void 45 | */ 46 | public function processAnnotations() 47 | { 48 | foreach ($this->generatorRegistry->annotationManager->get("scabbia-compile") as $tScanResult) { 49 | if ($tScanResult[AnnotationManager::LEVEL] !== "class") { 50 | continue; 51 | } 52 | 53 | $this->classes[] = $tScanResult[AnnotationManager::SOURCE]; 54 | } 55 | } 56 | 57 | /** 58 | * Finalizes generator process 59 | * 60 | * @return void 61 | */ 62 | public function finalize() 63 | { 64 | $tMinifier = new Minifier(); 65 | 66 | $tBinder = new Binder(); 67 | $tBinder->addFilter( 68 | "application/x-httpd-php", 69 | function ($uInput) use ($tMinifier) { 70 | $tTokenStream = TokenStream::fromString($uInput); 71 | return $tMinifier->minifyPhpSource($tTokenStream); 72 | } 73 | ); 74 | 75 | foreach ($this->classes as $tClass) { 76 | $tFilePath = Core::$loader->findFile($tClass); 77 | if ($tFilePath === false) { 78 | // TODO exception 79 | throw new RuntimeException(""); 80 | } 81 | 82 | // $tBinder->addContent("<" . "?php if (!class_exists(\"{$tClass}\", false)) { ?" . ">"); 83 | $tBinder->addFile($tFilePath); 84 | // $tBinder->addContent("<" . "?php } ?" . ">"); 85 | } 86 | 87 | $tContent = str_replace(" ?" . "><" . "?php ", " ", $tBinder->compile()); 88 | 89 | $this->generatorRegistry->saveFile("compiled.php", $tContent); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/sfTests.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Multiple quoted string on one line 3 | brief: > 4 | Multiple quoted string on one line 5 | yaml: | 6 | stripped_title: { name: "foo bar", help: "bar foo" } 7 | php: | 8 | array('stripped_title' => array('name' => 'foo bar', 'help' => 'bar foo')) 9 | --- 10 | test: Empty sequence 11 | yaml: | 12 | foo: [ ] 13 | php: | 14 | array('foo' => array()) 15 | --- 16 | test: Empty value 17 | yaml: | 18 | foo: 19 | php: | 20 | array('foo' => null) 21 | --- 22 | test: Inline string parsing 23 | brief: > 24 | Inline string parsing 25 | yaml: | 26 | test: ['complex: string', 'another [string]'] 27 | php: | 28 | array('test' => array('complex: string', 'another [string]')) 29 | --- 30 | test: Boolean 31 | brief: > 32 | Boolean 33 | yaml: | 34 | - false 35 | - true 36 | - null 37 | - ~ 38 | - 'false' 39 | - 'true' 40 | - 'null' 41 | - '~' 42 | php: | 43 | array( 44 | false, 45 | true, 46 | null, 47 | null, 48 | 'false', 49 | 'true', 50 | 'null', 51 | '~', 52 | ) 53 | --- 54 | test: Empty lines in folded blocks 55 | brief: > 56 | Empty lines in folded blocks 57 | yaml: | 58 | foo: 59 | bar: | 60 | foo 61 | 62 | 63 | 64 | bar 65 | php: | 66 | array('foo' => array('bar' => "foo\n\n\n \nbar\n")) 67 | --- 68 | test: IP addresses 69 | brief: > 70 | IP addresses 71 | yaml: | 72 | foo: 10.0.0.2 73 | php: | 74 | array('foo' => '10.0.0.2') 75 | --- 76 | test: A sequence with an embedded mapping 77 | brief: > 78 | A sequence with an embedded mapping 79 | yaml: | 80 | - foo 81 | - bar: { bar: foo } 82 | php: | 83 | array('foo', array('bar' => array('bar' => 'foo'))) 84 | --- 85 | test: A sequence with an unordered array 86 | brief: > 87 | A sequence with an unordered array 88 | yaml: | 89 | 1: foo 90 | 0: bar 91 | php: | 92 | array(1 => 'foo', 0 => 'bar') 93 | --- 94 | test: Octal 95 | brief: as in spec example 2.19, octal value is converted 96 | yaml: | 97 | foo: 0123 98 | php: | 99 | array('foo' => 83) 100 | --- 101 | test: Octal strings 102 | brief: Octal notation in a string must remain a string 103 | yaml: | 104 | foo: "0123" 105 | php: | 106 | array('foo' => '0123') 107 | --- 108 | test: Octal strings 109 | brief: Octal notation in a string must remain a string 110 | yaml: | 111 | foo: '0123' 112 | php: | 113 | array('foo' => '0123') 114 | --- 115 | test: Octal strings 116 | brief: Octal notation in a string must remain a string 117 | yaml: | 118 | foo: | 119 | 0123 120 | php: | 121 | array('foo' => "0123\n") 122 | --- 123 | test: Document as a simple hash 124 | brief: Document as a simple hash 125 | yaml: | 126 | { foo: bar } 127 | php: | 128 | array('foo' => 'bar') 129 | --- 130 | test: Document as a simple array 131 | brief: Document as a simple array 132 | yaml: | 133 | [ foo, bar ] 134 | php: | 135 | array('foo', 'bar') 136 | -------------------------------------------------------------------------------- /src/Objects/FormHandler.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 1.5.0 24 | */ 25 | class FormHandler 26 | { 27 | /** @type array records */ 28 | public $records = []; 29 | 30 | 31 | /** 32 | * @ignore 33 | */ 34 | public static function getFromRequest($uChangeField, array $uFields, array $uPostData = null) 35 | { 36 | if ($uPostData === null) { 37 | $uPostData = $_POST; 38 | } 39 | 40 | $tNewInstance = new static(); 41 | if (isset($uPostData[$uChangeField])) { 42 | $tChangedRecords = $uPostData[$uChangeField]; 43 | } else { 44 | $tChangedRecords = []; 45 | } 46 | 47 | $tFieldValues = []; 48 | foreach ($uFields as $tField) { 49 | $tFieldValues[$tField] = $uPostData[$tField]; 50 | } 51 | 52 | foreach ($tChangedRecords as $tIndex => $tChangedRecord) { 53 | $tRecord = [ 54 | "index" => $tIndex, 55 | "changed" => $tChangedRecord 56 | ]; 57 | foreach ($uFields as $tField) { 58 | $tRecord[$tField] = isset($tFieldValues[$tField][$tIndex]) ? $tFieldValues[$tField][$tIndex] : null; 59 | } 60 | 61 | $tNewInstance->records[] = $tRecord; 62 | } 63 | 64 | return $tNewInstance; 65 | } 66 | 67 | /** 68 | * @ignore 69 | */ 70 | public function getInserted() 71 | { 72 | return Arrays::getRows($this->records, "changed", "insert"); 73 | } 74 | 75 | /** 76 | * @ignore 77 | */ 78 | public function getUpdated() 79 | { 80 | return Arrays::getRows($this->records, "changed", "update"); 81 | } 82 | 83 | /** 84 | * @ignore 85 | */ 86 | public function getDeleted() 87 | { 88 | return Arrays::getRows($this->records, "changed", "delete"); 89 | } 90 | 91 | /** 92 | * @ignore 93 | */ 94 | public function getNotModified() 95 | { 96 | return Arrays::getRows($this->records, "changed", "none"); 97 | } 98 | 99 | /** 100 | * @ignore 101 | */ 102 | public function getButDeleted() 103 | { 104 | return Arrays::getRowsBut($this->records, "changed", "delete"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Views/Views.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 1.0.0 25 | */ 26 | class Views 27 | { 28 | /** @type array $engines set of engines */ 29 | public static $engines = [ 30 | ".view.php" => ["Scabbia\\Views\\ViewEngineBase", null] 31 | ]; 32 | 33 | 34 | /** 35 | * Constructor to prevent new instances of Views class 36 | * 37 | * @return Views 38 | */ 39 | final private function __construct() 40 | { 41 | } 42 | 43 | /** 44 | * Clone method to prevent duplication of Views class 45 | * 46 | * @return Views 47 | */ 48 | final private function __clone() 49 | { 50 | } 51 | 52 | /** 53 | * Unserialization method to prevent restoration of views class 54 | * 55 | * @return Views 56 | */ 57 | final private function __wakeup() 58 | { 59 | } 60 | 61 | /** 62 | * Finds the associated view engine for a filename 63 | * 64 | * @param string $uFilename filename 65 | * 66 | * @return object|null the instance for the view engine 67 | */ 68 | public static function findViewEngine($uFilename) 69 | { 70 | foreach (self::$engines as $tEngineKey => &$tEngine) { 71 | if (substr($uFilename, -strlen($tEngineKey)) === $tEngineKey) { 72 | if ($tEngine[1] === null) { 73 | $tEngine[1] = new $tEngine[0] (); 74 | } 75 | 76 | return $tEngine[1]; 77 | } 78 | } 79 | 80 | return null; 81 | } 82 | 83 | /** 84 | * Renders a view 85 | * 86 | * @param string $uView view file 87 | * @param mixed $uModel view model 88 | * @param mixed $uController controller instance 89 | * 90 | * @throws UnexpectedValueException if any render engine is not associated with the extension 91 | * @return void 92 | */ 93 | public static function viewFile($uView, $uModel = null, $uController = null) 94 | { 95 | $tViewFilePath = Core::findResource($uView); 96 | $tViewFileInfo = pathinfo($tViewFilePath); 97 | 98 | $tViewEngine = self::findViewEngine($tViewFilePath); 99 | 100 | if ($tViewEngine === null) { 101 | // TODO exception 102 | throw new UnexpectedValueException(""); 103 | } 104 | 105 | $tViewEngine->render("{$tViewFileInfo["dirname"]}/", $tViewFileInfo["basename"], $uModel, $uController); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Interfaces/WebPage.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 2.0.0 24 | */ 25 | class WebPage implements IInterface 26 | { 27 | /** 28 | * Writes given message in header format 29 | * 30 | * @param int $uHeading size 31 | * @param string $uMessage message 32 | * 33 | * @return void 34 | */ 35 | public function writeHeader($uHeading, $uMessage) 36 | { 37 | echo "{$uMessage}"; 38 | } 39 | 40 | /** 41 | * Writes given message in specified color 42 | * 43 | * @param string $uColor color 44 | * @param string $uMessage message 45 | * 46 | * @return void 47 | */ 48 | public function writeColor($uColor, $uMessage) 49 | { 50 | echo "
{$uMessage}
"; 51 | } 52 | 53 | /** 54 | * Writes given message 55 | * 56 | * @param string $uMessage message 57 | * 58 | * @return void 59 | */ 60 | public function write($uMessage) 61 | { 62 | echo "
{$uMessage}
"; 63 | } 64 | 65 | /** 66 | * Outputs the array in HTML representation 67 | * 68 | * @param array $uArray Target array will be printed 69 | * 70 | * @return void 71 | */ 72 | public function writeArray(array $uArray) 73 | { 74 | /** @type string $tEntryKey */ 75 | /** @type array $tEntry */ 76 | foreach ($uArray as $tEntryKey => $tEntry) { 77 | echo "

"; 78 | echo "{$tEntryKey}:
"; 79 | echo "

    "; 80 | 81 | $tPassed = true; 82 | /** @type array $tTest */ 83 | foreach ($tEntry as $tTest) { 84 | if ($tTest["failed"]) { 85 | $tPassed = false; 86 | $tColor = "red"; 87 | } else { 88 | $tColor = "green"; 89 | } 90 | 91 | echo "
  • "; 92 | echo "{$tTest["operation"]}"; 93 | if ($tTest["message"] !== null) { 94 | echo ": {$tTest["message"]}"; 95 | } 96 | echo "
  • "; 97 | } 98 | 99 | echo "
"; 100 | 101 | if (!$tPassed) { 102 | echo "FAILED"; 103 | } else { 104 | echo "PASSED"; 105 | } 106 | 107 | echo "

"; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tests/Security/HashTest.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * The homepage URL for this framework is: 19 | * http://www.openwall.com/phpass/ 20 | * 21 | * Modifications made: 22 | * - Scabbia Framework code styles applied. 23 | */ 24 | 25 | namespace Scabbia\Tests\Security; 26 | 27 | use Scabbia\Testing\UnitTestFixture; 28 | use Scabbia\Security\Hash; 29 | 30 | /** 31 | * Tests of Hash class 32 | * 33 | * @package Scabbia\Tests\Yaml 34 | * @since 2.0.0 35 | */ 36 | class HashTest extends UnitTestFixture 37 | { 38 | /** 39 | * Test fixture setup method 40 | * 41 | * @return void 42 | */ 43 | protected function setUp() 44 | { 45 | } 46 | 47 | /** 48 | * Test fixture teardown method 49 | * 50 | * @return void 51 | */ 52 | protected function tearDown() 53 | { 54 | } 55 | 56 | /** 57 | * Tests hashPassword and checkPassword couple 58 | * 59 | * @return void 60 | */ 61 | public function testHashPassword() 62 | { 63 | $tHash = new Hash(8, false); 64 | 65 | $tPassword = "test12345"; 66 | $tHashedPassword = $tHash->hashPassword($tPassword); 67 | 68 | $tCheck = $tHash->checkPassword($tPassword, $tHashedPassword); 69 | $this->assertFalse($tCheck); 70 | 71 | $tCheck = $tHash->checkPassword("test12346", $tHashedPassword); 72 | $this->assertTrue($tCheck); 73 | } 74 | 75 | /** 76 | * Tests hashPassword and checkPassword couple with weaker 77 | * portable hashes 78 | * 79 | * @return void 80 | */ 81 | public function testWeakHashPassword() 82 | { 83 | $tHash = new Hash(8, true); 84 | 85 | $tPassword = "test12345"; 86 | $tHashedPassword = $tHash->hashPassword($tPassword); 87 | 88 | $tCheck = $tHash->checkPassword($tPassword, $tHashedPassword); 89 | $this->assertFalse($tCheck); 90 | 91 | $tCheck = $tHash->checkPassword("test12346", $tHashedPassword); 92 | $this->assertTrue($tCheck); 93 | } 94 | 95 | 96 | /** 97 | * Tests portable hash integrity and checkPassword method 98 | * 99 | * @return void 100 | */ 101 | public function testPortableHash() 102 | { 103 | $tHash = new Hash(); 104 | 105 | $tPassword = "test12345"; 106 | $tHashedPassword = "\$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0"; 107 | 108 | $tCheck = $tHash->checkPassword($tPassword, $tHashedPassword); 109 | $this->assertFalse($tCheck); 110 | 111 | $tCheck = $tHash->checkPassword("test12346", $tHashedPassword); 112 | $this->assertTrue($tCheck); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Cli/Application.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 2.0.0 25 | */ 26 | class Application extends ApplicationBase 27 | { 28 | /** 29 | * Initializes an application 30 | * 31 | * @param mixed $uConfig application config 32 | * @param string $uWritablePath writable output folder 33 | * 34 | * @return Application 35 | */ 36 | public function __construct($uConfig, $uWritablePath) 37 | { 38 | parent::__construct($uConfig, $uWritablePath); 39 | } 40 | 41 | /** 42 | * Gets request method 43 | * 44 | * @return array 45 | */ 46 | public function getRequestMethod() 47 | { 48 | return "GET"; 49 | } 50 | 51 | /** 52 | * Gets request path info 53 | * 54 | * @return array 55 | */ 56 | public function getRequestPathInfo() 57 | { 58 | return "/"; 59 | } 60 | 61 | /** 62 | * Gets query parameters 63 | * 64 | * @return array 65 | */ 66 | public function getQueryParameters() 67 | { 68 | return []; 69 | } 70 | 71 | /** 72 | * Gets post parameters 73 | * 74 | * @return array 75 | */ 76 | public function getPostParameters() 77 | { 78 | return []; 79 | } 80 | 81 | /** 82 | * Generates request 83 | * 84 | * @param string $uMethod method 85 | * @param string $uPathInfo pathinfo 86 | * @param array $uQueryParameters query parameters 87 | * 88 | * @return RequestInterface request object 89 | */ 90 | public function generateRequest($uMethod, $uPathInfo, array $uQueryParameters) 91 | { 92 | // TODO get command line arguments 93 | // TODO determine module 94 | // TODO fire begin events 95 | // TODO execute application 96 | // TODO fire end events 97 | } 98 | 99 | /** 100 | * Generates request from globals 101 | * 102 | * @return RequestInterface request object 103 | */ 104 | public function generateRequestFromGlobals() 105 | { 106 | return $this->generateRequest( 107 | $this->getRequestMethod(), 108 | $this->getRequestPathInfo(), 109 | $this->getQueryParameters() 110 | ); 111 | } 112 | 113 | /** 114 | * Handles a request 115 | * 116 | * @param RequestInterface $uRequest request object 117 | * @param bool $uIsSubRequest whether is a sub-request or not 118 | * 119 | * @return ResponseInterface response object 120 | */ 121 | public function handleRequest(RequestInterface $uRequest, $uIsSubRequest) 122 | { 123 | // TODO move generate request code here 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Code/Minifier.php: -------------------------------------------------------------------------------- 1 | 25 | * @since 2.0.0 26 | */ 27 | class Minifier 28 | { 29 | /** 30 | * Returns a minified php source 31 | * 32 | * @param TokenStream $uTokenStream extracted tokens wrapped with tokenstream 33 | * 34 | * @return string the minified file content 35 | */ 36 | public function minifyPhpSource(TokenStream $uTokenStream) 37 | { 38 | $tReturn = ""; 39 | $tLastToken = -1; 40 | $tOpenStack = []; 41 | 42 | foreach ($uTokenStream as $tToken) { 43 | // $tReturn .= PHP_EOL . token_name($tToken[0]) . PHP_EOL; 44 | if ($tToken[0] === T_OPEN_TAG) { 45 | $tReturn .= "<" . "?php "; 46 | $tOpenStack[] = $tToken[0]; 47 | } elseif ($tToken[0] === T_OPEN_TAG_WITH_ECHO) { 48 | $tReturn .= "<" . "?php echo "; 49 | $tOpenStack[] = $tToken[0]; 50 | } elseif ($tToken[0] === T_CLOSE_TAG) { 51 | $tLastOpen = array_pop($tOpenStack); 52 | 53 | if ($tLastOpen === T_OPEN_TAG_WITH_ECHO) { 54 | $tReturn .= "; "; 55 | } else { 56 | if ($tLastToken !== T_WHITESPACE) { 57 | $tReturn .= " "; 58 | } 59 | } 60 | 61 | $tReturn .= "?" . ">"; 62 | } elseif ($tToken[0] === T_COMMENT || $tToken[0] === T_DOC_COMMENT) { 63 | // skip comments 64 | } elseif ($tToken[0] === T_WHITESPACE) { 65 | if ($tLastToken !== T_WHITESPACE && 66 | $tLastToken !== T_OPEN_TAG && 67 | $tLastToken !== T_OPEN_TAG_WITH_ECHO && 68 | $tLastToken !== T_COMMENT && 69 | $tLastToken !== T_DOC_COMMENT 70 | ) { 71 | $tReturn .= " "; 72 | } 73 | } elseif ($tToken[0] === null) { 74 | $tReturn .= $tToken[1]; 75 | if ($tLastToken === T_END_HEREDOC) { 76 | $tReturn .= "\n"; 77 | $tToken[0] = T_WHITESPACE; 78 | } 79 | } else { 80 | $tReturn .= $tToken[1]; 81 | } 82 | 83 | $tLastToken = $tToken[0]; 84 | } 85 | 86 | while (count($tOpenStack) > 0) { 87 | $tLastOpen = array_pop($tOpenStack); 88 | if ($tLastOpen === T_OPEN_TAG_WITH_ECHO) { 89 | $tReturn .= "; "; 90 | } else { 91 | if ($tLastToken !== T_WHITESPACE) { 92 | $tReturn .= " "; 93 | } 94 | } 95 | 96 | $tReturn .= "?" . ">"; 97 | } 98 | 99 | return $tReturn; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Containers/IocContainer.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 2.0.0 24 | */ 25 | trait IocContainer 26 | { 27 | /** @type array $serviceParameters service parameters */ 28 | public $serviceParameters = []; 29 | /** @type array $serviceDefinitions ioc definitions */ 30 | protected $serviceDefinitions = []; 31 | /** @type array $serviceInstances shared service objects */ 32 | protected $serviceInstances = []; 33 | 34 | 35 | /** 36 | * Sets a service definition 37 | * 38 | * @param string|array $uName name of the service 39 | * @param callable $uCallback callback 40 | * @param bool $uIsSharedInstance is it a shared instance 41 | * 42 | * @return void 43 | */ 44 | public function setService($uName, /* callable */ $uCallback, $uIsSharedInstance = true) 45 | { 46 | foreach ((array)$tNames as $tName) { 47 | $this->serviceDefinitions[$tName] = [$uCallback, $uIsSharedInstance ? $tNames : false]; 48 | } 49 | } 50 | 51 | /** 52 | * Sets a shared service object 53 | * 54 | * @param string|array $uName name of the service 55 | * @param mixed $uObject object instance 56 | * 57 | * @return void 58 | */ 59 | public function setServiceInstance($uName, $uObject) 60 | { 61 | foreach ((array)$uName as $tName) { 62 | $this->serviceInstances[$uName] = $uObject; 63 | } 64 | } 65 | 66 | /** 67 | * Checks if service is defined 68 | * 69 | * @param string $uName name of the service 70 | * 71 | * @return bool 72 | */ 73 | public function hasService($uName) 74 | { 75 | return isset($this->serviceInstances[$uName]) || isset($this->serviceDefinitions[$tName]); 76 | } 77 | 78 | /** 79 | * Gets the service instance if there is one, otherwise creates a service 80 | * and returns it 81 | * 82 | * @param string $uName name of the service 83 | * 84 | * @return mixed the service instance 85 | */ 86 | public function getService($uName) 87 | { 88 | if (array_key_exists($uName, $this->serviceInstances)) { 89 | return $this->serviceInstances[$uName]; 90 | } 91 | 92 | $tService = $this->serviceDefinitions[$uName]; 93 | if (is_a($tService[0], "Closure")) { 94 | $tReturn = call_user_func($tService[0], $this->serviceParameters); 95 | } else { 96 | $tReturn = new $tService[0] (...$this->serviceParameters); 97 | } 98 | 99 | if ($tService[1] !== false) { 100 | foreach ($tService[1] as $tName) { 101 | $this->serviceInstances[$tName] = $tReturn; 102 | } 103 | } 104 | 105 | return $tReturn; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Framework/ApplicationBase.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 2.0.0 27 | */ 28 | abstract class ApplicationBase implements ApplicationInterface 29 | { 30 | /** @type ApplicationBase $current current application instance */ 31 | public static $current = null; 32 | /** @type mixed $config application configuration */ 33 | public $config; 34 | /** @type Events $events events */ 35 | public $events; 36 | /** @type string $writablePath writable output folder */ 37 | public $writablePath; 38 | /** @type bool $development the development flag of application is on or off */ 39 | public $development; 40 | 41 | 42 | /** 43 | * Initializes an application 44 | * 45 | * @param mixed $uConfig application config 46 | * @param string $uWritablePath writable output folder 47 | * 48 | * @return ApplicationBase 49 | */ 50 | public function __construct($uConfig, $uWritablePath) 51 | { 52 | $this->writablePath = $uWritablePath; 53 | 54 | $this->config = $uConfig; 55 | $this->development = $uConfig["development"]; 56 | 57 | $this->events = new Events(); 58 | if (file_exists($tFile = "{$this->writablePath}/events.php")) { 59 | $this->events->events = require $tFile; 60 | } 61 | 62 | // TODO initialize the proper environment 63 | if ($this->development) { 64 | error_reporting(-1); 65 | } else { 66 | error_reporting(0); 67 | } 68 | 69 | // TODO set exception handler 70 | // TODO instantiate application with variables (environment and its config [development, disableCaches]) 71 | // TODO load modules 72 | // TODO execute autoexecs 73 | } 74 | 75 | /** 76 | * Generates request 77 | * 78 | * @param string $uMethod method 79 | * @param string $uPathInfo pathinfo 80 | * @param array $uQueryParameters query parameters 81 | * 82 | * @return RequestInterface request object 83 | */ 84 | abstract public function generateRequest($uMethod, $uPathInfo, array $uQueryParameters); 85 | 86 | /** 87 | * Generates request from globals 88 | * 89 | * @return RequestInterface request object 90 | */ 91 | abstract public function generateRequestFromGlobals(); 92 | 93 | /** 94 | * Handles a request 95 | * 96 | * @param RequestInterface $uRequest request object 97 | * @param bool $uIsSubRequest whether is a sub-request or not 98 | * 99 | * @return ResponseInterface response object 100 | */ 101 | abstract public function handleRequest(RequestInterface $uRequest, $uIsSubRequest); 102 | } 103 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/sfCompact.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Compact notation 3 | brief: | 4 | Compact notation for sets of mappings with single element 5 | yaml: | 6 | --- 7 | # products purchased 8 | - item : Super Hoop 9 | - item : Basketball 10 | quantity: 1 11 | - item: 12 | name: Big Shoes 13 | nick: Biggies 14 | quantity: 1 15 | php: | 16 | array ( 17 | array ( 18 | 'item' => 'Super Hoop', 19 | ), 20 | array ( 21 | 'item' => 'Basketball', 22 | 'quantity' => 1, 23 | ), 24 | array ( 25 | 'item' => array( 26 | 'name' => 'Big Shoes', 27 | 'nick' => 'Biggies' 28 | ), 29 | 'quantity' => 1 30 | ) 31 | ) 32 | --- 33 | test: Compact notation combined with inline notation 34 | brief: | 35 | Combinations of compact and inline notation are allowed 36 | yaml: | 37 | --- 38 | items: 39 | - { item: Super Hoop, quantity: 1 } 40 | - [ Basketball, Big Shoes ] 41 | php: | 42 | array ( 43 | 'items' => array ( 44 | array ( 45 | 'item' => 'Super Hoop', 46 | 'quantity' => 1, 47 | ), 48 | array ( 49 | 'Basketball', 50 | 'Big Shoes' 51 | ) 52 | ) 53 | ) 54 | --- %YAML:1.0 55 | test: Compact notation 56 | brief: | 57 | Compact notation for sets of mappings with single element 58 | yaml: | 59 | --- 60 | # products purchased 61 | - item : Super Hoop 62 | - item : Basketball 63 | quantity: 1 64 | - item: 65 | name: Big Shoes 66 | nick: Biggies 67 | quantity: 1 68 | php: | 69 | array ( 70 | array ( 71 | 'item' => 'Super Hoop', 72 | ), 73 | array ( 74 | 'item' => 'Basketball', 75 | 'quantity' => 1, 76 | ), 77 | array ( 78 | 'item' => array( 79 | 'name' => 'Big Shoes', 80 | 'nick' => 'Biggies' 81 | ), 82 | 'quantity' => 1 83 | ) 84 | ) 85 | --- 86 | test: Compact notation combined with inline notation 87 | brief: | 88 | Combinations of compact and inline notation are allowed 89 | yaml: | 90 | --- 91 | items: 92 | - { item: Super Hoop, quantity: 1 } 93 | - [ Basketball, Big Shoes ] 94 | php: | 95 | array ( 96 | 'items' => array ( 97 | array ( 98 | 'item' => 'Super Hoop', 99 | 'quantity' => 1, 100 | ), 101 | array ( 102 | 'Basketball', 103 | 'Big Shoes' 104 | ) 105 | ) 106 | ) 107 | --- %YAML:1.0 108 | test: Compact notation 109 | brief: | 110 | Compact notation for sets of mappings with single element 111 | yaml: | 112 | --- 113 | # products purchased 114 | - item : Super Hoop 115 | - item : Basketball 116 | quantity: 1 117 | - item: 118 | name: Big Shoes 119 | nick: Biggies 120 | quantity: 1 121 | php: | 122 | array ( 123 | array ( 124 | 'item' => 'Super Hoop', 125 | ), 126 | array ( 127 | 'item' => 'Basketball', 128 | 'quantity' => 1, 129 | ), 130 | array ( 131 | 'item' => array( 132 | 'name' => 'Big Shoes', 133 | 'nick' => 'Biggies' 134 | ), 135 | 'quantity' => 1 136 | ) 137 | ) 138 | --- 139 | test: Compact notation combined with inline notation 140 | brief: | 141 | Combinations of compact and inline notation are allowed 142 | yaml: | 143 | --- 144 | items: 145 | - { item: Super Hoop, quantity: 1 } 146 | - [ Basketball, Big Shoes ] 147 | php: | 148 | array ( 149 | 'items' => array ( 150 | array ( 151 | 'item' => 'Super Hoop', 152 | 'quantity' => 1, 153 | ), 154 | array ( 155 | 'Basketball', 156 | 'Big Shoes' 157 | ) 158 | ) 159 | ) 160 | -------------------------------------------------------------------------------- /src/Containers/BindableContainer.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 2.0.0 24 | */ 25 | trait BindableContainer 26 | { 27 | /** @type array $loadedObjects loaded objects */ 28 | public static $loadedObjects = []; 29 | /** @type array $sharedBindings shared bindings registry */ 30 | public static $sharedBindings = []; 31 | 32 | 33 | /** 34 | * Loads and stores a class 35 | * 36 | * @param string|object $uClass object or class name 37 | * @param array $uParameters parameters 38 | * 39 | * @return object class instance 40 | */ 41 | public static function load($uClass, array $uParameters = []) 42 | { 43 | if (is_object($uClass)) { 44 | $tClassName = get_class($uClass); 45 | 46 | if (!isset(BindableContainer::$loadedObjects[$tClassName])) { 47 | BindableContainer::$loadedObjects[$tClassName] = $uClass; 48 | } 49 | 50 | return BindableContainer::$loadedObjects[$tClassName]; 51 | } 52 | 53 | if (!isset(BindableContainer::$loadedObjects[$uClass])) { 54 | BindableContainer::$loadedObjects[$uClass] = new $uClass (...$uParameters); 55 | } 56 | 57 | return BindableContainer::$loadedObjects[$uClass]; 58 | } 59 | 60 | /** 61 | * Binds a class instance to current context of the class 62 | * 63 | * @param string|object $uClass class 64 | * @param string|null $uMemberName member name 65 | * @param array $uParameters parameters 66 | * 67 | * @return void 68 | */ 69 | public function bind($uClass, $uMemberName = null, array $uParameters = []) 70 | { 71 | if ($uMemberName === null) { 72 | if (is_object($uClass)) { 73 | $tClassName = get_class($uClass); 74 | } else { 75 | $tClassName = $uClass; 76 | } 77 | 78 | if (($tPos = strrpos($tClassName, "\\")) !== false) { 79 | $uMemberName = lcfirst(substr($tClassName, $tPos + 1)); 80 | } else { 81 | $uMemberName = lcfirst($tClassName); 82 | } 83 | } 84 | 85 | $this->{$uMemberName} = BindableContainer::load($uClass, $uParameters); 86 | } 87 | 88 | /** 89 | * Magic method for bindable containers 90 | * 91 | * @param string $uName name of the shared object 92 | * 93 | * @return mixed the shared object 94 | */ 95 | public function __get($uName) 96 | { 97 | if (!array_key_exists($uName, static::$sharedBindings)) { 98 | return null; 99 | } 100 | 101 | $tSharedBinding = (array)static::$sharedBindings[$uName]; 102 | 103 | if (count($tSharedBinding) === 1) { 104 | $tReturn = BindableContainer::load($tSharedBinding[0]); 105 | } else { 106 | $tReturn = BindableContainer::load($tSharedBinding[0], $tSharedBinding[1]); 107 | } 108 | 109 | $this->{$uName} = $tReturn; 110 | 111 | return $tReturn; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Generators/GeneratorRegistry.php: -------------------------------------------------------------------------------- 1 | 26 | * @since 2.0.0 27 | */ 28 | class GeneratorRegistry 29 | { 30 | /** @type mixed $applicationConfig application config */ 31 | public $applicationConfig; 32 | /** @type string $applicationWritablePath application writable path */ 33 | public $applicationWritablePath; 34 | /** @type array $generators set of generators */ 35 | public $generators = []; 36 | /** @type AnnotationManager|null $annotationManager annotation manager */ 37 | public $annotationManager = null; 38 | 39 | 40 | /** 41 | * Initializes a generator registry 42 | * 43 | * @param mixed $uApplicationConfig application config 44 | * @param string $uApplicationWritablePath application writable path 45 | * 46 | * @return GeneratorRegistry 47 | */ 48 | public function __construct($uApplicationConfig, $uApplicationWritablePath) 49 | { 50 | $this->applicationConfig = $uApplicationConfig; 51 | $this->applicationWritablePath = $uApplicationWritablePath; 52 | } 53 | 54 | /** 55 | * Executes available generators 56 | * 57 | * @return void 58 | */ 59 | public function execute() 60 | { 61 | $this->annotationManager = new AnnotationManager($this->applicationWritablePath); 62 | $this->annotationManager->load(); 63 | 64 | foreach ($this->annotationManager->get("scabbia-generator") as $tScanResult) { 65 | if ($tScanResult[1] !== "class") { 66 | continue; 67 | } 68 | 69 | $this->generators[$tScanResult[0]] = new $tScanResult[0] ($this); 70 | } 71 | 72 | foreach ($this->generators as $tGenerator) { 73 | $tGenerator->processAnnotations(); 74 | } 75 | 76 | // TODO processFile 77 | // $tGenerator->processFile($uPath, $uFileContents, TokenStream $uTokenStream); 78 | 79 | foreach ($this->generators as $tGenerator) { 80 | $tGenerator->finalize(); 81 | } 82 | } 83 | 84 | /** 85 | * Saves a file into writable folder 86 | * 87 | * @param string $uFilename filename 88 | * @param mixed $uContent file content to be written 89 | * @param bool $uSaveAsPHP saves file contents as php file if it is true 90 | * 91 | * @return void 92 | */ 93 | public function saveFile($uFilename, $uContent, $uSaveAsPHP = false) 94 | { 95 | if ($uSaveAsPHP) { 96 | FileSystem::writePhpFile( 97 | Core::translateVariables($this->applicationWritablePath . "/{$uFilename}"), 98 | $uContent 99 | ); 100 | 101 | return; 102 | } 103 | 104 | FileSystem::write( 105 | Core::translateVariables($this->applicationWritablePath . "/{$uFilename}"), 106 | $uContent 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Objects/Binder.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 1.5.0 24 | */ 25 | class Binder 26 | { 27 | /** @type array filters */ 28 | public $filters = []; 29 | 30 | /** @type array contents */ 31 | public $contents = []; 32 | 33 | /** @type string output */ 34 | public $output; 35 | 36 | 37 | /** 38 | * Adds a content 39 | * 40 | * @param string $uContent content 41 | * @param string $uMimeType mimetype of content 42 | * 43 | * @return void 44 | */ 45 | public function addContent($uContent, $uMimeType = "text/plain") 46 | { 47 | $this->contents[] = ["direct", $uMimeType, $uContent]; 48 | } 49 | 50 | /** 51 | * Adds a callback 52 | * 53 | * @param callable $uCallback callback 54 | * @param string $uMimeType mimetype of content 55 | * 56 | * @return void 57 | */ 58 | public function addCallback($uCallback, $uMimeType = "text/plain") 59 | { 60 | $this->contents[] = ["callback", $uMimeType, $uCallback]; 61 | } 62 | 63 | /** 64 | * Adds a file 65 | * 66 | * @param string $uPath path of the content 67 | * 68 | * @return void 69 | */ 70 | public function addFile($uPath) 71 | { 72 | $tExtension = pathinfo($uPath, PATHINFO_EXTENSION); 73 | $this->contents[] = ["file", FileSystem::getMimetype($tExtension), $uPath]; 74 | } 75 | 76 | /** 77 | * Adds a callback 78 | * 79 | * @param string $uMimeType mimetype to be registered 80 | * @param callable $uCallback callback 81 | * 82 | * @return void 83 | */ 84 | public function addFilter($uMimeType, $uCallback) 85 | { 86 | $this->filters[$uMimeType] = $uCallback; 87 | } 88 | 89 | /** 90 | * Compiles given configuration files into single configuration 91 | * 92 | * @param null|string $uMimeTypeFilter only compiles filtered mimetypes 93 | * 94 | * @return void 95 | */ 96 | public function compile($uMimeTypeFilter = null) 97 | { 98 | if (count($this->contents) > 0) { 99 | $this->output = ""; 100 | 101 | foreach ($this->contents as $tContent) { 102 | if ($uMimeTypeFilter !== null && $uMimeTypeFilter !== $tContent[1]) { 103 | continue; 104 | } 105 | 106 | if ($tContent[0] === "direct") { 107 | $tOutput = $tContent[2]; 108 | } elseif ($tContent[0] === "callback") { 109 | $tOutput = call_user_func($tContent[2]); 110 | } elseif ($tContent[0] === "file") { 111 | $tOutput = FileSystem::read($tContent[2]); 112 | } 113 | 114 | if (isset($this->filters[$tContent[1]])) { 115 | $tOutput = call_user_func($this->filters[$tContent[1]], $tOutput); 116 | } 117 | 118 | $this->output .= $tOutput; 119 | } 120 | 121 | $this->contents = []; 122 | } 123 | 124 | return $this->output; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Interfaces/Console.php: -------------------------------------------------------------------------------- 1 | 23 | * @since 2.0.0 24 | * 25 | * @todo add stdin features 26 | */ 27 | class Console implements IInterface 28 | { 29 | /** 30 | * Writes given message in header format 31 | * 32 | * @param int $uHeading size 33 | * @param string $uMessage message 34 | * 35 | * @return void 36 | */ 37 | public function writeHeader($uHeading, $uMessage) 38 | { 39 | if ($uHeading === 1) { 40 | $tChar = "="; 41 | } else { 42 | $tChar = "-"; 43 | } 44 | 45 | echo $uMessage, PHP_EOL, str_repeat($tChar, strlen($uMessage)), PHP_EOL; 46 | 47 | if ($uHeading === 1) { 48 | echo PHP_EOL; 49 | } 50 | } 51 | 52 | /** 53 | * Writes given message in specified color 54 | * 55 | * @param string $uColor color 56 | * @param string $uMessage message 57 | * 58 | * @return void 59 | */ 60 | public function writeColor($uColor, $uMessage) 61 | { 62 | if (strncasecmp(PHP_OS, "WIN", 3) === 0) { 63 | echo $uMessage, PHP_EOL; 64 | return; 65 | } 66 | 67 | if ($uColor === "black") { 68 | $tColor = "[0;30m"; 69 | } elseif ($uColor === "darkgray") { 70 | $tColor = "[1;30m"; 71 | } elseif ($uColor === "blue") { 72 | $tColor = "[0;34m"; 73 | } elseif ($uColor === "lightblue") { 74 | $tColor = "[1;34m"; 75 | } elseif ($uColor === "green") { 76 | $tColor = "[0;32m"; 77 | } elseif ($uColor === "lightgreen") { 78 | $tColor = "[1;32m"; 79 | } elseif ($uColor === "cyan") { 80 | $tColor = "[0;36m"; 81 | } elseif ($uColor === "lightcyan") { 82 | $tColor = "[1;36m"; 83 | } elseif ($uColor === "red") { 84 | $tColor = "[0;31m"; 85 | } elseif ($uColor === "lightred") { 86 | $tColor = "[1;31m"; 87 | } elseif ($uColor === "purple") { 88 | $tColor = "[0;35m"; 89 | } elseif ($uColor === "lightpurple") { 90 | $tColor = "[1;35m"; 91 | } elseif ($uColor === "brown") { 92 | $tColor = "[0;33m"; 93 | } elseif ($uColor === "yellow") { 94 | $tColor = "[1;33m"; 95 | } elseif ($uColor === "white") { 96 | $tColor = "[1;37m"; 97 | } else /* if ($uColor === "lightgray") */ { 98 | $tColor = "[0;37m"; 99 | } 100 | 101 | echo "\033", $tColor, $uMessage, "\033[0m", PHP_EOL; 102 | } 103 | 104 | /** 105 | * Writes given message 106 | * 107 | * @param string $uMessage message 108 | * 109 | * @return void 110 | */ 111 | public function write($uMessage) 112 | { 113 | echo $uMessage, PHP_EOL; 114 | } 115 | 116 | /** 117 | * Outputs the array to console 118 | * 119 | * @param array $uArray Target array will be printed 120 | * 121 | * @return void 122 | */ 123 | public function writeArray(array $uArray) 124 | { 125 | print_r($uArray); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Objects/BufferedUpdateCollection.php: -------------------------------------------------------------------------------- 1 | 21 | * @since 1.5.0 22 | */ 23 | class BufferedUpdateCollection 24 | { 25 | /** @type array items */ 26 | public $items; 27 | /** @type array queue */ 28 | public $queue; 29 | /** @type callable update function */ 30 | public $updateFunc; 31 | 32 | 33 | /** 34 | * Initializes a BufferedUpdateCollection class instance 35 | * 36 | * @param callable $uUpdateFunc update function 37 | * @param array $uArray initial items 38 | * 39 | * @return BufferedUpdateCollection 40 | */ 41 | public function __construct(/* callable */ $uUpdateFunc, array $uArray = []) 42 | { 43 | $this->updateFunc = $uUpdateFunc; 44 | $this->items = $uArray; 45 | $this->queue = []; 46 | } 47 | 48 | /** 49 | * Enqueues given keys into collection to get all elements at the same time 50 | * 51 | * @param string $uKey key for element 52 | * 53 | * @return void 54 | */ 55 | public function enqueue($uKey) 56 | { 57 | foreach ((array)$uKey as $tKey) { 58 | if (in_array($tKey, $this->queue, true) || $this->keyExists($tKey)) { 59 | continue; 60 | } 61 | 62 | $this->queue[] = $tKey; 63 | } 64 | } 65 | 66 | /** 67 | * Updates the collection by executing update function 68 | * 69 | * @return void 70 | */ 71 | public function update() 72 | { 73 | if ($this->updateFunc === null) { 74 | return; 75 | } 76 | 77 | if (count($this->queue) === 0) { 78 | return; 79 | } 80 | 81 | $this->items += call_user_func($this->updateFunc, $this->queue); 82 | $this->queue = []; 83 | } 84 | 85 | /** 86 | * Gets the specified element in the collection 87 | * 88 | * @param mixed $uKey key for element 89 | * @param mixed $uDefault default value if the key does not exist 90 | * 91 | * @return mixed the element or the default 92 | */ 93 | public function get($uKey, $uDefault = null) 94 | { 95 | if (in_array($uKey, $this->queue, true)) { 96 | $this->update(); 97 | } 98 | 99 | if (array_key_exists($uKey, $this->items)) { 100 | return $this->items[$uKey]; 101 | } 102 | 103 | return $uDefault; 104 | } 105 | 106 | /** 107 | * Gets the set of specified elements in the collection 108 | * 109 | * @param mixed $uKeys keys for elements 110 | * 111 | * @return mixed the set of elements 112 | */ 113 | public function getRange(array $uKeys) 114 | { 115 | $tItems = []; 116 | 117 | foreach ($uKeys as $tKey) { 118 | if (in_array($tKey, $this->queue, true)) { 119 | $this->update(); 120 | } 121 | 122 | if (array_key_exists($tKey, $this->items)) { 123 | $tItems[$tKey] = $this->items[$tKey]; 124 | } 125 | } 126 | 127 | return $tItems; 128 | } 129 | 130 | /** 131 | * Enqueues the keys then updates the collection in order 132 | * to get requested elements back 133 | * 134 | * @param mixed $uKeys keys for elements 135 | * 136 | * @return mixed the set of elements 137 | */ 138 | public function enqueueAndGetRange(array $uKeys) 139 | { 140 | $this->enqueue($uKeys); 141 | return $this->getRange($uKeys); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Events/Delegate.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 2.0.0 25 | */ 26 | class Delegate 27 | { 28 | /** @type array list of callbacks */ 29 | public $callbacks = null; 30 | /** @type mixed expected return value for interruption */ 31 | public $expectedReturn; 32 | 33 | 34 | /** 35 | * Constructs a new delegate in order to assign it to a member 36 | * 37 | * @param mixed $uExpectedReturn Expected return value for interruption 38 | * 39 | * @return Delegate a delegate 40 | */ 41 | public static function assign($uExpectedReturn = false) 42 | { 43 | $tNewInstance = new static($uExpectedReturn); 44 | 45 | return function (/* callable */ $uCallback = null, $uState = null, $uPriority = 10) use ($tNewInstance) { 46 | if ($uCallback !== null) { 47 | $tNewInstance->add($uCallback, $uState, $uPriority); 48 | } 49 | 50 | return $tNewInstance; 51 | }; 52 | } 53 | 54 | // @codingStandardsIgnoreStart 55 | /** 56 | * Unserializes an instance of delegate 57 | * 58 | * @param array $uPropertyBag properties set of unserialized object 59 | * 60 | * @return Delegate a delegate 61 | */ 62 | public static function __set_state(array $uPropertyBag) 63 | { 64 | $tNewInstance = new static($uPropertyBag["expectedReturn"]); 65 | $tNewInstance->callbacks = $uPropertyBag["callbacks"]; 66 | 67 | return $tNewInstance; 68 | } 69 | // @codingStandardsIgnoreEnd 70 | 71 | /** 72 | * Constructs a new instance of delegate 73 | * 74 | * @param mixed $uExpectedReturn Expected return value for interruption 75 | * 76 | * @return Delegate 77 | */ 78 | public function __construct($uExpectedReturn = false) 79 | { 80 | $this->expectedReturn = $uExpectedReturn; 81 | } 82 | 83 | /** 84 | * Adds a callback to delegate 85 | * 86 | * @param callback $uCallback callback method 87 | * @param mixed $uState state object 88 | * @param null|int $uPriority priority level 89 | * 90 | * @return void 91 | */ 92 | public function add(/* callable */ $uCallback, $uState = null, $uPriority = null) 93 | { 94 | // TODO SplPriorityQueue has a problem with serialization 95 | /* 96 | if ($this->callbacks === null) { 97 | $this->callbacks = new SplPriorityQueue(); 98 | } 99 | 100 | if ($uPriority === null) { 101 | $uPriority = 10; 102 | } 103 | 104 | $this->callbacks->insert([$uCallback, $uState], $uPriority); 105 | */ 106 | 107 | if ($this->callbacks === null) { 108 | $this->callbacks = []; 109 | } 110 | 111 | $this->callbacks[] = [$uCallback, $uState]; 112 | } 113 | 114 | /** 115 | * Invokes the event-chain execution 116 | * 117 | * @param array $uArgs arguments 118 | * 119 | * @return bool whether the execution is broken or not 120 | */ 121 | public function invoke(...$uArgs) 122 | { 123 | if ($this->callbacks !== null) { 124 | foreach ($this->callbacks as $tCallback) { 125 | // array_unshift($tEventArgs, $tCallback[1]); 126 | 127 | if (call_user_func($tCallback[0], ...$uArgs) === $this->expectedReturn) { 128 | return false; 129 | } 130 | } 131 | } 132 | 133 | return true; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Testing/Testing.php: -------------------------------------------------------------------------------- 1 | 27 | * @since 2.0.0 28 | */ 29 | class Testing 30 | { 31 | /** 32 | * Runs given unit tests 33 | * 34 | * @param array $uTestClasses set of unit test classes 35 | * @param IInterface $uInterface interface class 36 | * 37 | * @return int exit code 38 | */ 39 | public static function runUnitTests(array $uTestClasses, $uInterface = null) 40 | { 41 | if ($uInterface === null) { 42 | if (PHP_SAPI === "cli") { 43 | $uInterface = new Console(); 44 | } else { 45 | $uInterface = new WebPage(); 46 | } 47 | } 48 | 49 | $tIsEverFailed = false; 50 | 51 | $uInterface->writeHeader(1, "Unit Tests"); 52 | 53 | /** @type string $tTestClass */ 54 | foreach ($uTestClasses as $tTestClass) { 55 | $uInterface->writeHeader(2, $tTestClass); 56 | 57 | $tInstance = new $tTestClass (); 58 | $tInstance->test(); 59 | 60 | if ($tInstance->isFailed) { 61 | $tIsEverFailed = true; 62 | } 63 | 64 | // $uInterface->writeArray($tInstance->testReport); 65 | foreach ($tInstance->testReport as $tTestName => $tTest) { 66 | $tFails = []; 67 | foreach ($tTest as $tTestCase) { 68 | if ($tTestCase["failed"]) { 69 | $tFails[] = [ 70 | "operation" => $tTestCase["operation"], 71 | "message" => $tTestCase["message"] 72 | ]; 73 | } 74 | } 75 | 76 | if (count($tFails) === 0) { 77 | $uInterface->write(sprintf("[OK] %s", $tTestName)); 78 | } else { 79 | $uInterface->writeColor("red", sprintf("[FAIL] %s", $tTestName)); 80 | $uInterface->writeArray($tFails); 81 | } 82 | } 83 | } 84 | 85 | if ($tIsEverFailed) { 86 | return 1; 87 | } 88 | 89 | return 0; 90 | } 91 | 92 | /** 93 | * Starts the code coverage 94 | * 95 | * @return void 96 | */ 97 | public static function coverageStart() 98 | { 99 | if (!extension_loaded("xdebug")) { 100 | return; 101 | } 102 | 103 | xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); 104 | } 105 | 106 | /** 107 | * Stops the code coverage 108 | * 109 | * @return array results 110 | */ 111 | public static function coverageStop() 112 | { 113 | if (!extension_loaded("xdebug")) { 114 | return null; 115 | } 116 | 117 | $tCoverageData = xdebug_get_code_coverage(); 118 | xdebug_stop_code_coverage(); 119 | 120 | $tFinal = [ 121 | "files" => [], 122 | "total" => [ "coveredLines" => 0, "totalLines" => 0 ] 123 | ]; 124 | 125 | foreach ($tCoverageData as $tPath => $tLines) { 126 | $tFileCoverage = [ 127 | "path" => $tPath, 128 | "coveredLines" => array_keys($tLines), 129 | "totalLines" => FileSystem::getFileLineCount($tPath) 130 | ]; 131 | 132 | $tFinal["files"][] = $tFileCoverage; 133 | $tFinal["total"]["coveredLines"] += count($tFileCoverage["coveredLines"]); 134 | $tFinal["total"]["totalLines"] += $tFileCoverage["totalLines"]; 135 | } 136 | 137 | $tFinal["total"]["percentage"] = ($tFinal["total"]["coveredLines"] * 100) / $tFinal["total"]["totalLines"]; 138 | 139 | return $tFinal; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsBasicTests.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Simple Sequence 3 | brief: | 4 | You can specify a list in YAML by placing each 5 | member of the list on a new line with an opening 6 | dash. These lists are called sequences. 7 | yaml: | 8 | - apple 9 | - banana 10 | - carrot 11 | php: | 12 | array('apple', 'banana', 'carrot') 13 | --- 14 | test: Nested Sequences 15 | brief: | 16 | You can include a sequence within another 17 | sequence by giving the sequence an empty 18 | dash, followed by an indented list. 19 | yaml: | 20 | - 21 | - foo 22 | - bar 23 | - baz 24 | php: | 25 | array(array('foo', 'bar', 'baz')) 26 | --- 27 | test: Mixed Sequences 28 | brief: | 29 | Sequences can contain any YAML data, 30 | including strings and other sequences. 31 | yaml: | 32 | - apple 33 | - 34 | - foo 35 | - bar 36 | - x123 37 | - banana 38 | - carrot 39 | php: | 40 | array('apple', array('foo', 'bar', 'x123'), 'banana', 'carrot') 41 | --- 42 | test: Deeply Nested Sequences 43 | brief: | 44 | Sequences can be nested even deeper, with each 45 | level of indentation representing a level of 46 | depth. 47 | yaml: | 48 | - 49 | - 50 | - uno 51 | - dos 52 | php: | 53 | array(array(array('uno', 'dos'))) 54 | --- 55 | test: Simple Mapping 56 | brief: | 57 | You can add a keyed list (also known as a dictionary or 58 | hash) to your document by placing each member of the 59 | list on a new line, with a colon separating the key 60 | from its value. In YAML, this type of list is called 61 | a mapping. 62 | yaml: | 63 | foo: whatever 64 | bar: stuff 65 | php: | 66 | array('foo' => 'whatever', 'bar' => 'stuff') 67 | --- 68 | test: Sequence in a Mapping 69 | brief: | 70 | A value in a mapping can be a sequence. 71 | yaml: | 72 | foo: whatever 73 | bar: 74 | - uno 75 | - dos 76 | php: | 77 | array('foo' => 'whatever', 'bar' => array('uno', 'dos')) 78 | --- 79 | test: Nested Mappings 80 | brief: | 81 | A value in a mapping can be another mapping. 82 | yaml: | 83 | foo: whatever 84 | bar: 85 | fruit: apple 86 | name: steve 87 | sport: baseball 88 | php: | 89 | array( 90 | 'foo' => 'whatever', 91 | 'bar' => array( 92 | 'fruit' => 'apple', 93 | 'name' => 'steve', 94 | 'sport' => 'baseball' 95 | ) 96 | ) 97 | --- 98 | test: Mixed Mapping 99 | brief: | 100 | A mapping can contain any assortment 101 | of mappings and sequences as values. 102 | yaml: | 103 | foo: whatever 104 | bar: 105 | - 106 | fruit: apple 107 | name: steve 108 | sport: baseball 109 | - more 110 | - 111 | python: rocks 112 | perl: papers 113 | ruby: scissorses 114 | php: | 115 | array( 116 | 'foo' => 'whatever', 117 | 'bar' => array( 118 | array( 119 | 'fruit' => 'apple', 120 | 'name' => 'steve', 121 | 'sport' => 'baseball' 122 | ), 123 | 'more', 124 | array( 125 | 'python' => 'rocks', 126 | 'perl' => 'papers', 127 | 'ruby' => 'scissorses' 128 | ) 129 | ) 130 | ) 131 | --- 132 | test: Mapping-in-Sequence Shortcut 133 | todo: true 134 | brief: | 135 | If you are adding a mapping to a sequence, you 136 | can place the mapping on the same line as the 137 | dash as a shortcut. 138 | yaml: | 139 | - work on YAML.py: 140 | - work on Store 141 | php: | 142 | array(array('work on YAML.py' => array('work on Store'))) 143 | --- 144 | test: Sequence-in-Mapping Shortcut 145 | todo: true 146 | brief: | 147 | The dash in a sequence counts as indentation, so 148 | you can add a sequence inside of a mapping without 149 | needing spaces as indentation. 150 | yaml: | 151 | allow: 152 | - 'localhost' 153 | - '%.sourceforge.net' 154 | - '%.freepan.org' 155 | php: | 156 | array('allow' => array('localhost', '%.sourceforge.net', '%.freepan.org')) 157 | --- 158 | todo: true 159 | test: Merge key 160 | brief: | 161 | A merge key ('<<') can be used in a mapping to insert other mappings. If 162 | the value associated with the merge key is a mapping, each of its key/value 163 | pairs is inserted into the current mapping. 164 | yaml: | 165 | mapping: 166 | name: Joe 167 | job: Accountant 168 | <<: 169 | age: 38 170 | php: | 171 | array( 172 | 'mapping' => 173 | array( 174 | 'name' => 'Joe', 175 | 'job' => 'Accountant', 176 | 'age' => 38 177 | ) 178 | ) 179 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsFoldedScalars.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Single ending newline 3 | brief: > 4 | A pipe character, followed by an indented 5 | block of text is treated as a literal 6 | block, in which newlines are preserved 7 | throughout the block, including the final 8 | newline. 9 | yaml: | 10 | --- 11 | this: | 12 | Foo 13 | Bar 14 | php: | 15 | array('this' => "Foo\nBar\n") 16 | --- 17 | test: The '+' indicator 18 | brief: > 19 | The '+' indicator says to keep newlines at the end of text 20 | blocks. 21 | yaml: | 22 | normal: | 23 | extra new lines not kept 24 | 25 | preserving: |+ 26 | extra new lines are kept 27 | 28 | 29 | dummy: value 30 | php: | 31 | array( 32 | 'normal' => "extra new lines not kept\n", 33 | 'preserving' => "extra new lines are kept\n\n\n", 34 | 'dummy' => 'value' 35 | ) 36 | --- 37 | test: Three trailing newlines in literals 38 | brief: > 39 | To give you more control over how space 40 | is preserved in text blocks, YAML has 41 | the keep '+' and chomp '-' indicators. 42 | The keep indicator will preserve all 43 | ending newlines, while the chomp indicator 44 | will strip all ending newlines. 45 | yaml: | 46 | clipped: | 47 | This has one newline. 48 | 49 | 50 | 51 | same as "clipped" above: "This has one newline.\n" 52 | 53 | stripped: |- 54 | This has no newline. 55 | 56 | 57 | 58 | same as "stripped" above: "This has no newline." 59 | 60 | kept: |+ 61 | This has four newlines. 62 | 63 | 64 | 65 | same as "kept" above: "This has four newlines.\n\n\n\n" 66 | php: | 67 | array( 68 | 'clipped' => "This has one newline.\n", 69 | 'same as "clipped" above' => "This has one newline.\n", 70 | 'stripped' => 'This has no newline.', 71 | 'same as "stripped" above' => 'This has no newline.', 72 | 'kept' => "This has four newlines.\n\n\n\n", 73 | 'same as "kept" above' => "This has four newlines.\n\n\n\n" 74 | ) 75 | --- 76 | test: Extra trailing newlines with spaces 77 | todo: true 78 | brief: > 79 | Normally, only a single newline is kept 80 | from the end of a literal block, unless the 81 | keep '+' character is used in combination 82 | with the pipe. The following example 83 | will preserve all ending whitespace 84 | since the last line of both literal blocks 85 | contains spaces which extend past the indentation 86 | level. 87 | yaml: | 88 | --- 89 | this: | 90 | Foo 91 | 92 | 93 | kept: |+ 94 | Foo 95 | 96 | 97 | php: | 98 | array('this' => "Foo\n\n \n", 99 | 'kept' => "Foo\n\n \n" ) 100 | 101 | --- 102 | test: Folded Block in a Sequence 103 | brief: > 104 | A greater-then character, followed by an indented 105 | block of text is treated as a folded block, in 106 | which lines of text separated by a single newline 107 | are concatenated as a single line. 108 | yaml: | 109 | --- 110 | - apple 111 | - banana 112 | - > 113 | can't you see 114 | the beauty of yaml? 115 | hmm 116 | - dog 117 | php: | 118 | array( 119 | 'apple', 120 | 'banana', 121 | "can't you see the beauty of yaml? hmm\n", 122 | 'dog' 123 | ) 124 | --- 125 | test: Folded Block as a Mapping Value 126 | brief: > 127 | Both literal and folded blocks can be 128 | used in collections, as values in a 129 | sequence or a mapping. 130 | yaml: | 131 | --- 132 | quote: > 133 | Mark McGwire's 134 | year was crippled 135 | by a knee injury. 136 | source: espn 137 | php: | 138 | array( 139 | 'quote' => "Mark McGwire's year was crippled by a knee injury.\n", 140 | 'source' => 'espn' 141 | ) 142 | --- 143 | test: Three trailing newlines in folded blocks 144 | brief: > 145 | The keep and chomp indicators can also 146 | be applied to folded blocks. 147 | yaml: | 148 | clipped: > 149 | This has one newline. 150 | 151 | 152 | 153 | same as "clipped" above: "This has one newline.\n" 154 | 155 | stripped: >- 156 | This has no newline. 157 | 158 | 159 | 160 | same as "stripped" above: "This has no newline." 161 | 162 | kept: >+ 163 | This has four newlines. 164 | 165 | 166 | 167 | same as "kept" above: "This has four newlines.\n\n\n\n" 168 | php: | 169 | array( 170 | 'clipped' => "This has one newline.\n", 171 | 'same as "clipped" above' => "This has one newline.\n", 172 | 'stripped' => 'This has no newline.', 173 | 'same as "stripped" above' => 'This has no newline.', 174 | 'kept' => "This has four newlines.\n\n\n\n", 175 | 'same as "kept" above' => "This has four newlines.\n\n\n\n" 176 | ) 177 | -------------------------------------------------------------------------------- /src/Tasks/Tasks.php: -------------------------------------------------------------------------------- 1 | 29 | * @since 1.0.0 30 | */ 31 | class Tasks 32 | { 33 | /** @type object $tasks the tasks read from tasks file */ 34 | public static $tasks = []; 35 | /** @type array $config tasks configuration */ 36 | public static $config = null; 37 | 38 | 39 | /** 40 | * Constructor to prevent new instances of Tasks class 41 | * 42 | * @return Tasks 43 | */ 44 | final private function __construct() 45 | { 46 | } 47 | 48 | /** 49 | * Clone method to prevent duplication of Tasks class 50 | * 51 | * @return Tasks 52 | */ 53 | final private function __clone() 54 | { 55 | } 56 | 57 | /** 58 | * Unserialization method to prevent restoration of Tasks class 59 | * 60 | * @return Tasks 61 | */ 62 | final private function __wakeup() 63 | { 64 | } 65 | 66 | /** 67 | * Loads the tasks file. 68 | * 69 | * @param string $uTasksConfigPath The path of tasks configuration file 70 | * 71 | * @return void 72 | */ 73 | public static function load($uTasksConfigPath) 74 | { 75 | // load tasks.yml 76 | $tTasksYamlPath = FileSystem::combinePaths(Core::$loader->basepath, Core::translateVariables($uTasksConfigPath)); 77 | self::$config = Core::cachedRead( 78 | "file." . realpath($tTasksYamlPath), 79 | function () use ($tTasksYamlPath) { 80 | $tParser = new Parser(); 81 | return $tParser->parse(FileSystem::read($tTasksYamlPath)); 82 | }, 83 | [ 84 | "ttl" => 60 * 60 85 | ] 86 | ); 87 | 88 | // register tasks 89 | foreach (self::$config["tasks"] as $tTaskKey => $tTask) { 90 | self::$tasks[$tTaskKey] = $tTask; 91 | } 92 | } 93 | 94 | /** 95 | * Executes given task. 96 | * 97 | * @param array $uTasks The set of task line arguments 98 | * 99 | * @throws RuntimeException if task is not found 100 | * @return int exit code 101 | */ 102 | public static function execute(array $uTasks) 103 | { 104 | // TODO use interpreter 105 | // $tCommandInterpreter = new CommandInterpreter("Scabbia", "Scabbia Command Line Tool"); 106 | 107 | $tTaskName = trim(array_shift($uTasks)); 108 | 109 | if (isset(self::$tasks[$tTaskName])) { 110 | $tTask = self::$tasks[$tTaskName]; 111 | 112 | if (isset($tTask["class"])) { 113 | $tClass = $tTask["class"]; 114 | $tCallbacks = []; 115 | } else { 116 | $tClass = null; 117 | $tCallbacks = (array)$tTask["callback"]; 118 | } 119 | 120 | if (isset($tTask["config"])) { 121 | $tConfig = $tTask["config"]; 122 | } else { 123 | $tConfig = null; 124 | } 125 | } elseif (class_exists($tTaskName, true)) { 126 | $tClass = $tTaskName; 127 | $tCallbacks = []; 128 | $tConfig = null; 129 | } else { 130 | throw new RuntimeException("Task not found - " . $tTaskName . "."); 131 | } 132 | 133 | $tOutput = new Console(); 134 | 135 | if ($tClass !== null) { 136 | $tInstance = new $tClass ($tConfig, $tOutput); 137 | $tReturnCode = $tInstance->executeTask($uTasks); 138 | } else { 139 | $tReturnCode = 0; 140 | 141 | foreach ($tCallbacks as $tCallback) { 142 | $tReturn = call_user_func( 143 | Runtime::callbacks($tCallback), 144 | ...$uTasks 145 | ); 146 | 147 | if ($tReturn !== null && $tReturn !== 0) { 148 | $tReturnCode = $tReturn; 149 | break; 150 | } 151 | } 152 | } 153 | 154 | return $tReturnCode; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Yaml/ParseException.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * For the full copyright and license information, please view the LICENSE-MIT 19 | * file that was distributed with this source code. 20 | * 21 | * Modifications made: 22 | * - Scabbia Framework code styles applied. 23 | * - All dump methods are moved under Dumper class. 24 | * - Redundant classes removed. 25 | * - Namespace changed. 26 | * - Tests ported to Scabbia2. 27 | * - Encoding checks removed. 28 | */ 29 | 30 | namespace Scabbia\Yaml; 31 | 32 | use RuntimeException; 33 | 34 | /** 35 | * Exception class thrown when an error occurs during parsing 36 | * 37 | * @package Scabbia\Yaml 38 | * @author Fabien Potencier 39 | * @author Eser Ozvataf 40 | * @since 2.0.0 41 | */ 42 | class ParseException extends RuntimeException 43 | { 44 | /** @type null|string $parsedFile The file name where the error occurred */ 45 | protected $parsedFile; 46 | /** @type int $parsedLine The line where the error occurred */ 47 | protected $parsedLine; 48 | /** @type int|null $snippet The snippet of code near the problem */ 49 | protected $snippet; 50 | /** @type string $rawMessage The error message */ 51 | protected $rawMessage; 52 | 53 | 54 | /** 55 | * Constructor 56 | * 57 | * @param string $message The error message 58 | * @param int $parsedLine The line where the error occurred 59 | * @param int $snippet The snippet of code near the problem 60 | * @param string $parsedFile The file name where the error occurred 61 | * @param \Exception $previous The previous exception 62 | * 63 | * @return ParseException 64 | */ 65 | public function __construct( 66 | $message, 67 | $parsedLine = -1, 68 | $snippet = null, 69 | $parsedFile = null, 70 | \Exception $previous = null 71 | ) { 72 | $this->parsedFile = $parsedFile; 73 | $this->parsedLine = $parsedLine; 74 | $this->snippet = $snippet; 75 | $this->rawMessage = $message; 76 | 77 | $this->updateRepr(); 78 | 79 | parent::__construct($this->message, 0, $previous); 80 | } 81 | 82 | /** 83 | * Gets the snippet of code near the error 84 | * 85 | * @return string The snippet of code 86 | */ 87 | public function getSnippet() 88 | { 89 | return $this->snippet; 90 | } 91 | 92 | /** 93 | * Sets the snippet of code near the error 94 | * 95 | * @param string $snippet The code snippet 96 | * 97 | * @return void 98 | */ 99 | public function setSnippet($snippet) 100 | { 101 | $this->snippet = $snippet; 102 | 103 | $this->updateRepr(); 104 | } 105 | 106 | /** 107 | * Gets the filename where the error occurred 108 | * 109 | * This method returns null if a string is parsed. 110 | * 111 | * @return string The filename 112 | */ 113 | public function getParsedFile() 114 | { 115 | return $this->parsedFile; 116 | } 117 | 118 | /** 119 | * Sets the filename where the error occurred 120 | * 121 | * @param string $parsedFile The filename 122 | * 123 | * @return void 124 | */ 125 | public function setParsedFile($parsedFile) 126 | { 127 | $this->parsedFile = $parsedFile; 128 | 129 | $this->updateRepr(); 130 | } 131 | 132 | /** 133 | * Gets the line where the error occurred 134 | * 135 | * @return int The file line 136 | */ 137 | public function getParsedLine() 138 | { 139 | return $this->parsedLine; 140 | } 141 | 142 | /** 143 | * Sets the line where the error occurred 144 | * 145 | * @param int $parsedLine The file line 146 | */ 147 | public function setParsedLine($parsedLine) 148 | { 149 | $this->parsedLine = $parsedLine; 150 | 151 | $this->updateRepr(); 152 | } 153 | 154 | /** 155 | * Updates the generated message 156 | * 157 | * @return void 158 | */ 159 | protected function updateRepr() 160 | { 161 | $this->message = $this->rawMessage; 162 | 163 | $dot = false; 164 | if (substr($this->message, -1) === ".") { 165 | $this->message = substr($this->message, 0, -1); 166 | $dot = true; 167 | } 168 | 169 | if ($this->parsedFile !== null) { 170 | $this->message .= sprintf( 171 | " in %s", 172 | json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) 173 | ); 174 | } 175 | 176 | if ($this->parsedLine >= 0) { 177 | $this->message .= sprintf(" at line %d", $this->parsedLine); 178 | } 179 | 180 | if ($this->snippet) { 181 | $this->message .= sprintf(" (near \"%s\")", $this->snippet); 182 | } 183 | 184 | if ($dot) { 185 | $this->message .= "."; 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Yaml/Dumper.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * For the full copyright and license information, please view the LICENSE-MIT 19 | * file that was distributed with this source code. 20 | * 21 | * Modifications made: 22 | * - Scabbia Framework code styles applied. 23 | * - All dump methods are moved under Dumper class. 24 | * - Redundant classes removed. 25 | * - Namespace changed. 26 | * - Tests ported to Scabbia2. 27 | * - Encoding checks removed. 28 | */ 29 | 30 | namespace Scabbia\Yaml; 31 | 32 | use Scabbia\Yaml\Inline; 33 | 34 | /** 35 | * Dumper dumps PHP variables to YAML strings 36 | * 37 | * @package Scabbia\Yaml 38 | * @author Fabien Potencier 39 | * @author Eser Ozvataf 40 | * @since 2.0.0 41 | */ 42 | class Dumper 43 | { 44 | /** 45 | * Dumps a PHP value to YAML 46 | * 47 | * @param mixed $input The PHP value 48 | * @param int $inline The level where you switch to inline YAML 49 | * @param int $indentation The level of indentation (used internally) 50 | * 51 | * @return string The YAML representation of the PHP value 52 | */ 53 | public static function dump($input, $inline = 0, $indentation = 0) 54 | { 55 | $output = ""; 56 | $prefix = $indentation ? str_repeat(" ", $indentation) : ""; 57 | 58 | if ($inline <= 0 || !is_array($input) || count($input) === 0) { 59 | $output .= $prefix . self::dumpInline($input); 60 | } else { 61 | $isAHash = array_keys($input) !== range(0, count($input) - 1); 62 | 63 | foreach ($input as $key => $value) { 64 | $willBeInlined = (($inline - 1 <= 0) || !is_array($value) || count($value) === 0); 65 | 66 | $output .= $prefix . 67 | ($isAHash ? self::dumpInline($key) . ":" : "-") . 68 | ($willBeInlined ? " " : "\n") . 69 | self::dump($value, $inline - 1, ($willBeInlined ? 0 : $indentation)) . 70 | ($willBeInlined ? "\n" : ""); 71 | } 72 | } 73 | 74 | return $output; 75 | } 76 | 77 | /** 78 | * Dumps a given PHP variable to a YAML string 79 | * 80 | * @param mixed $value The PHP variable to convert 81 | * 82 | * @return string The YAML string representing the PHP array 83 | */ 84 | public static function dumpInline($value) 85 | { 86 | if (is_resource($value)) { 87 | return "null"; 88 | } elseif (is_object($value)) { 89 | return "!!php/object:" . serialize($value); 90 | } elseif (is_array($value)) { 91 | return self::dumpInlineArray($value); 92 | } elseif ($value === null) { 93 | return "null"; 94 | } elseif ($value === true) { 95 | return "true"; 96 | } elseif ($value === false) { 97 | return "false"; 98 | } elseif (ctype_digit($value)) { 99 | return is_string($value) ? "\"$value\"" : (int)$value; 100 | } elseif (is_numeric($value)) { 101 | $locale = setlocale(LC_NUMERIC, 0); 102 | if ($locale !== false) { 103 | setlocale(LC_NUMERIC, "C"); 104 | } 105 | if (is_string($value)) { 106 | $repr = "'$value'"; 107 | } elseif (is_infinite($value)) { 108 | $repr = str_ireplace("INF", ".Inf", strval($value)); 109 | } else { 110 | $repr = strval($value); 111 | } 112 | 113 | if ($locale !== false) { 114 | setlocale(LC_NUMERIC, $locale); 115 | } 116 | 117 | return $repr; 118 | } elseif (Escaper::requiresDoubleQuoting($value)) { 119 | return Escaper::escapeWithDoubleQuotes($value); 120 | } elseif (Escaper::requiresSingleQuoting($value)) { 121 | return Escaper::escapeWithSingleQuotes($value); 122 | } elseif ($value === "") { 123 | return "''"; 124 | } elseif (preg_match(Inline::getTimestampRegex(), $value) || 125 | in_array(strtolower($value), ["null", "~", "true", "false"]) 126 | ) { 127 | return "'$value'"; 128 | } else { 129 | return $value; 130 | } 131 | } 132 | 133 | /** 134 | * Dumps a PHP array to a YAML string 135 | * 136 | * @param array $value The PHP array to dump 137 | * 138 | * @return string The YAML string representing the PHP array 139 | */ 140 | protected static function dumpInlineArray(array $value) 141 | { 142 | // array 143 | $keys = array_keys($value); 144 | $func = function ($v, $w) { 145 | return (int)$v + $w; 146 | }; 147 | 148 | if ((count($keys) === 1 && $keys[0] === "0") || 149 | (count($keys) > 1 && array_reduce($keys, $func, 0) == count($keys) * (count($keys) - 1) / 2) 150 | ) { 151 | $output = []; 152 | foreach ($value as $val) { 153 | $output[] = self::dumpInline($val); 154 | } 155 | 156 | return "[" . implode(", ", $output) . "]"; 157 | } 158 | 159 | // mapping 160 | $output = []; 161 | foreach ($value as $key => $val) { 162 | $output[] = self::dumpInline($key) . ": " . self::dumpInline($val); 163 | } 164 | 165 | return "{ " . implode(", ", $output) . " }"; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/Config/Config.php: -------------------------------------------------------------------------------- 1 | 27 | * @since 1.0.0 28 | */ 29 | class Config 30 | { 31 | /** @type int NONE no flag */ 32 | const NONE = 0; 33 | /** @type int OVERWRITE overwrite existing nodes by default */ 34 | const OVERWRITE = 1; 35 | /** @type int FLATTEN flatten nodes by default */ 36 | const FLATTEN = 2; 37 | 38 | 39 | /** @type array configuration paths */ 40 | public $paths = []; 41 | /** @type array configuration content */ 42 | public $content = []; 43 | 44 | 45 | /** 46 | * Loads a configuration file 47 | * 48 | * @param string|array $uPath path of configuration file to be loaded 49 | * 50 | * @return mixed 51 | */ 52 | public static function load($uPath) 53 | { 54 | $tInstance = new static(); 55 | foreach ((array)$uPath as $tPath) { 56 | $tInstance->add($tPath); 57 | } 58 | 59 | return $tInstance; 60 | } 61 | 62 | /** 63 | * Adds a file into configuration compilation 64 | * 65 | * @param string $uPath path of configuration file 66 | * @param int $uFlags loading flags 67 | * 68 | * @return void 69 | */ 70 | public function add($uPath, $uFlags = self::NONE) 71 | { 72 | $this->paths[] = [$uPath, $uFlags]; 73 | } 74 | 75 | /** 76 | * Compiles given configuration files into single configuration 77 | * 78 | * @return array final configuration 79 | */ 80 | public function get() 81 | { 82 | // TODO mass caching with pathnames and flags 83 | foreach ($this->paths as $tPath) { 84 | $tConfigPath = Core::translateVariables($tPath[0]); 85 | $tConfigContent = Core::cachedRead( 86 | "file." . realpath($tConfigPath), 87 | function () use ($tConfigPath) { 88 | $tParser = new Parser(); 89 | return $tParser->parse(FileSystem::read($tConfigPath)); 90 | }, 91 | [ 92 | "ttl" => 60 * 60 93 | ] 94 | ); 95 | 96 | $this->process($this->content, $tConfigContent, $tPath[1]); 97 | } 98 | 99 | return $this->content; 100 | } 101 | 102 | /** 103 | * Gets the YAML representation of configuration stack 104 | * 105 | * @return string YAML 106 | */ 107 | public function dump() 108 | { 109 | return Dumper::dump($this->get(), 1); 110 | } 111 | 112 | /** 113 | * Processes the configuration file in order to simplify its accessibility 114 | * 115 | * @param mixed $uTarget target reference 116 | * @param mixed $uNode source object 117 | * @param int $uFlags loading flags 118 | * 119 | * @return void 120 | */ 121 | public function process(&$uTarget, $uNode, $uFlags) 122 | { 123 | $tQueue = [ 124 | [[], $uNode, $uFlags, &$uTarget, null, false] 125 | ]; 126 | 127 | do { 128 | $tItem = array_pop($tQueue); 129 | 130 | if ($tItem[4] === null) { 131 | $tRef = &$tItem[3]; 132 | } else { 133 | $tRef = &$tItem[3][$tItem[4]]; 134 | } 135 | 136 | if (is_scalar($tItem[1]) || $tItem[1] === null) { 137 | if (!isset($tRef) || ($tItem[2] & self::OVERWRITE) === self::OVERWRITE) { 138 | $tRef = $tItem[1]; 139 | } 140 | 141 | continue; 142 | } 143 | 144 | if (!is_array($tRef) || ($tItem[2] & self::OVERWRITE) === self::OVERWRITE) { 145 | $tRef = []; // initialize as an empty array 146 | } 147 | 148 | foreach ($tItem[1] as $tKey => $tSubnode) { 149 | $tFlags = $tItem[2]; 150 | $tListNode = false; 151 | 152 | $tNodeParts = explode("|", $tKey); 153 | $tNodeKey = array_shift($tNodeParts); 154 | 155 | if ($tItem[5] && is_numeric($tNodeKey)) { 156 | $tNodeKey = count($tRef); 157 | } 158 | 159 | foreach ($tNodeParts as $tNodePart) { 160 | if ($tNodePart === "disabled") { 161 | continue 2; 162 | } elseif ($tNodePart === "development") { 163 | if (ApplicationBase::$current === null || !ApplicationBase::$current->development) { 164 | continue 2; 165 | } 166 | } elseif ($tNodePart === "list") { 167 | $tListNode = true; 168 | } elseif ($tNodePart === "important") { 169 | $tFlags |= self::OVERWRITE; 170 | } elseif ($tNodePart === "flat") { 171 | $tFlags |= self::FLATTEN; 172 | } 173 | } 174 | 175 | $tNewNodeKey = $tItem[0]; 176 | if (($tFlags & self::FLATTEN) === self::FLATTEN) { 177 | $tNodeKey = ltrim("{$tItem[4]}/{$tNodeKey}", "/"); 178 | $tNewNodeKey[] = $tNodeKey; 179 | 180 | $tQueue[] = [$tNewNodeKey, $tSubnode, $tFlags, &$tRef, $tNodeKey, $tListNode]; 181 | } else { 182 | $tNewNodeKey[] = $tNodeKey; 183 | $tQueue[] = [$tNewNodeKey, $tSubnode, $tFlags, &$tRef[$tNodeKey], null, $tListNode]; 184 | } 185 | } 186 | } while (count($tQueue) > 0); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Code/AnnotationManager.php: -------------------------------------------------------------------------------- 1 | 27 | * @since 2.0.0 28 | */ 29 | class AnnotationManager 30 | { 31 | /** @type int SOURCE source */ 32 | const SOURCE = 0; 33 | /** @type int LEVEL level */ 34 | const LEVEL = 1; 35 | /** @type int MEMBER member */ 36 | const MEMBER = 2; 37 | /** @type int VALUE value */ 38 | const VALUE = 3; 39 | 40 | 41 | /** @type string $applicationWritablePath application writable path */ 42 | public $applicationWritablePath; 43 | /** @type Parser|null $parser yaml parser */ 44 | public $parser = null; 45 | /** @type AnnotationScanner|null $annotationScanner annotation scanner */ 46 | public $annotationScanner = null; 47 | /** @type array $annotationMap annotation map */ 48 | public $annotationMap = null; 49 | 50 | 51 | /** 52 | * Initializes an annotation manager 53 | * 54 | * @param string $uApplicationWritablePath application writable path 55 | * 56 | * @return AnnotationManager 57 | */ 58 | public function __construct($uApplicationWritablePath) 59 | { 60 | $this->applicationWritablePath = $uApplicationWritablePath; 61 | } 62 | 63 | /** 64 | * Loads saved annotations or start over scanning 65 | * 66 | * @return void 67 | */ 68 | public function load() 69 | { 70 | $tAnnotationMapPath = $this->applicationWritablePath . "/annotations.php"; 71 | 72 | // TODO and not in development mode 73 | if (file_exists($tAnnotationMapPath)) { 74 | $this->annotationMap = require $tAnnotationMapPath; 75 | } else { 76 | $this->scan(); 77 | // TODO if not in readonly mode 78 | FileSystem::writePhpFile($tAnnotationMapPath, $this->annotationMap); 79 | } 80 | } 81 | 82 | /** 83 | * Scans all files to find annotations 84 | * 85 | * @return void 86 | */ 87 | public function scan() 88 | { 89 | // initialize annotation scanner 90 | $this->annotationScanner = new AnnotationScanner(); 91 | 92 | // -- scan composer maps 93 | $tFolders = Core::$loader->getComposerFolders(); 94 | 95 | foreach ($tFolders as $tPath) { 96 | if (file_exists($tPath[1])) { 97 | FileSystem::getFilesWalk( 98 | $tPath[1], 99 | "*.php", 100 | true, 101 | [$this, "scanFile"], 102 | $tPath[0] 103 | ); 104 | } 105 | } 106 | 107 | $this->annotationMap = $this->annotationScanner->result; 108 | unset($this->annotationScanner); 109 | } 110 | 111 | /** 112 | * Scans given file to search for classes 113 | * 114 | * @param string $uFile file 115 | * @param string $uNamespacePrefix namespace prefix 116 | * 117 | * @return void 118 | */ 119 | public function scanFile($uFile, $uNamespacePrefix) 120 | { 121 | $tFileContents = FileSystem::read($uFile); 122 | $tTokenStream = TokenStream::fromString($tFileContents); 123 | 124 | $this->annotationScanner->process($tTokenStream, $uNamespacePrefix); 125 | } 126 | 127 | /** 128 | * Gets annotations 129 | * 130 | * @param string $uAnnotation tag of the annotation 131 | * @param bool $uIsYaml whether is in yaml format or not 132 | * 133 | * @return Iterator annotations in [class, level, member, value] 134 | */ 135 | public function get($uAnnotation, $uIsYaml = false) 136 | { 137 | return Core::cachedRead( 138 | "annotation.{$uAnnotation}.{$uIsYaml}", 139 | function () use ($uAnnotation, $uIsYaml) { 140 | $tResult = []; 141 | 142 | if ($uIsYaml && $this->parser === null) { 143 | $this->parser = new Parser(); 144 | } 145 | 146 | foreach ($this->annotationMap as $tClass => $tAnnotationLevel) { 147 | if ($tAnnotationLevel === null) { 148 | continue; 149 | } 150 | 151 | foreach ($tAnnotationLevel as $tAnnotationLevelKey => $tMemberAnnotations) { 152 | foreach ($tMemberAnnotations as $tAnnotationMemberKey => $tAnnotations) { 153 | if (isset($tAnnotations[$uAnnotation])) { 154 | $tValue = $tAnnotations[$uAnnotation]; 155 | 156 | if ($uIsYaml) { 157 | foreach ($tValue as &$tValueRef) { 158 | $tValueRef = $this->parser->parse($tValueRef); 159 | } 160 | } 161 | 162 | $tResult[] = [ 163 | self::SOURCE => $tClass, 164 | self::LEVEL => $tAnnotationLevelKey, 165 | self::MEMBER => $tAnnotationMemberKey, 166 | self::VALUE => $tValue 167 | ]; 168 | } 169 | } 170 | } 171 | } 172 | 173 | return $tResult; 174 | }, 175 | [ 176 | "ttl" => 60 * 60 177 | ] 178 | ); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Code/TokenStream.php: -------------------------------------------------------------------------------- 1 | 27 | * @since 2.0.0 28 | */ 29 | class TokenStream implements Countable, SeekableIterator 30 | { 31 | /** @type array $tokens set of tokens */ 32 | public $tokens = []; 33 | /** @type int $position */ 34 | public $position = 0; 35 | 36 | 37 | /** 38 | * Creates a new tokenstream instance from a code piece 39 | * 40 | * @param string $uCode the string consists of codes to turned into tokens 41 | * 42 | * @return TokenStream the new instance 43 | */ 44 | public static function fromString($uCode) 45 | { 46 | return new static(token_get_all($uCode)); 47 | } 48 | 49 | /** 50 | * Initializes a token stream 51 | * 52 | * @param array $uTokens set of tokens 53 | * 54 | * @return TokenStream 55 | */ 56 | public function __construct(array $uTokens = []) 57 | { 58 | foreach ($uTokens as $tToken) { 59 | if (is_array($tToken)) { 60 | $this->tokens[] = [$tToken[0], $tToken[1]]; 61 | } else { 62 | $this->tokens[] = [null, $tToken]; 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Count elements of an object 69 | * 70 | * @return int the count as an integer 71 | */ 72 | public function count() 73 | { 74 | return count($this->tokens); 75 | } 76 | 77 | /** 78 | * Seeks to a position 79 | * 80 | * @param int $uPosition the position to seek to 81 | * 82 | * @throws OutOfBoundsException 83 | * @return void 84 | */ 85 | public function seek($uPosition) 86 | { 87 | if (!isset($this->tokens[$uPosition])) { 88 | throw new OutOfBoundsException("invalid seek position ({$uPosition})"); 89 | } 90 | 91 | $this->position = $uPosition; 92 | } 93 | 94 | /** 95 | * Rewind the Iterator to the first element 96 | * 97 | * @return void 98 | */ 99 | public function rewind() 100 | { 101 | $this->position = 0; 102 | } 103 | 104 | /** 105 | * Return the current element 106 | * 107 | * @return mixed 108 | */ 109 | public function current() 110 | { 111 | return $this->tokens[$this->position]; 112 | } 113 | 114 | /** 115 | * Return the key of the current element 116 | * 117 | * @return int 118 | */ 119 | public function key() 120 | { 121 | return $this->position; 122 | } 123 | 124 | /** 125 | * Move forward to next element 126 | * 127 | * @return void 128 | */ 129 | public function next() 130 | { 131 | $this->position++; 132 | } 133 | 134 | /** 135 | * Checks if current position is valid 136 | * 137 | * @return bool true on success or false on failure 138 | */ 139 | public function valid() 140 | { 141 | return isset($this->tokens[$this->position]); 142 | } 143 | 144 | /** 145 | * Move back to previous element 146 | * 147 | * @return mixed 148 | */ 149 | public function prev() 150 | { 151 | $this->position--; 152 | } 153 | 154 | /** 155 | * Gets next token if condition is true 156 | * 157 | * @param array|integer $uType 158 | * @param array|string|null $uValue 159 | * 160 | * @return bool 161 | */ 162 | public function nextIf($uType, $uValue = null) 163 | { 164 | if ($this->test($uType, $uValue)) { 165 | $this->position++; 166 | return true; 167 | } 168 | 169 | return false; 170 | } 171 | 172 | /** 173 | * Advances until a token with the given type is found 174 | * 175 | * @param array|integer $uType 176 | * @param array|string|null $uValue 177 | * 178 | * @return mixed 179 | */ 180 | public function nextUntil($uType, $uValue = null) 181 | { 182 | while (!$this->test($uType, $uValue)) { 183 | $this->position++; 184 | 185 | if (!isset($this->tokens[$this->position])) { 186 | return false; 187 | } 188 | } 189 | 190 | return true; 191 | } 192 | 193 | /** 194 | * Tests the current token for a condition 195 | * 196 | * @param array|integer $uType 197 | * @param array|string|null $uValue 198 | * 199 | * @return mixed 200 | */ 201 | public function test($uType, $uValue = null) 202 | { 203 | $tToken = $this->tokens[$this->position]; 204 | 205 | if ($uValue !== null) { 206 | if (is_array($uValue) && !in_array($tToken[1], $uValue)) { 207 | return false; 208 | } 209 | 210 | if ($tToken[1] !== $uValue) { 211 | return false; 212 | } 213 | } 214 | 215 | if ($tToken[0] !== $uType) { 216 | return false; 217 | } 218 | 219 | return true; 220 | } 221 | 222 | /** 223 | * Tests the current token for a condition or throws an exception otherwise 224 | * 225 | * @param array|integer $uType 226 | * @param array|string|null $uValue 227 | * @param string|null $uMessage 228 | * 229 | * @throws UnexpectedValueException 230 | * @return void 231 | */ 232 | public function expect($uType, $uValue = null, $uMessage = null) 233 | { 234 | if (!$this->test($uType, $uValue)) { 235 | $tToken = $this->tokens[$this->position]; 236 | 237 | throw new UnexpectedValueException(sprintf( 238 | "%sUnexpected token \"%s\" of value \"%s\" (\"%s\" expected%s)", 239 | $uMessage ? "{$uMessage}. " : "", 240 | token_name($tToken[0]), 241 | $tToken[1], 242 | token_name($uType), 243 | ($uValue ? sprintf(" with value \"%s\"", $uValue) : "") 244 | )); 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/Router/Router.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 2.0.0 25 | * 26 | * Routing related code based on the nikic's FastRoute solution: 27 | * http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html 28 | */ 29 | class Router 30 | { 31 | /** @type string VARIABLE_REGEX Regex expression of variables */ 32 | const VARIABLE_REGEX = <<<'REGEX' 33 | ~\{ 34 | \s* ([a-zA-Z][a-zA-Z0-9_]*) \s* 35 | (?: 36 | : \s* ([^{}]*(?:\{(?-1)\}[^{}*])*) 37 | )? 38 | \}~x 39 | REGEX; 40 | 41 | /** @type string DEFAULT_DISPATCH_REGEX Regex expression of default dispatch */ 42 | const DEFAULT_DISPATCH_REGEX = "[^/]+"; 43 | 44 | /** @type int FOUND route found */ 45 | const FOUND = 0; 46 | /** @type int NOT_FOUND route not found */ 47 | const NOT_FOUND = 1; 48 | /** @type int METHOD_NOT_ALLOWED route method is not allowed */ 49 | const METHOD_NOT_ALLOWED = 2; 50 | 51 | 52 | /** @type null|array route definitions */ 53 | public static $routes = null; 54 | 55 | 56 | /** 57 | * Constructor to prevent new instances of Router class 58 | * 59 | * @return Router 60 | */ 61 | final private function __construct() 62 | { 63 | } 64 | 65 | /** 66 | * Clone method to prevent duplication of Router class 67 | * 68 | * @return Router 69 | */ 70 | final private function __clone() 71 | { 72 | } 73 | 74 | /** 75 | * Unserialization method to prevent restoration of Router class 76 | * 77 | * @return Router 78 | */ 79 | final private function __wakeup() 80 | { 81 | } 82 | 83 | /** 84 | * The dispatch method 85 | * 86 | * @param string $uMethod http method 87 | * @param string $uPathInfo path 88 | * 89 | * @return mixed 90 | */ 91 | public static function dispatch($uMethod, $uPathInfo) 92 | { 93 | if (self::$routes === null) { 94 | $tRoutesFilePath = ApplicationBase::$current->writablePath . "/routes.php"; 95 | self::$routes = require $tRoutesFilePath; 96 | } 97 | 98 | if (isset(self::$routes["static"][$uPathInfo])) { 99 | $tRoute = self::$routes["static"][$uPathInfo]; 100 | 101 | if (isset($tRoute[$uMethod])) { 102 | return [ 103 | "status" => self::FOUND, 104 | "callback" => $tRoute[$uMethod], 105 | "parameters" => [] 106 | ]; 107 | } elseif ($uMethod === "HEAD" && isset($tRoute["GET"])) { 108 | return [ 109 | "status" => self::FOUND, 110 | "callback" => $tRoute["GET"], 111 | "parameters" => [] 112 | ]; 113 | } else { 114 | return [ 115 | "status" => self::METHOD_NOT_ALLOWED, 116 | "methods" => array_keys($tRoute) 117 | ]; 118 | } 119 | } 120 | 121 | foreach (self::$routes["variable"] as $tVariableRoute) { 122 | if (preg_match($tVariableRoute["regex"], $uPathInfo, $tMatches) !== 1) { 123 | continue; 124 | } 125 | 126 | $tRoute = $tVariableRoute["routeMap"][count($tMatches)]; 127 | if (!isset($tRoute[$uMethod])) { 128 | if ($uMethod === "HEAD" && isset($tRoute["GET"])) { 129 | $uMethod = "GET"; 130 | } else { 131 | return [ 132 | "status" => self::METHOD_NOT_ALLOWED, 133 | "methods" => array_keys($tRoute) 134 | ]; 135 | } 136 | } 137 | 138 | list($tCallback, $tVariableNames) = $tRoute[$uMethod]; 139 | 140 | $tVariables = []; 141 | $tCount = 0; 142 | foreach ($tVariableNames as $tVariableName) { 143 | $tVariables[$tVariableName] = $tMatches[++$tCount]; 144 | } 145 | 146 | return [ 147 | "status" => self::FOUND, 148 | "callback" => $tCallback, 149 | "parameters" => $tVariables 150 | ]; 151 | } 152 | 153 | return [ 154 | "status" => self::NOT_FOUND 155 | ]; 156 | } 157 | 158 | /** 159 | * Parses routes of the following form: 160 | * "/user/{name}/{id:[0-9]+}" 161 | * 162 | * @param string $uRoute route pattern 163 | * 164 | * @return array 165 | */ 166 | public static function parse($uRoute) 167 | { 168 | if (!preg_match_all(self::VARIABLE_REGEX, $uRoute, $tMatches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { 169 | return [$uRoute]; 170 | } 171 | 172 | $tOffset = 0; 173 | $tRouteData = []; 174 | foreach ($tMatches as $tMatch) { 175 | if ($tMatch[0][1] > $tOffset) { 176 | $tRouteData[] = substr($uRoute, $tOffset, $tMatch[0][1] - $tOffset); 177 | } 178 | 179 | $tRouteData[] = [ 180 | $tMatch[1][0], 181 | isset($tMatch[2]) ? trim($tMatch[2][0]) : self::DEFAULT_DISPATCH_REGEX 182 | ]; 183 | 184 | $tOffset = $tMatch[0][1] + strlen($tMatch[0][0]); 185 | } 186 | 187 | if ($tOffset !== strlen($uRoute)) { 188 | $tRouteData[] = substr($uRoute, $tOffset); 189 | } 190 | 191 | return $tRouteData; 192 | } 193 | 194 | /** 195 | * Generates a path using named routes 196 | * 197 | * @param string $uName name of route 198 | * @param array $uParameters parameters 199 | * 200 | * @return false|string 201 | */ 202 | public static function path($uName, array $uParameters = []) 203 | { 204 | if (self::$routes === null) { 205 | $tRoutesFilePath = ApplicationBase::$current->writablePath . "/routes.php"; 206 | self::$routes = require $tRoutesFilePath; 207 | } 208 | 209 | if (!isset(self::$routes["named"][$uName])) { 210 | return false; 211 | } 212 | 213 | $tNamedRoute = self::$routes["named"][$uName]; 214 | $tLink = $tNamedRoute[0]; 215 | foreach ($tNamedRoute[1] as $tParameter) { 216 | if (isset($uParameters[$tParameter])) { 217 | $tValue = $uParameters[$tParameter]; 218 | } else { 219 | $tValue = ""; 220 | } 221 | 222 | $tLink = str_replace("{{$tParameter}}", $tValue, $tLink); 223 | } 224 | 225 | return $tLink; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/Generators/GenerateTask.php: -------------------------------------------------------------------------------- 1 | 33 | * @since 2.0.0 34 | * 35 | * @todo only pass annotations requested by generator 36 | */ 37 | class GenerateTask extends TaskBase 38 | { 39 | /** @type array $generators set of generators */ 40 | public $generators = []; 41 | /** @type AnnotationScanner|null $annotationScanner annotation scanner */ 42 | public $annotationScanner = null; 43 | 44 | 45 | /** 46 | * Registers the tasks itself to a command interpreter instance 47 | * 48 | * @param CommandInterpreter $uCommandInterpreter interpreter to be registered at 49 | * 50 | * @return void 51 | */ 52 | public static function registerToCommandInterpreter(CommandInterpreter $uCommandInterpreter) 53 | { 54 | $uCommandInterpreter->addCommand( 55 | "generate", 56 | "Calls all generators registered to your project", 57 | [ 58 | // type, name, description 59 | [Console::OPTION_FLAG, "--clean", ""] 60 | ] 61 | ); 62 | } 63 | 64 | /** 65 | * Initializes the generate task 66 | * 67 | * @param mixed $uConfig configuration 68 | * @param IInterface $uInterface interface class 69 | * 70 | * @return GenerateTask 71 | */ 72 | public function __construct($uConfig, $uInterface) 73 | { 74 | parent::__construct($uConfig, $uInterface); 75 | } 76 | 77 | /** 78 | * Executes the task 79 | * 80 | * @param array $uParameters parameters 81 | * 82 | * @throws RuntimeException if configuration is invalid 83 | * @return int exit code 84 | */ 85 | public function executeTask(array $uParameters) 86 | { 87 | /* 88 | if (count($uParameters) === 0) { 89 | $tProjectFile = "etc/project.yml"; 90 | $tApplicationKey = "default"; 91 | } else { 92 | $tExploded = explode("/", $uParameters[0], 2); 93 | if (count($tExploded) === 1) { 94 | $tProjectFile = "etc/project.yml"; 95 | $tApplicationKey = $tExploded[0]; 96 | } else { 97 | $tProjectFile = $tExploded[0]; 98 | $tApplicationKey = $tExploded[1]; 99 | } 100 | } 101 | 102 | $tProjectFile = FileSystem::combinePaths(Core::$loader->basepath, Core::translateVariables($tProjectFile)); 103 | $uApplicationConfig = Config::load($tProjectFile)->get(); 104 | 105 | if (!isset($uApplicationConfig[$tApplicationKey])) { 106 | throw new RuntimeException(sprintf("invalid configuration - %s::%s", $tProjectFile, $tApplicationKey)); 107 | } 108 | 109 | // TODO is sanitizing $tApplicationKey needed for paths? 110 | $tApplicationWritablePath = Core::$loader->basepath . "/var/generated/app.{$tApplicationKey}"; 111 | 112 | if (!file_exists($tApplicationWritablePath)) { 113 | mkdir($tApplicationWritablePath, 0777, true); 114 | } 115 | 116 | // initialize annotation scanner 117 | $this->annotationScanner = new AnnotationScanner(); 118 | 119 | // initialize generators read from configuration 120 | if (isset($this->config["generators"])) { 121 | foreach ($this->config["generators"] as $tTaskGeneratorClass) { 122 | $tInstance = new $tTaskGeneratorClass ( 123 | $uApplicationConfig[$tApplicationKey], 124 | $tApplicationWritablePath 125 | ); 126 | 127 | foreach ($tInstance->annotations as $tAnnotationKey => $tAnnotation) { 128 | $this->annotationScanner->annotations[$tAnnotationKey] = $tAnnotation; 129 | } 130 | 131 | $this->generators[$tTaskGeneratorClass] = $tInstance; 132 | } 133 | } 134 | 135 | // -- scan composer maps 136 | Core::pushSourcePaths($uApplicationConfig[$tApplicationKey]); 137 | $tFolders = iterator_to_array(Core::$loader->getComposerFolders(), false); 138 | 139 | $this->interface->writeColor("green", "Composer Maps:"); 140 | foreach ($tFolders as $tFolder) { 141 | $this->interface->writeColor("white", sprintf("- [%s] \\%s => %s", $tFolder[2], $tFolder[0], $tFolder[1])); 142 | } 143 | 144 | foreach ($this->generators as $tGenerator) { 145 | $tGenerator->initialize(); 146 | } 147 | 148 | // -- process files 149 | foreach ($tFolders as $tPath) { 150 | if (file_exists($tPath[1])) { 151 | FileSystem::getFilesWalk( 152 | $tPath[1], 153 | "*.*", 154 | true, 155 | [$this, "processFile"], 156 | $tPath[0] 157 | ); 158 | } 159 | } 160 | 161 | foreach ($this->generators as $tGenerator) { 162 | $tGenerator->processAnnotations($this->annotationScanner->result); 163 | } 164 | 165 | foreach ($this->generators as $tGenerator) { 166 | $tGenerator->finalize(); 167 | } 168 | 169 | foreach ($this->generators as $tGenerator) { 170 | $tGenerator->dump(); 171 | } 172 | 173 | Core::popSourcePaths(); 174 | */ 175 | 176 | $tApplication = ApplicationBase::$current; 177 | $tGeneratorRegistry = new GeneratorRegistry($tApplication->config, $tApplication->writablePath); 178 | $tGeneratorRegistry->execute(); 179 | 180 | $this->interface->writeColor("yellow", "done."); 181 | 182 | return 0; 183 | } 184 | 185 | /** 186 | * Processes given file to search for classes 187 | * 188 | * @param string $uFile file 189 | * @param string $uNamespacePrefix namespace prefix 190 | * 191 | * @return void 192 | */ 193 | /* 194 | public function processFile($uFile, $uNamespacePrefix) 195 | { 196 | $tFileContents = FileSystem::read($uFile); 197 | $tTokenStream = TokenStream::fromString($tFileContents); 198 | 199 | foreach ($this->generators as $tGenerator) { 200 | $tGenerator->processFile($uFile, $tFileContents, $tTokenStream); 201 | } 202 | 203 | if (substr($uFile, -4) !== ".php") { 204 | return; 205 | } 206 | 207 | $this->annotationScanner->process($tTokenStream, $uNamespacePrefix); 208 | } 209 | */ 210 | } 211 | -------------------------------------------------------------------------------- /tests/Yaml/Fixtures/YtsTypeTransfers.yml: -------------------------------------------------------------------------------- 1 | --- %YAML:1.0 2 | test: Strings 3 | brief: > 4 | Any group of characters beginning with an 5 | alphabetic or numeric character is a string, 6 | unless it belongs to one of the groups below 7 | (such as an Integer or Time). 8 | yaml: | 9 | String 10 | php: | 11 | 'String' 12 | --- 13 | test: String characters 14 | brief: > 15 | A string can contain any alphabetic or 16 | numeric character, along with many 17 | punctuation characters, including the 18 | period, dash, space, quotes, exclamation, and 19 | question mark. 20 | yaml: | 21 | - What's Yaml? 22 | - It's for writing data structures in plain text. 23 | - And? 24 | - And what? That's not good enough for you? 25 | - No, I mean, "And what about Yaml?" 26 | - Oh, oh yeah. Uh.. Yaml for Ruby. 27 | php: | 28 | array( 29 | "What's Yaml?", 30 | "It's for writing data structures in plain text.", 31 | "And?", 32 | "And what? That's not good enough for you?", 33 | "No, I mean, \"And what about Yaml?\"", 34 | "Oh, oh yeah. Uh.. Yaml for Ruby." 35 | ) 36 | --- 37 | test: Indicators in Strings 38 | brief: > 39 | Be careful using indicators in strings. In particular, 40 | the comma, colon, and pound sign must be used carefully. 41 | yaml: | 42 | the colon followed by space is an indicator: but is a string:right here 43 | same for the pound sign: here we have it#in a string 44 | the comma can, honestly, be used in most cases: [ but not in, inline collections ] 45 | php: | 46 | array( 47 | 'the colon followed by space is an indicator' => 'but is a string:right here', 48 | 'same for the pound sign' => 'here we have it#in a string', 49 | 'the comma can, honestly, be used in most cases' => array('but not in', 'inline collections') 50 | ) 51 | --- 52 | test: Forcing Strings 53 | brief: > 54 | Any YAML type can be forced into a string using the 55 | explicit !str method. 56 | yaml: | 57 | date string: !str 2001-08-01 58 | number string: !str 192 59 | php: | 60 | array( 61 | 'date string' => '2001-08-01', 62 | 'number string' => '192' 63 | ) 64 | --- 65 | test: Single-quoted Strings 66 | brief: > 67 | You can also enclose your strings within single quotes, 68 | which allows use of slashes, colons, and other indicators 69 | freely. Inside single quotes, you can represent a single 70 | quote in your string by using two single quotes next to 71 | each other. 72 | yaml: | 73 | all my favorite symbols: '#:!/%.)' 74 | a few i hate: '&(*' 75 | why do i hate them?: 'it''s very hard to explain' 76 | entities: '£ me' 77 | php: | 78 | array( 79 | 'all my favorite symbols' => '#:!/%.)', 80 | 'a few i hate' => '&(*', 81 | 'why do i hate them?' => 'it\'s very hard to explain', 82 | 'entities' => '£ me' 83 | ) 84 | --- 85 | test: Double-quoted Strings 86 | brief: > 87 | Enclosing strings in double quotes allows you 88 | to use escapings to represent ASCII and 89 | Unicode characters. 90 | yaml: | 91 | i know where i want my line breaks: "one here\nand another here\n" 92 | php: | 93 | array( 94 | 'i know where i want my line breaks' => "one here\nand another here\n" 95 | ) 96 | --- 97 | test: Multi-line Quoted Strings 98 | todo: true 99 | brief: > 100 | Both single- and double-quoted strings may be 101 | carried on to new lines in your YAML document. 102 | They must be indented a step and indentation 103 | is interpreted as a single space. 104 | yaml: | 105 | i want a long string: "so i'm going to 106 | let it go on and on to other lines 107 | until i end it with a quote." 108 | php: | 109 | array('i want a long string' => "so i'm going to ". 110 | "let it go on and on to other lines ". 111 | "until i end it with a quote." 112 | ) 113 | 114 | --- 115 | test: Plain scalars 116 | todo: true 117 | brief: > 118 | Unquoted strings may also span multiple lines, if they 119 | are free of YAML space indicators and indented. 120 | yaml: | 121 | - My little toe is broken in two places; 122 | - I'm crazy to have skied this way; 123 | - I'm not the craziest he's seen, since there was always the German guy 124 | who skied for 3 hours on a broken shin bone (just below the kneecap); 125 | - Nevertheless, second place is respectable, and he doesn't 126 | recommend going for the record; 127 | - He's going to put my foot in plaster for a month; 128 | - This would impair my skiing ability somewhat for the 129 | duration, as can be imagined. 130 | php: | 131 | array( 132 | "My little toe is broken in two places;", 133 | "I'm crazy to have skied this way;", 134 | "I'm not the craziest he's seen, since there was always ". 135 | "the German guy who skied for 3 hours on a broken shin ". 136 | "bone (just below the kneecap);", 137 | "Nevertheless, second place is respectable, and he doesn't ". 138 | "recommend going for the record;", 139 | "He's going to put my foot in plaster for a month;", 140 | "This would impair my skiing ability somewhat for the duration, ". 141 | "as can be imagined." 142 | ) 143 | --- 144 | test: 'Null' 145 | brief: > 146 | You can use the tilde '~' character for a null value. 147 | yaml: | 148 | name: Mr. Show 149 | hosted by: Bob and David 150 | date of next season: ~ 151 | php: | 152 | array( 153 | 'name' => 'Mr. Show', 154 | 'hosted by' => 'Bob and David', 155 | 'date of next season' => null 156 | ) 157 | --- 158 | test: Boolean 159 | brief: > 160 | You can use 'true' and 'false' for Boolean values. 161 | yaml: | 162 | Is Gus a Liar?: true 163 | Do I rely on Gus for Sustenance?: false 164 | php: | 165 | array( 166 | 'Is Gus a Liar?' => true, 167 | 'Do I rely on Gus for Sustenance?' => false 168 | ) 169 | --- 170 | test: Integers 171 | dump_skip: true 172 | brief: > 173 | An integer is a series of numbers, optionally 174 | starting with a positive or negative sign. Integers 175 | may also contain commas for readability. 176 | yaml: | 177 | zero: 0 178 | simple: 12 179 | one-thousand: 1,000 180 | negative one-thousand: -1,000 181 | php: | 182 | array( 183 | 'zero' => 0, 184 | 'simple' => 12, 185 | 'one-thousand' => 1000, 186 | 'negative one-thousand' => -1000 187 | ) 188 | --- 189 | test: Integers as Map Keys 190 | brief: > 191 | An integer can be used a dictionary key. 192 | yaml: | 193 | 1: one 194 | 2: two 195 | 3: three 196 | php: | 197 | array( 198 | 1 => 'one', 199 | 2 => 'two', 200 | 3 => 'three' 201 | ) 202 | --- 203 | test: Floats 204 | dump_skip: true 205 | brief: > 206 | Floats are represented by numbers with decimals, 207 | allowing for scientific notation, as well as 208 | positive and negative infinity and "not a number." 209 | yaml: | 210 | a simple float: 2.00 211 | larger float: 1,000.09 212 | scientific notation: 1.00009e+3 213 | php: | 214 | array( 215 | 'a simple float' => 2.0, 216 | 'larger float' => 1000.09, 217 | 'scientific notation' => 1000.09 218 | ) 219 | --- 220 | test: Time 221 | todo: true 222 | brief: > 223 | You can represent timestamps by using 224 | ISO8601 format, or a variation which 225 | allows spaces between the date, time and 226 | time zone. 227 | yaml: | 228 | iso8601: 2001-12-14t21:59:43.10-05:00 229 | space separated: 2001-12-14 21:59:43.10 -05:00 230 | php: | 231 | array( 232 | 'iso8601' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), 233 | 'space separated' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ) 234 | ) 235 | --- 236 | test: Date 237 | todo: true 238 | brief: > 239 | A date can be represented by its year, 240 | month and day in ISO8601 order. 241 | yaml: | 242 | 1976-07-31 243 | php: | 244 | date( 1976, 7, 31 ) 245 | -------------------------------------------------------------------------------- /src/Objects/CommandInterpreter.php: -------------------------------------------------------------------------------- 1 | 24 | * @since 2.0.0 25 | */ 26 | class CommandInterpreter 27 | { 28 | const PARAMETER = 0; 29 | const PARAMETER_REQUIRED = 1; 30 | const OPTION = 2; 31 | const OPTION_MULTIPLE = 3; 32 | const OPTION_FLAG = 4; 33 | 34 | protected $title; 35 | protected $description; 36 | protected $commands = []; 37 | 38 | /** 39 | * Initializes an interpreter 40 | * 41 | * @param string $uTitle title of interpreter 42 | * @param string $uDescription description for the interpreter 43 | * 44 | * @return CommandInterpreter 45 | */ 46 | public function __construct($uTitle, $uDescription) 47 | { 48 | $this->title = $uTitle; 49 | $this->description = $uDescription; 50 | } 51 | 52 | /** 53 | * Adds a new command to interpreter 54 | * 55 | * @param string $uCommandName name of the command 56 | * @param string $uCommandDescription command description 57 | * @param array $uParameters parameters 58 | * 59 | * @return void 60 | */ 61 | public function addCommand($uCommandName, $uCommandDescription, array $uParameters) 62 | { 63 | $this->commands[$uCommandName] = [$uCommandDescription, $uParameters]; 64 | } 65 | 66 | /** 67 | * Displays the help 68 | * 69 | * @param IInterface $uInterface interface class 70 | * 71 | * @return void 72 | */ 73 | public function help($uInterface) 74 | { 75 | $uInterface->write(sprintf("%s\n", $this->title)); 76 | $uInterface->write(sprintf("%s\n", $this->description)); 77 | 78 | foreach ($this->commands as $tCommandKey => $tCommand) { 79 | $uInterface->write(sprintf("- %s: %s\n", $tCommandKey, $tCommand[0])); 80 | } 81 | } 82 | 83 | /** 84 | * Parses and executes a command 85 | * 86 | * @param string $uCommandLine command and arguments 87 | * 88 | * @return array|null result of command execution 89 | */ 90 | public function parse($uCommandLine) 91 | { 92 | $tParts = $this->split($uCommandLine); 93 | $tPartCommand = array_shift($tParts[0]); 94 | 95 | if (/* $tPartCommand !== false && */ !isset($this->commands[$tPartCommand])) { 96 | return null; 97 | } 98 | 99 | return $this->executeCommand($tPartCommand, $tParts[0], $tParts[1]); 100 | } 101 | 102 | /** 103 | * Splits a command into its components 104 | * 105 | * @param string $uInput the command 106 | * 107 | * @return array components of the given command 108 | */ 109 | protected function split($uInput) 110 | { 111 | $tParts = [[], []]; 112 | $tBuffer = ""; 113 | $tQuote = false; 114 | 115 | for ($tPosition = 0, $tLen = strlen($uInput); $tPosition < $tLen; $tPosition++) { 116 | if ($uInput[$tPosition] === "\"") { 117 | $tQuote = !$tQuote; 118 | continue; 119 | } elseif (ctype_space($uInput[$tPosition])) { 120 | if (strlen($tBuffer) == 0) { 121 | continue; 122 | } 123 | 124 | if (!$tQuote) { 125 | if (strncmp($tBuffer, "--", 2) === 0) { 126 | $tParts[1][] = $tBuffer; 127 | } else { 128 | $tParts[0][] = $tBuffer; 129 | } 130 | 131 | $tBuffer = ""; 132 | continue; 133 | } 134 | } 135 | 136 | $tBuffer .= $uInput[$tPosition]; 137 | } 138 | 139 | if (strlen($tBuffer) > 0) { 140 | if (strncmp($tBuffer, "--", 2) === 0) { 141 | $tParts[1][] = $tBuffer; 142 | } else { 143 | $tParts[0][] = $tBuffer; 144 | } 145 | } 146 | 147 | return $tParts; 148 | } 149 | 150 | /** 151 | * Executes a command 152 | * 153 | * @param string $uCommandKey name of the command 154 | * @param array $uCommandParameters parameters 155 | * @param array $uCommandOptions options 156 | * 157 | * @throws UnexpectedValueException 158 | * @return array 159 | */ 160 | protected function executeCommand($uCommandKey, array $uCommandParameters = [], array $uCommandOptions = []) 161 | { 162 | $tParameters = []; 163 | 164 | if (!isset($this->commands[$uCommandKey])) { 165 | throw new UnexpectedValueException(sprintf("command not found - %s", $uCommandKey)); 166 | } 167 | 168 | foreach ($this->commands[$uCommandKey][1] as $tOption) { 169 | if ($tOption[0] === Console::PARAMETER) { 170 | $tParameters[$tOption[1]] = array_shift($uCommandParameters); 171 | } elseif ($tOption[0] === Console::PARAMETER_REQUIRED) { 172 | $tParameters[$tOption[1]] = array_shift($uCommandParameters); 173 | if ($tParameters[$tOption[1]] === false) { 174 | throw new UnexpectedValueException(sprintf("%s parameter required for command %s", $tOption[1], $uCommandKey)); 175 | } 176 | } elseif ($tOption[0] === Console::OPTION || 177 | $tOption[0] === Console::OPTION_MULTIPLE || 178 | $tOption[0] === Console::OPTION_FLAG) { 179 | $tParameters[$tOption[1]] = $this->extractParameter($uCommandOptions, $tOption); 180 | } 181 | } 182 | 183 | if (count($uCommandOptions) > 0) { 184 | throw new UnexpectedValueException(sprintf("Invalid options used - %s", implode($uCommandOptions, ", "))); 185 | } 186 | 187 | return $tParameters; 188 | } 189 | 190 | /** 191 | * Extracts a parameter 192 | * 193 | * @param array $uCommandParameters set of command parameters 194 | * @param string $uOption name of the option 195 | * @param bool $uAssignment whether is it an assignment or not 196 | * 197 | * @return mixed extracted parameter 198 | */ 199 | protected function extractParameter(&$uCommandParameters, $uOption, $uAssignment = false) 200 | { 201 | if ($uOption[0] === Console::OPTION) { 202 | $tReturn = null; 203 | $tOption = "{$uOption[1]}="; 204 | } elseif ($uOption[0] === Console::OPTION_MULTIPLE) { 205 | $tReturn = []; 206 | $tOption = "{$uOption[1]}="; 207 | } elseif ($uOption[0] === Console::OPTION_FLAG) { 208 | $tReturn = false; 209 | $tOption = $uOption[1]; 210 | } 211 | $tOptionLength = strlen($tOption); 212 | 213 | $tUnsetKeys = []; 214 | 215 | foreach ($uCommandParameters as $tCommandParameterKey => $tCommandParameter) { 216 | if ($uOption[0] === Console::OPTION) { 217 | if (strncmp($tCommandParameter, $tOption, $tOptionLength) === 0) { 218 | $tUnsetKeys[] = $tCommandParameterKey; 219 | $tReturn = substr($tCommandParameter, $tOptionLength); 220 | } 221 | } elseif ($uOption[0] === Console::OPTION_MULTIPLE) { 222 | if (strncmp($tCommandParameter, $tOption, $tOptionLength) === 0) { 223 | $tUnsetKeys[] = $tCommandParameterKey; 224 | $tReturn[] = substr($tCommandParameter, $tOptionLength); 225 | } 226 | } elseif ($uOption[0] === Console::OPTION_FLAG) { 227 | if (strcmp($tCommandParameter, $tOption) === 0) { 228 | $tUnsetKeys[] = $tCommandParameterKey; 229 | $tReturn = true; 230 | } 231 | } 232 | } 233 | 234 | foreach ($tUnsetKeys as $tUnsetKey) { 235 | unset($uCommandParameters[$tUnsetKey]); 236 | } 237 | 238 | return $tReturn; 239 | } 240 | } 241 | --------------------------------------------------------------------------------