{"id":16827290,"url":"https://github.com/deminy/background-processing-in-php","last_synced_at":"2025-04-11T04:15:36.708Z","repository":{"id":151093095,"uuid":"141636283","full_name":"deminy/background-processing-in-php","owner":"deminy","description":"Technical discussion on background processing in PHP web applications with test code included.","archived":false,"fork":false,"pushed_at":"2018-07-28T06:16:00.000Z","size":19,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-11T04:15:32.151Z","etag":null,"topics":["background-processing","php","php-fpm"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deminy.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":"2018-07-19T22:20:41.000Z","updated_at":"2023-12-27T19:21:08.000Z","dependencies_parsed_at":"2023-04-09T05:32:07.036Z","dependency_job_id":null,"html_url":"https://github.com/deminy/background-processing-in-php","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deminy%2Fbackground-processing-in-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deminy%2Fbackground-processing-in-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deminy%2Fbackground-processing-in-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deminy%2Fbackground-processing-in-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deminy","download_url":"https://codeload.github.com/deminy/background-processing-in-php/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248339226,"owners_count":21087215,"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":["background-processing","php","php-fpm"],"created_at":"2024-10-13T11:20:19.392Z","updated_at":"2025-04-11T04:15:36.681Z","avatar_url":"https://github.com/deminy.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Background Processing in PHP [![Build Status](https://travis-ci.com/deminy/background-processing-in-php.svg?branch=master)](https://travis-ci.com/deminy/background-processing-in-php) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/deminy/background-processing-in-php/blob/master/LICENSE)\n\nTechnical discussion on background processing in PHP web applications with test code included.\n\n## Table of Contents\n\n- [Assumptions](#assumptions)\n- [Common Background Processing Techniques in PHP](#common-background-processing-techniques-in-php)\n- [Execution Order of PHP Code](#execution-order-of-php-code)\n- [How Does Function fastcgi_finish_request() Affect HTTP Responses in PHP-FPM](#how-does-function-fastcgi_finish_request-affect-http-responses-in-php-fpm)\n    - [When Function fastcgi_finish_request() Not Used](#when-function-fastcgi_finish_request-in-use)\n    - [When Function fastcgi_finish_request() in Use](#when-function-fastcgi_finish_request-not-used)\n- [Run Our Test Code](#run-our-test-code)\n    - [Prepare Test Environment](#prepare-test-environment)\n    - [Test 1: How Does Function register_shutdown_function() Affect HTTP Responses?](#test-1-how-does-function-register_shutdown_function-affect-http-responses)\n    - [Test 2: How Does Function fastcgi_finish_request() Affect HTTP Responses?](#test-2-how-does-function-fastcgi_finish_request-affect-http-responses)\n    - [Test 3: What Happens When Function register_shutdown_function() and fastcgi_finish_request() Both in Use?](#test-3-what-happens-when-function-register_shutdown_function-and-fastcgi_finish_request-both-in-use)\n- [Conclusion](#conclusion)\n- [Footnotes](#footnotes)\n\n## Assumptions\n\nOur discussion is mostly about PHP microservices and web applications, especially under PHP-FPM. PHP CLI won't be discussed.\n\nAlso, we won't cover edge cases during the discussion, like call _exit()_ within registered shutdown functions.\n\n## Common Background Processing Techniques in PHP\n\n__1.__ Execute an external program in background.\n\nExternal programs can be executed in background typically like following:\n\n```php\n\u003c?php\nexec('curl example.com \u003e /dev/null 2\u003e\u00261 \u0026');\n?\u003e\n```\n\nThis approach is not recommended due to lack of visibility and control over external programs, although it's a common solution in many places.\n\n__2.__ Execute in a child process.\n\nNot an option for web applications since [the _PCNTL_ extension](http://php.net/manual/en/book.pcntl.php) is meant to be used under CLI (and early CGI) only.\u003csup\u003e1\u003c/sup\u003e\n\n__3.__ In destructor methods. \n\nAccording to [php.net](http://php.net/manual/en/language.oop5.decon.php#language.oop5.decon.destructor):\n\n\u003e The destructor method will be called as soon as there are no other references to a particular object, or in any order during the shutdown sequence.\n\nThis approach is unreliable and shouldn't be considered in practice.\n\n__4.__ Through registered shutdown functions.\n\nThis is to register one or more background processing functions through _register_shutdown_function()_. It is a popular\nsolution for error handling and logging in PHP, as you can see from many PHP libraries and tools like _Laravel_/_Lumen_,\n_Symfony_, _Monolog_, _Bugsnag_, _Blackfire_, etc.\n \nThere are two drawbacks to this approach. First, data printed out from registered shutdown functions will be\nincluded in HTTP responses; secondly, it could slow down HTTP responses. In the following sections, we will show how they\nmay happen, and how to use registered shutdown functions in PHP-FPM without worrying about these side effects.\n\n__5.__ Through a queue server or a job server.\n\nThis is a popular solution, especially for heavy tasks. However, same as #4, this could still slow down HTTP responses.\nOne typical example is that when the queue server is connected through TCP directly and the PHP web server has terrible\nnetwork connection at the time. This side effect could also be avoided in PHP-FPM, as mentioned in #4 and discussed in\nfollowing sections.\n\n__6.__ Use function [fastcgi_finish_request()](http://php.net/fastcgi_finish_request) in PHP-FPM.\n\nThis is our favorite approach for lightweight background tasks, and we use package [crowdstar/background-processing](https://github.com/Crowdstar/background-processing)\nfor that. Please check [the README file](https://packagist.org/packages/crowdstar/background-processing) in that package about possible side effects.\n\nIf you choose #4 and #5 as you solution, you may still consider to call function _fastcgi_finish_request()_ or use\npackage _crowdstar/background-processing_ at the end of your PHP application just to make HTTP responses faster.\n\n## Execution Order of PHP Code\n\n1. Generic PHP code.\n2. Function call _exit()_. If not called explicitly, you may assume it's called at the end of the PHP code.\n3. PHP shutdown functions registered through _register_shutdown_function()_.\n4. Destructor methods of non-destroyed objects during the shutdown sequence.\n\n## How Does Function fastcgi_finish_request() Affect HTTP Responses in PHP-FPM\n\nWe use following code piece to run under PHP-FPM as an example for discussion.\n\n```php\n\u003c?php\necho 1;\n\nregister_shutdown_function(function () {echo 3;});\n$a = new class {public function __destruct() {echo 4;}};\n\nfastcgi_finish_request(); // This line will be commented out for discussion purpose.\n\n// NOTE: any code starting from here still gets executed no matter if function fastcgi_finish_request() is called or not.\n\necho 2;\nexit();\necho 5; // Unreachable code.\n?\u003e\n```\n\n### When Function fastcgi_finish_request() in Use\n\nIn this case, Only data printed out before first function call to _fastcgi_finish_request()_ will be sent back in HTTP\nresponse. So the HTTP response is \"__1__\".\n\nHowever, rest code still runs as usual. So PHP shutdown functions and destructor methods of non-destroyed objects (_$a_\nin this case) always executed although data they print out won't be included in HTTP response. We will prove it with test\ncode discussed below.\n\n### When Function fastcgi_finish_request() Not Used\n\nHere is what will be printed out and send back in HTTP response:\n\n1. Anything before function call _exit()_. If not called explicitly, you may assume it's called at the end of the PHP code.\n2. PHP shutdown functions registered through _register_shutdown_function()_.\n3. Destructor methods of non-destroyed objects during the shutdown sequence.\n\nSo the HTTP response is \"__1234__\".\n\n## Run Our Test Code\n\n### Prepare Test Environment\n\nPlease run following commands to have test environment prepared:\n\n```bash\n# Use Docker to launch web server at URL http://127.0.0.1 with web root pointing to folder ./www\ndocker-compose up -d\n# Now run composer update to load 3rd-party library \"crowdstar/background-processing\" for testing purpose.\ncomposer update --no-dev # You may run command \"composer update\" instead\n```\n\nWe have three tests discussed below, and each test includes two HTTP calls. One is to try to write same data to HTTP response\nand to a disk file, and the other one is to send disk file content to HTTP response after first HTTP call. Source code of\nthose PHP endpoints can be found under folder _./www_.\n\n### Test 1: How Does Function register_shutdown_function() Affect HTTP Responses?\n\nFirst, please run command _curl 127.0.0.1/write1_ to write same data to HTTP response and a disk file. Here is\nwhat showed up in HTTP response:\n\n```text\nExecuted when function exit() is called.\nExecuted in a function registered through register_shutdown_function().\nExecuted in the destruct method of an object during the shutdown sequence.\n```\n\nNext, please run command _curl 127.0.0.1/read_ to print out what has been written in the disk file during previous\nHTTP request. The output should look like this:\n\n```text\nExecuted when function exit() is called.\nExecuted in a function registered through register_shutdown_function().\nExecuted in the destruct method of an object during the shutdown sequence.\n```\n\nWhat have we observed from the PHP code and the output?\n\n\u003e Data explicitly printed out during PHP shutdown sequence (registered shutdown functions and destructor methods\n\u003e of non-destroyed objects) are included in HTTP response, and your PHP code has to complete PHP shutdown\n\u003e sequence first before sending back HTTP response to the client.\n\n### Test 2: How Does Function fastcgi_finish_request() Affect HTTP Responses?\n\nFirst, please run command _curl 127.0.0.1/write2_ to write same data to HTTP response and a disk file. This\nHTTP call should return an empty response back (nothing printed out).\n\nNext, please run command _curl 127.0.0.1/read_ to print out what has been written in the disk file during previous\nHTTP request. The output should look like this:\n\n```text\nExecuted after function fastcgi_finish_request() is called.\nExecuted when function exit() is called.\nExecuted in the destruct method of an object during the shutdown sequence.\n```\n\nWhat have we observed from the PHP code and the output?\n\n\u003e Data explicitly printed out before function call _fastcgi_finish_request()_ will be in your HTTP response,\n\u003e and anything printed out after function call _fastcgi_finish_request()_ (especially those printed out during\n\u003e PHP shutdown sequence) won't be send back to HTTP client.\n\n### Test 3: What Happens When Function register_shutdown_function() and fastcgi_finish_request() Both in Use?\n\nFirst, please run command _curl 127.0.0.1/write3_ to write same data to HTTP response and a disk file. This\nHTTP call should return an empty response back (nothing printed out).\n\nNext, please run command _curl 127.0.0.1/read_ to print out what has been written in the disk file during previous\nHTTP request. The output should look like this:\n\n```text\nExecuted after function fastcgi_finish_request() is called.\nExecuted when function exit() is called.\nExecuted in a function registered through register_shutdown_function().\nExecuted in the destruct method of an object during the shutdown sequence.\n```\n\nWhat have we observed from the PHP code and the output?\n\n\u003e We observed similar results as test 2, and we noticed that by calling function _fastcgi_finish_request()_,\n\u003e we don't have to wait registered shutdown functions and destructor methods to finish during the PHP\n\u003e shutdown sequence first before sending back HTTP response to the client. Because of this, calling function\n\u003e _fastcgi_finish_request()_ at the end of your PHP application could speed up HTTP responses, typically\n\u003e when you use shutdown functions to handle something like error handling.\n\n## Conclusion\n\n1. Using registered shutdown functions may slow down your HTTP request, especially when it takes time to run those\nshutdown functions.\n2. Under PHP-FPM, we recommend using package [crowdstar/background-processing](https://github.com/Crowdstar/background-processing)\nfor simple background processing, although you should be aware of certain limitations and side effects with this approach.\n3. When using error monitoring/reporting libraries like [Bugsnag](https://github.com/bugsnag/bugsnag-php) (which makes HTTP calls to\nreport errors), you may consider calling function _fastcgi_finish_request()_ properly at the end of your PHP application for performance reason.\nBecause of this, we use package [crowdstar/background-processing](https://github.com/Crowdstar/background-processing) in\nour microservices even we don't have anything to process in the background.\n\n## Footnotes\n\n\u003csup\u003e1\u003c/sup\u003ePHP CLI uses a single process model while PHP-FPM not, which means duplicated resources (file/socket handles)\ncannot be appropriately managed in the child process. You may find a more detailed discussion on this by Joe Watkins from [here](https://stackoverflow.com/a/35029409/2752269).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeminy%2Fbackground-processing-in-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeminy%2Fbackground-processing-in-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeminy%2Fbackground-processing-in-php/lists"}