{"id":33217115,"url":"https://github.com/overtrue/yaf-skeleton","last_synced_at":"2026-01-13T22:58:45.074Z","repository":{"id":45143529,"uuid":"128028069","full_name":"overtrue/yaf-skeleton","owner":"overtrue","description":"The Yaf testable skeleton and composer supported.","archived":true,"fork":false,"pushed_at":"2021-11-30T16:05:03.000Z","size":702,"stargazers_count":140,"open_issues_count":1,"forks_count":22,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-12T12:19:00.884Z","etag":null,"topics":["skeleton-application","yac","yaf","yaf-skeleton"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/overtrue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["overtrue"]}},"created_at":"2018-04-04T07:57:41.000Z","updated_at":"2025-02-22T18:25:04.000Z","dependencies_parsed_at":"2022-07-13T16:50:28.358Z","dependency_job_id":null,"html_url":"https://github.com/overtrue/yaf-skeleton","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/overtrue/yaf-skeleton","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overtrue%2Fyaf-skeleton","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overtrue%2Fyaf-skeleton/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overtrue%2Fyaf-skeleton/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overtrue%2Fyaf-skeleton/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/overtrue","download_url":"https://codeload.github.com/overtrue/yaf-skeleton/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overtrue%2Fyaf-skeleton/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28400681,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","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":["skeleton-application","yac","yaf","yaf-skeleton"],"created_at":"2025-11-16T14:00:18.916Z","updated_at":"2026-01-13T22:58:45.065Z","avatar_url":"https://github.com/overtrue.png","language":"PHP","funding_links":["https://github.com/sponsors/overtrue"],"categories":["框架( Frameworks )"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eYaf skeleton\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eThe Yaf testable skeleton and composer supported.\u003c/p\u003e\n\n[![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me-button-s.svg?raw=true)](https://github.com/sponsors/overtrue)\n\n# Requirement\n\n- PHP \u003e= 7.0\n- Yaf \u003e= 3.0\n\n# Installation\n\n1. Update `yaf.ini`:\n```init\n[yaf]\nyaf.use_namespace=1\nyaf.use_spl_autoload=1\n...\n```\n\n2. Create project.\n\n```shell\n$ composer create-project overtrue/yaf-skeleton myapp -vvv\n```\n\n3. Web server rewrite rules:\n\n    #### Apache\n\n    ```conf\n    #.htaccess\n    RewriteEngine On\n    RewriteCond %{REQUEST_FILENAME} !-f\n    RewriteRule .* index.php\n    ```\n\n    #### Nginx\n\n    ```\n    server {\n      listen 80;\n      server_name  myapp.com;\n      root   /path/to/myapp;\n      index  index.php index.html index.htm;\n\n      if (!-e $request_filename) {\n        rewrite ^/(.*)  /index.php/$1 last;\n      }\n    }\n    ```\n\n    #### Lighttpd\n\n    ```\n    $HTTP[\"host\"] =~ \"(www.)?myapp.com$\" {\n      url.rewrite = (\n         \"^/(.+)/?$\"  =\u003e \"/index.php/$1\",\n      )\n    }\n    ```\n\n\n# Application structor\n\n```\n├── .scripts\n│   ├── .gitkeep\n│   ├── common.sh\n│   ├── job_phpcs.sh\n│   ├── job_phpmd.sh\n│   ├── job_phpunit.sh\n│   ├── sami.phar\n│   └── sami.php\n├── app\n│   ├── commands                # sora commands (namespace：\\App\\Commands)\n│   ├── controllers             # Yaf Controllers (namespace：\\)\n│   ├── exceptions              # Exceptions (namespace：\\App\\Exceptions)\n│   ├── facades                 # Service Facades (namespace：\\)\n│   ├── plugins                 # Yaf plugins (namespace：\\)\n│   ├── presenters              # Object presenters (namespace：\\App\\Presenters)\n│   ├── services                # Services, the 3rd service bridges (namespace：\\App\\Services)\n│   ├── traits                  # Traits (namespace：\\App\\Traits)\n│   ├── views                   # Template files\n│   ├── helpers.php             # Herlpers\n│   ├── Bootstrap.php           # Yaf Bootstrap file\n├── config\n│   ├── application.ini     # Yaf config file\n├── public                  # web extrence\n│   └── index.php\n├── sora                    # The command line tool\n├── tests                   # Unit tests\n└── vendor                  #\n├── phpunit.xml.dist        # PHPUnit config file\n├── .gitignore\n├── .php_cs                 # PHP-CS-Fixer config file\n├── composer.json\n├── composer.lock\n├── README.md\n```\n\n\n# Controllers\n\n\n```shell\n$ ./sora make:controller Foo_Bar # or：foo_bar/FooBar/FooBarController\n#\n# /www/myapp/app/controllers/Foo/Bar.php Created!\n# /www/myapp/tests/controllers/Foo/BarTest.php Created!\n```\n\n\nAll controllers are created in the `app/controllers` directory，and test files are also created in the `tests/controllers` directorty.\n\nOf course, you can also create tests independently：\n\n```shell\n$ ./sora make:test Foo_Bar # Also supports multiple type controller names\n# /www/myapp/tests/controllers/Foo/BarTest.php Created!\n```\n\n\n### The handle() method\n\nThe controller entry method `handle()`:\n\n```php\n\u003c?php\n\n\nclass ExampleController extends BaseController\n{\n    public function handle()\n    {\n        return 'Hello world!';\n        // return json(['foo' =\u003e 'bar']);\n        // return redirect('https://easywechat.com');\n        // return view('welcome', ['name' =\u003e 'MyApp']); // template engine require.\n    }\n}\n\n```\n\n# Views\n\nThere is no built-in template engine. If you need to use a PHP template, we recommend that you use [Plates](http://platesphp.com/v3/), Plates is a native PHP template system that's fast, easy to use and easy to extend.\n\n```sh\n$ composer require league/plates -vvv\n```\n\nYou can use `view(string $template, array $data)` helper in controller `handle` method as a result.\n\nfor example:\n\n```php\n    public function handle()\n    {\n        $data = [\n            'name' =\u003e 'overtrue',\n            'age' =\u003e 28,\n        ];\n\n        return view('profile-page', $data);\n    }\n```\n\n```html\n\u003c!--app/views/profile-page.php:--\u003e\n\n\u003ch1\u003e\u003c?= $name ?\u003e\u003c/h1\u003e\n\u003cp\u003e\u003c?= $age ?\u003e\u003c/p\u003e\n```\n\nMore usage please read the [Plates Docs](http://platesphp.com/v3/).\n\n# Unit tests\n\n\u003e The difficulty of writing unit tests is inversely proportional to the quality of your code. The higher the code quality, the lower the difficulty of unit testing, so design your code well.\n\nThe unit test for creating the controller can be done with the following command：\n\n```shell\n$ ./sora make:test Foo_BarController\n```\n\n## Write test cases\n\nTo create a controller test object, use，you can use the `mock_controller` function:\n\n```php\n$controller = mock_controller(Foo_BarController::class);\n\n// Indicates the method to mock, and the protected method is also mockable\n$controller = mock_controller(Foo_BarController::class, ['getUsers', 'getApp']);\n```\n\n## Assertion\n\nWe have such a controller:\n\n```php\n    ...\n    public function handle()\n    {\n        $params = Request::only('uids', 'screen_name', 'trim_status', 'has_extend', 'simplify', 'is_encoded');\n\n        $users = $this-\u003eget('main.users.show_batch', $params);\n\n        return $users;\n    }\n    ...\n```\n\nSo the test should cover the above three behaviors：\n\n```php\npublic function testHandle()\n{\n    $input = [\n        'uids' =\u003e '2193182644',\n        'screen_name' =\u003e '安正超',\n        'trim_status' =\u003e 0,\n        'has_extend' =\u003e 1,\n        'simplify' =\u003e 0,\n        'is_encoded' =\u003e 0,\n        'foo' =\u003e 'bar',\n    ];\n\n    Request::shouldReceive('only')\n            -\u003ewith('uids', 'screen_name', 'trim_status', 'has_extend', 'simplify', 'is_encoded')\n            -\u003eandReturn($input);\n\n    $controller = mock_controller(Users_Show_BatchController::class, ['get']); // mock the `get` method\n\n    $controller-\u003eshouldReceive('get')-\u003ewith('main.users.show_batch', array_except($_GET, ['foo']))\n                                    -\u003eandReturn('foo')\n                                    -\u003eonce();\n\n    $response = $controller-\u003ehandle();\n\n    $this-\u003eassertSame('foo', $response);\n}\n```\n\n## Facade Assertion\n\n```php\nRequest::shouldReceive('get')-\u003ewith('mid')-\u003eandReturn('mock-mid')-\u003eonce();\nLog::shouldReceive('action')-\u003ewith(48, 'oid', 'ext')-\u003eonce();\n..\n```\n\n\n### Built-in auxiliary assertion methods\n\n```php\n$this-\u003eshouldAbort($message, $code);\n```\n\nThey are used to correspond to exceptions thrown by `abort($message, $code);` in the controller\n\n### Some helper methods in test case classes\n\nMock request method：\n\n```php\n$this-\u003emethod('post');\n```\n\nMock request `uri`：\n\n```php\n$this-\u003euri('/foo/bar?uid=12345');\n```\n\nMock config：\n\n```php\n$this-\u003econfig('foo', 'bar');\n```\n\nMock request IP：\n\n```php\n$this-\u003eip('127.0.0.1');\n```\n\nMock $_SERVER vars：\n\n```php\n$this-\u003eserver('REQUEST_URI', '/foo/bar');\n```\n\n# Docs\n\n- Yaf Docs: http://www.php.net/manual/en/book.yaf.php\n- Plates Docs: http://platesphp.com/v3/\n- PHPUnit Docs: https://phpunit.de/manual/current/zh_cn/phpunit-book.html\n- Mockery Docs: http://docs.mockery.io/en/latest/\n\n\n## :heart: Sponsor me \n\n[![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me.svg?raw=true)](https://github.com/sponsors/overtrue)\n\n如果你喜欢我的项目并想支持它，[点击这里 :heart:](https://github.com/sponsors/overtrue)\n\n\n## Project supported by JetBrains\n\nMany thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.\n\n[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue)\n\n## PHP 扩展包开发\n\n\u003e 想知道如何从零开始构建 PHP 扩展包？\n\u003e\n\u003e 请关注我的实战课程，我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)\n\n# License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovertrue%2Fyaf-skeleton","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovertrue%2Fyaf-skeleton","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovertrue%2Fyaf-skeleton/lists"}