idimsh/php-inotify-monitor

PHP Inotify 扩展和 React PHP 的包装库,用于监视文件/目录的三个主要事件:创建、修改、删除

dev-master 2018-11-30 01:22 UTC

This package is auto-updated.

Last update: 2024-09-29 05:08:37 UTC


README

Build Status Scrutinizer Code Quality Code Coverage Software License

React PHP Inotify Monitor / Watcher

PHP Inotify 扩展和 React PHP 的包装库

一个利用 React PHP Event LoopReact Inotify 监视文件系统变化的库

它被创建来监视三个基本事件:创建、修改和删除。并且将使用 React Inotify 需要的许多事件减少到简单的三个。

要求

  • PHP >= 7.1
  • inotify php 扩展
  • 支持 inotify 扩展的类似 Linux 的系统

功能

  • 监视文件和目录
  • 使用 shell 模式配置要监视的模式
  • 配置监视的嵌套级别,支持全局级别和模式级别
  • 在运行循环之前,基本目录不需要存在,它将自动(可选)等待创建。
  • 优化速度

安装

$ composer require idimsh/php-inotify-monitor

composer.json

{
  "require": {
    "idimsh/php-inotify-monitor": "dev-master"
  }
}

基本用法

require_once 'vendor/autoload.php';

use Dimsh\React\Filesystem\Monitor\Monitor;
use Dimsh\React\Filesystem\Monitor\MonitorConfigurator;

$monitor = new Monitor(MonitorConfigurator::factory()
  ->setBaseDirectory('/tmp')
  ->setLevel(2)
  ->setFilesToMonitor([
    '*.yaml',
  ]));
$monitor
  ->on(Monitor::EV_CREATE, function ($path, $monitor) {
      echo "created:   $path\n";
  })
  ->on(Monitor::EV_MODIFY, function ($path, $monitor) {
      echo "modified:  $path\n";
  })
  ->on(Monitor::EV_DELETE, function ($path, $monitor) {
      echo "deleted:   $path\n";
  })
  ->run();

以下代码将监视 /tmp/ 内部最多两个级别的 *.yaml 文件,因此以下模式匹配: /tmp/*.yaml/tmp/*/*.yaml,但不匹配 /tmp/*/*/*.yaml

定义了两个类

  • MonitorConfigurator:用于配置监视器
  • Monitor:定义事件处理器,并在捕获事件时执行操作。
    构造函数接受一个 MonitorConfigurator 对象作为依赖项。
    继承自 \Evenement\EventEmitter

MonitorConfigurator 的 API 方法

  1. setBaseDirectory():定义监视器将监视其更改的目录,它必须是绝对路径,如果不是:使用当前工作目录构造绝对路径,因此空基本目录是有效的。
    一般规则是:不要将其设置为根目录 '/',并且不要在大量文件正在修改的目录上进行操作,如: /var/log/
    如果指定的目录表示文件,则抛出 \Exception

  2. setLevel():指定要在基本目录内部递归到的级别,默认为 1,即在基本目录内直接。
    如果基本目录是 /var/www/,那么
        /var/www/file1.html 在级别 1
        /var/www/html/file2.html 在级别 2
    将级别设置为 0(零)将递归到所有子目录。

  3. setFilesToMonitor([]):接受一个数组,该数组包含表示监视器将监视更改的文件和/或目录的 shell 模式。
    当一个文件(或目录)匹配在此处定义的模式时,已创建、修改或删除,则调用该事件定义的处理函数,并传递文件或目录的绝对路径以及监视器对象实例。
    可以通过指定以斜杠 '/' 结尾的 shell 模式来针对目录,文件模式不以斜杠结尾。

    中间不包含斜杠 '/' 的模式递归到由 setLevel() 定义的级别,因此类似 '*.yaml' 的条目将匹配任何扩展名为 yaml 的文件,直到定义的级别。
    但是 包含中间或开头斜杠的模式被视为绝对形式,并且不是递归的,所以像 '*/*.yaml' 这样的条目(与条目 '/*/*.yaml' 完全等价)将匹配基目录第2级下的任何扩展名为 yaml 的文件,并且它不会在任何其他级别匹配它们(除非列表中的其他条目指示这样做)。

    模式不得包含基目录 在从它们的绝对路径中删除基目录后,将检查基目录内部的文件/目录。

  4. setFireModifiedOnDirectories():默认为 false。设置为 true 以在目录上触发修改事件,这发生在目录属性(mtime、权限)更改时。只有当目录与 setFilesToMonitor([]) 中指定的模式之一匹配时,才会触发修改事件 (再次,匹配目录的模式是那些以斜杠 '/' 结尾的)
    为了在设置此标志时监视基目录本身,模式条目将是空字符串 '',它具有特殊含义:如果 fire_modified_on_directories 设置为 on,则匹配基目录。

  5. setMonitorCreatedOnly():默认为 false。设置为 true 以仅监视 "CREATED" 事件,由 Monitor 本身用于快速等待不存在的基目录。

  6. setAutoCreateNotFoundMonitor():默认为 false。设置为 true,Monitor 实例将自动为不存在的基目录创建另一个内部 Monitor。通常当 Monitor 运行且基目录在文件系统中不存在时,运行调用将立即返回,并且不会进行任何监视。将此标志设置为 true 将创建一个内部 Monitor 对象实例,该实例针对监视父 Monitor 实例的基目录的创建进行了优化。

示例

MonitorConfigurator::factory()
  ->setBaseDirectory('/tmp')
  ->setLevel(2)
  ->setFilesToMonitor([
    '*.yaml',    # Will monitor and matches files: '/tmp/*.yaml', '/tmp/*/*.yaml' only
    '/*.xml',    # Will monitor and matches files: '/tmp/*.xml' only, '/tmp/*/*.xml' are not 
                 # monitored because this pattern starts with a slash and is not recursive.
    'config*/',  # Will monitor and matches directories: '/tmp/config-yaml/', '/tmp/config*/', '/tmp/*/config*/'
                 # (This only makes scense if setFireModifiedOnDirectories() is set to true)
                
  ]));
MonitorConfigurator::factory()
  ->setBaseDirectory('/etc')
  ->setLevel(5)
  ->setFilesToMonitor([
    'nginx/*.conf',    # Will monitor and matches files: '/etc/nginx/*.conf', this pattern constains 
                       # a slash so not recursive and is at level 2
                       # In fact with this config level is set internally to 2, since there is no 
                       # pattern which will match at levels 3 to 5.
  ]));

Monitor 的 API 方法

Monitor 的构造函数接受一个 MonitorConfigurator 实例,可选地接受一个外部 React PHP LoopInterface(或 React PHP EventLoop)的实例。如果没有传递外部 EventLoop,将创建一个内部的一个。
必须 运行 循环,并且 Monitor 的 run() 方法将在 EventLoop(外部或内部)上执行 run() 调用。
如果例如两个 Monitor 需要监视两个不同的基目录(每个有不同的模式),则创建一个外部 Loop,将其传递给两个 Monitor 实例,并在初始化后外部触发 run()

Monitor 扩展了 EventEmitter 类,并且可以使用 on() 方法设置其实例以侦听事件,可识别的事件是

  • Monitor::EV_CREATE
  • Monitor::EV_MODIFY
  • Monitor::EV_DELETE

每个事件的回调函数将传递事件发生的文件/目录路径以及触发事件的 Monitor 实例。
将带有尾随斜杠的目录传递到回调中。

在 EventLoop 上调用 run() 将会阻塞,因此使用此类的工作进程必须处理它。

  1. run():将运行事件循环(无论是传递给构造函数的外部一个还是内部创建的一个)。此调用将阻塞。
    如果调用 run() 并将外部事件循环传递给构造函数,将生成一个类型为 user 的警告,以通知调用者外部事件循环是打算由调用者而不是 Monitor 本身调用的。

  2. stop():通过删除所有已注册的监视来停止 Monitor。

  3. stopAll():通过删除所有已注册的监视并在事件循环上调用 stop() 来停止 Monitor,导致它停止。

  4. stopQuick():通过快速调用inotify close停止监控。除非在特殊情况下(如POSIX信号处理器内部),否则不建议使用。

  5. stopQuickAll():与stopQuick()类似,还会在事件循环中调用stop。

示例

/**
 * This example will monitor the file upload directory of a web application
 * for PHP files and automatically delete any PHP file created or uploaded.
 */
 
require_once 'vendor/autoload.php';

use Dimsh\React\Filesystem\Monitor\Monitor;
use Dimsh\React\Filesystem\Monitor\MonitorConfigurator;

$base_dir = '/tmp/uploads';

$monitor_config = MonitorConfigurator::factory()
  ->setLevel(2)
  ->setAutoCreateNotFoundMonitor(true)// wait for $base_dir to be created if not exists.
  ->setFireModifiedOnDirectories(true)
  ->setFilesToMonitor([
    '*.php',                  // monitor any php file in the uploads directory.
    '',                       // since ->setFireModifiedOnDirectories(true) is set,
                              // this entry means that we want to be notified if
                              // the $base_dir has been modified also (permissions).
    '*/*.swf',                // monitor swf files at level 2 only
    '/stop-if-created/',      // monitor this exact directory at level 1, and in our
                              // event handler we will stop the monitor if this
                              // directory is created.
  ]);

try {
    $monitor_config->setBaseDirectory($base_dir);
} catch (\Exception $e) {
    die("exception thrown: [{$e->getMessage()}]\n");
}
$monitor = new Monitor($monitor_config);
$monitor
  ->on(Monitor::EV_CREATE, function ($path, $monitor) use ($base_dir) {
      /** @var Monitor $monitor */
      if ($path === "$base_dir/stop-if-created/") {
          echo "stopping ... \n";
          $monitor->stop();
      }
      if ($monitor->hasTrailingSlash($path)) {
          // this will just print once for:
          // "directory created:   $base_dir/stop-if-created/"
          // since we are not monitoring any directory with our set patterns
          // except one, the stop will occur after all event handlers are fired.
          echo "directory created:   $path\n";
      } else {
          echo "file created:   $path\n";
          if (strtolower(substr($path, -4)) === '.php') {
              echo "php files are not allowed to be created in upload directory\n";
              @unlink($path);
          }
      }
  })
  ->on(Monitor::EV_MODIFY, function ($path, $monitor) use ($base_dir) {
      if ($base_dir === $path) {
          echo "base directory modified:  $path\n";
      } else {
          echo "modified:  $path\n";
      }
  })
  ->on(Monitor::EV_DELETE, function ($path, $monitor) {
      // this will be printed for PHP files which we are deleting on our created handler also.
      echo "deleted:   $path\n";
  });

$monitor->run();

注意事项

如果遇到如下PHP警告
PHP警告:在1vendor/mkraemer/react-inotify/src/MKraemer/ReactInotify/Inotify.php中调用inotify_add_watch()时,达到了inotify监视器的用户限制,或者内核未能分配所需资源。

以下命令可以解决问题

$ echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_user_watches && \
echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_queued_events && \
echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_user_instances && \
sudo sysctl -p

但这个警告可能表明您正在使用过多资源设置监视器,这可能意味着配置错误。

许可证

MIT

欢迎贡献