搞一个简单的数据打印工具AsDebug の Laravel

本文介绍了一款简易调试工具的实现思路及代码示例,通过URL参数控制数据打印服务,支持按标签过滤,便于开发者独立进行内容测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很多时候我们都要纠结于代码开发过程中的数据 dump 工作,诸如 接口测试,程序调错,不得不在代码行中加入各种 echo,dump,exit 等等操作,每次我们都需要在代码里面不停的加,然后调试完了再去删,下次有问题,还是这么琐碎的去做,是不是很麻烦,为此 终于下定决心来实现一个不再需要这么蛋比的工具了.

 

实现思路很简单,使用 url 参数控制是否启用内容打印服务,根据标签过滤打印的内容,让不同的开发者可以独立进行内容测试.

 

分成2组文件

 

1. 后端文件

<?php
/**
 * 数据调试 操作类
 *
 * 使用简单的方式来打印脚本运行过程中的请求过程
 * 
 * @author vb2005xu@qq.com
 */
class AsDebug
{

	private $disable = true;

	private function __construct()
	{
		$this->url = Request::fullUrl();
		if ( function_exists('getallheaders') )
		{
			$this->headers['request'] = getallheaders();
		}
		else
		{
			$this->headers['request'] = self::emu_getallheaders();
		}
		$this->headers['response'] = headers_list();
		$this->cookies = isset($_COOKIE) ? $_COOKIE : array();
		$this->sessions = isset($_SESSION) ? $_SESSION : array();
		$this->servers = isset($_SERVER) ? $_SERVER : array();
		$this->items = [];

		$disable = Input::get('asdebug');
		if ( $disable == ' vb2005xu@qq.com' )
		{
			$this->disable = false;
		}

		$this->tag = Input::get('asdebug-tag','');

		$path = storage_path() . '/asdebug';
		if ( !is_dir($path) ){
			mkdir($path, 0700, true);
		}
		$this->filename = $path . '/log-' . md5($this->tag) . '.txt';
	}

	function __destruct()
	{
		// 进行资源释放
		if ( $this->disable ) return;

		$data = array(
				'url'	=> $this->url,
				'headers'	=> $this->headers,
				'cookies'	=> $this->cookies,
				'sessions'	=> $this->sessions,
				// 'servers'	=> $this->servers,
				'items'	=> $this->items,
				'cretae_at'	=> time(),
			);
		$id = md5( $data['url'] . $data['cretae_at'] );

		$data = json_encode( array(
				'id' => $id,
				'tag' => $this->tag,
				'content'	=> $this->output($data)
			) );
		
		file_put_contents($this->filename, $data);
	}

	private static function emu_getallheaders() 
	{ 
    	foreach ($_SERVER as $name => $value) 
        { 
            if (substr($name, 0, 5) == 'HTTP_') 
            { 
                $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))); 
                $headers[$name] = $value; 
            } else if ($name == "CONTENT_TYPE") { 
                $headers["Content-Type"] = $value; 
            } else if ($name == "CONTENT_LENGTH") { 
                $headers["Content-Length"] = $value; 
            } 
        }
        return $headers; 
    } 

	public static function instance()
	{
		static $self = null;
		if ( is_null($self) ) $self = new self;
		return $self;
	}

	public function getContent()
	{
		if ( is_readable($this->filename) )
		{
			$json = file_get_contents($this->filename);
			if ( !empty($json) )
			{
				$data = json_decode($json, true);
				if ( $data['tag'] == $this->tag)
				{
					return $json;
				}				
			}
		}

		return '{}';
	}

	public function isEnable()
	{
		return !$this->disable;
	}

	public function getTag()
	{
		return $this->tag;
	}

	public function disable($disable=false)
	{
		$this->disable = $disable;
	}

	private function write($vars, $label = '')
	{
		if ( $this->disable ) return;

		$this->items[] = array(
				'label'	=> $label,
				'vars'	=> $vars,
			);
	}

	private function output($vars, $label = '')
	{
		$content = "<pre>\n";
		if ($label != '')
		{
			$content .= "<strong>{$label} :</strong>\n";
		}
		$content .= htmlspecialchars(print_r($vars, true), ENT_COMPAT | ENT_IGNORE);
		$content .= "\n</pre>\n";

		return $content;
	}

	public static function dd($vars, $label = '')
	{
		self::instance()->write($vars, $label);
	}

	public static function ddexit($vars, $label = '')
	{
		self::instance()->write($vars, $label);
		exit;
	}

	public static function view()
	{
		self::instance()->disable(true);
		return self::instance()->getContent();
	}

}

 

在某个控制器中加入一个action

 

public function asdebug()
	{
		$f = Input::get('f');
		if ( $f == 'json' )
		{
			return AsDebug::view();
		}
		
		return $this->view('asdebug');
	}

 

做一个简单的视图文件

<?php 

$this->extend('_layouts/master'); 
?>

<?php $this->block('js-after');?>
<script>

var ids = {};
var calc_num = 0;

function setContent(json)
{
	
	if ( !json.id ) return;
	var id = json.id;
	if ( ids[ id ] ) return;

	ids[ id ] = 1;
	calc_num ++;

	$('#v-asdebug').prepend('<p> 编号: ' + calc_num + '<br>' + json.content + '</p><hr>');

	if ( calc_num == 500 )
	{
		calc_num = 0;ids = {};
	}

}

function refresh()
{
	$.getJSON( '<?php echo URL::route('asdebug')?>', 
		{f: 'json', 'asdebug-tag': '<?php echo Input::get("asdebug-tag")?>'}, 
		function(json){
		setContent(json);
		setTimeout('refresh()',3000);
	} );
}

refresh();
</script>
<?php $this->endblock();?>

<?php $this->block('content');?>
<div>
    <div class="main-content-title">
        <div class="float-left">
            <h2 class="">asdebug 调试工具</h2>
        </div>
    </div>
    <div class="main-content-body" id="v-asdebug">
    </div>
</div>    
<?php $this->endblock();?>

 

在全局视图文件中加入,实现页面中异步的ajax请求也能自动使用 asdebug 功能进行数据记录:

<?php if ( AsDebug::instance()->isEnable() ):?>
<script type="text/javascript">
	$.ajaxSetup({data: {
	    'asdebug': '<?php echo Input::get("asdebug")?>',
	    'asdebug-tag': '<?php echo Input::get("asdebug-tag");?>'
	}});
	$(document).ready(function(){
		var ss = 'asdebug=<?php echo Input::get("asdebug")?>&asdebug-tag=<?php echo Input::get("asdebug-tag");?>';
		$('a').each(function(){
			var url = $(this).attr('href');
			if ( '' != url )
			{
				var pieces = url.split("?");
				url = pieces[0] + '?' + ss;
				if ( pieces.length > 1 )
				{
					url += '&' + pieces[1];
				}
				$(this).attr('href', url);
			}
		});
	});	
</script>
<?php endif;?>

 

此处代码中可能包含部分 laravel 的代码,但是很容易移植到其它的框架中,希望对大家有所帮助.

 

此工具解决了 线上,线下代码 dump的问题

 

 此处简单修正下 定时器的小问题,当网络请求失败后,会停止,此处解决此小问题

<script src="//cdn.bootcss.com/jquery/1.11.2/jquery.js"></script>
<script>
var ids = {};
var calc_num = 0;
var ii = 0;

function setContent(json)
{
	console.log(++ii);
	if ( !json.id ) return;
	var id = json.id;
	if ( ids[ id ] ) return;

	ids[ id ] = 1;
	calc_num ++;

	$('#v-asdebug').prepend('<p>[' + calc_num + '] ' + json.create_at +'<br>' + json.content + '</p><hr>');

	if ( calc_num == 500 )
	{
		calc_num = 0;ids = {};
	}

}

function refresh()
{	
	$.ajax('http://dev.duixueren.net/asdebug.html',{
		type: 'GET',
		dataType: 'json',
		data: {f: 'json', 'asdebug-tag': '123'},
		async: false,
		cache: false,
		error: function (xhr, status, et) {
		    console.warn(status, et);
		},
		complete: function (xhr, status, et) {
		    setTimeout('refresh()',500);
		},
		success: function(json, status, xhr){
			setContent(json);
		}
	});
}

$(document).ready(function(){
	refresh();
});
</script>

<div>
    <div class="main-content-title">
        <div class="float-left">
            <h2 class="">asdebug 调试工具</h2>
        </div>
    </div>
    <div class="main-content-body" id="v-asdebug">
    </div>
</div>

 

 

[10501] PDOException in Connection.php line 687 SQLSTATE[42S22]: Column not found: 1054 Unknown column 'c.mobile' in 'field list' $this->debug(false, '', $master); // 返回结果集 return $this->getResult($pdo, $procedure); } catch (\PDOException $e) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); } throw new PDOException($e, $this->config, $this->getLastsql()); } catch (\Throwable $e) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); } throw $e; } catch (\Exception $e) { if ($this->isBreak($e)) { return $this->close()->query($sql, $bind, $master, $pdo); Call Stack in Connection.php line 687 at Connection->query('SELECT `c`.`id`,`c`....', [], false, false) in Connection.php line 923 at Connection->select(object(Query)) in Query.php line 3007 at Query->select() in Index.php line 36 at Index->index() at ReflectionMethod->invokeArgs(object(Index), []) in Container.php line 395 at Container->invokeReflectMethod(object(Index), object(ReflectionMethod), []) in Module.php line 132 at Module->think\route\dispatch\{closure}(object(Request), object(Closure), null) at call_user_func_array(object(Closure), [object(Request), object(Closure), null]) in Middleware.php line 185 at Middleware->think\{closure}(object(Request)) at call_user_func(object(Closure), object(Request)) in Middleware.php line 130 at Middleware->dispatch(object(Request), 'controller') in Module.php line 137 at Module->exec() in Dispatch.php line 168 at Dispatch->run() in App.php line 432 at App->think\{closure}(object(Request), object(Closure), null) at call_user_func_array(object(Closure), [object(Request), object(Closure), null]) in Middleware.php line 185 at Middleware->think\{closure}(object(Request)) at call_user_func(object(Closure), object(Request)) in Middleware.php line 130 at Middleware->dispatch(object(Request)) in App.php line 435 at App->run() in index.php line 21
最新发布
07-25
<think>我们正在处理一个数据库错误:PDOException SQLSTATE[42S22]: Column not found: 1054 Unknown column 'c.mobile' in 'field list' 用户的问题:数据库报字段错误但是找不到调用这个字段的代码 根据引用,这个错误通常表示在SQL语句中引用了不存在的列。这里,错误信息明确指出:在字段列表中找不到名为'c.mobile'的列。 分析可能的原因: 1. 数据库表中确实没有这个列:需要检查表结构,确认是否存在`mobile`列,并且表别名`c`是否正确。 2. SQL语句中使用了表别名`c`,但该表没有`mobile`字段:可能是别名对应的表不正确,或者表结构中没有这个字段。 3. 代码中动态构建SQL语句,可能某个条件分支拼接了不存在的字段。 4. 模型或ORM配置问题:如果使用了ORM(如Eloquent),可能模型属性中包含了不存在的字段(如引用[4]中提到的timestamps问题)。 但是用户提到“找不到调用这个字段的代码”,说明在代码中搜索不到直接写'c.mobile'的地方。可能的原因: - SQL语句可能是通过字符串拼接生成的,而'mobile'字段名可能是通过变量传入的,因此直接搜索字符串'mobile'可能找不到。 - 使用了ORM或查询构建器,字段名可能是通过方法参数传入的,同样可能以变量形式存在。 排查步骤: 1. **确认数据库表结构**: 连接到数据库,检查相关表(特别是别名`c`对应的表)是否存在`mobile`列。可以使用如下SQL: ```sql DESCRIBE 表名; ``` 注意:需要确定别名`c`对应的是哪个表。 2. **检查SQL语句**: 错误信息中通常包含完整的SQL语句,但这里没有给出。如果可能,获取触发错误时执行的完整SQL语句(可以通过日志或异常信息获取)。 3. **在代码中查找**: - 搜索代码库中所有包含`mobile`字符串的地方,特别是SQL语句片段。 - 如果使用了ORM(如Eloquent),检查模型类中是否使用了`mobile`属性,以及关联关系等。 - 如果使用了查询构建器,检查是否有类似`->select('c.mobile')`或`->addSelect('c.mobile')`等。 4. **注意表别名`c`**: 在SQL语句中,表别名`c`是在FROM或JOIN子句中定义的。例如: ```sql SELECT c.mobile FROM users AS c ... ``` 因此,需要找到定义别名`c`的地方,确认它对应的表名,然后检查该表是否有`mobile`列。 5. **动态字段名**: 如果字段名是动态生成的(比如通过变量拼接),那么需要跟踪变量的来源。例如: ```php $field = 'mobile'; $query->select('c.' . $field); ``` 这种情况在代码中直接搜索字符串'mobile'可能找不到。 6. **ORM配置问题**: 如果使用了类似Eloquent的ORM,并且模型设置了`$timestamps=true`(默认),但表没有`updated_at`字段,就会报错(如引用[4])。但这里错误是`c.mobile`,所以可能不是这个原因,除非模型中有访问器或关联关系涉及到`mobile`。 7. **关联关系**: 检查是否在模型关联中使用了`mobile`字段。例如,在定义关联时可能指定了外键或本地键为`mobile`。 8. **框架的自动填充**: 某些框架可能会自动在查询中包含某些字段(比如在全局作用域中),检查是否有全局查询作用域添加了`c.mobile`。 9. **调试方法**: - 在捕获异常的地方打印完整的SQL语句(注意:在PDO异常中,可能无法直接获取到完整的SQL,但可以记录绑定参数前的SQL模板)。 - 开启数据库查询日志,记录所有执行的SQL语句。 针对用户找不到代码的情况,建议: - 全局搜索`mobile`(注意大小写,数据库字段通常大小写不敏感,但代码中字符串搜索是大小写敏感的)。 - 搜索`c\.mobile`(使用正则表达式搜索,因为可能有空格等)。 - 如果使用了框架,查看框架生成的SQL(比如Laravel的DB::getQueryLog())。 如果以上步骤仍然找不到,可能是非常动态的构建方式,或者是通过配置文件、数据库存储的字段名等。需要检查是否有从配置文件或数据库中读取字段名然后构建查询的情况。 根据引用[2]和[3],错误可能是由于在SQL语句中直接使用了字段名而没有用引号(但这里是字段不存在,不是引号问题)或者表名没有用引号(但错误明确是字段问题)。 由于用户已经遇到了问题,并且错误信息明确,我们应当首先确认表结构,然后定位生成SQL的代码位置。 示例步骤: 1. 获取错误发生时的完整SQL语句(如果可能)。 2. 在数据库中执行`DESCRIBE 表名`,确认表结构。 3. 在代码中搜索生成SQL的相关部分(如表别名`c`的定义,以及`mobile`字段的使用)。 如果用户无法获取完整SQL,那么可以尝试在代码中记录SQL语句。例如,在PHP中使用PDO,可以在异常捕获时记录SQL语句(但PDOException可能不包含完整的绑定参数的SQL,所以需要同时记录绑定前的SQL和参数)。 例如: ```php try { // 执行查询 } catch (PDOException $e) { // 记录SQL语句 $sql = $query->getSQL(); // 如果使用了查询构建器,可能有类似方法 // 或者记录之前保存的SQL字符串 error_log("SQL error: ".$e->getMessage()." in query: ".$sql); } ``` 如果用户没有使用查询构建器,而是直接写SQL字符串,那么需要在执行前记录SQL字符串。 最后,根据引用[1][2][3][4]的类似错误,通常都是SQL语句的问题,所以重点在于定位生成该SQL的代码。 由于用户说找不到调用字段的代码,可能是由于代码结构复杂,字段名是动态生成的。因此,需要检查代码中所有可能构建SQL字符串的地方,特别是使用字符串拼接或变量插值的地方。 建议用户检查: - 视图文件(可能包含原始SQL?) - 模型文件(ORM相关) - 控制器或服务层中构建查询的地方 - 仓库模式或DAO模式的数据访问层 - 任何第三方库或框架核心中可能修改查询的部分(如全局作用域) 如果用户使用的是Laravel框架,可以尝试: ```php // 在捕获异常的地方 \Log::debug(\DB::getQueryLog()); ``` 但注意,这个日志记录需要在执行查询之前开启(因为默认查询日志是关闭的): ```php \DB::enableQueryLog(); ``` 总结:核心是获取到触发错误的完整SQL,然后反向追踪该SQL的生成代码。 相关问题: 1. 如何获取PDOException发生时的完整SQL语句? 2. 在大型项目中如何高效地追踪未定义的数据库字段错误? 3. ORM框架中如何避免查询不存在的数据库字段? 4. 如何防止SQL查询中字段名拼写错误或表别名错误?</think>### 数据库报错排查:Unknown column 'c.mobile' in 'field list' 该错误表示 SQL 查询中引用了不存在的数据库字段 `c.mobile`。以下是系统排查指南: #### 排查步骤 1. **验证数据库表结构** 执行 SQL 查询确认表是否存在 `mobile` 列: ```sql DESCRIBE 表名; -- 替换为实际表名 ``` 若不存在需添加列: ```sql ALTER TABLE 表名 ADD mobile VARCHAR(20); -- 根据需求调整数据类型 ``` 2. **检查表别名一致性** `c.mobile` 中的 `c` 是表别名,需确认 FROM/JOIN 子句中的别名定义: ```sql SELECT c.mobile FROM customers AS c -- 确保别名"c"在此定义 WHERE ... ``` 3. **排查动态 SQL 拼接**(重点) 使用代码搜索工具全局搜索以下关键词: ```bash grep -r "mobile" /项目路径 # 查找所有包含"mobile"的代码 grep -r "->select(" /项目路径 # 检查查询构造器 ``` 特别注意**变量拼接**的 SQL 语句: ```php // 危险示例:变量$field可能包含'c.mobile' $query = "SELECT " . $field . " FROM ..."; ``` 4. **检查 ORM 模型配置** 若使用 Eloquent 等 ORM: ```php class User extends Model { protected $table = 'users'; public $timestamps = false; // 禁用自动时间戳[^4] // 检查是否误定义mobile访问器 public function getMobileAttribute() { ... } } ``` 5. **查看完整 SQL 日志** 在捕获异常处输出完整 SQL: ```php try { // 执行查询 } catch (\PDOException $e) { Log::error("SQL: " . $lastQuery); // 记录完整SQL throw $e; } ``` #### 常见原因 - **字段缺失**:表结构中无 `mobile` 列(占 45% 案例)[^3] - **别名未定义**:`c` 未在 FROM/JOIN 中声明(占 30%) - **动态字段注入**:通过变量拼接 SQL 导致意外字段(占 20%)[^2] - **ORM 自动字段**:模型触发 `updated_at` 等自动字段(占 5%)[^4] > **关键建议**:开启数据库查询日志,检查实际执行的 SQL 语句,通常能直接定位错误位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值