2025-04-14
#!/bin/bash
# 列出当前目录和子目录的文件.
find ./
# 查找当前目录和子目录下*.sh文件.
find ./ -name "*.sh" -print
# 查找当前目录和子目录下*.sh文件.并忽略大小写.
find ./ -iname "*.sh" -print
# 匹配多个条件.
find ./ \( -name "*.sh" -o -name "*.txt" \) -print
# 正则匹配多个条件
find ./ -regex ".*\(\.txt\|\.sh\)$"
# 忽略大小写
find ./ -iregex ".*\(\.txt\|\.sh\)$"
# -path用来匹配目录和文件名.
find ./ -path "*sh"
# -maxdepth目录深度.
find ./ -maxdepth 2  \( -name "*.sh" -o -name "*.txt" \) -print
# 打印深度为2的文件
find ./ -maxdepth 2 -type f
# 打印深度为2的目录
find ./ -maxdepth 2 -type d
# 列出当前目录所有软件链接.
find ./ -type l
# -atime 最后一次访问时间
# -mtime 最后一次修改时间
# -ctime 最后一次改变的时间
# 这里和上面一样。只是以分钟计数.
# -amain
# -mmain
# -cmain
# 2天内被修改的文件.
find ./ -type f -mtime -2
# 查询当前目录文件超过1kb的文件.
# b 块
# c 字节
# w 字
# K kb
# M  mb
# G gb
find ./ -type f -size +1k
# -delete 查询并删除
find ./ -type f -size +1k -delete
# 查询权限为644的文件
find ./ -type f -perm 644 -print
# 查询所有PHP文件中权限为644的文件
find ./ -type f -name "*.php" ! -perm 644 -print
find ./ -type f -name "*.php" -perm 644 -exec chmod +x {} \;
# 找出文件并执行chmod +增加读权限.
find ./ -type f -name "*.sh" -perm 644 -exec chmod +x {} \;
# 将查找的结果打印到allf.txt文件
find ./ -type f -name "*.sh" -perm 644 -exec cat {} \; > allf.txt
# 转换文件权限为数字
find ./ -printf '%m %p\n'
# 查找排除.git和node_module目录
find ./ -type l -name "*.php" \( -name ".git" -prune \) -o \( -name "node_module" -prune \)

 

标签: Linux
2025-04-14

被这个问题debug了一个上午. 

class boot {
    static $table;
}

class customer extends boot {
    public static function get_table() {
        static::$table = 'customer';
    }
}

class api extends boot {
    static function get_table() {
        static::$table = 'api_log_use';
        call_user_func(['customer', 'get_table']);
        var_dump(static::$table);
    }

}

api::get_table();

 

当在api类中call customer类的时候。这个$table变量会被覆盖. 最终解决办法: 在每个类中独立定义$table这个变量

class boot {
    static $table;
}

class customer extends boot {
    static $table;
    public static function get_table() {
        static::$table = 'customer';
    }
}

class api extends boot {
    static $table;
    static function get_table() {
        static::$table = 'api_log_use';
        call_user_func(['customer', 'get_table']);
        var_dump(static::$table);
    }

}

api::get_table();

 

标签: PHP
2025-04-14

Drupal的序列化和反序列化用的是 symfony/serializer

服务

$serializer = \Drupal::service("serializer");

 

Node序列化成xml.

$node = node_load(1);

$serializer = \Drupal::service("serializer");
$xml = $serializer->serialize($node, 'xml');

// 再解析回来
$deserializedData = $serializer->deserialize($xml, $node::class, 'xml');
dpm($deserializedData);

解析XML为数组

$data = <<<EOF
<?xml version="1.0"?>
<response><userid>1</userid><username>xiukunabcd</username></response>
EOF;

$serializer = \Drupal::service("serializer");
$deserializedData = $serializer->decode($data, 'xml');

dpm($deserializedData);

 

定义csv解析和反解析, 先定义一个服务:

services:
  mymodule.encoder.csv:
    class: Drupal\mymodule\Encoder\CsvEncoder
    tags:
      - { name: encoder, format: csv }

 

目录在src/Encoder/CsvEncoder.php

<?php
namespace Drupal\rest_output\Encoder;

class CsvEncoder extends \Symfony\Component\Serializer\Encoder\CsvEncoder {
  protected $format = 'csv';

  public function decode(string $data, string $format, array $context = []): mixed
  {
    $context[CsvEncoder::AS_COLLECTION_KEY] = false;
    return parent::decode($data, $format, $context); // TODO: Change the autogenerated stub
  }
}

 

测试

$node = node_load(1);

$serializer = \Drupal::service("serializer");
$xml = $serializer->serialize($node, 'csv');


$deserializedData = $serializer->deserialize($xml, $node::class, 'csv');
dpm($deserializedData);

在目录中vendor/symfony/serializer/Encoder还可能看到有yaml的解析。 可以使用上面代码复写

 

标签: Drupal
2025-04-14

比如页面中。需要向用户发邮件和写入用户访问日志等。其实这些并不需要直接表现在页面。因为会影响页面响应速度. 我们可以利用drupal terminate功能将这种功能在terminate event中实现并不需要影响页面输出

terminate功能请看index.php

<?php

/**
 * @file
 * The PHP page that serves all page requests on a Drupal installation.
 *
 * All Drupal code is released under the GNU General Public License.
 * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
 */
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
$autoloader = require_once 'autoload.php';

$kernel = new DrupalKernel('prod', $autoloader);

$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();

$kernel->terminate($request, $response);

 

在$response->send()了以后其实页面就已经完成了,这里使用的是

fastcgi_finish_request, 这里请看我的另一篇文章。PHP提前响应页面

。下面的$kernel->terminate()这里其实是在后台悄悄完成。drupal提供了event

return [
      KernelEvents::TERMINATE => ['onKernelTerminate'],
 ];

 

功能实现

先定义一个function用来保存需要在terminate中需要调用的函数和参数

function _terminate_run($callback, $arguments = []) {
  $terminate_callbacks = &drupal_static('terminate_callbacks');
  if (!isset($terminate_callbacks)) {
    $terminate_callbacks = [];
  }
  $terminate_callbacks[] = [
    'callback' => $callback,
    'arguments' => $arguments
  ];
}

 

接着定义一个服务。用来执行event

services:
  mymodule.event_subscriber:
    class: Drupal\mymodule\EventSubscriber\AutomatedTaskSubscriber
    tags:
      - { name: event_subscriber }

 

定义这个类

<?php

declare(strict_types=1);

namespace Drupal\mymodule\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * @todo Add description for this subscriber.
 */
final class AutomatedTaskSubscriber implements EventSubscriberInterface {


  /**
   * Kernel response event handler.
   */
  public function onKernelTerminate(): void {
    // 通过函数获取保存的callback
    $terminate_callbacks = drupal_static('terminate_callbacks');
    if ($terminate_callbacks) {
      foreach ($terminate_callbacks as $callback_infos) {
        try {
          \Drupal::logger('terminate_callbacks')->notice('terminate callback is running: <pre>' . print_r($callback_infos, true));
          call_user_func($callback_infos['callback'], $callback_infos['arguments']);
        } catch (\Exception $e) {
          \Drupal::logger('terminate_callbacks')->error('terminate callback is error: ' . $e->getMessage());
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      KernelEvents::TERMINATE => ['onKernelTerminate'],
    ];
  }

}

 

测试

 

function write_file_fuc($time) {
  file_put_contents('write_file_fuc.txt', $time);
}


/**
 * Returns responses for test_code routes.
 */
class TestCodeController extends ControllerBase {



  public function writefile($time) {
    sleep(10);
    file_put_contents('time.txt', $time);
  }

  /**
   * Builds the response.
   */
  public function build() {

    _terminate_run([$this, 'writefile'], [time()]);
    _terminate_run('write_file_fuc', [time()]);
    $build['content'] = [
      '#type' => 'item',
      '#markup' => $this->t('It works!'),
    ];

    return $build;
  }

}

 

这里可以看到。页面响应速度会很快而且不会影响写入一个文件的功能. 就算写入文件需要等10秒也是后端执行

我们通常这里可以用来做一些不影响页面响应但是又要很久的事情。比如发邮件。比如写日志

 

标签: PHP
2025-04-14

应用场景

1. 用户获取接口. 获取接口了以后还需要写入日志. 但是写入日志后面这段用户可以不用知道。可以先把数据响应给用户
2. 我们提供一个通知接口. 我们在收到接口了以后直接可以告诉用户收到了。而不是处理一大堆逻辑成功了再通知用户我们收到了

php实现

<?php

function delayOutput($output) {
    $is_fastcgi = function_exists("fastcgi_finish_request");
    if (!$is_fastcgi) {
        ob_end_clean();
        ob_start();
        header("Connection: close\r\n");
        header("Content-Encoding: none\r\n");
    }

    echo $output;

    if(!$is_fastcgi){
        $size = ob_get_length();
        header("Content-Length: ". $size . "\r\n");
        ob_end_flush();
        flush();
        header("Test-Header: fastcgi_finish_request\r\n");
        ignore_user_abort(true);
    }else{
        fastcgi_finish_request();
    }
}

delayOutput('hello world');
sleep(5);
file_put_contents('delay.txt', 'delay');

在Drupal接口中如何用这个东西

namespace Drupal\mymodule\Plugin\rest\resource;

use Drupal\rest\Plugin\ResourceBase;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
 * Represents TestApi records as resources.
 *
 * @RestResource (
 *   id = "rest_output_testapi",
 *   label = @Translation("TestApi"),
 *   uri_paths = {
 *     "canonical" = "/api/testapi"
 *   }
 * )
 *
 * For entities, it is recommended to use REST resource plugin provided by
 * Drupal core.
 * @see \Drupal\rest\Plugin\rest\resource\EntityResource
 */
final class TestapiResource extends ResourceBase {


  /**
   * Responds to GET requests.
   */
  public function get() {
    $data = [
      'userid' => 1,
      'username' => 'xiukunabcd'
    ];
    $response = new JsonResponse($data);
    // 因为这里的send已经实现了fastcgi_finish_request. 所以不需要过多的逻辑
    $response->send();

    //先输出内容. 后面用于写入日志啊这种不需要响应给用户的处理
    sleep(20);
    file_put_contents('test.txt', 'test');
    
  }
}

 

标签: PHP
2025-04-14

参与者

  1. 表达式接口:这是我们的表达式的蓝图,定义了一种解释不同类型表达式的方法。
  2. 终端表达式:这些是返回值的主要表达式。
  3. 非终端表达式:这些表达式表示操作并使用终端表达式来完成其任务

 

标签: 设计模式
2025-04-14

工厂方法模式在 PHP 代码中被广泛使用。当您需要为代码提供高度灵活性时,它非常有用。

案例:手机加工

定义interface

interface PhoneProcessInterface {
  public function process($phone_info);
}

 

定义各种手机加工的厂商

class IphoneProcess implements PhoneProcessInterface {
  public function process($phone_info)
  {
    echo 'iphone process....';
  }
}

class HuaweiProcess implements PhoneProcessInterface {
  public function process($phone_info)
  {
    echo 'Huawei process....';
  }
}

class SamsungProcess implements PhoneProcessInterface {
  public function process($phone_info)
  {
    echo "SamSung process...";
  }
}

定义工厂会帮助实例化. 外部直接使用

class PhoneFactory {
  public function samsung($phone_info) {
    return (new SamsungProcess())->process($phone_info);
  }

  public function huawei($phone_info) {
    return (new HuaweiProcess())->process($phone_info);
  }


  public function iphone($phone_info) {
    return (new IphoneProcess())->process($phone_info);
  }
}

测试

$phone_info = [
  'name' => 'iphone',
  'version' => '15'
];
$phone_factory = new PhoneFactory();
if ($phone_info['name'] == 'iphone') {
  $phone_factory->iphone($phone_info);
}

 

输出Iphone process

 

标签: 设计模式
2025-04-14

单例

单例很简单就是一个类一个类只初始化一次

 

定义一个单例类

class Single {
  protected static $instance = NULL;

  public static function getInstance():static {
    if (self::$instance == NULL) {
      self::$instance = new static;
    }
    return self::$instance;
  }
}

 

继承和使用

class DB extends Single {
}

class Logger extends Single {

}

$db = DB::getInstance();
$logger = Logger::getInstance();

 

多例

如果我们需要的不仅仅是一个记录器实例,因为有些消息必须写入文件中,有些消息需要通过电子邮件发送

定义多例

class Multiple {
  protected static array $instance = [];

  public static function getInstance($key) {
    if (!isset(self::$instance[$key])) {
      self::$instance[$key] = new static;
    }
    return self::$instance[$key];
  }
}

 

// 定义写入日志的接口
interface LoggerHanderInterface {
  public function save($level, $message, $context);
}

 

定义3种写入日志的方式

class DbLogger implements LoggerHanderInterface {
  public function save($level, $message, $context) {
    print ("Save to db $level: $message");
  }
}

class FileLogger implements LoggerHanderInterface {
  public function save($level, $message, $context) {
    print ("Save File$level: $message");
  }
}

class MailLogger implements LoggerHanderInterface {
  public function save($level, $message, $context) {
    print ("Send mail $level: $message");
  }
}

 

最后定义logger. 这里使用了psr\Log这个包.

class Logger extends Multiple implements \Psr\Log\LoggerInterface {
  use \Psr\Log\LoggerTrait;

  private LoggerHanderInterface $handle;

  public function setHandle(LoggerHanderInterface $hander) {
    $this->handle = $hander;
  }

  public function log($level, $message, array $context = array())
  {
    $this->handle->save($level, $message, $context);
  }
}

 

测试

$db_logger = Logger::getInstance('db');
$db_logger->setHandle(new DbLogger());

$file_logger = Logger::getInstance('file');
$file_logger->setHandle(new FileLogger());

$mail_logger = Logger::getInstance('mail');
$mail_logger->setHandle(new MailLogger());


// 打印hander 是 DbLogger
$logger1 = Logger::getInstance('db');
var_dump($logger1);

// 打印hander 是 DbLogger
$logger2 = Logger::getInstance('db');
var_dump($logger2);

// 打印hander 是 MailLogger
$logger3 = Logger::getInstance('mail');
var_dump($logger3);

 

标签: 设计模式
2025-04-14

当你需要操作别人遗留下来的项目,或者说第三方的代码的时候。尤其是通常情况下,这些代码你不容易去重构它们,也没有提供测试(tests)。这个时候,你就可以创建一个facade(“外观”),去将原来的代码“包裹”起来,以此来简化或优化其使用场景。

原文

 

标签: 设计模式
2025-04-14

当一个对象或者数据有多种展示方式的时候用装饰者模式.  比如数组可以以json, xml的展示方式. 再比如用户数据A页面会有一种展示方式. B页面会有一种展示方式

 

数组装饰

现在来用装饰者模式展示array. 先定义一个对象用来接受数组和输出输出.

class OutputData {

    private DecoratorInterface $decorator;

    public function __construct(private array $data) {}

    public function setDecorator(DecoratorInterface $decorator) {
        $this->decorator = $decorator;
    }

    public function output() {
        return $this->decorator->renderData($this->data);
    }

}

 

定义装饰者, 分别定义JSON和XML

 

interface DecoratorInterface {
    public function renderData($data);
}

class XMLRender implements DecoratorInterface {
    function toXml(SimpleXMLElement $object, array $data) {
        foreach ($data as $key => $value) {
            // if the key is an integer, it needs text with it to actually work.
            $valid_key  = is_numeric($key) ? "key_$key" : $key;
            $new_object = $object->addChild(
                $valid_key,
                is_array($value) ? null : htmlspecialchars($value)
            );

            if (is_array($value)) {
                $this->toXml($new_object, $value);
            }
        }
    }
    public function renderData($data) {
        $xml = new SimpleXMLElement('<root/>');
        $this->toXml($xml, $data);
        return $xml->saveXML();
    }
}

class JsonRender implements DecoratorInterface {
    public function renderData($data) {
        return json_encode($data);
    }
}

 

最后根据所需输出指定类型

$data = new OutputArray([
    'books' => [
        'Abook',
        'Bbook',
        'Cbook'
    ],
    'cart' => [
        'Acart',
        'Bcart',
        'Ccart'
    ]
]);
$data->setDecorator(new XMLRender());
$data->setDecorator(new JsonRender());
print_r($data->output());

 

商品装饰

定义菜品接口

interface FoodItem {
    // 定义附加的成本.
    public function cost();
}

 

// 定义一个手抓饼. 成本为10块钱.
class HandCake implements FoodItem {
    private array  $items = [];
    public function cost() {
        return 10;
    }

    public function addItem(FoodItem $item) {
        $this->items[] = $item;
    }

    public function Calculation() {
        $total = $this->cost();
        foreach ($this->items as $item) {
            $total += $item->cost();
        }
        return $total;
    }
}

 

定义小菜可选增加

// 定义鸡蛋小菜的成品为4块
class Egg implements FoodItem {
    public function cost () {
        return 4;
    }
}

// 定义一个火腿价格为6块
class Ham implements FoodItem {
    public function cost () {
        return 6;
    }
}

$handcake = new HandCake();
$handcake->addItem(new Egg());
$handcake->addItem(new Egg());
$handcake->addItem(new Egg());
$handcake->addItem(new Ham());
$handcake->addItem(new Ham());
print $handcake->Calculation();

产品活动装饰

定义一个商品类

class Product {
    public string $title;
    public float $price;
}

定义活动接口

interface ProductActivities {
    public function getReduce(Product $product);
}

定义活动

// 双十一减5块.
class Two11 implements ProductActivities {
    public function getReduce($product) {
        return 5;
    }
}

// 满500减50.
class Filled500 implements ProductActivities {
    public function getReduce(Product $product) {
        if ($product->price > 500) {
            return 50;
        }
    }
}

 

定义产品建造者来获取产品

class ProductBuilder {
    private Product $product;
    private array $activities;

    public function __construct($title, $price) {
        $product = new Product();
        $product->title = $title;
        $product->price = $price;
        $this->product = $product;
    }

    /**
     * 获取商品.
     *
     * @return mixed
     */
    public function getProduct() {
        // 根据活动重新计算减少的价格.
        $price = $this->product->price;
        foreach ($this->activities as $activity) {
            $price -= (int)$activity->getReduce($this->product);
        }
        $this->product->price = $price;
        return $this->product;
    }

    /**
     * 添加活动.
     *
     * @param $activity
     */
    public function addActivity($activity) {
        $this->activities[] = $activity;
    }
}

最后测试

$product = new ProductBuilder('Iphone16', '9000');
$product->addActivity(new Two11());
$product->addActivity(new Filled500());
print_r($product->getProduct());

 

标签: 设计模式