]> git.mxchange.org Git - friendica.git/blob - src/Worker/CronJobs.php
Rename dbesc to DBA::escape
[friendica.git] / src / Worker / CronJobs.php
1 <?php
2 /**
3  * @file src/worker/CronJobs.php
4  */
5 namespace Friendica\Worker;
6
7 use Friendica\App;
8 use Friendica\BaseObject;
9 use Friendica\Core\Cache;
10 use Friendica\Core\Config;
11 use Friendica\Database\DBA;
12 use Friendica\Model\Contact;
13 use Friendica\Model\GContact;
14 use Friendica\Model\Photo;
15 use Friendica\Model\User;
16 use Friendica\Network\Probe;
17 use Friendica\Protocol\PortableContact;
18
19 require_once 'include/dba.php';
20 require_once 'mod/nodeinfo.php';
21
22 class CronJobs
23 {
24         public static function execute($command = '')
25         {
26                 $a = BaseObject::getApp();
27
28                 // No parameter set? So return
29                 if ($command == '') {
30                         return;
31                 }
32
33                 logger("Starting cronjob " . $command, LOGGER_DEBUG);
34
35                 // Call possible post update functions
36                 // see src/Database/PostUpdate.php for more details
37                 if ($command == 'post_update') {
38 // Post updates will be reenabled (hopefully in a few days) when most item works are done
39 //                      PostUpdate::update();
40                         return;
41                 }
42
43                 // update nodeinfo data
44                 if ($command == 'nodeinfo') {
45                         nodeinfo_cron();
46                         return;
47                 }
48
49                 // Expire and remove user entries
50                 if ($command == 'expire_and_remove_users') {
51                         self::expireAndRemoveUsers();
52                         return;
53                 }
54
55                 if ($command == 'update_contact_birthdays') {
56                         Contact::updateBirthdays();
57                         return;
58                 }
59
60                 if ($command == 'update_photo_albums') {
61                         self::updatePhotoAlbums();
62                         return;
63                 }
64
65                 // Clear cache entries
66                 if ($command == 'clear_cache') {
67                         self::clearCache($a);
68                         return;
69                 }
70
71                 // Repair missing Diaspora values in contacts
72                 if ($command == 'repair_diaspora') {
73                         self::repairDiaspora($a);
74                         return;
75                 }
76
77                 // Repair entries in the database
78                 if ($command == 'repair_database') {
79                         self::repairDatabase();
80                         return;
81                 }
82
83                 logger("Xronjob " . $command . " is unknown.", LOGGER_DEBUG);
84
85                 return;
86         }
87
88         /**
89          * @brief Update the cached values for the number of photo albums per user
90          */
91         private static function updatePhotoAlbums()
92         {
93                 $r = q("SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`");
94                 if (!DBA::isResult($r)) {
95                         return;
96                 }
97
98                 foreach ($r as $user) {
99                         Photo::clearAlbumCache($user['uid']);
100                 }
101         }
102
103         /**
104          * @brief Expire and remove user entries
105          */
106         private static function expireAndRemoveUsers()
107         {
108                 // expire any expired regular accounts. Don't expire forums.
109                 $condition = ["NOT `account_expired` AND `account_expires_on` > ? AND `account_expires_on` < UTC_TIMESTAMP() AND `page-flags` = 0", NULL_DATE];
110                 DBA::update('user', ['account_expired' => true], $condition);
111
112                 // Remove any freshly expired account
113                 $users = DBA::select('user', ['uid'], ['account_expired' => true, 'account_removed' => false]);
114                 while ($user = DBA::fetch($users)) {
115                         User::remove($user['uid']);
116                 }
117
118                 // delete user records for recently removed accounts
119                 $users = DBA::select('user', ['uid'], ["`account_removed` AND `account_expires_on` < UTC_TIMESTAMP() - INTERVAL 3 DAY"]);
120                 while ($user = DBA::fetch($users)) {
121                         DBA::delete('user', ['uid' => $user['uid']]);
122                 }
123         }
124
125         /**
126          * @brief Clear cache entries
127          *
128          * @param App $a
129          */
130         private static function clearCache(App $a)
131         {
132                 $last = Config::get('system', 'cache_last_cleared');
133
134                 if ($last) {
135                         $next = $last + (3600); // Once per hour
136                         $clear_cache = ($next <= time());
137                 } else {
138                         $clear_cache = true;
139                 }
140
141                 if (!$clear_cache) {
142                         return;
143                 }
144
145                 // clear old cache
146                 Cache::clear();
147
148                 // clear old item cache files
149                 clear_cache();
150
151                 // clear cache for photos
152                 clear_cache($a->get_basepath(), $a->get_basepath() . "/photo");
153
154                 // clear smarty cache
155                 clear_cache($a->get_basepath() . "/view/smarty3/compiled", $a->get_basepath() . "/view/smarty3/compiled");
156
157                 // clear cache for image proxy
158                 if (!Config::get("system", "proxy_disabled")) {
159                         clear_cache($a->get_basepath(), $a->get_basepath() . "/proxy");
160
161                         $cachetime = Config::get('system', 'proxy_cache_time');
162                         if (!$cachetime) {
163                                 $cachetime = PROXY_DEFAULT_TIME;
164                         }
165                         $condition = ['`uid` = 0 AND `resource-id` LIKE "pic:%" AND `created` < NOW() - INTERVAL ? SECOND', $cachetime];
166                         DBA::delete('photo', $condition);
167                 }
168
169                 // Delete the cached OEmbed entries that are older than three month
170                 DBA::delete('oembed', ["`created` < NOW() - INTERVAL 3 MONTH"]);
171
172                 // Delete the cached "parse_url" entries that are older than three month
173                 DBA::delete('parsed_url', ["`created` < NOW() - INTERVAL 3 MONTH"]);
174
175                 // Maximum table size in megabyte
176                 $max_tablesize = intval(Config::get('system', 'optimize_max_tablesize')) * 1000000;
177                 if ($max_tablesize == 0) {
178                         $max_tablesize = 100 * 1000000; // Default are 100 MB
179                 }
180                 if ($max_tablesize > 0) {
181                         // Minimum fragmentation level in percent
182                         $fragmentation_level = intval(Config::get('system', 'optimize_fragmentation')) / 100;
183                         if ($fragmentation_level == 0) {
184                                 $fragmentation_level = 0.3; // Default value is 30%
185                         }
186
187                         // Optimize some tables that need to be optimized
188                         $r = q("SHOW TABLE STATUS");
189                         foreach ($r as $table) {
190
191                                 // Don't optimize tables that are too large
192                                 if ($table["Data_length"] > $max_tablesize) {
193                                         continue;
194                                 }
195
196                                 // Don't optimize empty tables
197                                 if ($table["Data_length"] == 0) {
198                                         continue;
199                                 }
200
201                                 // Calculate fragmentation
202                                 $fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]);
203
204                                 logger("Table " . $table["Name"] . " - Fragmentation level: " . round($fragmentation * 100, 2), LOGGER_DEBUG);
205
206                                 // Don't optimize tables that needn't to be optimized
207                                 if ($fragmentation < $fragmentation_level) {
208                                         continue;
209                                 }
210
211                                 // So optimize it
212                                 logger("Optimize Table " . $table["Name"], LOGGER_DEBUG);
213                                 q("OPTIMIZE TABLE `%s`", DBA::escape($table["Name"]));
214                         }
215                 }
216
217                 Config::set('system', 'cache_last_cleared', time());
218         }
219
220         /**
221          * @brief Repair missing values in Diaspora contacts
222          *
223          * @param App $a
224          */
225         private static function repairDiaspora(App $a)
226         {
227                 $starttime = time();
228
229                 $r = q("SELECT `id`, `url` FROM `contact`
230                         WHERE `network` = '%s' AND (`batch` = '' OR `notify` = '' OR `poll` = '' OR pubkey = '')
231                                 ORDER BY RAND() LIMIT 50", DBA::escape(NETWORK_DIASPORA));
232                 if (!DBA::isResult($r)) {
233                         return;
234                 }
235
236                 foreach ($r AS $contact) {
237                         // Quit the loop after 3 minutes
238                         if (time() > ($starttime + 180)) {
239                                 return;
240                         }
241
242                         if (!PortableContact::reachable($contact["url"])) {
243                                 continue;
244                         }
245
246                         $data = Probe::uri($contact["url"]);
247                         if ($data["network"] != NETWORK_DIASPORA) {
248                                 continue;
249                         }
250
251                         logger("Repair contact " . $contact["id"] . " " . $contact["url"], LOGGER_DEBUG);
252                         q("UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d",
253                                 DBA::escape($data["batch"]), DBA::escape($data["notify"]), DBA::escape($data["poll"]), DBA::escape($data["pubkey"]),
254                                 intval($contact["id"]));
255                 }
256         }
257
258         /**
259          * @brief Do some repairs in database entries
260          *
261          */
262         private static function repairDatabase()
263         {
264                 // Sometimes there seem to be issues where the "self" contact vanishes.
265                 // We haven't found the origin of the problem by now.
266                 $r = q("SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`)");
267                 if (DBA::isResult($r)) {
268                         foreach ($r AS $user) {
269                                 logger('Create missing self contact for user ' . $user['uid']);
270                                 Contact::createSelfFromUserId($user['uid']);
271                         }
272                 }
273
274                 // Set the parent if it wasn't set. (Shouldn't happen - but does sometimes)
275                 // This call is very "cheap" so we can do it at any time without a problem
276                 q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0");
277
278                 // There was an issue where the nick vanishes from the contact table
279                 q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
280
281                 // Update the global contacts for local users
282                 $r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`");
283                 if (DBA::isResult($r)) {
284                         foreach ($r AS $user) {
285                                 GContact::updateForUser($user["uid"]);
286                         }
287                 }
288
289                 /// @todo
290                 /// - remove thread entries without item
291                 /// - remove sign entries without item
292                 /// - remove children when parent got lost
293                 /// - set contact-id in item when not present
294         }
295 }