File: extension.cache.dbm.php

package info (click to toggle)
php-getid3 1.9.24%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,692 kB
  • sloc: php: 29,646; makefile: 4
file content (270 lines) | stat: -rw-r--r-- 8,459 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
<?php

/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org>               //
//  available at https://github.com/JamesHeinrich/getID3       //
//            or https://www.getid3.org                        //
//            or http://getid3.sourceforge.net                 //
//                                                             //
// extension.cache.dbm.php - part of getID3()                  //
// Please see readme.txt for more information                  //
//                                                            ///
/////////////////////////////////////////////////////////////////
//                                                             //
// This extension written by Allan Hansen <ahØartemis*dk>      //
//                                                            ///
/////////////////////////////////////////////////////////////////


/**
* This is a caching extension for getID3(). It works the exact same
* way as the getID3 class, but return cached information very fast
*
* Example:
*
*    Normal getID3 usage (example):
*
*       require_once 'getid3/getid3.php';
*       $getID3 = new getID3;
*       $getID3->encoding = 'UTF-8';
*       $info1 = $getID3->analyze('file1.flac');
*       $info2 = $getID3->analyze('file2.wv');
*
*    getID3_cached usage:
*
*       require_once 'getid3/getid3.php';
*       require_once 'getid3/extension.cache.dbm.php';
*       $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
*                                          '/tmp/getid3_cache.lock');
*       $getID3->encoding = 'UTF-8';
*       $info1 = $getID3->analyze('file1.flac');
*       $info2 = $getID3->analyze('file2.wv');
*
*
* Supported Cache Types
*
*   SQL Databases:          (use extension.cache.mysql)
*
*   cache_type          cache_options
*   -------------------------------------------------------------------
*   mysql               host, database, username, password
*
*
*   DBM-Style Databases:    (this extension)
*
*   cache_type          cache_options
*   -------------------------------------------------------------------
*   gdbm                dbm_filename, lock_filename
*   ndbm                dbm_filename, lock_filename
*   db2                 dbm_filename, lock_filename
*   db3                 dbm_filename, lock_filename
*   db4                 dbm_filename, lock_filename  (PHP5 required)
*
*   PHP must have write access to both dbm_filename and lock_filename.
*
*
* Recommended Cache Types
*
*   Infrequent updates, many reads      any DBM
*   Frequent updates                    mysql
*/


class getID3_cached_dbm extends getID3
{
    /**
     * @var null|resource|Dba\Connection
     */
    private $dba; // @phpstan-ignore-line

    /**
     * @var resource|bool|null
     */
    private $lock;

    /**
     * @var string
     */
    private $cache_type;

    /**
     * @var string
     */
    private $dbm_filename;

    /**
     * @var string
     */
    private $lock_filename;

    /**
     * constructor - see top of this file for cache type and cache_options
     *
     * @param string $cache_type
     * @param string $dbm_filename
     * @param string $lock_filename
     *
     * @throws Exception
     * @throws getid3_exception
     */
    public function __construct($cache_type, $dbm_filename, $lock_filename) {

        // Check for dba extension
        if (!extension_loaded('dba')) {
            throw new Exception('PHP is not compiled with dba support, required to use DBM style cache.');
        }

        // Check for specific dba driver
        if (!function_exists('dba_handlers') || !in_array($cache_type, dba_handlers())) {
            throw new Exception('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
        }

        // Store lock filename for cleanup operations
        $this->lock_filename = $lock_filename;

        // Create lock file if needed
        if (!file_exists($this->lock_filename)) {
            if (!touch($this->lock_filename)) {
                throw new Exception('failed to create lock file: '.$this->lock_filename);
            }
        }

        // Open lock file for writing with read/write mode (w+) to prevent truncation on BSD systems
        $this->lock = fopen($this->lock_filename, 'w+');
        if (!$this->lock) {
            throw new Exception('Cannot open lock file: '.$this->lock_filename);
        }

        // Acquire exclusive write lock to lock file
        if (!flock($this->lock, LOCK_EX)) {
            fclose($this->lock);
            throw new Exception('Cannot acquire lock: '.$this->lock_filename);
        }

        // Store connection parameters
        $this->cache_type = $cache_type;
        $this->dbm_filename = $dbm_filename;

        try {
            // Try to open existing DBM file
            $this->dba = dba_open($this->dbm_filename, 'w', $this->cache_type);

            // Create new DBM file if it didn't exist
            if (!$this->dba) {
                $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
                if (!$this->dba) {
                    throw new Exception('failed to create dbm file: '.$this->dbm_filename);
                }

                // Insert getID3 version number
                dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
            }

            // Check version number and clear cache if changed
            if (dba_fetch(getID3::VERSION, $this->dba) != getID3::VERSION) {
                $this->clear_cache();
            }

        } catch (Exception $e) {
            $this->safe_close();
            throw $e;
        }

        // Register destructor
        register_shutdown_function(array($this, '__destruct'));

        parent::__construct();
    }

    /**
     * Destructor - ensure proper cleanup of resources
     */
    public function __destruct() {
        $this->safe_close();
    }

    /**
     * Safely close all resources with error handling
     */
    private function safe_close() {
        try {
            // Close DBM connection if open
            if (is_resource($this->dba)) {
                dba_close($this->dba);
                $this->dba = null;
            }

            // Release lock if acquired
            if (is_resource($this->lock)) {
                flock($this->lock, LOCK_UN);
                fclose($this->lock);
                $this->lock = null;
            }
        } catch (Exception $e) {
            error_log('getID3_cached_dbm cleanup error: ' . $e->getMessage());
        }
    }

    /**
     * Clear cache and recreate DBM file
     *
     * @throws Exception
     */
    public function clear_cache() {
        $this->safe_close();

        // Create new dbm file
        $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
        if (!$this->dba) {
            throw new Exception('failed to clear cache/recreate dbm file: '.$this->dbm_filename);
        }

        // Insert getID3 version number
        dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);

        // Re-register shutdown function
        register_shutdown_function(array($this, '__destruct'));
    }

    /**
     * Analyze file and cache results
     *
     * @param string $filename
     * @param int $filesize
     * @param string $original_filename
     * @param resource $fp
     *
     * @return mixed
     */
    public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
        try {
            $key = null;
            if (file_exists($filename)) {
                // Calc key: filename::mod_time::size - should be unique
                $key = $filename.'::'.filemtime($filename).'::'.filesize($filename);

                // Lookup key in cache
                $result = dba_fetch($key, $this->dba);

                // Cache hit
                if ($result !== false) {
                    return unserialize($result);
                }
            }

            // Cache miss - perform actual analysis
            $result = parent::analyze($filename, $filesize, $original_filename, $fp);

            // Store result in cache if key was generated
            if ($key !== null) {
                dba_replace($key, serialize($result), $this->dba);
            }

            return $result;

        } catch (Exception $e) {
            $this->safe_close();
            throw $e;
        }
    }
}