├── .gitignore ├── README.md ├── composer.json ├── examples └── example.php └── src └── MKraemer └── ReactInotify └── Inotify.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | /composer.lock 3 | /composer.phar 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Inotify 2 | 3 | Basic inotify bindings for [React PHP](https://github.com/reactphp). 4 | 5 | ##Install 6 | This library requires PHP5.3 and the [inotify PECL extension](http://pecl.php.net/package/inotify). 7 | The best way to install this library is through [composer](http://getcomposer.org): 8 | 9 | ```JSON 10 | { 11 | "require": { 12 | "mkraemer/react-inotify": "1.1.0" 13 | } 14 | } 15 | ``` 16 | ## Usage 17 | 18 | This library provides the Inotify class which takes an event loop and optionally the timer interval in which the inotify events should be checked as constructor arguments. 19 | After initializing the class, you can use the add() method to add paths to the list of watched paths. Use the [inotify bitmasks](http://www.php.net/manual/en/inotify.constants.php) to define to which filesystem operations to listen. 20 | 21 | ```php 22 | add('/tmp/', IN_CLOSE_WRITE | IN_CREATE | IN_DELETE); 28 | $inotify->add('/var/log/', IN_CLOSE_WRITE | IN_CREATE | IN_DELETE); 29 | 30 | $inotify->on(IN_CLOSE_WRITE, function ($path) { 31 | echo 'File closed after writing: '.$path.PHP_EOL; 32 | }); 33 | 34 | $inotify->on(IN_CREATE, function ($path) { 35 | echo 'File created: '.$path.PHP_EOL; 36 | }); 37 | 38 | $inotify->on(IN_DELETE, function ($path) { 39 | echo 'File deleted: '.$path.PHP_EOL; 40 | }); 41 | 42 | $loop->run(); 43 | 44 | ``` 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mkraemer/react-inotify", 3 | "description": "Inotify bindings for ReactPHP", 4 | "keywords": ["inotify", "react"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Marius Krämer", 9 | "email": "marius@marius-kraemer.de" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-0": { "MKraemer": "src/" } 14 | }, 15 | "require": { 16 | "php": ">=5.3.0", 17 | "ext-inotify": "*", 18 | "evenement/evenement": "~1.0|~2.0", 19 | "react/event-loop": "^0.2|^0.3|^0.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/example.php: -------------------------------------------------------------------------------- 1 | add('/tmp/', IN_CLOSE_WRITE | IN_CREATE | IN_DELETE); 9 | $inotify->add('/var/log/', IN_CLOSE_WRITE | IN_CREATE | IN_DELETE); 10 | 11 | $inotify->on(IN_CLOSE_WRITE, function ($path) { 12 | echo 'File closed after writing: '.$path.PHP_EOL; 13 | }); 14 | 15 | $inotify->on(IN_CREATE, function ($path) { 16 | echo 'File created: '.$path.PHP_EOL; 17 | }); 18 | 19 | $inotify->on(IN_DELETE, function ($path) { 20 | echo 'File deleted: '.$path.PHP_EOL; 21 | }); 22 | 23 | $loop->run(); 24 | -------------------------------------------------------------------------------- /src/MKraemer/ReactInotify/Inotify.php: -------------------------------------------------------------------------------- 1 | loop = $loop; 38 | } 39 | 40 | /** 41 | * Checks all new inotify events available 42 | * and emits them via evenement 43 | */ 44 | public function __invoke() 45 | { 46 | if (false !== ($events = \inotify_read($this->inotifyHandler))) { 47 | foreach ($events as $event) { 48 | // make sure the watch descriptor assigned to this event is 49 | // still valid. removing watch descriptors via 'remove()' 50 | // implicitly sends a final event with mask IN_IGNORE set: 51 | // http://php.net/manual/en/inotify.constants.php#constant.in-ignored 52 | if (isset($this->watchDescriptors[$event['wd']])) { 53 | $path = $this->watchDescriptors[$event['wd']]['path']; 54 | $this->emit($event['mask'], array($path . $event['name'])); 55 | } 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * Adds a path to the list of watched paths 62 | * 63 | * @param string $path Path to the watched file or directory 64 | * @param integer $mask Bitmask of inotify constants 65 | * @return integer unique watch identifier, can be used to remove() watch later 66 | */ 67 | public function add($path, $mask) 68 | { 69 | if ($this->inotifyHandler === false) { 70 | // inotifyHandler not started yet => start a new one 71 | $this->inotifyHandler = \inotify_init(); 72 | stream_set_blocking($this->inotifyHandler, 0); 73 | 74 | // wait for any file events by reading from inotify handler asynchronously 75 | $this->loop->addReadStream($this->inotifyHandler, $this); 76 | } 77 | $descriptor = \inotify_add_watch($this->inotifyHandler, $path, $mask); 78 | $this->watchDescriptors[$descriptor] = array('path' => $path); 79 | return $descriptor; 80 | } 81 | 82 | /** 83 | * remove/cancel the given watch identifier previously aquired via add() 84 | * i.e. stop watching the associated path 85 | * 86 | * @param integer $descriptor watch identifier previously returned from add() 87 | */ 88 | public function remove($descriptor) 89 | { 90 | if (isset($this->watchDescriptors[$descriptor])) { 91 | unset($this->watchDescriptors[$descriptor]); 92 | 93 | if ($this->watchDescriptors) { 94 | // there are still watch paths remaining => only remove this descriptor 95 | \inotify_rm_watch($this->inotifyHandler, $descriptor); 96 | } else { 97 | // no more paths watched => close whole handler 98 | $this->close(); 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * close the inotifyHandler and clear all pending events (if any) 105 | */ 106 | public function close() 107 | { 108 | if ($this->inotifyHandler !== false) { 109 | $this->loop->removeReadStream($this->inotifyHandler); 110 | 111 | fclose($this->inotifyHandler); 112 | 113 | $this->inotifyHandler = false; 114 | $this->watchDescriptors = array(); 115 | } 116 | } 117 | } 118 | --------------------------------------------------------------------------------