-
-
Notifications
You must be signed in to change notification settings - Fork 182
/
Filesystem.php
157 lines (130 loc) · 3.93 KB
/
Filesystem.php
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
<?php
namespace Rubix\ML\Persisters;
use Rubix\ML\Encoding;
use Rubix\ML\Persistable;
use Rubix\ML\Helpers\Params;
use Rubix\ML\Exceptions\InvalidArgumentException;
use Rubix\ML\Exceptions\RuntimeException;
use function is_dir;
use function is_file;
use function is_readable;
use function is_writable;
use function file_get_contents;
use function file_put_contents;
use function time;
/**
* Filesystem
*
* Filesystems are local or remote storage drives that are organized by files
* and folders. The filesystem persister serializes models to a file at a
* user-specified path.
*
* @category Machine Learning
* @package Rubix/ML
* @author Andrew DalPino
*/
class Filesystem implements Persister
{
/**
* The extension to give files created as part of a persistable's save history.
*
* @var string
*/
public const HISTORY_EXT = 'old';
/**
* The path to the model file on the filesystem.
*
* @var string
*/
protected string $path;
/**
* Should we keep a history of past saves?
*
* @var bool
*/
protected bool $history;
/**
* @param string $path
* @param bool $history
* @throws InvalidArgumentException
*/
public function __construct(string $path, bool $history = false)
{
if (empty($path)) {
throw new InvalidArgumentException('Path cannot be empty.');
}
if (is_dir($path)) {
throw new InvalidArgumentException('Path must be to a file, folder given.');
}
$this->path = $path;
$this->history = $history;
}
/**
* Save an encoding.
*
* @param Encoding $encoding
* @throws \RuntimeException
*/
public function save(Encoding $encoding) : void
{
if (!is_file($this->path) and !is_writable(dirname($this->path))) {
throw new RuntimeException('Folder does not exist or is not writable');
}
if (is_file($this->path) and !is_writable($this->path)) {
throw new RuntimeException("File {$this->path} is not writable.");
}
if ($encoding->bytes() === 0) {
throw new RuntimeException('Encoding does not contain any data.');
}
if ($this->history and is_file($this->path)) {
$timestamp = (string) time();
$filename = "{$this->path}-$timestamp." . self::HISTORY_EXT;
$num = 0;
while (is_file($filename)) {
$filename = "{$this->path}-$timestamp-" . ++$num . '.' . self::HISTORY_EXT;
}
if (!rename($this->path, $filename)) {
throw new RuntimeException('Could not create history file.');
}
}
$success = file_put_contents($this->path, $encoding->data(), LOCK_EX);
if (!$success) {
throw new RuntimeException('Could not write to the filesystem.');
}
}
/**
* Load a persisted encoding.
*
* @throws \RuntimeException
* @return Encoding
*/
public function load() : Encoding
{
if (!is_file($this->path)) {
throw new RuntimeException("File {$this->path} does not exist.");
}
if (!is_readable($this->path)) {
throw new RuntimeException("File {$this->path} is not readable.");
}
$data = file_get_contents($this->path);
if ($data === false) {
throw new RuntimeException('Could not load data from filesystem.');
}
$encoding = new Encoding($data);
if ($encoding->bytes() === 0) {
throw new RuntimeException('File does not contain any data.');
}
return $encoding;
}
/**
* Return the string representation of the object.
*
* @internal
*
* @return string
*/
public function __toString() : string
{
return "Filesystem (path: {$this->path}, history: " . Params::toString($this->history) . ')';
}
}