├── .DS_Store
├── .github
├── dependabot.yml
└── workflows
│ └── main.yml
├── .gitignore
├── .php-cs-fixer.cache
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── phpunit.xml
├── rector.php
├── src
├── .github
│ └── workflows
│ │ └── tests.yml
├── RouteCollection.php
├── Router.php
└── RouterEngine.php
└── tests
├── Demo
├── App
│ ├── Main.php
│ └── Test.php
├── Appo
│ └── Hi.php
├── Bye.php
├── Hello.php
├── Main.php
├── Param.php
└── Test.php
├── Pest.php
└── Unit
├── ManualRouteTest.php
├── RouterCollectionTest.php
├── RouterEngineTest.php
└── RouterTest.php
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scrawler-labs/router/f801eff3f786cc82f187752d67c2e81de13fbbf9/.DS_Store
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "composer" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
13 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push, pull_request,workflow_dispatch]
3 | jobs:
4 | router-tests:
5 | runs-on: ubuntu-latest
6 | strategy:
7 | matrix:
8 | operating-system: ['ubuntu-latest']
9 | php-versions: ['8. 3']
10 | phpunit-versions: ['latest']
11 | include:
12 | - operating-system: 'ubuntu-latest'
13 | php-versions: '8.3'
14 | steps:
15 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
16 | - uses: shivammathur/setup-php@v2
17 | with:
18 | php-version: '8.3'
19 | - name: Update Composer
20 | run: sudo composer self-update --no-interaction
21 | - name: Run Composer Install
22 | run: composer install --no-interaction
23 | - name: run tests
24 | run: vendor/bin/pest --coverage-clover ./clover.xml
25 | - name: run static analysis
26 | run: vendor/bin/phpstan analyse src --level 5
27 | - name: Upload to Codecov
28 | uses: codecov/codecov-action@v4
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /nbproject/private/
2 | /vendor
--------------------------------------------------------------------------------
/.php-cs-fixer.cache:
--------------------------------------------------------------------------------
1 | {"php":"8.3.17","version":"3.68.5","indent":" ","lineEnding":"\n","rules":{"align_multiline_comment":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"class_reference_name_casing":true,"clean_namespace":true,"concat_space":true,"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_declaration":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":false,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["attribute","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_hidden_params":true,"remove_inheritdoc":true},"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"nullable_type_declaration":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"ordered_types":{"null_adjustment":"always_last","sort_algorithm":"none"},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":{"groups":[["Annotation","NamedArgumentConstructor","Target"],["author","copyright","license"],["category","package","subpackage"],["property","property-read","property-write"],["deprecated","link","see","since"]]},"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_around_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"statement_indentation":{"stick_comment_to_next_continuous_control_statement":true},"switch_continue_to_break":true,"trailing_comma_in_multiline":{"after_heredoc":true,"elements":["array_destructuring","arrays","match","parameters"]},"trim_array_spaces":true,"type_declaration_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"array_indentation":true,"array_syntax":true,"cast_spaces":true,"new_with_parentheses":{"anonymous_class":false},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"src\/RouterEngine.php":"2a985df07328dadd7b0ebe98d1b7b7a2","src\/RouteCollection.php":"7e45144fce71de0fe2b1453cfb6f6ad6","src\/Router.php":"741188a7e30c3a4f7e34f7e1654b9370"}}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Corpusvision Technologies Pvt Ltd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 | 🔥An Fully Automatic, Framework independent, RESTful PHP Router component🔥
17 | 🇮🇳 Made in India 🇮🇳
18 |
19 |
20 | 
21 |
22 |
23 | Complete docs can be found [here](https://component.scrawlerlabs.com/router/)
24 |
25 | ## 🤔 Why use Scrawler Router?
26 | - Fully automatic, you dont need to define single manual route.
27 | - Support manual route defination for your edge use case.
28 | - No configrations , works out of the box with any php project.
29 | - Stable and well tested.
30 | - Saves lot of time while building RESTful applications
31 |
32 |
33 | ## 💻 Installation
34 | You can install Scrawler Router via Composer. If you don't have composer installed , you can download composer from [here](https://getcomposer.org/download/)
35 |
36 | ```sh
37 | composer require scrawler/router
38 | ```
39 |
40 | ## ✨ Setup
41 |
42 | Note 4.x release changes the way router handles request and response, if you still wanna continue using old way with symfony components goto [3.x branch](https://github.com/scrawler-labs/router/tree/3.x)
43 |
44 | ```php
45 | register($dir,$namespace);
55 |
56 | /**
57 | * you can now also enable route caching by passing your own PSR 16 implementation
58 | * $cache = new Psr\SimpleCache\CacheInterface();
59 | * $router->enableCache($cache);
60 | **/
61 |
62 | // Fetch method and URI from somewhere
63 | $httpMethod = $_SERVER['REQUEST_METHOD'];
64 | $uri = $_SERVER['REQUEST_URI'];
65 |
66 | // Strip query string (?foo=bar) and decode URI
67 | if (false !== $pos = strpos($uri, '?')) {
68 | $uri = substr($uri, 0, $pos);
69 | }
70 | $uri = rawurldecode($uri);
71 |
72 | //Dispatch route and get back the response
73 | [$status,$handler,$args,$debug] = $router->dispatch($httpMethod,$uri);
74 | switch ($status){
75 | case \Scrawler\Router\Router::NOT_FOUND:
76 | //handle 404 error
77 | // $debug contains extra debug info useful to check failure in automatic routing
78 | break;
79 | case \Scrawler\Router\Router::METHOD_NOT_ALLOWED:
80 | //handle 405 method not allowed
81 | break;
82 | case \Scrawler\Router\Router::FOUND:
83 | //call the handler
84 | $response = call_user_func($handler,...$args);
85 | // Send Response
86 | //echo $response
87 | }
88 |
89 | ```
90 |
91 | Done now whatever request occurs it will be automatically routed . You don't have define a single route
92 |
93 |
94 | ## ✏️ Manual routing
95 | Information on manual routing can be found in [docs](https://component.scrawlerlabs.com/router/)
96 |
97 |
98 |
99 | ## 🦊 How it Works?
100 |
101 | The automatic routing is possible by following some conventions. Lets take a example lets say a controller Hello
102 |
103 | ```php
104 |
117 |
118 | ## 🔥 How does it do it automatically?
119 |
120 | Each request to the server is interpreted by Scrawler Router in following way:
121 |
122 | `METHOD /controller/function/arguments1/arguments2`
123 |
124 | The controller and function that would be invoked will be
125 |
126 | ```php
127 |
156 |
157 | ## ⁉️ How should I name my function for automatic routing?
158 |
159 | The function name in the controller should be named according to following convention:
160 | `methodFunctionname`
161 | Note:The method should always be written in small and the first word of function name should always start with capital.
162 | Method is the method used while calling url. Valid methods are:
163 |
164 | ```
165 | all - maps any kind of request method i.e it can be get,post etc
166 | get - mpas url called by GET method
167 | post - maps url called by POST method
168 | put - maps url called by PUT method
169 | delete - maps url called by DELETE method
170 | ```
171 | Some eg. of valid function names are:
172 | ```
173 | getArticles, postUser, putResource
174 | ```
175 | Invalid function names are:
176 | ```
177 | GETarticles, Postuser, PutResource
178 | ```
179 |
180 |
181 | ## 🏠 Website home page
182 | Scrawler Router uses a special function name `allIndex()` and special controller name `Main`. So If you want to make a controller for your landing page `\` the controller will be defines as follows
183 | ```php
184 | // Inside main.php
185 | class Main
186 | {
187 | // All request to your landing page will be resolved to this controller
188 | // ALternatively you can use getIndex() to resolve only get request
189 | public function allIndex()
190 | {
191 | }
192 | }
193 | ```
194 |
195 |
196 | ## 🌟 Main Controller
197 | Class name with `Main` signifies special meaning in Scrawler Router , if you wanna define pages route URL you can use main controler
198 | ```php
199 | // Inside main.php
200 | class Main
201 | {
202 | // Resolves `/`
203 | public function getIndex()
204 | {
205 | }
206 |
207 | // Resolves `/abc`
208 | public function getAbc()
209 | {
210 |
211 | }
212 |
213 | // Resolves `/hello`
214 | public function getHello()
215 | {
216 |
217 | }
218 | }
219 | ```
220 |
221 |
222 | ## 👉 Index function
223 | Just like `Main` controller `allIndex(), getIndex(), postIndex()` etc signifies a special meaning , urls with only controller name and no function name will try to resolve into this function.
224 | ```php
225 | // Inside hello.php
226 | class Hello
227 | {
228 | // Resolves `/hello`
229 | public function getIndex()
230 | {
231 |
232 | }
233 |
234 | // Resolves `/hello/abc`
235 | public function getAbc()
236 | {
237 |
238 | }
239 | }
240 | ```
241 |
242 |
243 |
244 | ## 👏 Supporters
245 | If you have reached here consider giving a star to help this project ❤️
246 | [](https://github.com/scrawler-labs/router/stargazers)
247 |
248 | Thank You for your forks and contributions
249 | [](https://github.com/scrawler-labs/router/network/members)
250 |