]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/bookmark/BookmarkIO.php
099653b0e62d0b7acc7adee5c6f573c721238611
[github/shaarli/Shaarli.git] / application / bookmark / BookmarkIO.php
1 <?php
2
3 namespace Shaarli\Bookmark;
4
5 use malkusch\lock\mutex\Mutex;
6 use malkusch\lock\mutex\NoMutex;
7 use Shaarli\Bookmark\Exception\DatastoreNotInitializedException;
8 use Shaarli\Bookmark\Exception\EmptyDataStoreException;
9 use Shaarli\Bookmark\Exception\NotWritableDataStoreException;
10 use Shaarli\Config\ConfigManager;
11
12 /**
13 * Class BookmarkIO
14 *
15 * This class performs read/write operation to the file data store.
16 * Used by BookmarkFileService.
17 *
18 * @package Shaarli\Bookmark
19 */
20 class BookmarkIO
21 {
22 /**
23 * @var string Datastore file path
24 */
25 protected $datastore;
26
27 /**
28 * @var ConfigManager instance
29 */
30 protected $conf;
31
32
33 /** @var Mutex */
34 protected $mutex;
35
36 /**
37 * string Datastore PHP prefix
38 */
39 protected static $phpPrefix = '<?php /* ';
40 /**
41 * string Datastore PHP suffix
42 */
43 protected static $phpSuffix = ' */ ?>';
44
45 /**
46 * LinksIO constructor.
47 *
48 * @param ConfigManager $conf instance
49 */
50 public function __construct(ConfigManager $conf, Mutex $mutex = null)
51 {
52 if ($mutex === null) {
53 // This should only happen with legacy classes
54 $mutex = new NoMutex();
55 }
56 $this->conf = $conf;
57 $this->datastore = $conf->get('resource.datastore');
58 $this->mutex = $mutex;
59 }
60
61 /**
62 * Reads database from disk to memory
63 *
64 * @return BookmarkArray instance
65 *
66 * @throws NotWritableDataStoreException Data couldn't be loaded
67 * @throws EmptyDataStoreException Datastore file exists but does not contain any bookmark
68 * @throws DatastoreNotInitializedException File does not exists
69 */
70 public function read()
71 {
72 if (! file_exists($this->datastore)) {
73 throw new DatastoreNotInitializedException();
74 }
75
76 if (!is_writable($this->datastore)) {
77 throw new NotWritableDataStoreException($this->datastore);
78 }
79
80 $content = null;
81 $this->mutex->synchronized(function () use (&$content) {
82 $content = file_get_contents($this->datastore);
83 });
84
85 // Note that gzinflate is faster than gzuncompress.
86 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439
87 $links = unserialize(gzinflate(base64_decode(
88 substr($content, strlen(self::$phpPrefix), -strlen(self::$phpSuffix))
89 )));
90
91 if (empty($links)) {
92 if (filesize($this->datastore) > 100) {
93 throw new NotWritableDataStoreException($this->datastore);
94 }
95 throw new EmptyDataStoreException();
96 }
97
98 return $links;
99 }
100
101 /**
102 * Saves the database from memory to disk
103 *
104 * @param BookmarkArray $links instance.
105 *
106 * @throws NotWritableDataStoreException the datastore is not writable
107 */
108 public function write($links)
109 {
110 if (is_file($this->datastore) && !is_writeable($this->datastore)) {
111 // The datastore exists but is not writeable
112 throw new NotWritableDataStoreException($this->datastore);
113 } else if (!is_file($this->datastore) && !is_writeable(dirname($this->datastore))) {
114 // The datastore does not exist and its parent directory is not writeable
115 throw new NotWritableDataStoreException(dirname($this->datastore));
116 }
117
118 $data = self::$phpPrefix.base64_encode(gzdeflate(serialize($links))).self::$phpSuffix;
119
120 $this->mutex->synchronized(function () use ($data) {
121 file_put_contents(
122 $this->datastore,
123 $data
124 );
125 });
126 }
127 }