* Use helper::header() instead of header().

This commit is contained in:
朱金勇
2023-07-03 15:26:08 +00:00
parent 3fd6df05e8
commit a3c848d7af
12 changed files with 684 additions and 26 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"require": {
"spiral/roadrunner": "v2.0",
"nyholm/psr7": "^1.8",
"spiral/roadrunner-jobs": "^2.0",
"spiral/goridge": "^3.2",
"spiral/roadrunner-kv": "^3.0"
}
}
+289
View File
@@ -0,0 +1,289 @@
<?php
declare(strict_types=1);
use Nyholm\Psr7\Stream;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\{ResponseInterface, StreamInterface};
/**
* Roadrunner的response类。
* Response class for RoadRunner.
*
* @package zand
*/
class zandResponse implements ResponseInterface
{
private array $headers = array();
private array $headerNames = array();
private bool $sent = false;
public string $cookieDomain = '';
public string $cookiePath = '/';
public int $statusCode = 200;
public string $reasonPhrase = '';
private $protocol = '1.1';
public Stream $stream;
public bool $cookieSecure = false;
private const PHRASES = array(
100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing',
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported',
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect',
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons',
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 511 => 'Network Authentication Required',
);
public function __construct()
{
$this->stream = Stream::create('');
}
public function getProtocolVersion(): string
{
return $this->protocol;
}
public function withProtocolVersion($version): MessageInterface
{
if (!is_scalar($version)) {
throw new InvalidArgumentException('Protocol version must be a string');
}
if ($this->protocol === $version) {
return $this;
}
$new = clone $this;
$new->protocol = (string) $version;
return $new;
}
public function cleanup(): void
{
$this->headers = array();
$this->statusCode = 200;
$this->sent = false;
$this->reasonPhrase = '';
}
public function getHeaders(): array
{
return $this->headers;
}
public function getHeaderLine($header): string
{
return implode(', ', $this->getHeader($header));
}
public function setHeader(string $name, ?string $value = null): static
{
$this->headers[$name] = [$value];
return $this;
}
public function addHeader(string $name, string $value): static
{
$this->headers[$name][] = $value;
return $this;
}
public function hasHeader($header): bool
{
return isset($this->headerNames[strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')]);
}
public function withHeader($header, $value): MessageInterface
{
$normalized = strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
$new = clone $this;
if (isset($new->headerNames[$normalized])) {
unset($new->headers[$new->headerNames[$normalized]]);
}
$new->headerNames[$normalized] = $header;
$new->headers[$header] = $value;
return $new;
}
public function withAddedHeader($header, $value): MessageInterface
{
if (!is_string($header) || '' === $header) {
throw new InvalidArgumentException('Header name must be an RFC 7230 compatible string');
}
$new = clone $this;
$new->setHeaders([$header => $value]);
return $new;
}
public function withoutHeader($header): MessageInterface
{
if (!is_string($header)) {
throw new InvalidArgumentException('Header name must be an RFC 7230 compatible string');
}
$normalized = strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
if (!isset($this->headerNames[$normalized])) {
return $this;
}
$header = $this->headerNames[$normalized];
$new = clone $this;
unset($new->headers[$header], $new->headerNames[$normalized]);
return $new;
}
public function getHeader($header): array
{
if (!is_string($header)) {
throw new InvalidArgumentException('Header name must be an RFC 7230 compatible string');
}
$header = strtr($header, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
if (!isset($this->headerNames[$header])) {
return [];
}
$header = $this->headerNames[$header];
return $this->headers[$header];
}
public function deleteHeader(string $name): static
{
unset($this->headers[$name]);
return $this;
}
public function setContentType(string $type, string $charset = null): static
{
$this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
return $this;
}
public function redirect(string $url, int $code = 302): void
{
$this->setCode($code);
$this->setHeader('Location', $url);
}
public function isSent(): bool
{
return $this->sent;
}
public function setSent(bool $sent): static
{
$this->sent = true;
return $this;
}
public function getStatusCode(): int
{
return $this->statusCode;
}
public function setStatus($code, $reasonPhrase = '')
{
$this->statusCode = $code;
if ((null === $reasonPhrase || '' === $reasonPhrase) && isset(self::PHRASES[$this->statusCode])) {
$reasonPhrase = self::PHRASES[$this->statusCode];
}
$this->reasonPhrase = $reasonPhrase;
}
public function withStatus($code, $reasonPhrase = ''): ResponseInterface
{
if (!is_int($code) && !is_string($code)) {
throw new InvalidArgumentException('Status code has to be an integer');
}
$code = (int) $code;
if ($code < 100 || $code > 599) {
throw new InvalidArgumentException(\sprintf('Status code has to be an integer between 100 and 599. A status code of %d was given', $code));
}
$new = clone $this;
$new->statusCode = $code;
if ((null === $reasonPhrase || '' === $reasonPhrase) && isset(self::PHRASES[$new->statusCode])) {
$reasonPhrase = self::PHRASES[$new->statusCode];
}
$new->reasonPhrase = $reasonPhrase;
return $new;
}
public function getReasonPhrase(): string
{
return $this->reasonPhrase;
}
public function getBody(): StreamInterface
{
if (null === $this->stream) {
$this->stream = Stream::create('');
}
return $this->stream;
}
public function withBody(StreamInterface $body): MessageInterface
{
if ($body === $this->stream) {
return $this;
}
$new = clone $this;
$new->stream = $body;
return $new;
}
public function setBody(string $body)
{
$this->stream = Stream::create($body);
}
public function setCookie(string $name, string $value, $expire, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httpOnly = null, ?string $sameSite = null): static
{
$headerValue = sprintf('%s=%s; path=%s; SameSite=%s', $name, urlencode($value), $path ?? ($domain ? '/' : $this->cookiePath), $sameSite ?? 'Lax');
if($expire)
{
$headerValue .= '; Expires='.(date('D, d M Y H:i:s T', strtotime($expire)));
}
$cookieDomain = $domain ?? $this->cookieDomain;
if($cookieDomain && !$path) $headerValue .= '; domain='.$cookieDomain;
if($secure ?? $this->cookieSecure) $headerValue .= '; secure';
if($httpOnly || $httpOnly === null) $headerValue .= '; HttpOnly';
$this->addHeader('Set-Cookie', $headerValue);
return $this;
}
public function deleteCookie(string $name, string $path = null, string $domain = null, bool $secure = null): static
{
$this->setCookie($name, '', 0, $path, $domain, $secure);
return $this;
}
}
+361
View File
@@ -0,0 +1,361 @@
<?php
declare(strict_types=1);
use Nyholm\Psr7\Factory\Psr17Factory;
use Spiral\Goridge\RPC\RPC;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\Jobs\Consumer;
use Spiral\RoadRunner\Jobs\Jobs;
use Spiral\RoadRunner\Jobs\Task\ReceivedTaskInterface;
use Spiral\RoadRunner\Worker;
include "vendor/autoload.php";
include 'response.class.php';
/**
* The zand router class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* router类。
* The router class.
*
* @package framework
*/
include dirname(__DIR__) . '/router.class.php';
class zandRouter extends router
{
/**
* 构造方法, 设置路径,类,超级变量等。注意:
* 1.应该使用createApp()方法实例化router类;
* 2.如果$appRoot为空,框架会根据$appName计算应用路径。
*
* The construct function.
* Prepare all the paths, classes, super objects and so on.
* Notice:
* 1. You should use the createApp() method to get an instance of the router.
* 2. If the $appRoot is empty, the framework will compute the appRoot according the $appName
*
* @param string $appName the name of the app
* @param string $appRoot the root path of the app
* @access public
* @return void
*/
public function __construct(string $appName = 'demo', string $appRoot = '')
{
$_SERVER['HTTP_USER_AGENT'] = '';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['SCRIPT_FILENAME'] = dirname(__DIR__, 2) . '/www/index.php';
$this->worker = new zandWorker();
$this->consumer = new Consumer();
parent::__construct($appName, $appRoot);
}
/**
* 开启session。
* Start session.
*
* @access public
* @return void
*/
public function startSession()
{
$sessionName = $this->config->sessionVar;
if(!defined('SESSION_STARTED'))
{
global $config;
$driver = $config->db->driver;
if(!class_exists($driver))
{
$classFile = $this->coreLibRoot . 'dao' . DS . $driver . '.class.php';
include($classFile);
}
$dao = new $driver();
$ztSessionHandler = new zandSession($dao);
session_set_save_handler(
$ztSessionHandler->open(...),
$ztSessionHandler->close(...),
$ztSessionHandler->read(...),
$ztSessionHandler->write(...),
$ztSessionHandler->destroy(...),
$ztSessionHandler->gc(...)
);
session_name($sessionName);
session_set_cookie_params(0, $this->config->webRoot, '', $this->config->cookieSecure, true);
define('SESSION_STARTED', true);
}
else
{
$this->sessionID = isset($_COOKIE[$sessionName]) ? $_COOKIE[$sessionName] : session_create_id();
session_id($this->sessionID);
session_start();
$this->worker->response->setCookie($sessionName, $this->sessionID, 0);
}
}
}
/**
* 消息队列的消息类型。
* Message in queue.
*
* @package zand
*/
class zandMessage
{
public $id;
public $type;
public $command;
}
/**
* 消息队列。
* Message queue.
*
* @package zand
*/
class zandQueue
{
private $mq;
public function __construct($queueName)
{
$jobs = new Jobs(RPC::create('tcp://127.0.0.1:6001'));
$this->mq = $jobs->connect('crons');
}
public function push($message)
{
$task = $this->mq->create(zandMessage::class, $message);
$this->mq->dispatch($task);
}
}
/**
* HTTP worker.
*
* @package zand
*/
class zandWorker
{
/**
* RoadRunner PSR7 worker.
*
* @var object
* @access private
*/
private $psr7;
/**
* response.
*
* @var object
* @access public
*/
public $response;
/**
* Constructor.
*
* @access public
* @return void
*/
public function __construct()
{
$worker = Worker::create();
$factory = new Psr17Factory();
$this->psr7 = new PSR7Worker($worker, $factory, $factory, $factory);
$this->response = new zandResponse();
}
/**
* Wait request to run.
*
* @access public
* @return void
*/
public function waitRequest()
{
$request = $this->psr7->waitRequest();
$this->initGlobal($request);
$this->response = new zandResponse();
}
/**
* Init global variables.
*
* @param object $request
* @access public
* @return void
*/
public function initGlobal($request)
{
$_SERVER = $request->getServerParams();
$_SERVER['REQUEST_TIME'] = time();
$_SERVER['REQUEST_TIME_FLOAT'] = microtime(true);
$_SERVER['SERVER_PROTOCOL'] = $request->getUri()->getScheme();
$_SERVER['REQUEST_METHOD'] = $request->getMethod();
$_SERVER['SERVER_NAME'] = $request->getUri()->getHost();
$_SERVER['SERVER_PORT'] = $request->getUri()->getPort();
$_SERVER['REQUEST_URI'] = $request->getUri()->getPath();
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['PHP_SELF'] = 'index.php';
$_SERVER['PATH_TRANSLATED'] = 'index.php';
$_SERVER['HTTP_HOST'] = $_SERVER['SERVER_NAME'] . (in_array($_SERVER['SERVER_PORT'], array(80, 443)) ? '' : ':' . $_SERVER['SERVER_PORT']);
$_GET = $request->getQueryParams();
$_POST = $request->getParsedBody();
$_COOKIE = $request->getCookieParams();
$_FILE = $request->getUploadedFiles();
}
/**
* Send response.
*
* @param string $body
* @access public
* @return void
*/
public function respond(string $body)
{
$this->response->setBody($body);
$this->psr7->respond($this->response);
}
/**
* Send error.
*
* @param Exception $e
* @access public
* @return void
*/
public function error(Exception $e)
{
$this->psr7->getWorker()->error((string)$e);
}
}
/**
* MySQL实现的Session管理.
* Session handler implements by MySQL.
*
* @package zand
*/
class zandSession
{
/**
* DAO for database.
*
* @var object
* @access private
*/
private $dao;
/**
* Constructor.
*
* @param object $dao
* @access public
* @return void
*/
public function __construct($dao)
{
$this->dao = $dao;
}
/**
* Open session.
*
* @param string $savePath
* @param string $sessionName
* @access public
* @return bool
*/
public function open($savePath, $sessionName)
{
$this->savePath = $savePath;
$this->sessionName = $sessionName;
return true;
}
/**
* Close session.
*
* @access public
* @return bool
*/
public function close()
{
return true;
}
/**
* Read session.
*
* @param string $id
* @access public
* @return string
*/
public function read($id)
{
$result = $this->dao->select('data')->from(TABLE_SESSION)->where('id')->eq($id)->fetch();
return $result ? $result->data : '';
}
/**
* Write session.
*
* @param string $id
* @param string $data
* @access public
* @return bool
*/
public function write($id, $data)
{
$data = array('id' => $id, 'data' => $data, 'timestamp' => time());
$this->dao->replace(TABLE_SESSION)->data($data)->exec();
return true;
}
/**
* Destroy session.
*
* @param string $id
* @access public
* @return bool
*/
public function destroy($id)
{
$this->dao->delete(TABLE_SESSION)->where('id')->eq($id)->exec();
return true;
}
/**
* GC for session.
*
* @param int $maxlifetime
* @access public
* @return bool
*/
public function gc($maxlifetime)
{
$this->dao->delete(TABLE_SESSION)->where('timestamp')->lt(time() - intval($maxlifetime))->exec();
return true;
}
}
+6 -6
View File
@@ -20,13 +20,13 @@ class zfileContentEntry extends entry
*/
public function get($fileID)
{
ob_end_clean();
ob_end_clean();
header("Content-type: application/octet-stream");
header("Content-Transfer-Encoding: binary");
header("Accept-Ranges: bytes");
// header("Content-Length: " . filesize($filePath));
header("Content-Disposition: attachment; filename=\"hello.txt\"");
helper::header('Content-type', 'application/octet-stream');
helper::header('Content-Transfer-Encoding', 'binary');
helper::header('Accept-Ranges', 'bytes');
// helper::header('Content-Length', filesize($filePath));
helper::header('Content-Disposition', 'attachment; filename="hello.txt"');
echo 'hello';
}
+1 -2
View File
@@ -131,8 +131,7 @@ class cron extends control
if(empty($this->config->global->cron)) return;
/* Zand queue. */
$zand = $this->app->loadClass('zand');
$queue = $zand->connectQueue('crons');
$queue = new zandQueue('crons');
/* Schedule loop. */
$cronTimes = array();
+2 -2
View File
@@ -2589,7 +2589,7 @@ class execution extends control
$this->loadModel('kanban');
/* Compatibility IE8. */
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7");
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) helper::header('X-UA-Compatible', 'IE=EmulateIE7');
$this->execution->setMenu($executionID);
$execution = $this->execution->getById($executionID);
@@ -2934,7 +2934,7 @@ class execution extends control
public function storyKanban($executionID)
{
/* Compatibility IE8*/
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7");
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) helper::header('X-UA-Compatible', 'IE=EmulateIE7');
$this->execution->setMenu($executionID);
$execution = $this->loadModel('execution')->getById($executionID);
+5 -5
View File
@@ -496,13 +496,13 @@ class file extends control
for($i = 0; $i < $obLevel; $i++) ob_end_clean();
$mime = (isset($file->extension) and in_array($file->extension, $this->config->file->imageExtensions)) ? "image/{$file->extension}" : $this->config->file->mimes['default'];
header("Content-type: $mime");
helper::header('Content-type', $mime);
$cacheMaxAge = 10 * 365 * 24 * 3600;
header("Cache-Control: private");
header("Pragma: cache");
header("Expires:" . gmdate("D, d M Y H:i:s", time() + $cacheMaxAge) . " GMT");
header("Cache-Control: max-age=$cacheMaxAge");
helper::header('Cache-Control', 'private');
helper::header('Pragma', 'cache');
helper::header('Expires', gmdate('D, d M Y H:i:s', time() + $cacheMaxAge) . ' GMT');
helper::header('Cache-Control', "max-age=$cacheMaxAge");
$handle = fopen($file->realPath, "r");
if($handle)
+4 -4
View File
@@ -1088,10 +1088,10 @@ class fileModel extends model
$mimes = $this->config->file->mimes;
$contentType = isset($mimes[$fileType]) ? $mimes[$fileType] : $mimes['default'];
header("Content-type: $contentType");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Pragma: no-cache");
header("Expires: 0");
helper::header('Content-type', $contentType);
helper::header('Content-Disposition', "attachment; filename=\"$fileName\"");
helper::header('Pragma', 'no-cache');
helper::header('Expires', '0');
if($type == 'content') helper::end($content);
if($type == 'file' and file_exists($content))
{
+3 -3
View File
@@ -734,7 +734,7 @@ class instance extends control
{
if(!$this->checkCneToken())
{
header("HTTP/1.1 401");
helper::setStatus(401);
return print(json_encode(array('code' => 401, 'message' => 'Invalid token.')));
}
@@ -760,7 +760,7 @@ class instance extends control
{
if(!$this->checkCneToken())
{
header("HTTP/1.1 401");
helper::setStatus(401);
return print(json_encode(array('code' => 401, 'message' => 'Invalid token.')));
}
@@ -796,7 +796,7 @@ class instance extends control
{
if(!$this->checkCneToken())
{
header("HTTP/1.1 401");
helper::setStatus(401);
return print(json_encode(array('code' => 401, 'message' => 'Invalid token.')));
}
+1 -1
View File
@@ -234,7 +234,7 @@ class misc extends control
$obLevel = ob_get_level();
for($i = 0; $i < $obLevel; $i++) ob_end_clean();
header('Content-Type: image/jpeg');
helper::header('Content-Type', 'image/jpeg');
$captcha = $this->app->loadClass('captcha');
$this->session->set($sessionVar, $captcha->getPhrase());
$captcha->build()->output();
+2 -2
View File
@@ -344,7 +344,7 @@ class sso extends control
$url = "https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=%s&app_id=%s";
$url = sprintf($url, $redirectURI, $appID, $state);
header("location: $url");
helper::header('location', $url);
}
/**
@@ -412,7 +412,7 @@ class sso extends control
$this->user->login($user);
$indexUrl = $this->createLink('my', 'index');
header("location: $indexUrl");
helper::header('location', $indexUrl);
}
/**
+1 -1
View File
@@ -41,7 +41,7 @@ include '../../common/view/header.lite.html.php';
if($denyType == 'noview')
{
$menuName = isset($lang->$menu->common) ? $lang->$module->common : $menu;
if(isset($lang->menu->$menu))list($menuName) = explode('|', $lang->menu->$menu);
if(isset($lang->menu->$menu)) list($menuName) = explode('|', $lang->menu->$menu);
printf($lang->user->errorView, $menuName);
}
?>