官方文檔
* https://hyperf.wiki/2.0/#/README
初步搭建
1. 安裝項目
composer create-project hyperf/hyperf-skeleton
2. 項目根目錄配置為當前目錄的安裝目錄根目錄,即不存在public目錄
3. 配置Nginx反向代理
# 至少需要一個 Hyperf 節點,多個配置多行 upstream hyperf { # Hyperf HTTP Server 的 IP 及 端口 server 127.0.0.1:9501; server 127.0.0.1:9502; } server { # 監聽端口 listen 80; # 綁定的域名,填寫您的域名 server_name proxy.hyperf.io; location / { # 將客戶端的 Host 和 IP 信息一並轉發到對應節點 proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 轉發Cookie,設置 SameSite proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; # 執行代理訪問真實服務器 proxy_pass http://hyperf; } }
4. 寶塔Nginx配置為(同LNMP的Nginx配置)
upstream hyperf { # Hyperf HTTP Server 的 IP 及 端口 server 127.0.0.1:9501; server 127.0.0.1:9502; } server { listen 80; server_name hyperf.com.net; index index.php index.html index.htm default.php default.htm default.html; root /www/wwwroot/hyperf-skeleton; #SSL-START SSL相關配置,請勿刪除或修改下一行帶注釋的404規則 #error_page 404/404.html; #SSL-END #ERROR-PAGE-START 錯誤頁配置,可以注釋、刪除或修改 #error_page 404 /404.html; #error_page 502 /502.html; #ERROR-PAGE-END #PHP-INFO-START PHP引用配置,可以注釋或修改 include enable-php-74.conf; #PHP-INFO-END location / { # 將客戶端的 Host 和 IP 信息一並轉發到對應節點 proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 轉發Cookie,設置 SameSite proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; # 執行代理訪問真實服務器 proxy_pass http://hyperf; } #REWRITE-START URL重寫規則引用,修改后將導致面板設置的偽靜態規則失效 include /www/server/panel/vhost/rewrite/hyperf.kefu.com.net.conf; #REWRITE-END #禁止訪問的文件或目錄 location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) { return 404; } #一鍵申請SSL證書驗證目錄相關設置 location ~ \.well-known{ allow all; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; error_log off; access_log /dev/null; } location ~ .*\.(js|css)?$ { expires 12h; error_log off; access_log /dev/null; } access_log /www/wwwlogs/hyperf.kefu.com.net.log; error_log /www/wwwlogs/hyperf.kefu.com.net.error.log; }
5.啟動http服務命令
php bin/hyperf.php start
或者重寫啟動文件watch。執行命令為:php watch
#!/usr/bin/env php <?php /** * Hyperf Watch Hot Reload Scripts * From: https://github.com/ha-ni-cc/hyperf-watch * Author: hanicc@qq.com * Usage: * Open the terminal console in the project root directory and enter:php watch * 在項目根目錄下打開終端控制台,輸入:php watch * If you want to clean the /runtime/container cache, enter: php watch -c * 如果你想要清除/runtime/container緩存,則輸入:php watch -c */ # PHP Bin File PHP程序所在路徑(默認自動獲取) const PHP_BIN_FILE = 'which php'; # Watch Dir 監聽目錄(默認監聽腳本所在的根目錄) const WATCH_DIR = __DIR__ . '/'; # Watch Ext 監聽擴展名(多個可用英文逗號隔開) const WATCH_EXT = 'php,env'; # Exclude Dir 排除目錄(不監聽的目錄,數組形式) const EXCLUDE_DIR = ['vendor', 'runtime', 'public']; # Entry Point File 入口文件 const ENTRY_POINT_FILE = __DIR__ . '/bin/hyperf.php'; # Start Command 啟動命令 const START_COMMAND = [ENTRY_POINT_FILE, 'start']; # PID File Path PID文件路徑 const PID_FILE_PATH = __DIR__ . '/runtime/hyperf.pid'; # Scan Interval 掃描間隔(毫秒,默認2000) const SCAN_INTERVAL = 2000; if (!function_exists('exec')) { echo '[x] 請在php.ini配置中取消禁用exec方法' . PHP_EOL; exit(1); } define('PHP', PHP_BIN_FILE == 'which php' ? exec('which php') : PHP_BIN_FILE); if (!file_exists(PHP) || !is_executable(PHP)) { echo '[x] PHP bin (" ' . PHP . ' ") 路徑沒有找到或無法執行,請確認路徑正確?' . PHP_EOL; exit(1); } if (!file_exists(ENTRY_POINT_FILE)) { echo '[x] 入口文件 ("' . ENTRY_POINT_FILE . '") 沒有找到,請確認文件存在?' . PHP_EOL; exit(1); } # 加載env $content = @file_get_contents('.env'); $values = array_filter(preg_split("/(\r\n|\n|\r)/", $content)); foreach ($values as $val) { if (substr($val, 0, 1) === '#') { continue; } list($name, $value) = explode('=', $val); $_ENV[$name] = $value; } use Swoole\Process; use Swoole\Timer; use Swoole\Event; swoole_async_set(['enable_coroutine' => false, 'log_level' => SWOOLE_LOG_INFO]); $hashes = []; $serve = null; echo "🚀 Start @ " . date('Y-m-d H:i:s') . PHP_EOL; start(); state(); Timer::tick(SCAN_INTERVAL, 'watch'); function killOldProcess() { // pid存在則關閉存在的進程 if (file_exists(PID_FILE_PATH) && $pid = @file_get_contents(PID_FILE_PATH)) { if (!@posix_kill($pid)) forceKill(); } else forceKill(); } function forceKill($match = '') { if (!$match) { $match = @$_ENV['APP_NAME'] . '.Master'; } // 適配MacOS if (PHP_OS == 'Darwin') $match = ENTRY_POINT_FILE; $command = "ps -ef | grep '$match' | grep -v grep | awk '{print $2}' | xargs kill -9 2>&1"; // 找不到pid,強殺進程 exec($command); } function start() { // 殺舊進程 killOldProcess(); global $serve; $serve = new Process('serve', true); $serve->start(); if (false === $serve->pid) { echo swoole_strerror(swoole_errno()) . PHP_EOL; exit(1); } addEvent($serve); } function addEvent($serve) { Event::add($serve->pipe, function () use (&$serve) { $message = @$serve->read(); if (!empty($message)) { echo $message; } }); } function watch() { global $hashes; foreach ($hashes as $pathName => $currentHash) { if (!file_exists($pathName)) { unset($hashes[$pathName]); continue; } $newHash = fileHash($pathName); if ($newHash != $currentHash) { change(); state(); break; } } } function state() { global $hashes; $files = phpFiles(WATCH_DIR); $hashes = array_combine($files, array_map('fileHash', $files)); $count = count($hashes); echo "📡 Watching $count files..." . PHP_EOL; } function change() { global $serve; echo "🔄 Restart @ " . date('Y-m-d H:i:s') . PHP_EOL; Process::kill($serve->pid); start(); } function serve(Process $serve) { $opt = getopt('c'); # if (isset($opt['c'])) echo exec(PHP . ' ' . ENTRY_POINT_FILE . ' di:init-proxy') . '..' . PHP_EOL; if (isset($opt['c'])) delDir('./runtime/container'); $serve->exec(PHP, START_COMMAND); } function fileHash(string $pathname): string { $contents = @file_get_contents($pathname); if (false === $contents) { return 'deleted'; } return md5($contents); } function phpFiles(string $dirname): array { $directory = new RecursiveDirectoryIterator($dirname); $filter = new Filter($directory); $iterator = new RecursiveIteratorIterator($filter); return array_map(function ($fileInfo) { return $fileInfo->getPathname(); }, iterator_to_array($iterator)); } function delDir($path) { if (is_dir($path)) { //掃描一個目錄內的所有目錄和文件並返回數組 $dirs = scandir($path); foreach ($dirs as $dir) { //排除目錄中的當前目錄(.)和上一級目錄(..) if ($dir != '.' && $dir != '..') { //如果是目錄則遞歸子目錄,繼續操作 $sonDir = $path . '/' . $dir; if (is_dir($sonDir)) { //遞歸刪除 delDir($sonDir); //目錄內的子目錄和文件刪除后刪除空目錄 @rmdir($sonDir); } else { //如果是文件直接刪除 @unlink($sonDir); } } } @rmdir($path); } } class Filter extends RecursiveFilterIterator { public function accept() { if ($this->current()->isDir()) { if (preg_match('/^\./', $this->current()->getFilename())) { return false; } return !in_array($this->current()->getFilename(), EXCLUDE_DIR); } $list = array_map(function (string $item): string { return "\.$item"; }, explode(',', WATCH_EXT)); $list = implode('|', $list); return preg_match("/($list)$/", $this->current()->getFilename()); } }
6. 啟動報錯
ERROR Swoole short name have to disable before start server, please set swoole.use_shortname = off into your php.ini.
php.ini文件最后一行新增 swoole.use_shortname = off 即可
7. 訪問當前配置域名返回