首页 > 代码库 > 从阅读Discuz的核心代码并给出注释的经历分析程序员该如何阅读代码?
从阅读Discuz的核心代码并给出注释的经历分析程序员该如何阅读代码?
本文标签: 程序员 php Discuz的核心代码 框架 深度学习框架
阅读优秀的代码,是技术水平成长的最佳途径。记得每个进来的新人,我都做过阅读优秀代码的要求,但几乎都只能坚持很少一段时间而已。
前晚大家还在开玩笑的讨论,都是因为看了前人的一些写法,才学会了一些乱七八糟的花招。
晚上我又开始重新阅读Discuz的核心代码,花了1h多的时间,才完成一个core文件的注释。
注释后的代码:
<?php
/**
* [Discuz!] (C)2001-2099 Comsenz Inc.
* This is NOT a freeware, use is subject to license terms
*
* $Id: class_core.php 33982 2013-09-12 06:36:35Z hypowang $
*/
error_reporting(E_ALL);
define(‘IN_DISCUZ‘, true);
define(‘DISCUZ_ROOT‘, substr(dirname(__FILE__), 0, -12));
define(‘DISCUZ_CORE_DEBUG‘, false);
define(‘DISCUZ_TABLE_EXTENDABLE‘, false);
set_exception_handler(array(‘core‘, ‘handleException‘));
if (DISCUZ_CORE_DEBUG) {
set_error_handler(array(‘core‘, ‘handleError‘));
register_shutdown_function(array(‘core‘, ‘handleShutdown‘));
}
if (function_exists(‘spl_autoload_register‘)) {
spl_autoload_register(array(‘core‘, ‘autoload‘));
} else {
function __autoload($class)
{
return core::autoload($class);
}
}
C::creatapp();
/**
* Discuz框架入口类
*/
class core
{
/**
*
* @var array 暂存所有已实例化的table对象
*/
private static $_tables;
/**
*
* @var array 暂存当前以加载类路径映射关系
*/
private static $_imports;
/**
*
* @var discuz_application 应用程序实例对象
*/
private static $_app;
/**
*
* @var discuz_memory
*/
private static $_memory;
/**
* 获取单例应用对象
* @return discuz_application
*/
public static function app()
{
return self::$_app;
}
/**
* 创建全局单例应用对象
* @return discuz_application
*/
public static function creatapp()
{
if (!is_object(self::$_app)) {
self::$_app = discuz_application::instance();
}
return self::$_app;
}
/**
* 创建Discuz体系的Table对象
* @param string $name
* @return discuz_table
*/
public static function t($name)
{
return self::_make_obj($name, ‘table‘, DISCUZ_TABLE_EXTENDABLE);
}
/**
* 创建Discuz体系下的Model对象
* @param string $name
* @return discuz_model
*/
public static function m($name)
{
$args = array();
if (func_num_args() > 1) {
$args = func_get_args();
unset($args[0]);
}
return self::_make_obj($name, ‘model‘, true, $args);
}
/**
* 创建对象实例
* @param string $name 类标识,格式 "#$pluginid#$name"
* @param string $type 代表命名空间,只支持一级,文件名也需加上这级前缀
* @param boolean $extendable 专用于table类
* @param array $p 专用于table类,传递给类的构造方法参数的数据
* @return mixed
*/
protected static function _make_obj($name, $type, $extendable = false, $p = array())
{
$pluginid = null;
if ($name[0] === ‘#‘) {
list(, $pluginid, $name) = explode(‘#‘, $name);
}
$cname = $type . ‘_‘ . $name;
if (!isset(self::$_tables[$cname])) {
if (!class_exists($cname, false)) {
self::import(($pluginid ? ‘plugin/‘ . $pluginid : ‘class‘) . ‘/‘ . $type . ‘/‘ . $name);
}
if ($extendable) {
self::$_tables[$cname] = new discuz_container();
switch (count($p)) {
case 0: self::$_tables[$cname]->obj = new $cname();
break;
case 1: self::$_tables[$cname]->obj = new $cname($p[1]);
break;
case 2: self::$_tables[$cname]->obj = new $cname($p[1], $p[2]);
break;
case 3: self::$_tables[$cname]->obj = new $cname($p[1], $p[2], $p[3]);
break;
case 4: self::$_tables[$cname]->obj = new $cname($p[1], $p[2], $p[3], $p[4]);
break;
case 5: self::$_tables[$cname]->obj = new $cname($p[1], $p[2], $p[3], $p[4], $p[5]);
break;
default: $ref = new ReflectionClass($cname);
self::$_tables[$cname]->obj = $ref->newInstanceArgs($p);
unset($ref);
break;
}
} else {
self::$_tables[$cname] = new $cname();
}
}
return self::$_tables[$cname];
}
/**
* 获取全局缓存处理对象实例
* @return discuz_memory
*/
public static function memory()
{
if (!self::$_memory) {
self::$_memory = new discuz_memory();
self::$_memory->init(self::app()->config[‘memory‘]);
}
return self::$_memory;
}
/**
* 手动导入Discuz体系下的类库
* @param string $name 文件名 文件名中如果含有/,代表文件名自动加上上一级命名空间前缀,如 ‘abc/edf/hi‘对应文件 ‘abc/edf/edf_hi.php‘
* @param string $folder 相对于/source/目录下的目录名
* @param boolean $force true代表必须要导入成功;false,代表导入失败就返回false;默认true
* @return boolean 导入结果
* @throws Exception $force参数为true时,如果导入失败就抛出异常
*/
public static function import($name, $folder = ‘‘, $force = true)
{
$key = $folder . $name;
if (!isset(self::$_imports[$key])) {
$path = DISCUZ_ROOT . ‘/source/‘ . $folder;
if (strpos($name, ‘/‘) !== false) {
$pre = basename(dirname($name));
$filename = dirname($name) . ‘/‘ . $pre . ‘_‘ . basename($name) . ‘.php‘;
} else {
$filename = $name . ‘.php‘;
}
if (is_file($path . ‘/‘ . $filename)) {
include $path . ‘/‘ . $filename;
self::$_imports[$key] = true;
return true;
} elseif (!$force) {
return false;
} else {
throw new Exception(‘Oops! System file lost: ‘ . $filename);
}
}
return true;
}
public static function handleException($exception)
{
discuz_error::exception_error($exception);
}
public static function handleError($errno, $errstr, $errfile, $errline)
{
if ($errno & DISCUZ_CORE_DEBUG) {
discuz_error::system_error($errstr, false, true, false);
}
}
public static function handleShutdown()
{
if (($error = error_get_last()) && $error[‘type‘] & DISCUZ_CORE_DEBUG) {
discuz_error::system_error($error[‘message‘], false, true, false);
}
}
/**
* 用于向PHP注册Discuz框架下(source/class/)类的自动加载规则
*
* Discuz框架的自动加载规范既不遵循PSR-0,也不遵循PSR-4,都还没有完整的Vendor标识概念。
* 由于PHP底层不区分类名大小写,并且会统一转换为小写,所以Discuz根据这个原则,规定:
* 1. 所有类名和命名空间的部分都是小写,对应的文件名和目录名也是强制转为小写去匹配;
* 同时,类文件名都会带上第一级命名空间的部分,
* 如 discuz_application(我们在写的时候,为了与其它框架保持一致的风格,
* 可以写成Discuz_Application,也能正常使用) 类对应文件
* source/class/discuz/application.php;
* 2. Discuz甚至只提倡使用一级命名空间。
*
* @param string $class
* @return boolean 加载成功,返回true;除非手动调用class_exists(‘class_1‘, true)函数触发该方法时类不存在会返回false;
* 否则类加载失败时,输出错误信息并终止程序
* @see core::import()
*/
public static function autoload($class)
{
$class = strtolower($class);
if (strpos($class, ‘_‘) !== false) {
list($folder) = explode(‘_‘, $class);
$file = ‘class/‘ . $folder . ‘/‘ . substr($class, strlen($folder) + 1);
} else {
$file = ‘class/‘ . $class;
}
try {
self::import($file);
return true;
} catch (Exception $exc) {
$trace = $exc->getTrace();
foreach ($trace as $log) {
if (empty($log[‘class‘]) && $log[‘function‘] == ‘class_exists‘) {
return false;
}
}
discuz_error::exception_error($exc);
}
}
/**
* 开启性能分析
* @param string $name 跟踪标识,命名格式 "#分组#标识名称" 或者"标识名称"
*/
public static function analysisStart($name)
{
$key = ‘other‘;
if ($name[0] === ‘#‘) {
list(, $key, $name) = explode(‘#‘, $name);
}
if (!isset($_ENV[‘analysis‘])) {
$_ENV[‘analysis‘] = array();
}
if (!isset($_ENV[‘analysis‘][$key])) {
$_ENV[‘analysis‘][$key] = array();
$_ENV[‘analysis‘][$key][‘sum‘] = 0;
}
$_ENV[‘analysis‘][$key][$name][‘start‘] = microtime(TRUE);
$_ENV[‘analysis‘][$key][$name][‘start_memory_get_usage‘] = memory_get_usage();
$_ENV[‘analysis‘][$key][$name][‘start_memory_get_real_usage‘] = memory_get_usage(true);
$_ENV[‘analysis‘][$key][$name][‘start_memory_get_peak_usage‘] = memory_get_peak_usage();
$_ENV[‘analysis‘][$key][$name][‘start_memory_get_peak_real_usage‘] = memory_get_peak_usage(true);
}
/**
* 停止性能分析
* @param string $name 跟踪标识 格式与开启方法参数同
* @return array 返回指定标识的性能参数内容,格式为:
* [
* time => ,
* stop_memory_get_usage => ,
* stop_memory_get_real_usage => ,
* stop_memory_get_peak_usage => ,
* stop_memory_get_peak_real_usage =>
* ]
*/
public static function analysisStop($name)
{
$key = ‘other‘;
if ($name[0] === ‘#‘) {
list(, $key, $name) = explode(‘#‘, $name);
}
if (isset($_ENV[‘analysis‘][$key][$name][‘start‘])) {
$diff = round((microtime(TRUE) - $_ENV[‘analysis‘][$key][$name][‘start‘]) * 1000, 5);
$_ENV[‘analysis‘][$key][$name][‘time‘] = $diff;
$_ENV[‘analysis‘][$key][‘sum‘] = $_ENV[‘analysis‘][$key][‘sum‘] + $diff;
unset($_ENV[‘analysis‘][$key][$name][‘start‘]);
$_ENV[‘analysis‘][$key][$name][‘stop_memory_get_usage‘] = memory_get_usage();
$_ENV[‘analysis‘][$key][$name][‘stop_memory_get_real_usage‘] = memory_get_usage(true);
$_ENV[‘analysis‘][$key][$name][‘stop_memory_get_peak_usage‘] = memory_get_peak_usage();
$_ENV[‘analysis‘][$key][$name][‘stop_memory_get_peak_real_usage‘] = memory_get_peak_usage(true);
}
return $_ENV[‘analysis‘][$key][$name];
}
}
/**
* 框架入口类,core的类名简写
* @see core
*/
class C extends core
{
}
/**
* DB访问对象,主要是使用其静态方法 类名简写
* @see discuz_database
*/
class DB extends discuz_database
{
}
对于这种框架性的代码,不熟悉的话,因为牵涉太多,可能觉得无从下手。所以,一般是先多花一点时间,搞清楚代码的目录结构,做到了然于胸。
下一步,整理文件之间的依赖关系,最好整理成独立的文字,帮大脑形成直观的印象。
然后找准入口文件,从依赖最少的部分看起,越是Library类型的,依赖的东西越少,但一两个lib看过后暂时就没必要看下去了。还是得从框架入口看起,搞清楚框架的运行原理和加载流程。我们的大脑都进灰了,思维没有那么清晰,这个过程,也需要笔记的辅助。
尽可能多做注释,从每个类的含义、包含的功能、每个方法的参数返回值类型、方法的用法举例,实现的原理等,通过注释文字,还原代码作者的思想,进而吸收转换成自己的思想。
判断你会否阅读代码的基本标准,就是能否将代码注释得清晰明了,不再需要额外的辅助文档。因为,从技术人员的角度,代码中的注解性文档,是可以通过工具如phpdoc生成api文档的,相对于一件事情多种作用。无法编写代码,还是阅读代码,都得注意程序设计过程中思路清晰、目标明确。
本文来源:http://www.cnblogs.com/x3d/
写在最后:FOR Freedom 看看外边的世界,以及IT这一行,少不了去Google查资料,最后,安利一个V——PN代理。一枝红杏 加速器,去Google查资料是绝对首选,连接速度快,使用也方便。我买的是99¥一年的,通过这个链接(http://my.yizhihongxing.com/aff.php?aff=2509)注册后付费时输上优惠码wh80,平摊下来,每月才7块钱,特实惠。
本文标签: 程序员 php Discuz的核心代码 框架 深度学习框架
转自 SUN‘S BLOG - 专注互联网知识,分享互联网精神!
原文地址: 《从阅读Discuz的核心代码并给出注释的经历分析程序员该如何阅读代码?》
相关阅读: 《Tensorflow【机器学习】:关于fast neural style【快速风格化图像】的理解和实现》
相关阅读:《我是 G 粉,一直关注 Google,最近 Google 有一些小动作,可能很多人不太了解》
相关阅读:《机器学习引领认知领域的技术创新,那么SaaS行业会被机器学习如何改变?》
相关阅读:《VPS 教程系列:Dnsmasq + DNSCrypt + SNI Proxy 顺畅访问 Google 配置教程》
相关阅读: 对程序员有用:2017最新能上Google的hosts文件下载及总结网友遇到的各种hosts问题解决方法及配置详解
相关阅读:《Aaron Swartz – 互联网天才开挂的人生历程:每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的?》
相关阅读:《网站环境apache + php + mysql 的XAMPP,如何实现一个服务器上配置多个网站?》相关阅读:《什么是工程师文化?各位工程师是为什么活的?作为一个IT或互联网公司为什么要工程师文
相关阅读: 《win10永久激活教程以及如何查看windows系统是不是永久激活?》
相关BLOG:SUN’S BLOG - 专注互联网知识,分享互联网精神!去看看:www.whosmall.com
从阅读Discuz的核心代码并给出注释的经历分析程序员该如何阅读代码?