boot.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. <?php
  2. header('Content-Type: text/html; charset=utf-8');date_default_timezone_set("PRC");define('DEVER_TIME', $_SERVER['REQUEST_TIME']);define('DEVER_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR);
  3. if (defined('DEVER_CRON')) {
  4. Dever::cron();
  5. } elseif (defined('DEVER_SERVER')) {
  6. Dever::server();
  7. } else {
  8. Dever::fpm();
  9. }
  10. class Dever
  11. {
  12. protected static $instances = [];
  13. protected static $requestId = null;
  14. protected static $requestInstances = [];
  15. protected static $counter = 0;
  16. protected static $reflectionCache = [];
  17. protected static $bindings = [];
  18. protected static $commit = [];
  19. protected static $data = [];
  20. protected static $dependencyCache = [];
  21. protected static $bootstrapped = false;
  22. protected static $requestScopedClasses = [
  23. Dever\App::class => true,
  24. Dever\Route::class => true,
  25. Dever\Debug::class => true,
  26. Dever\Output::class => true,
  27. Dever\Paginator::class => true,
  28. Dever\Model::class => true,
  29. Dever\Helper\Curl::class => true,
  30. ];
  31. protected static $settingCache = [];
  32. public static function cron()
  33. {
  34. self::bootstrap();
  35. $http_worker = new \Workerman\Worker();
  36. $http_worker->count = DEVER_WORKER;
  37. $http_worker->reloadable = true;
  38. $http_worker->max_request = 0;
  39. $http_worker->onWorkerStart = function($worker) {
  40. self::get(Dever\Project::class)->register();
  41. self::call(DEVER_CRON, $worker->id);
  42. };
  43. \Workerman\Worker::runAll();
  44. }
  45. public static function server()
  46. {
  47. self::bootstrap();
  48. $http_worker = new \Workerman\Worker("http://0.0.0.0:" . DEVER_SERVER);
  49. $http_worker->count = DEVER_WORKER;
  50. $http_worker->reloadable = true;
  51. $http_worker->max_request = 0;
  52. $http_worker->onMessage = function(\Workerman\Connection\TcpConnection $connection, $request) {
  53. self::beginRequest();
  54. try {
  55. $path = $request->path();
  56. if ($path === '/favicon.ico') {
  57. $connection->send(new \Workerman\Protocols\Http\Response(204)); // 返回空内容
  58. return;
  59. }
  60. $env = new Dever\Helper\Wokerman($request, $connection);
  61. try {
  62. $output = self::run($env->getData());
  63. } catch (\Throwable $e) {
  64. $output = $e->getMessage();
  65. }
  66. //$output = 'test';
  67. $headers = [];
  68. if (!strstr($output, '<pre>')) {
  69. $headers = [
  70. 'Content-Type' => 'application/json; charset=utf-8',
  71. 'Access-Control-Allow-Origin' => '*', // 允许所有域
  72. 'Access-Control-Allow-Methods' => 'GET, POST, OPTIONS', // 允许请求方法
  73. 'Access-Control-Allow-Headers' => 'Content-Type, Authorization', // 允许的自定义头
  74. ];
  75. }
  76. // 统一处理 OPTIONS 预检请求
  77. if ($request->method() === 'OPTIONS') {
  78. $connection->send(new \Workerman\Protocols\Http\Response(204, $headers)); // 不返回内容
  79. return;
  80. }
  81. $connection->send(new \Workerman\Protocols\Http\Response(200, $headers, $output));
  82. } catch (\Throwable $e) {
  83. $connection->send(self::out()->error($e->getMessage()));
  84. Dever::log('server_error', $e->getMessage());
  85. } finally {
  86. self::endRequest();
  87. }
  88. };
  89. \Workerman\Worker::runAll();
  90. }
  91. public static function fpm()
  92. {
  93. self::bootstrap();
  94. self::beginRequest();
  95. try {
  96. $result = self::run();
  97. header('Content-Type: application/json');
  98. header('Content-Length: ' . strlen($result));
  99. print_r($result);
  100. } catch (\Throwable $e) {
  101. print_r($e->getMessage());
  102. } finally {
  103. self::endRequest();
  104. }
  105. }
  106. public static function bootstrap()
  107. {
  108. if (self::$bootstrapped) {
  109. return;
  110. }
  111. spl_autoload_register(['Dever', 'autoload']);
  112. self::$bootstrapped = true;
  113. }
  114. public static function request(callable $callback)
  115. {
  116. self::beginRequest();
  117. try {
  118. return $callback();
  119. } finally {
  120. self::endRequest();
  121. }
  122. }
  123. public static function run($data = null)
  124. {
  125. $route = self::get(Dever\Route::class)->get($data);
  126. unset($data);
  127. //$out = self::out();
  128. self::get(Dever\Debug::class)->init();
  129. self::get(Dever\Project::class)->register();
  130. $settings = self::setting();
  131. if (!empty($settings['cache'])) {
  132. $index = DEVER_APP_NAME . DIRECTORY_SEPARATOR . $route['l'];
  133. if (isset($settings['cache'][$index])) {
  134. $expire = $settings['cache'][$index];
  135. if (isset($route['shell'])) {
  136. unset($route['shell']);
  137. }
  138. $key = md5(DEVER_APP_NAME . http_build_query($route));
  139. if ($result = self::cache($key)) {
  140. return self::out()->success($result);
  141. }
  142. }
  143. }
  144. if ($route['l'] && strpos($route['l'], '.')) {
  145. list($class, $method) = explode('.', $route['l']);
  146. $class = strtr(ucwords(strtr($class, '/', ' ')), ' ', '\\');
  147. if (strpos($class, 'Manage') === 0) {
  148. $class = str_replace('Manage\\', '', $class);
  149. $class = DEVER_APP_NAME . '\\Manage\\Api\\' . $class;
  150. } else {
  151. $class = DEVER_APP_NAME . '\\Api\\' . $class;
  152. }
  153. $result = self::out()->success(self::load($class)->loadDevelop($method, self::input(), true));
  154. } else {
  155. $result = self::out()->success('ok');
  156. }
  157. if (isset($expire)) {
  158. self::cache($key, $result, $expire);
  159. }
  160. return $result;
  161. }
  162. protected static function beginRequest()
  163. {
  164. self::$counter++;
  165. self::$requestId = 'req_' . getmypid() . '_' . self::$counter . '_' . microtime(true);
  166. self::$requestInstances[self::$requestId] = [];
  167. self::$commit[self::$requestId] = false;
  168. self::$data[self::$requestId] = [];
  169. }
  170. public static function endRequest()
  171. {
  172. if (self::$requestId) {
  173. self::clearRequestState(self::$requestId);
  174. }
  175. self::$requestId = null;
  176. if ((self::$counter % 50) === 0 && function_exists('gc_collect_cycles')) {
  177. gc_collect_cycles();
  178. }
  179. }
  180. protected static function clearRequestState($requestId)
  181. {
  182. if (isset(self::$requestInstances[$requestId])) {
  183. unset(self::$requestInstances[$requestId]);
  184. }
  185. if (isset(self::$commit[$requestId])) {
  186. unset(self::$commit[$requestId]);
  187. }
  188. if (isset(self::$data[$requestId])) {
  189. unset(self::$data[$requestId]);
  190. }
  191. }
  192. public static function getCommit()
  193. {
  194. if (!self::$requestId) {
  195. return true;
  196. }
  197. return empty(self::$commit[self::$requestId]);
  198. }
  199. public static function setCommit()
  200. {
  201. if (!self::$requestId) {
  202. self::beginRequest();
  203. }
  204. self::$commit[self::$requestId] = true;
  205. }
  206. public static function setData($key, $data)
  207. {
  208. if (!self::$requestId) {
  209. self::beginRequest();
  210. }
  211. return self::$data[self::$requestId][$key] = $data;
  212. }
  213. public static function getData($key)
  214. {
  215. if (!self::$requestId) {
  216. return false;
  217. }
  218. return self::$data[self::$requestId][$key] ?? false;
  219. }
  220. public static function resolve($class)
  221. {
  222. if (isset(self::$bindings[$class])) {
  223. $binding = self::$bindings[$class];
  224. $concrete = $binding['concrete'];
  225. if ($concrete instanceof \Closure) {
  226. $object = $concrete();
  227. } elseif (is_string($concrete)) {
  228. $object = self::make($concrete);
  229. } else {
  230. $object = $concrete;
  231. }
  232. if ($binding['shared']) {
  233. self::$instances[$class] = $object;
  234. }
  235. return $object;
  236. }
  237. return self::build($class);
  238. }
  239. public static function build($class)
  240. {
  241. if (!isset(self::$reflectionCache[$class])) {
  242. self::$reflectionCache[$class] = new \ReflectionClass($class);
  243. }
  244. $refClass = self::$reflectionCache[$class];
  245. $constructor = $refClass->getConstructor();
  246. if (!$constructor || $constructor->getNumberOfParameters() === 0) {
  247. return new $class();
  248. }
  249. if (!isset(self::$dependencyCache[$class])) {
  250. $dependencies = [];
  251. foreach ($constructor->getParameters() as $param) {
  252. $type = $param->getType();
  253. if ($type && !$type->isBuiltin()) {
  254. $dependencies[] = ['class' => $type->getName()];
  255. } elseif ($param->isDefaultValueAvailable()) {
  256. $dependencies[] = ['value' => $param->getDefaultValue()];
  257. } else {
  258. throw new \Exception("无法解析 {$class} 的依赖参数 \${$param->getName()}");
  259. }
  260. }
  261. self::$dependencyCache[$class] = $dependencies;
  262. }
  263. $dependencies = [];
  264. foreach (self::$dependencyCache[$class] as $dependency) {
  265. if (isset($dependency['class'])) {
  266. $dependencies[] = self::get($dependency['class']);
  267. } else {
  268. $dependencies[] = $dependency['value'];
  269. }
  270. }
  271. return $refClass->newInstanceArgs($dependencies);
  272. }
  273. public static function bind($abstract, $concrete, $shared = false)
  274. {
  275. self::$bindings[$abstract] = compact('concrete', 'shared');
  276. }
  277. public static function get($class, $requestScope = null, $key = null)
  278. {
  279. if ($key === null) $key = $class;
  280. if ($requestScope === null) {
  281. $requestScope = self::shouldRequestScope($key, $class);
  282. }
  283. if ($requestScope) {
  284. if (!self::$requestId) {
  285. self::beginRequest();
  286. }
  287. if (!isset(self::$requestInstances[self::$requestId][$key])) {
  288. self::$requestInstances[self::$requestId][$key] = self::resolve($class);
  289. }
  290. return self::$requestInstances[self::$requestId][$key];
  291. } else {
  292. if (!isset(self::$instances[$key])) {
  293. self::$instances[$key] = self::resolve($class);
  294. }
  295. return self::$instances[$key];
  296. }
  297. }
  298. protected static function shouldRequestScope($key, $class)
  299. {
  300. if (isset(self::$requestScopedClasses[$key])) {
  301. return self::$requestScopedClasses[$key];
  302. }
  303. if (isset(self::$requestScopedClasses[$class])) {
  304. return self::$requestScopedClasses[$class];
  305. }
  306. return false;
  307. }
  308. public static function registerRequestScoped($class, $flag = true)
  309. {
  310. self::$requestScopedClasses[$class] = $flag;
  311. }
  312. public static function make($class)
  313. {
  314. return self::resolve($class);
  315. }
  316. public static function autoload($class)
  317. {
  318. if (strpos($class, 'Dever') === 0 || strpos($class, 'Workerman') === 0) {
  319. require_once DEVER_PATH . 'src/' . str_replace('\\', '/', $class) . '.php';
  320. } else {
  321. $temp = explode('\\', $class, 2);
  322. if (empty($temp[1])) {
  323. self::error($class . ' error');
  324. }
  325. [$app, $name] = $temp;
  326. $name = strtr($name, '\\', '/');
  327. $project = self::project($app, false);
  328. if ($project) {
  329. if (strpos($project['path'], 'http') === 0) {
  330. $class = $project;
  331. } else {
  332. if (strpos($name, 'Manage') === 0) {
  333. $path = 'manage';
  334. $name = str_replace('Manage/', '', $name);
  335. } else {
  336. $path = 'app';
  337. }
  338. require_once $project['path'] . $path . DIRECTORY_SEPARATOR . $name . '.php';
  339. }
  340. }
  341. }
  342. }
  343. public static function call($class, $param = [])
  344. {
  345. if (!is_array($param)) $param = [$param];
  346. if (strpos($class, '?')) {
  347. list($class, $temp) = explode('?', $class);
  348. parse_str($temp, $temp);
  349. foreach ($temp as $k => $v) {
  350. array_unshift($param, $v);
  351. }
  352. }
  353. list($class, $method) = explode('.', $class);
  354. $class = strtr($class, '/', '\\');
  355. return self::load($class)->$method(...$param);
  356. }
  357. public static function load($class)
  358. {
  359. return self::get(Dever\App::class, true, 'app:' . $class)->__initialize($class);
  360. }
  361. public static function db($table, $store = 'default', $partition = false, $path = 'table')
  362. {
  363. return self::get(Dever\Model::class, true, 'model:' . $table)->__initialize($table, $store, $partition, $path);
  364. }
  365. public static function option($table, $type = '', $where = [])
  366. {
  367. $data = Dever::db($table)->select($where);
  368. if ($type) {
  369. if (is_bool($type)) {
  370. $type = '不选择';
  371. }
  372. $default = [0 => ['id' => -1, 'name' => $type]];
  373. $data = array_merge($default, $data);
  374. }
  375. return $data;
  376. }
  377. public static function field($table, $id, $default = '无', $key = 'name')
  378. {
  379. if ($id && $id > 0) {
  380. $info = Dever::db($table)->find($id);
  381. return $info[$key];
  382. }
  383. return $default;
  384. }
  385. # 定义常用方法,这里不用__callStatic
  386. public static function input(...$args)
  387. {
  388. return self::get(Dever\Route::class)->input(...$args);
  389. }
  390. public static function url(...$args)
  391. {
  392. return self::get(Dever\Route::class)->url(...$args);
  393. }
  394. public static function host(...$args)
  395. {
  396. return self::get(Dever\Route::class)->host(...$args);
  397. }
  398. public static function debug(...$args)
  399. {
  400. return self::get(Dever\Debug::class)->add(...$args);
  401. }
  402. public static function page(...$args)
  403. {
  404. return self::get(Dever\Paginator::class)->get(...$args);
  405. }
  406. public static function config(...$args)
  407. {
  408. $result = self::get(Dever\Config::class)->get(...$args);
  409. if ($args && $args[0] === 'setting') {
  410. self::$settingCache[self::projectCacheKey()] = $result;
  411. }
  412. return $result;
  413. }
  414. public static function setting($key = null, $default = null)
  415. {
  416. $cacheKey = self::projectCacheKey();
  417. if (!isset(self::$settingCache[$cacheKey])) {
  418. self::$settingCache[$cacheKey] = self::config('setting');
  419. }
  420. $settings = self::$settingCache[$cacheKey];
  421. if ($key === null) {
  422. return $settings;
  423. }
  424. return $settings[$key] ?? $default;
  425. }
  426. protected static function projectCacheKey()
  427. {
  428. if (defined('DEVER_PROJECT_PATH')) {
  429. return DEVER_PROJECT_PATH;
  430. }
  431. return 'global';
  432. }
  433. public static function project(...$args)
  434. {
  435. return self::get(Dever\Project::class)->load(...$args);
  436. }
  437. public static function log(...$args)
  438. {
  439. return self::get(Dever\Log::class)->add(...$args);
  440. }
  441. public static function out()
  442. {
  443. return self::get(Dever\Output::class);
  444. }
  445. public static function error(...$args)
  446. {
  447. return self::out()->error(...$args);
  448. }
  449. public static function success(...$args)
  450. {
  451. return self::out()->success(...$args);
  452. }
  453. public static function apply($file)
  454. {
  455. [$app, $file] = explode('/', $file, 2);
  456. $project = Dever::project($app);
  457. require_once $project['path'] . $file . '.php';
  458. }
  459. public static function session(...$args)
  460. {
  461. return Dever\Session::oper(...$args);
  462. }
  463. public static function view(...$args)
  464. {
  465. return self::get(Dever\View::class)->show(...$args);
  466. }
  467. public static function file(...$args)
  468. {
  469. return self::get(Dever\File::class)->get(...$args);
  470. }
  471. public static function data()
  472. {
  473. return self::get(Dever\File::class)->data();
  474. }
  475. public static function rule(...$args)
  476. {
  477. return Dever\Helper\Rule::get(...$args);
  478. }
  479. public static function number(...$args)
  480. {
  481. return Dever\Helper\Math::format(...$args);
  482. }
  483. public static function math(...$args)
  484. {
  485. $key = $args[0];
  486. $args = array_slice($args, 1);
  487. return Dever\Helper\Math::$key(...$args);
  488. }
  489. public static function curl(...$args)
  490. {
  491. return self::get(Dever\Helper\Curl::class)->load(...$args);
  492. }
  493. public static function cache($key, $value = false)
  494. {
  495. if (self::setting('redis')) {
  496. if ($value) {
  497. if ($value == 'delete') {
  498. return \Dever\Helper\Redis::delete($key);
  499. }
  500. return \Dever\Helper\Redis::set($key, self::json_encode($value));
  501. } else {
  502. return self::json_decode(\Dever\Helper\Redis::get($key));
  503. }
  504. }
  505. return false;
  506. }
  507. public static function shell($value)
  508. {
  509. return self::check(self::input('shell'), $value);
  510. }
  511. public static function store($store = 'default', $partition = false)
  512. {
  513. $database = self::setting('database', []);
  514. if (!isset($database[$store])) {
  515. throw new \RuntimeException('database store not configured: ' . $store);
  516. }
  517. $setting = $database[$store];
  518. $class = 'Dever\\Store\\' . $setting['type'];
  519. return $class::getInstance($store, $setting, $partition);
  520. }
  521. public static function in_array($array, $value, $key = 'id', $show = 'name')
  522. {
  523. $column = array_column($array, $key);
  524. if ($column) {
  525. $index = array_search($value, $column);
  526. if ($index >= 0) {
  527. return $array[$index][$show];
  528. }
  529. }
  530. return false;
  531. }
  532. public static function issets($input, $value = false)
  533. {
  534. if (isset($input[$value])) {
  535. if (is_string($input[$value]) && !strlen($input[$value])) {
  536. return false;
  537. }
  538. return $input[$value];
  539. }
  540. return false;
  541. }
  542. public static function check($var, $find)
  543. {
  544. if (is_array($var)) {
  545. $var = implode(',', $var);
  546. }
  547. return strpos(',' . $var . ',', ',' . $find . ',') !== false;
  548. }
  549. public static function json_encode($value)
  550. {
  551. return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  552. }
  553. public static function json_decode($value)
  554. {
  555. return json_decode($value, true);
  556. }
  557. public static function array_order($array, $key, $sort)
  558. {
  559. $reorder = array_column($array, $key);
  560. array_multisort($reorder, $sort, $array);
  561. return $array;
  562. }
  563. public static function uuid()
  564. {
  565. mt_srand((double)microtime() * 10000);
  566. $charid = strtoupper(self::id());
  567. $hyphen = chr(45);
  568. return chr(123).substr($charid, 0, 8).$hyphen.substr($charid, 8, 4).$hyphen.substr($charid,12, 4).$hyphen.substr($charid,16, 4).$hyphen.substr($charid,20,12).chr(125);
  569. }
  570. public static function id()
  571. {
  572. $charid = strtoupper(md5(uniqid(mt_rand(), true)));
  573. return substr($charid, 0, 8) . substr($charid, 8, 4) . substr($charid, 12, 4) . substr($charid, 16, 4) . substr($charid, 20, 12);
  574. }
  575. public static function is_file($file)
  576. {
  577. if (strtolower(substr($file, 0, 4)) == 'http') {
  578. $header = get_headers($file, true);
  579. return isset($header[0]) && (strpos($header[0], '200') || strpos($header[0], '304'));
  580. } else {
  581. return is_file($file);
  582. }
  583. }
  584. public static function subdir($dir)
  585. {
  586. return array_filter(scandir($dir), function($file) use ($dir) {
  587. return is_dir($dir . '/' . $file) && $file !== '.' && $file !== '..';
  588. });
  589. }
  590. }