首页  >  文章  >  后端开发  >  超简洁PHPMVC

超简洁PHPMVC

WBOY
WBOY原创
2016-07-25 08:47:441703浏览
原生PHP语法来渲染页面,同时提供了widget功能
  1. /**
  2. * 获取和设置配置参数 支持批量定义
  3. * 如果$key是关联型数组,则会按K-V的形式写入配置
  4. * 如果$key是数字索引数组,则返回对应的配置数组
  5. * @param string|array $key 配置变量
  6. * @param array|null $value 配置值
  7. * @return array|null
  8. */
  9. function C($key,$value=null){
  10. static $_config = array();
  11. $args = func_num_args();
  12. if($args == 1){
  13. if(is_string($key)){ //如果传入的key是字符串
  14. return isset($_config[$key])?$_config[$key]:null;
  15. }
  16. if(is_array($key)){
  17. if(array_keys($key) !== range(0, count($key) - 1)){ //如果传入的key是关联数组
  18. $_config = array_merge($_config, $key);
  19. }else{
  20. $ret = array();
  21. foreach ($key as $k) {
  22. $ret[$k] = isset($_config[$k])?$_config[$k]:null;
  23. }
  24. return $ret;
  25. }
  26. }
  27. }else{
  28. if(is_string($key)){
  29. $_config[$key] = $value;
  30. }else{
  31. halt('传入参数不正确');
  32. }
  33. }
  34. return null;
  35. }
  36. /**
  37. * 调用Widget
  38. * @param string $name widget名
  39. * @param array $data 传递给widget的变量列表,key为变量名,value为变量值
  40. * @return void
  41. */
  42. function W($name, $data = array()){
  43. $fullName = $name.'Widget';
  44. if(!class_exists($fullName)){
  45. halt('Widget '.$name.'不存在');
  46. }
  47. $widget = new $fullName();
  48. $widget->invoke($data);
  49. }
  50. /**
  51. * 终止程序运行
  52. * @param string $str 终止原因
  53. * @param bool $display 是否显示调用栈,默认不显示
  54. * @return void
  55. */
  56. function halt($str, $display=false){
  57. Log::fatal($str.' debug_backtrace:'.var_export(debug_backtrace(), true));
  58. header("Content-Type:text/html; charset=utf-8");
  59. if($display){
  60. echo "
    ";
  61. debug_print_backtrace();
  62. echo "";
  63. }
  64. echo $str;
  65. exit;
  66. }
  67. /**
  68. * 获取数据库实例
  69. * @return DB
  70. */
  71. function M(){
  72. $dbConf = C(array('DB_HOST','DB_PORT','DB_USER','DB_PWD','DB_NAME','DB_CHARSET'));
  73. return DB::getInstance($dbConf);
  74. }
  75. /**
  76. * 如果文件存在就include进来
  77. * @param string $path 文件路径
  78. * @return void
  79. */
  80. function includeIfExist($path){
  81. if(file_exists($path)){
  82. include $path;
  83. }
  84. }
  85. /**
  86. * 总控类
  87. */
  88. class SinglePHP {
  89. /**
  90. * 控制器
  91. * @var string
  92. */
  93. private $c;
  94. /**
  95. * Action
  96. * @var string
  97. */
  98. private $a;
  99. /**
  100. * 单例
  101. * @var SinglePHP
  102. */
  103. private static $_instance;
  104. /**
  105. * 构造函数,初始化配置
  106. * @param array $conf
  107. */
  108. private function __construct($conf){
  109. C($conf);
  110. }
  111. private function __clone(){}
  112. /**
  113. * 获取单例
  114. * @param array $conf
  115. * @return SinglePHP
  116. */
  117. public static function getInstance($conf){
  118. if(!(self::$_instance instanceof self)){
  119. self::$_instance = new self($conf);
  120. }
  121. return self::$_instance;
  122. }
  123. /**
  124. * 运行应用实例
  125. * @access public
  126. * @return void
  127. */
  128. public function run(){
  129. if(C('USE_SESSION') == true){
  130. session_start();
  131. }
  132. C('APP_FULL_PATH', getcwd().'/'.C('APP_PATH').'/');
  133. includeIfExist( C('APP_FULL_PATH').'/common.php');
  134. $pathMod = C('PATH_MOD');
  135. $pathMod = empty($pathMod)?'NORMAL':$pathMod;
  136. spl_autoload_register(array('SinglePHP', 'autoload'));
  137. if(strcmp(strtoupper($pathMod),'NORMAL') === 0 || !isset($_SERVER['PATH_INFO'])){
  138. $this->c = isset($_GET['c'])?$_GET['c']:'Index';
  139. $this->a = isset($_GET['a'])?$_GET['a']:'Index';
  140. }else{
  141. $pathInfo = isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:'';
  142. $pathInfoArr = explode('/',trim($pathInfo,'/'));
  143. if(isset($pathInfoArr[0]) && $pathInfoArr[0] !== ''){
  144. $this->c = $pathInfoArr[0];
  145. }else{
  146. $this->c = 'Index';
  147. }
  148. if(isset($pathInfoArr[1])){
  149. $this->a = $pathInfoArr[1];
  150. }else{
  151. $this->a = 'Index';
  152. }
  153. }
  154. if(!class_exists($this->c.'Controller')){
  155. halt('控制器'.$this->c.'不存在');
  156. }
  157. $controllerClass = $this->c.'Controller';
  158. $controller = new $controllerClass();
  159. if(!method_exists($controller, $this->a.'Action')){
  160. halt('方法'.$this->a.'不存在');
  161. }
  162. call_user_func(array($controller,$this->a.'Action'));
  163. }
  164. /**
  165. * 自动加载函数
  166. * @param string $class 类名
  167. */
  168. public static function autoload($class){
  169. if(substr($class,-10)=='Controller'){
  170. includeIfExist(C('APP_FULL_PATH').'/Controller/'.$class.'.class.php');
  171. }elseif(substr($class,-6)=='Widget'){
  172. includeIfExist(C('APP_FULL_PATH').'/Widget/'.$class.'.class.php');
  173. }else{
  174. includeIfExist(C('APP_FULL_PATH').'/Lib/'.$class.'.class.php');
  175. }
  176. }
  177. }
  178. /**
  179. * 控制器类
  180. */
  181. class Controller {
  182. /**
  183. * 视图实例
  184. * @var View
  185. */
  186. private $_view;
  187. /**
  188. * 构造函数,初始化视图实例,调用hook
  189. */
  190. public function __construct(){
  191. $this->_view = new View();
  192. $this->_init();
  193. }
  194. /**
  195. * 前置hook
  196. */
  197. protected function _init(){}
  198. /**
  199. * 渲染模板并输出
  200. * @param null|string $tpl 模板文件路径
  201. * 参数为相对于App/View/文件的相对路径,不包含后缀名,例如index/index
  202. * 如果参数为空,则默认使用$controller/$action.php
  203. * 如果参数不包含"/",则默认使用$controller/$tpl
  204. * @return void
  205. */
  206. protected function display($tpl=''){
  207. if($tpl === ''){
  208. $trace = debug_backtrace();
  209. $controller = substr($trace[1]['class'], 0, -10);
  210. $action = substr($trace[1]['function'], 0 , -6);
  211. $tpl = $controller . '/' . $action;
  212. }elseif(strpos($tpl, '/') === false){
  213. $trace = debug_backtrace();
  214. $controller = substr($trace[1]['class'], 0, -10);
  215. $tpl = $controller . '/' . $tpl;
  216. }
  217. $this->_view->display($tpl);
  218. }
  219. /**
  220. * 为视图引擎设置一个模板变量
  221. * @param string $name 要在模板中使用的变量名
  222. * @param mixed $value 模板中该变量名对应的值
  223. * @return void
  224. */
  225. protected function assign($name,$value){
  226. $this->_view->assign($name,$value);
  227. }
  228. /**
  229. * 将数据用json格式输出至浏览器,并停止执行代码
  230. * @param array $data 要输出的数据
  231. */
  232. protected function ajaxReturn($data){
  233. echo json_encode($data);
  234. exit;
  235. }
  236. /**
  237. * 重定向至指定url
  238. * @param string $url 要跳转的url
  239. * @param void
  240. */
  241. protected function redirect($url){
  242. header("Location: $url");
  243. exit;
  244. }
  245. }
  246. /**
  247. * 视图类
  248. */
  249. class View {
  250. /**
  251. * 视图文件目录
  252. * @var string
  253. */
  254. private $_tplDir;
  255. /**
  256. * 视图文件路径
  257. * @var string
  258. */
  259. private $_viewPath;
  260. /**
  261. * 视图变量列表
  262. * @var array
  263. */
  264. private $_data = array();
  265. /**
  266. * 给tplInclude用的变量列表
  267. * @var array
  268. */
  269. private static $tmpData;
  270. /**
  271. * @param string $tplDir
  272. */
  273. public function __construct($tplDir=''){
  274. if($tplDir == ''){
  275. $this->_tplDir = './'.C('APP_PATH').'/View/';
  276. }else{
  277. $this->_tplDir = $tplDir;
  278. }
  279. }
  280. /**
  281. * 为视图引擎设置一个模板变量
  282. * @param string $key 要在模板中使用的变量名
  283. * @param mixed $value 模板中该变量名对应的值
  284. * @return void
  285. */
  286. public function assign($key, $value) {
  287. $this->_data[$key] = $value;
  288. }
  289. /**
  290. * 渲染模板并输出
  291. * @param null|string $tplFile 模板文件路径,相对于App/View/文件的相对路径,不包含后缀名,例如index/index
  292. * @return void
  293. */
  294. public function display($tplFile) {
  295. $this->_viewPath = $this->_tplDir . $tplFile . '.php';
  296. unset($tplFile);
  297. extract($this->_data);
  298. include $this->_viewPath;
  299. }
  300. /**
  301. * 用于在模板文件中包含其他模板
  302. * @param string $path 相对于View目录的路径
  303. * @param array $data 传递给子模板的变量列表,key为变量名,value为变量值
  304. * @return void
  305. */
  306. public static function tplInclude($path, $data=array()){
  307. self::$tmpData = array(
  308. 'path' => C('APP_FULL_PATH') . '/View/' . $path . '.php',
  309. 'data' => $data,
  310. );
  311. unset($path);
  312. unset($data);
  313. extract(self::$tmpData['data']);
  314. include self::$tmpData['path'];
  315. }
  316. }
  317. /**
  318. * Widget类
  319. * 使用时需继承此类,重写invoke方法,并在invoke方法中调用display
  320. */
  321. class Widget {
  322. /**
  323. * 视图实例
  324. * @var View
  325. */
  326. protected $_view;
  327. /**
  328. * Widget名
  329. * @var string
  330. */
  331. protected $_widgetName;
  332. /**
  333. * 构造函数,初始化视图实例
  334. */
  335. public function __construct(){
  336. $this->_widgetName = get_class($this);
  337. $dir = C('APP_FULL_PATH') . '/Widget/Tpl/';
  338. $this->_view = new View($dir);
  339. }
  340. /**
  341. * 处理逻辑
  342. * @param mixed $data 参数
  343. */
  344. public function invoke($data){}
  345. /**
  346. * 渲染模板
  347. * @param string $tpl 模板路径,如果为空则用类名作为模板名
  348. */
  349. protected function display($tpl=''){
  350. if($tpl == ''){
  351. $tpl = $this->_widgetName;
  352. }
  353. $this->_view->display($tpl);
  354. }
  355. /**
  356. * 为视图引擎设置一个模板变量
  357. * @param string $name 要在模板中使用的变量名
  358. * @param mixed $value 模板中该变量名对应的值
  359. * @return void
  360. */
  361. protected function assign($name,$value){
  362. $this->_view->assign($name,$value);
  363. }
  364. }
  365. /**
  366. * 数据库操作类
  367. * 使用方法:
  368. * DB::getInstance($conf)->query('select * from table');
  369. * 其中$conf是一个关联数组,需要包含以下key:
  370. * DB_HOST DB_USER DB_PWD DB_NAME
  371. * 可以用DB_PORT和DB_CHARSET来指定端口和编码,默认3306和utf8
  372. */
  373. class DB {
  374. /**
  375. * 数据库链接
  376. * @var resource
  377. */
  378. private $_db;
  379. /**
  380. * 保存最后一条sql
  381. * @var string
  382. */
  383. private $_lastSql;
  384. /**
  385. * 上次sql语句影响的行数
  386. * @var int
  387. */
  388. private $_rows;
  389. /**
  390. * 上次sql执行的错误
  391. * @var string
  392. */
  393. private $_error;
  394. /**
  395. * 实例数组
  396. * @var array
  397. */
  398. private static $_instance = array();
  399. /**
  400. * 构造函数
  401. * @param array $dbConf 配置数组
  402. */
  403. private function __construct($dbConf){
  404. if(!isset($dbConf['DB_CHARSET'])){
  405. $dbConf['DB_CHARSET'] = 'utf8';
  406. }
  407. $this->_db = mysql_connect($dbConf['DB_HOST'].':'.$dbConf['DB_PORT'],$dbConf['DB_USER'],$dbConf['DB_PWD']);
  408. if($this->_db === false){
  409. halt(mysql_error());
  410. }
  411. $selectDb = mysql_select_db($dbConf['DB_NAME'],$this->_db);
  412. if($selectDb === false){
  413. halt(mysql_error());
  414. }
  415. mysql_set_charset($dbConf['DB_CHARSET']);
  416. }
  417. private function __clone(){}
  418. /**
  419. * 获取DB类
  420. * @param array $dbConf 配置数组
  421. * @return DB
  422. */
  423. static public function getInstance($dbConf){
  424. if(!isset($dbConf['DB_PORT'])){
  425. $dbConf['DB_PORT'] = '3306';
  426. }
  427. $key = $dbConf['DB_HOST'].':'.$dbConf['DB_PORT'];
  428. if(!isset(self::$_instance[$key]) || !(self::$_instance[$key] instanceof self)){
  429. self::$_instance[$key] = new self($dbConf);
  430. }
  431. return self::$_instance[$key];
  432. }
  433. /**
  434. * 转义字符串
  435. * @param string $str 要转义的字符串
  436. * @return string 转义后的字符串
  437. */
  438. public function escape($str){
  439. return mysql_real_escape_string($str, $this->_db);
  440. }
  441. /**
  442. * 查询,用于select语句
  443. * @param string $sql 要查询的sql
  444. * @return bool|array 查询成功返回对应数组,失败返回false
  445. */
  446. public function query($sql){
  447. $this->_rows = 0;
  448. $this->_error = '';
  449. $this->_lastSql = $sql;
  450. $this->logSql();
  451. $res = mysql_query($sql,$this->_db);
  452. if($res === false){
  453. $this->_error = mysql_error($this->_db);
  454. $this->logError();
  455. return false;
  456. }else{
  457. $this->_rows = mysql_num_rows($res);
  458. $result = array();
  459. if($this->_rows >0) {
  460. while($row = mysql_fetch_array($res, MYSQL_ASSOC)){
  461. $result[] = $row;
  462. }
  463. mysql_data_seek($res,0);
  464. }
  465. return $result;
  466. }
  467. }
  468. /**
  469. * 查询,用于insert/update/delete语句
  470. * @param string $sql 要查询的sql
  471. * @return bool|int 查询成功返回影响的记录数量,失败返回false
  472. */
  473. public function execute($sql) {
  474. $this->_rows = 0;
  475. $this->_error = '';
  476. $this->_lastSql = $sql;
  477. $this->logSql();
  478. $result = mysql_query($sql, $this->_db) ;
  479. if ( false === $result) {
  480. $this->_error = mysql_error($this->_db);
  481. $this->logError();
  482. return false;
  483. } else {
  484. $this->_rows = mysql_affected_rows($this->_db);
  485. return $this->_rows;
  486. }
  487. }
  488. /**
  489. * 获取上一次查询影响的记录数量
  490. * @return int 影响的记录数量
  491. */
  492. public function getRows(){
  493. return $this->_rows;
  494. }
  495. /**
  496. * 获取上一次insert后生成的自增id
  497. * @return int 自增ID
  498. */
  499. public function getInsertId() {
  500. return mysql_insert_id($this->_db);
  501. }
  502. /**
  503. * 获取上一次查询的sql
  504. * @return string sql
  505. */
  506. public function getLastSql(){
  507. return $this->_lastSql;
  508. }
  509. /**
  510. * 获取上一次查询的错误信息
  511. * @return string 错误信息
  512. */
  513. public function getError(){
  514. return $this->_error;
  515. }
  516. /**
  517. * 记录sql到文件
  518. */
  519. private function logSql(){
  520. Log::sql($this->_lastSql);
  521. }
  522. /**
  523. * 记录错误日志到文件
  524. */
  525. private function logError(){
  526. $str = '[SQL ERR]'.$this->_error.' SQL:'.$this->_lastSql;
  527. Log::warn($str);
  528. }
  529. }
  530. /**
  531. * 日志类
  532. * 使用方法:Log::fatal('error msg');
  533. * 保存路径为 App/Log,按天存放
  534. * fatal和warning会记录在.log.wf文件中
  535. */
  536. class Log{
  537. /**
  538. * 打日志,支持SAE环境
  539. * @param string $msg 日志内容
  540. * @param string $level 日志等级
  541. * @param bool $wf 是否为错误日志
  542. */
  543. public static function write($msg, $level='DEBUG', $wf=false){
  544. if(function_exists('sae_debug')){ //如果是SAE,则使用sae_debug函数打日志
  545. $msg = "[{$level}]".$msg;
  546. sae_set_display_errors(false);
  547. sae_debug(trim($msg));
  548. sae_set_display_errors(true);
  549. }else{
  550. $msg = date('[ Y-m-d H:i:s ]')."[{$level}]".$msg."\r\n";
  551. $logPath = C('APP_FULL_PATH').'/Log/'.date('Ymd').'.log';
  552. if($wf){
  553. $logPath .= '.wf';
  554. }
  555. file_put_contents($logPath, $msg, FILE_APPEND);
  556. }
  557. }
  558. /**
  559. * 打印fatal日志
  560. * @param string $msg 日志信息
  561. */
  562. public static function fatal($msg){
  563. self::write($msg, 'FATAL', true);
  564. }
  565. /**
  566. * 打印warning日志
  567. * @param string $msg 日志信息
  568. */
  569. public static function warn($msg){
  570. self::write($msg, 'WARN', true);
  571. }
  572. /**
  573. * 打印notice日志
  574. * @param string $msg 日志信息
  575. */
  576. public static function notice($msg){
  577. self::write($msg, 'NOTICE');
  578. }
  579. /**
  580. * 打印debug日志
  581. * @param string $msg 日志信息
  582. */
  583. public static function debug($msg){
  584. self::write($msg, 'DEBUG');
  585. }
  586. /**
  587. * 打印sql日志
  588. * @param string $msg 日志信息
  589. */
  590. public static function sql($msg){
  591. self::write($msg, 'SQL');
  592. }
  593. }
  594. /**
  595. * ExtException类,记录额外的异常信息
  596. */
  597. class ExtException extends Exception{
  598. /**
  599. * @var array
  600. */
  601. protected $extra;
  602. /**
  603. * @param string $message
  604. * @param array $extra
  605. * @param int $code
  606. * @param null $previous
  607. */
  608. public function __construct($message = "", $extra = array(), $code = 0, $previous = null){
  609. $this->extra = $extra;
  610. parent::__construct($message, $code, $previous);
  611. }
  612. /**
  613. * 获取额外的异常信息
  614. * @return array
  615. */
  616. public function getExtra(){
  617. return $this->extra;
  618. }
  619. }
复制代码


声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn