{"id":13660347,"url":"https://github.com/noodlehaus/dispatch","last_synced_at":"2026-03-17T16:04:43.342Z","repository":{"id":2358754,"uuid":"3322218","full_name":"noodlehaus/dispatch","owner":"noodlehaus","description":"a tiny library for quick and easy PHP apps","archived":false,"fork":false,"pushed_at":"2025-06-18T16:16:00.000Z","size":613,"stargazers_count":535,"open_issues_count":0,"forks_count":101,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-12-21T01:55:06.055Z","etag":null,"topics":["framework","micro-framework","microframework","middleware","php","php-framework","php-micro-framework","php-microframework","php-router","router"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"indy-golang/simple-chat","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noodlehaus.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2012-02-01T05:37:06.000Z","updated_at":"2025-12-16T20:19:35.000Z","dependencies_parsed_at":"2022-08-20T08:31:35.583Z","dependency_job_id":"f71c6ac5-79c0-430e-8082-2e6f503ff99c","html_url":"https://github.com/noodlehaus/dispatch","commit_stats":{"total_commits":415,"total_committers":18,"mean_commits":"23.055555555555557","dds":"0.10120481927710845","last_synced_commit":"a0ff8f576afc16fa69735d0df7b38fa8f5514194"},"previous_names":[],"tags_count":101,"template":false,"template_full_name":null,"purl":"pkg:github/noodlehaus/dispatch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noodlehaus%2Fdispatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noodlehaus%2Fdispatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noodlehaus%2Fdispatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noodlehaus%2Fdispatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noodlehaus","download_url":"https://codeload.github.com/noodlehaus/dispatch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noodlehaus%2Fdispatch/sbom","scorecard":{"id":693942,"data":{"date":"2025-08-11","repo":{"name":"github.com/noodlehaus/dispatch","commit":"df1cdabb6a19edc7937f69054386876ba1aaaeda"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/17 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"1 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T03:04:42.649Z","repository_id":2358754,"created_at":"2025-08-22T03:04:42.650Z","updated_at":"2025-08-22T03:04:42.650Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30626906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T14:16:03.965Z","status":"ssl_error","status_checked_at":"2026-03-17T14:16:03.380Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","micro-framework","microframework","middleware","php","php-framework","php-micro-framework","php-microframework","php-router","router"],"created_at":"2024-08-02T05:01:20.384Z","updated_at":"2026-03-17T16:04:43.336Z","avatar_url":"https://github.com/noodlehaus.png","language":"PHP","readme":"## dispatch\n\n- a tiny library for quick and easy PHP apps\n- requires at least PHP 8.x\n\n## functions\n\nBelow is the list of functions provided by `dispatch`.\n\n```php\nfunction dispatch(...$args): void;\nfunction route(string $method, string $path, callable ...$handlers): void;\nfunction _404(callable $handler = null): callable;\nfunction apply(...$args): void;\nfunction bind(string $name, callable $transform): void;\nfunction action(string $method, string $path, callable ...$handlers): array;\nfunction response(string $body, int $code = 200, array $headers = []): callable;\nfunction redirect(string $location, int $code = 302): callable;\nfunction serve(array $routes, string $reqmethod, string $reqpath, ...$args): callable;\nfunction phtml(string $path, array $vars = []): string;\nfunction stash(string $key, mixed $value = null): mixed;\n```\n\nHere's a sample of how you'd usually use them in an app.\n\n```php\n\u003c?php\n\nrequire 'path/to/dispatch.php';\n\n# This is a named route parameter binding. If a requested URI has a\n# :name parameter in the matching route (eg. /profiles/:user), the mapped\n# callback gets executed, and the return value gets used as a replacement\n# for the named parameter value.\nbind('user', function (string $username, $db): array {\n  $user = loadUserProfileByUsername($db, $username);\n  return $user;\n});\n\n# Sample middleware that is applied to all routes. Note that\n# the middleware function requires the first two params to be $next which\n# is a callable to the next middleware, and the $params named params\n# associative array. The $params array is always passed, and not optional.\n# Other arguments that follow are ones forwarded from the dispatch() call.\napply(function (callable $next, array $params, $db) {\n  if (isDeviceRestricted($_SERVER)) {\n    # returning a response here breaks the middleware chain\n    return response('Forbidden', 403);\n  }\n  # we move on to the next middleware\n  return $next();\n});\n\n# Sample middleware that gets applied to all routes, and also uses the\n# stash() function to store values we'll need later.\napply(function ($next) {\n  # stash is a function for storing values that can be accessed\n  # anywhere in your handlers. values stored only lasts within the same\n  # request context.\n  stash('favicon.ico', file_get_contents(__DIR__.'/static/favicon.ico'));\n  return $next();\n});\n\n# Sample middleware that gets applied to routes matching\n# the regular expression argument.\napply('^/admin/', function ($next, $params, $db) {\n  # note that because of the named parameter binding above, the\n  # value of $params['user'] is already the loaded user profile\n  if (!isAdmin($params['user'])) {\n    return response('Forbidden', 403);\n  }\n  return $next();\n}\n\n# Replace default 404 handler\n_404(fn() =\u003e response(phtml('not-found'), 404));\n\n# Sample route that has a named parameter value. Named parameters gets\n# passed to the handlers as the first argument as an associative array.\n# Arguments that follow the named parameters array are values passed through\n# dispatch(...).\nroute('GET', '/profiles/:user', function (array $params, $db) {\n\n  # because of the named param binding for user, this will\n  # contain the user profile loaded by the named param handler\n  $user = $params['user'];\n\n  # the $db argument was forwarded from the dispatch() call below\n  $meta = loadUserMetadata($db, $user['username']);\n\n  # phtml() is a function that loads a phtml file and populates it with\n  # values from the passed in associative array.\n  return response(phtml(__DIR__.'/templates/profile', ['user' =\u003e $user]));\n});\n\n# Sample route that has no named parameter so it doesn't receive the $params\n# associative array. Only dispatch() arguments get forwarded to the handler.\nroute('GET', '/index', function ($db) {\n  $users = loadTopUsers($db);\n  return response(phtml(__DIR__.'/templates/index', ['users' =\u003e $users]));\n});\n\n# Sample route that has an inline middleware passed in. Note that the\n# middleware function should still follow the middleware function signature.\nroute(\n  'GET',\n  '/favicon.ico',\n  # inline middleware\n  function ($next, $params, $db) {\n    logDeviceAccess($db, $_SERVER);\n    return $next();\n  },\n  # this is the main handler\n  function () {\n    # stash is a request-scoped storage\n    return response(stash('favicon.ico'));\n  }\n);\n\n# App routing entry point. All arguments passed to dispatch get forwarded to\n# matching route handlers after the named params array.\n$db = createDatabaseConnection();\ndispatch($db);\n\n```\n\nOnce `dispatch(...)` is called, it will try to match the current request to any\nof the mapped routes via `route(...)`. When it finds a match, it will then do the\nfollowing sequence:\n\n1. Execute all named parameter bindings from `bind(...)`\n2. Execute all global middleware and matching middleware from `apply(...)`\n3. Invoke the handler for the matching route.\n\nBecause of this sequence, it means that any transformations done by `bind(...)`\nmappings will have already updated the values inside the `$params` array that's\nforwarded down the execution chain.\n\n\n## URL Rewriting as a standalone PHP file\n\nIf you are running Dispatch as a stanalone PHP file, either on the root path or not (eg: `path/to/search.php`), this can cause errors with path matching.\n\nYou can use the `DISPATCH_PATH_PREFIX` global to mimic URL rewriting used in other libraries;\n\n```php\n\u003c?php\n// file: path/to/search.php\n\n// NOTE: this must be defined before calling the dispatch.php file\ndefine('DISPATCH_PATH_PREFIX', '/path/to/search.php');\n\nrequire __DIR__ . '/dispatch.php';\n\nroute('GET', '/testing', function() {\n  echo \"test page on subpath \" . $_SERVER['REQUEST_URI'];\n});\n\ndispatch();\n```\n\n\n## license\n\nMIT\n","funding_links":[],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoodlehaus%2Fdispatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoodlehaus%2Fdispatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoodlehaus%2Fdispatch/lists"}