{"id":19456392,"url":"https://github.com/slack-php/slack-php-app-framework","last_synced_at":"2025-07-06T01:04:15.824Z","repository":{"id":43075038,"uuid":"337916048","full_name":"slack-php/slack-php-app-framework","owner":"slack-php","description":"Robust PHP framework for building Slack apps in PHP","archived":false,"fork":false,"pushed_at":"2023-10-05T09:52:58.000Z","size":280,"stargazers_count":48,"open_issues_count":8,"forks_count":16,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-20T21:23:56.570Z","etag":null,"topics":["block-kit","framework","php","slack","slack-apps","slack-bot","slack-php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slack-php.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-02-11T03:04:19.000Z","updated_at":"2024-12-12T14:30:57.000Z","dependencies_parsed_at":"2024-01-03T04:56:08.309Z","dependency_job_id":"c381a8eb-af25-4950-bc73-fca16c79b9cb","html_url":"https://github.com/slack-php/slack-php-app-framework","commit_stats":{"total_commits":33,"total_committers":2,"mean_commits":16.5,"dds":"0.030303030303030276","last_synced_commit":"21c5ebe015ade6bbe381081050112fc263655a1a"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/slack-php/slack-php-app-framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-php%2Fslack-php-app-framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-php%2Fslack-php-app-framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-php%2Fslack-php-app-framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-php%2Fslack-php-app-framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slack-php","download_url":"https://codeload.github.com/slack-php/slack-php-app-framework/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slack-php%2Fslack-php-app-framework/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261960506,"owners_count":23236572,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["block-kit","framework","php","slack","slack-apps","slack-bot","slack-php"],"created_at":"2024-11-10T17:17:02.370Z","updated_at":"2025-07-06T01:04:15.796Z","avatar_url":"https://github.com/slack-php.png","language":"PHP","readme":"\u003cheader\u003e\n  \u003ch1 align=\"center\"\u003eSlack App Framework for PHP\u003c/h1\u003e\n  \u003cp align=\"center\"\u003eBy Jeremy Lindblom (\u003ca href=\"https://twitter.com/jeremeamia\"\u003e@jeremeamia\u003c/a\u003e)\u003c/p\u003e\n\u003c/header\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://repository-images.githubusercontent.com/337916048/1ab9fb00-87f7-11eb-9eda-f25ffb705e87\" alt=\"Slack PHP logo written in PHP's font\" width=\"65%\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"http://php.net/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/code-php%207.4-8892bf.svg\" alt=\"Coded in PHP 7.4\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://packagist.org/packages/slack-php/slack-block-kit\"\u003e\n    \u003cimg src=\"https://img.shields.io/packagist/v/slack-php/slack-app-framework.svg\" alt=\"Packagist Version\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://actions-badge.atrox.dev/slack-php/slack-php-app-framework/goto?ref=main\"\u003e\n    \u003cimg alt=\"Build Status\" src=\"https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fslack-php%2Fslack-php-app-framework%2Fbadge%3Fref%3Dmain\u0026style=flat\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n# Introduction\n\nA PHP framework for building Slack apps. It takes inspiration from Slack's Bolt frameworks.\n\nIf you are new to Slack app development, you will want to learn about it on\n[Slack's website](https://api.slack.com/start). This library is only useful if you already understand the basics of\nbuilding Slack applications.\n\n## Installation\n\n- Requires PHP 7.4+\n- Use Composer to install: `composer require slack-php/slack-app-framework`\n\n## General Usage\n\n### Quick Warning\n\nThis library has been heavily dogfooded, but the project is severely lacking in test coverage and documentation, so\nuse it at your own risk, as the MIT license advises.\n\n- Contributions welcome (especially for documentation and tests).\n- For questions, feedback, suggestions, etc., use [Discussions][].\n- For issues or concerns, use [Issues][].\n\n### Development Patterns\n\nWhen creating an app, you can configure your app from the Slack website. The framework is designed to recieve requests\nfrom all of your app's interaction points, so you should configure all of the URLs (e.g., in **Slash Commands**,\n**Interactivity \u0026 Shortcuts** (don't forget the _Select Menus_ section), and **Event Subscriptions**) to point to the\nroot URL of your deployed app code.\n\nWhen developing the app code, you declare one or more `Listener`s using the `App`'s routing methods that correspond to\nthe different types of app interaction. `Listener`s can be declared as closures, or as objects and class names of type\n`SlackPhp\\Framework\\Listener`. A `Listener` receives a `Context` object, which contains the payload data provided by\nSlack to the app and provides methods for all the actions you can take to interact with or communicate back to Slack.\n\n## Quick Example\n\nThis small app responds to the `/cool` slash command.\n\n\u003e Assumptions:\n\u003e\n\u003e - You have required the Composer autoloader to enable autoloading of the framework files.\n\u003e - You have set `SLACK_SIGNING_KEY` in the environment (e.g., `putenv(\"SLACK_SIGNING_KEY=foo\");`)\n\n```php\n\u003c?php\n\nuse SlackPhp\\Framework\\App;\nuse SlackPhp\\Framework\\Context;\n\nApp::new()\n    -\u003ecommand('cool', function (Context $ctx) {\n        $ctx-\u003eack(':thumbsup: That is so cool!');\n    })\n    -\u003erun();\n```\n\n## Example Application\n\nThe \"Hello World\" app says hello to you, by utilizing every type of app interactions, including: slash commands, block\nactions, block suggestions (i.e., options for menus), shortcuts (both global and message level), modals, events, and\nthe app home page.\n\n\u003cdetails\u003e\n\u003csummary\u003e\"Hello World\" app code\u003c/summary\u003e\n\n\u003e Assumptions:\n\u003e\n\u003e - You have required the Composer autoloader to enable autoloading of the framework files.\n\u003e - You have set `SLACK_SIGNING_KEY` in the environment (e.g., `putenv(\"SLACK_SIGNING_KEY=foo\");`)\n\u003e - You have set `SLACK_BOT_TOKEN` in the environment (e.g., `putenv(\"SLACK_BOT_TOKEN=bar\");`)\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse SlackPhp\\BlockKit\\Surfaces\\{Message, Modal};\nuse SlackPhp\\Framework\\{App, Context, Route};\n\n// Helper for creating a modal with the \"hello-form\" for choosing a greeting.\n$createModal = function (): Modal {\n    return Modal::new()\n        -\u003etitle('Choose a Greeting')\n        -\u003esubmit('Submit')\n        -\u003ecallbackId('hello-form')\n        -\u003enotifyOnClose(true)\n        -\u003etap(function (Modal $modal) {\n            $modal-\u003enewInput('greeting-block')\n                -\u003elabel('Which Greeting?')\n                -\u003enewSelectMenu('greeting')\n                -\u003eforExternalOptions()\n                -\u003eplaceholder('Choose a greeting...');\n        });\n};\n\nApp::new()\n    // Handles the `/hello` slash command.\n    -\u003ecommand('hello', function (Context $ctx) {\n        $ctx-\u003eack(Message::new()-\u003etap(function (Message $msg) {\n            $msg-\u003enewSection()\n                -\u003emrkdwnText(':wave: Hello world!')\n                -\u003enewButtonAccessory('open-form')\n                -\u003etext('Choose a Greeting');\n        }));\n    })\n    // Handles the \"open-form\" button click.\n    -\u003eblockAction('open-form', function (Context $ctx) use ($createModal) {\n        $ctx-\u003emodals()-\u003eopen($createModal());\n    })\n    // Handles when the \"greeting\" select menu needs its options.\n    -\u003eblockSuggestion('greeting', function (Context $ctx) {\n        $ctx-\u003eoptions(['Hello', 'Howdy', 'Good Morning', 'Hey']);\n    })\n    // Handles when the \"hello-form\" modal is submitted.\n    -\u003eviewSubmission('hello-form', function (Context $ctx) {\n        $state = $ctx-\u003epayload()-\u003egetState();\n        $greeting = $state-\u003eget('greeting-block.greeting.selected_option.value');\n        $ctx-\u003eview()-\u003eupdate(\":wave: {$greeting} world!\");\n    })\n    // Handles when the \"hello-form\" modal is closed without submitting.\n    -\u003eviewClosed('hello-form', function (Context $ctx) {\n        $ctx-\u003elogger()-\u003enotice('User closed hello-form modal early.');\n    })\n    // Handles when the \"hello-global\" global shortcut is triggered from the lightning menu.\n    -\u003eglobalShortcut('hello-global', function (Context $ctx) use ($createModal) {\n        $ctx-\u003emodals()-\u003eopen($createModal());\n    })\n    // Handles when the \"hello-message\" message shortcut is triggered from a message context menu.\n    -\u003emessageShortcut('hello-message', function (Context $ctx) {\n        $user = $ctx-\u003efmt()-\u003euser($ctx-\u003epayload()-\u003eget('message.user'));\n        $ctx-\u003esay(\":wave: Hello {$user}!\", null, $ctx-\u003epayload()-\u003eget('message.ts'));\n    })\n    // Handles when the Hello World app \"home\" is accessed.\n    -\u003eevent('app_home_opened', function (Context $ctx) {\n        $user = $ctx-\u003efmt()-\u003euser($ctx-\u003epayload()-\u003eget('event.user'));\n        $ctx-\u003ehome(\":wave: Hello {$user}!\");\n    })\n    // Handles when any public message contains the word \"hello\".\n    -\u003eevent('message', Route::filter(\n        ['event.channel_type' =\u003e 'channel', 'event.text' =\u003e 'regex:/^.*hello.*$/i'],\n        function (Context $ctx) {\n            $user = $ctx-\u003efmt()-\u003euser($ctx-\u003epayload()-\u003eget('event.user'));\n            $ctx-\u003esay(\":wave: Hello {$user}!\");\n        })\n    )\n    // Run that app to process the incoming Slack request.\n    -\u003erun();\n```\n\n\u003c/details\u003e\n\n### Object-Oriented Version\n\nYou can alternatively create your App and Listeners as a set of classes. I recommend this approach if you have more than\na few listeners or if your listeners are complicated. Here is an example of how the \"Hello World\" app would look when\ndeveloped in this way.\n\n\u003cdetails\u003e\n\u003csummary\u003e\"Hello World\" app code\u003c/summary\u003e\n\n`App.php` \n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace MyApp;\n\nuse SlackPhp\\Framework\\{BaseApp, Route, Router};\nuse MyApp\\Listeners;\n\nclass MyCoolApp extends BaseApp\n{\n    protected function prepareRouter(Router $router): void\n    {\n        $router-\u003ecommand('hello', Listeners\\HelloCommand::class)\n            -\u003eblockAction('open-form', Listeners\\OpenFormButtonClick::class)\n            -\u003eblockSuggestion('greeting', Listeners\\GreetingOptions::class)\n            -\u003eviewSubmission('hello-form', Listeners\\FormSubmission::class)\n            -\u003eviewClosed('hello-form', Listeners\\FormClosed::class)\n            -\u003eglobalShortcut('hello-global', Listeners\\HelloGlobalShortcut::class)\n            -\u003emessageShortcut('hello-message', Listeners\\HelloMessageShortcut::class)\n            -\u003eevent('app_home_opened', Listeners\\AppHome::class)\n            -\u003eevent('message', Route::filter(\n                ['event.channel_type' =\u003e 'channel', 'event.text' =\u003e 'regex:/^.*hello.*$/i'],\n                Listeners\\HelloMessage::class\n            ));\n    }\n}\n```\n\n`index.php`\n\n\u003e Assumptions:\n\u003e\n\u003e - You have required the Composer autoloader to enable autoloading of the framework files.\n\u003e - You have configured composer.json so that your `MyApp` namespaced code is autoloaded.\n\u003e - You have set `SLACK_SIGNING_KEY` in the environment (e.g., `putenv(\"SLACK_SIGNING_KEY=foo\");`)\n\u003e - You have set `SLACK_BOT_TOKEN` in the environment (e.g., `putenv(\"SLACK_BOT_TOKEN=bar\");`)\n\n```php\n\u003c?php\n\nuse MyApp\\MyCoolApp;\n\n$app = new MyCoolApp();\n$app-\u003erun();\n```\n\n\u003c/details\u003e\n\n## Handling Requests with the `Context` Object\n\nThe `Context` object is the main point of interaction between your app and Slack. Here are all the things you can do\nwith the `Context`:\n\n```\n// To respond (ack) to incoming Slack request:\n$ctx-\u003eack(Message|array|string|null)  // Responds to request with 200 (and optional message)\n$ctx-\u003eoptions(OptionList|array|null)  // Responds to request with an options list\n$ctx-\u003eview(): View\n  -\u003eclear()                           // Responds to modal submission by clearing modal stack\n  -\u003eclose()                           // Responds to modal submission by clearing current modal\n  -\u003eerrors(array)                     // Responds to modal submission by providing form errors\n  -\u003epush(Modal|array|string)          // Responds to modal submission by pushing new modal to stack\n  -\u003eupdate(Modal|array|string)        // Responds to modal submission by updating current modal\n\n// To call Slack APIs (to send messages, open/update modals, etc.) after the ack:\n$ctx-\u003erespond(Message|array|string)   // Responds to message. Uses payload.response_url\n$ctx-\u003esay(Message|array|string)       // Responds in channel. Uses API and payload.channel.id\n$ctx-\u003emodals(): Modals\n  -\u003eopen(Modal|array|string)          // Opens a modal. Uses API and payload.trigger_id\n  -\u003epush(Modal|array|string)          // Pushes a new modal. Uses API and payload.trigger_id\n  -\u003eupdate(Modal|array|string)        // Updates a modal. Uses API and payload.view.id\n$ctx-\u003ehome(AppHome|array|string)      // Modifies App Home for user. Uses API and payload.user.id\n$ctx-\u003eapi(string $api, array $params) // Use Slack API client for arbitrary API operations\n\n// Access payload or other contextual data:\n$ctx-\u003epayload(): Payload              // Returns the payload of the incoming request from Slack\n$ctx-\u003egetAppId(): ?string             // Gets the app ID, if it's known\n$ctx-\u003eget(string): mixed              // Gets a value from the context\n$ctx-\u003eset(string, mixed)              // Sets a value in the context\n$ctx-\u003eisAcknowledged(): bool          // Returns true if ack has been sent\n$ctx-\u003eisDeferred(): bool              // Returns true if additional processing will happen after the ack\n\n// Access additional helpers:\n$ctx-\u003eblocks(): Blocks                // Returns a helper for creating Block Kit surfaces\n$ctx-\u003efmt(): Formatter                // Returns the \"mrkdwn\" formatting helper for Block Kit text\n$ctx-\u003elogger(): LoggerInterface       // Returns an instance of the configured PSR-3 logger\n$ctx-\u003econtainer(): ContainerInterface // Returns an instance of the configured PSR-11 container\n```\n\n## High Level Design\n\n![UML diagram of the framework](https://yuml.me/68717414.png)\n\n\u003cdetails\u003e\n\u003csummary\u003eYUML Source\u003c/summary\u003e\n\u003cpre\u003e\n[AppServer]\u003c\u003e-runs\u003e[App]\n[AppServer]creates-\u003e[Context]\n[App]\u003c\u003e-\u003e[AppConfig]\n[App]\u003c\u003e-\u003e[Router]\n[Router]-^[Listener]\n[Router]\u003c\u003e1-*\u003e[Listener]\n[Listener]handles-\u003e[Context]\n[Context]\u003c\u003e-\u003e[Payload]\n[Context]\u003c\u003e-\u003e[AppConfig]\n[Context]\u003c\u003e-\u003e[_Clients_;RespondClient;ApiClient]\n[Context]\u003c\u003e-\u003e[_Helpers_;BlockKit;Modals;View]\n[Context]\u003c\u003e-\u003e[_Metadata_]\n[AppConfig]\u003c\u003e-\u003e[Logger]\n[AppConfig]\u003c\u003e-\u003e[Container]\n[AppConfig]\u003c\u003e-\u003e[_Credentials_]\n\u003c/pre\u003e\n\u003c/details\u003e\n\n## Socket Mode\n\n[Socket mode][] support is provided by a separate package. See [slack-php/slack-php-socket-mode][].\n\n## Not Implemented\n\nThe following features are known to be missing:\n\n- OAuth flow for handling installations to a different workspace.\n    - Though there are some class in the `SlackPhp\\Framework\\Auth` namespace if you need to roll your own right now.\n\n## Standards Used\n\n- PSR-1, PSR-12: Coding Style\n- PSR-3: Logger Interface\n- PSR-4: Autoloading\n- PSR-7, PSR-15, PSR-17: HTTP\n- PSR-11: Container Interface\n\n[Discussions]: https://github.com/slack-php/slack-php-app-framework/discussions\n[Issues]: https://github.com/slack-php/slack-php-app-framework/issues\n[Pull Request]: https://github.com/slack-php/slack-php-app-framework/pulls\n[Socket Mode]: https://api.slack.com/apis/connections/socket\n[slack-php/slack-php-socket-mode]: https://github.com/slack-php/slack-php-socket-mode\n","funding_links":[],"categories":[":hammer_and_wrench: \u0026nbsp; Libraries and SDKs"],"sub_categories":["PHP"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslack-php%2Fslack-php-app-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslack-php%2Fslack-php-app-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslack-php%2Fslack-php-app-framework/lists"}