{"id":13765770,"url":"https://github.com/teqneers/ext-direct","last_synced_at":"2025-04-01T00:32:35.078Z","repository":{"id":35188587,"uuid":"39446639","full_name":"teqneers/ext-direct","owner":"teqneers","description":"A base component to integrate Sencha Ext JS Ext.direct into a PHP application","archived":true,"fork":false,"pushed_at":"2024-03-18T10:21:39.000Z","size":274,"stargazers_count":10,"open_issues_count":2,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-31T08:06:47.185Z","etag":null,"topics":["ext-direct","extjs","php","php-library","sencha"],"latest_commit_sha":null,"homepage":null,"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/teqneers.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-21T13:23:49.000Z","updated_at":"2025-03-18T12:04:01.000Z","dependencies_parsed_at":"2024-08-03T16:14:11.163Z","dependency_job_id":null,"html_url":"https://github.com/teqneers/ext-direct","commit_stats":{"total_commits":186,"total_committers":6,"mean_commits":31.0,"dds":"0.15591397849462363","last_synced_commit":"4b229720542c2229e282c8173a6f2f80e948b528"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teqneers%2Fext-direct","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teqneers%2Fext-direct/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teqneers%2Fext-direct/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teqneers%2Fext-direct/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teqneers","download_url":"https://codeload.github.com/teqneers/ext-direct/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246563355,"owners_count":20797443,"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":["ext-direct","extjs","php","php-library","sencha"],"created_at":"2024-08-03T16:00:45.536Z","updated_at":"2025-04-01T00:32:30.062Z","avatar_url":"https://github.com/teqneers.png","language":"PHP","funding_links":[],"categories":["Extensions, components"],"sub_categories":["Direct connectors"],"readme":"# ext-direct\n\nA base component to integrate Sencha Ext JS Ext.direct into a PHP application\n\n[![Build Status](https://github.com/teqneers/ext-direct/actions/workflows/php.yml/badge.svg)](https://github.com/teqneers/ext-direct/actions/workflows/php.yml)\n\n## Introduction\n\nThis library provides a server-side implementation for Sencha *Ext.direct* an RPC-style communication component that is\npart of Sencha's *Ext JS* and *Sencha Touch*.\n\n\u003e Ext Direct is a platform and language agnostic remote procedure call (RPC) protocol. Ext Direct allows\n\u003e for seamless communication between the client side of an Ext JS application and any server platform that\n\u003e conforms to the specification. Ext Direct is stateless and lightweight, supporting features like API discovery,\n\u003e call batching, and server to client events.\n\nCurrently this library is only used as the foundation\nof [teqneers/ext-direct-bundle](https://github.com/teqneers/ext-direct-bundle), a Symfony bundle that integrates *\nExt.direct* into a Symfony based application. We have not tried to use the library as a stand-alone component or in any\nother context than a Symfony environment, so the following is only how it should work theoretically without the bundle.\nWe'd appreciate any help and contribution to make the library more useful outside the bundle.\n\n## Installation\n\nYou can install this library using composer\n\n    composer require teqneers/ext-direct\n\nor add the package to your composer.json file directly.\n\n## Example\n\nThe naming strategy determins how PHP class names and namespaces are translated into Javascript-compatible *Ext.direct*\naction names. The default naming strategy translates the `\\` namspapce separator into a `.`. So `My\\Namespace\\Service`\nis translated into `My.namespace.Service`. Please note that the transformation must be\nreversible (`My.namespace.Service` =\u003e `My\\Namespace\\Service`).\n\n```php\n$namingStrategy = new TQ\\ExtDirect\\Service\\DefaultNamingStrategy();\n```\n\nThe service registry uses a metadata factory from the [`jms/metadata`](https://github.com/schmittjoh/metadata) library\nand an associated annotation driver (which in turn uses\na [`doctrine/annotations`](https://github.com/doctrine/annotations.git)\nannotation reader) to read meta information about possible annotated service classes.\n\n```php\n$serviceRegistry = new TQ\\ExtDirect\\Service\\DefaultServiceRegistry(\n    new Metadata\\MetadataFactory(\n        new TQ\\ExtDirect\\Metadata\\Driver\\AnnotationDriver(\n            new Doctrine\\Common\\Annotations\\AnnotationReader()\n        )\n    ),\n    $namingStrategy\n);\n```\n\nThe service registry can be filled manually by calling `addServices()` or `addService()` or by importing services using\na `TQ\\ExtDirect\\Service\\ServiceLoader`. The default implementation\n`\\TQ\\ExtDirect\\Service\\PathServiceLoader` can read classes from a set of given paths.\n\nThe event dispatcher is optional but is required to use features like argument conversion and validation, result\nconversion of the profiling listener.\n\n```php\n$eventDispatcher = new Symfony\\Component\\EventDispatcher\\EventDispatcher();\n```\n\nThe router is used to translate incoming *Ext.direct* requests into PHP method calls to the correct service class.\nThe `ContainerServiceFactory` supports retrieving services from a Symfony dependency injection container or\ninstantiating simple services which take no constructor arguments at all. Static service calls bypass the service\nfactory.\n\n```php\n$router = new TQ\\ExtDirect\\Router\\Router(\n    new TQ\\ExtDirect\\Router\\ServiceResolver(\n        $serviceRegistry,\n        new TQ\\ExtDirect\\Service\\ContainerServiceFactory(\n            /* a Symfony\\Component\\DependencyInjection\\ContainerInterface */\n        )\n    ),\n    $eventDispatcher\n);\n```\n\nThe endpoint object is a facade in front of all the *Ext.direct* server-side components. With\nits `createServiceDescription()` method one can obtain a standard-compliant API- description while `handleRequest()`\ntakes a `Symfony\\Component\\HttpFoundation\\Request`\nand returns a `Symfony\\Component\\HttpFoundation\\Response` which contains the *Ext.direct*\nresponse for the service calls received.\n\n```php\n$endpoint = TQ\\ExtDirect\\Service\\Endpoint(\n    'default', // endpoint id\n    new TQ\\ExtDirect\\Description\\ServiceDescriptionFactory(\n        $serviceRegistry,\n        'My.api',\n        $router,\n        new TQ\\ExtDirect\\Router\\RequestFactory(),\n        'My.api.REMOTING_API'\n    )\n);\n```\n\nThe endpoint manager is just a simple collection of endpoints which allow retrieval using the endpoint id. This allows\neasy exposure of multiple independent APIs.\n\n```php\n$manager = new TQ\\ExtDirect\\Service\\EndpointManager();\n$manager-\u003eaddEndpoint($endpoint);\n$defaultEndpoint = $manager-\u003egetEndpoint('default');\n\n$apiResponse = $defaultEndpoint-\u003ecreateServiceDescription('/path/to/router');\n$apiResponse-\u003esend();\n\n$request = Symfony\\Component\\HttpFoundation\\Request::createFromGlobals();\n$response = $defaultEndpoint-\u003ehandleRequest($request);\n$response-\u003esend();\n```\n\nThe routing process can be manipulated and augmented by using event listeners on the event dispatcher passed into the\nrouter. The library provides four event subscribers that allow\n\n- converting arguments prior to calling the service method\n- validation of arguments prior to calling the service method\n- converting the service method call result before sending it back to the client\n- instrumenting the router to gain timing information (used to augment the Symfony profiler timeline)\n\nThe shipped argument and result converters use the [`jms/serializer`](https://github.com/schmittjoh/serializer) library\nto provide extended\n(de-)serialization capabilities, while the default argument validator makes use of\nthe [`symfony/validator`](https://github.com/symfony/Validator)\nlibrary.\n\n```php\n$eventDispatcher-\u003eaddSubscriber(\n    new TQ\\ExtDirect\\Router\\EventListener\\ArgumentConversionListener(\n        new TQ\\ExtDirect\\Router\\ArgumentConverter(/* a JMS\\Serializer\\Serializer */)\n    )\n);\n$eventDispatcher-\u003eaddSubscriber(\n    new TQ\\ExtDirect\\Router\\EventListener\\ArgumentValidationListener(\n        new TQ\\ExtDirect\\Router\\ArgumentValidator(/* a Symfony\\Component\\Validator\\Validator\\ValidatorInterface */)\n    )\n);\n$eventDispatcher-\u003eaddSubscriber(\n    new TQ\\ExtDirect\\Router\\EventListener\\ResultConversionListener(\n        new TQ\\ExtDirect\\Router\\ResultConverter(/* a JMS\\Serializer\\Serializer */)\n    )\n);\n$eventDispatcher-\u003eaddSubscriber(\n    new TQ\\ExtDirect\\Router\\EventListener\\StopwatchListener(\n        /* a Symfony\\Component\\Stopwatch\\Stopwatch */\n    )\n);\n```\n\n## Service Annotations\n\nServices to be exposed via the *Ext.direct* API must be decorated with appropriate meta information. Currently this is\nonly possible using annotations (like the ones known from Doctrine, Symfony or other modern PHP libraries).\n\nEach service class that will be exposed as an *Ext.direct* action is required to be annotated\nwith `TQ\\ExtDirect\\Annotation\\Action`. The `Action` annotation optionally takes a service id parameter for services that\nare neither static nor can be instantiated with a parameter-less constructor.\n\n```php\nuse TQ\\ExtDirect\\Annotation as Direct;\n\n/**\n * @Direct\\Action()\n */\nclass Service1\n{\n    // service will be instantiated using the parameter-less constructor if called method is not static\n}\n\n/**\n * @Direct\\Action(\"app.direct.service2\")\n */\nclass Service2\n{\n    // service will be retrieved from the dependency injection container using id \"app.direct.service2\" if called method is not static\n}\n```\n\nAdditionally each method that ill be exposed on an *Ext.direct* action is required to be annotated\nwith `TQ\\ExtDirect\\Annotation\\Method`. The `Method` annotation optionally takes either `true` to designate the method as\nbeing a form\nhandler ([taking regular form posts](http://docs.sencha.com/extjs/6.0/direct/specification.html#Remoting_form_submission))\nor `false` to designate the method as being a regular *Ext.direct* method (this is the default).\n\n```php\n/**\n * @Direct\\Action(\"app.direct.service3\")\n */\nclass Service3\n{\n    /**\n     * @Direct\\Method()\n     */\n    public function methodA()\n    {\n        // regular method\n    }\n\n    /**\n     * @Direct\\Method(true)\n     */\n    public function methodB()\n    {\n        // form handler method\n    }\n}\n```\n\n**Extended features such as named parameters and strict named parameters described the in the *Ext.direct* specification\nare currently not exposed through the annotation system.**\n\nParameters that go into a method that is being called via an *Ext.direct* request can be annotated as well to apply\nparameter validation. This requires that the `TQ\\ExtDirect\\Router\\EventListener\\ArgumentValidationListener` is\nregistered with the appropriate event dispatcher.\n\n```php\nuse Symfony\\Component\\Validator\\Constraints as Assert;\n\n/**\n * @Direct\\Action(\"app.direct.service4\")\n */\nclass Service4\n{\n    /**\n     * @Direct\\Method()\n     * @Direct\\Parameter(\"a\", { @Assert\\NotNull(), @Assert\\Type(\"int\") })\n     *\n     * @param int $a\n     */\n    public function methodA($a)\n    {\n    }\n}\n```\n\nIf the signature of the method being called exposes parameter(s) with a type-hint\nfor `Symfony\\Component\\HttpFoundation\\Request`\nand/or `TQ\\ExtDirect\\Router\\Request`, the incoming Symfony HTTP request and/or the raw *Ext.direct* request are injected\ninto the method call automatically. This is especially important form form handling methods because there is no other\nway to access the incoming HTTP request parameters (form post).\n\nAs soon as the `TQ\\ExtDirect\\Router\\EventListener\\ArgumentConversionListener` is enabled, one can use strictly-typed\nobject parameters on service methods. These arguments will be automatically deserialized from the incoming JSON request\nand will be injected into the method call.\n\nThe same is true for returning objects from a service method call. If\nthe `TQ\\ExtDirect\\Router\\EventListener\\ResultConversionListener`\nis enabled, return values are automatically serialized to JSON even if they are non-trivial objects.\n\nBoth the argument as well as the return value conversion is based on the\nexcellent [`jms/serializer`](https://github.com/schmittjoh/serializer)\nlibrary by Johannes Schmitt. See the [documentation](http://jmsyst.com/libs/serializer) for more information.\n\n## Specification\n\nThe Ext Direct Specification can be found\non [Sencha's documentation website](http://docs.sencha.com/extjs/6.0/direct/specification.html).\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 TEQneers GmbH \u0026 Co. KG\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the\nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteqneers%2Fext-direct","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteqneers%2Fext-direct","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteqneers%2Fext-direct/lists"}