SystemService.php (10182B)
1 <?php 2 3 namespace PartKeepr\CoreBundle\Services; 4 5 use Doctrine\Bundle\DoctrineBundle\Registry; 6 use Doctrine\DBAL\Version as DBALVersion; 7 use Doctrine\ORM\EntityManager; 8 use Doctrine\ORM\Tools\SchemaTool; 9 use Doctrine\ORM\Version as ORMVersion; 10 use Guzzle\Http\Client; 11 use PartKeepr\CoreBundle\System\OperatingSystem; 12 use PartKeepr\CoreBundle\System\SystemInformationRecord; 13 use PartKeepr\CronLoggerBundle\Services\CronLoggerService; 14 use Symfony\Component\DependencyInjection\ContainerAware; 15 use Symfony\Component\DependencyInjection\ContainerInterface; 16 17 class SystemService extends ContainerAware 18 { 19 /** 20 * @var EntityManager 21 */ 22 private $entityManager; 23 24 /** 25 * @var VersionService 26 */ 27 private $versionService; 28 29 /** 30 * @var CronLoggerService 31 */ 32 private $cronLoggerService; 33 34 public function __construct( 35 Registry $doctrine, 36 ContainerInterface $container, 37 VersionService $versionService, 38 CronLoggerService $cronLoggerService 39 ) { 40 $this->entityManager = $doctrine->getManager(); 41 $this->setContainer($container); 42 $this->versionService = $versionService; 43 $this->cronLoggerService = $cronLoggerService; 44 } 45 46 /** 47 * Returns a list of system information records. 48 * 49 * Please note that it is not defined which information is returned; the result 50 * should be seen as "informational" to the system operator, not for automated purposes. 51 * 52 * @return SystemInformationRecord[] An array of SystemInformationRecords 53 */ 54 public function getSystemInformation() 55 { 56 $aData = []; 57 58 $aData[] = new SystemInformationRecord('Doctrine ORM', ORMVersion::VERSION, 'Libraries'); 59 $aData[] = new SystemInformationRecord('Doctrine DBAL', DBALVersion::VERSION, 'Libraries'); 60 61 $aData[] = new SystemInformationRecord('PHP Version', phpversion(), 'System'); 62 63 $os = new OperatingSystem(); 64 65 $aData[] = new SystemInformationRecord('Operating System Type', $os->getPlatform(), 'System'); 66 $aData[] = new SystemInformationRecord('Operating System Release', $os->getRelease(), 'System'); 67 68 $aData[] = new SystemInformationRecord('memory_limit', ini_get('memory_limit'), 'PHP'); 69 $aData[] = new SystemInformationRecord('post_max_size', ini_get('post_max_size'), 'PHP'); 70 $aData[] = new SystemInformationRecord('upload_max_filesize', ini_get('upload_max_filesize'), 'PHP'); 71 $aData[] = new SystemInformationRecord('allow_url_fopen', ini_get('allow_url_fopen'), 'PHP'); 72 $aData[] = new SystemInformationRecord('max_execution_time', ini_get('max_execution_time'), 'PHP'); 73 74 $queryCache = get_class($this->entityManager->getConfiguration()->getQueryCacheImpl()); 75 $metadataCache = get_class($this->entityManager->getConfiguration()->getMetadataCacheImpl()); 76 77 $aData[] = new SystemInformationRecord('Query Cache Implementation', $queryCache, 'PHP'); 78 $aData[] = new SystemInformationRecord('Metadata Cache Implementation', $metadataCache, 'PHP'); 79 80 $aData[] = new SystemInformationRecord( 81 'Disk Space (Total)', 82 $this->format_bytes($this->getTotalDiskSpace()), 83 'PartKeepr' 84 ); 85 86 $aData[] = new SystemInformationRecord( 87 'Disk Space (Free)', 88 $this->format_bytes($this->getFreeDiskSpace()), 89 'PartKeepr' 90 ); 91 92 $aData[] = new SystemInformationRecord( 93 'Disk Space (Used)', 94 $this->format_bytes($this->getUsedDiskSpace()), 95 'PartKeepr' 96 ); 97 98 $aData[] = new SystemInformationRecord( 99 'Data Directory', 100 realpath($this->container->getParameter('partkeepr.filesystem.data_directory')), 101 'PartKeepr' 102 ); 103 104 $aData[] = new SystemInformationRecord( 105 'PartKeepr Version', 106 $this->versionService->getCanonicalVersion(), 107 'PartKeepr' 108 ); 109 110 return $aData; 111 } 112 113 /** 114 * Returns the database schema status. 115 * 116 * This method is usuall called once the user logs in, and alerts him if the schema is not up-to-date. 117 * 118 * Returns either status incomplete if the schema is not up-to-date, or complete if everything is OK. 119 */ 120 public function getSystemStatus() 121 { 122 if ($this->container->getParameter('partkeepr.cronjob_check')) { 123 $inactiveCronjobs = $this->cronLoggerService->getInactiveCronjobs( 124 $this->container->getParameter('partkeepr.required_cronjobs') 125 ); 126 } else { 127 // Skip cronjob tests 128 $inactiveCronjobs = []; 129 } 130 131 return [ 132 'inactiveCronjobCount' => count($inactiveCronjobs), 133 'inactiveCronjobs' => $inactiveCronjobs, 134 'schemaStatus' => $this->getSchemaStatus(), 135 'schemaQueries' => $this->getSchemaQueries(), 136 ]; 137 } 138 139 /** 140 * Checks if the schema is up-to-date. If yes, it returns "complete", if not, it returns "incomplete". 141 * 142 * @param none 143 * 144 * @return string Either "complete" or "incomplete" 145 */ 146 protected function getSchemaStatus() 147 { 148 $queries = $this->getSchemaQueries(); 149 150 if (count($queries) > 0) { 151 return 'incomplete'; 152 } else { 153 return 'complete'; 154 } 155 } 156 157 /** 158 * Returns all queries to be executed for a proper database update. 159 * 160 * @return array 161 */ 162 protected function getSchemaQueries() 163 { 164 $metadatas = $this->entityManager->getMetadataFactory()->getAllMetadata(); 165 166 $schemaTool = new SchemaTool($this->entityManager); 167 168 return $schemaTool->getUpdateSchemaSql($metadatas, true); 169 } 170 171 /** 172 * Returns the available disk space for the configured data_dir. 173 * 174 * @return float 175 */ 176 public function getFreeDiskSpace() 177 { 178 if ($this->container->getParameter('partkeepr.filesystem.quota') === false) { 179 return disk_free_space($this->container->getParameter('partkeepr.filesystem.data_directory')); 180 } else { 181 return $this->getTotalDiskSpace() - $this->getUsedDiskSpace(); 182 } 183 } 184 185 public function getTotalDiskSpace() 186 { 187 if ($this->container->getParameter('partkeepr.filesystem.quota') === false) { 188 return disk_total_space($this->container->getParameter('partkeepr.filesystem.data_directory')); 189 } else { 190 return $this->container->getParameter('partkeepr.filesystem.quota'); 191 } 192 } 193 194 /** 195 * Returns the used disk space occupied by attachments etc. 196 * 197 * Does not count temporary files. 198 * 199 * @return int 200 */ 201 public function getUsedDiskSpace() 202 { 203 if ($this->container->getParameter('partkeepr.filesystem.quota') === false) { 204 return $this->getTotalDiskSpace() - $this->getFreeDiskSpace(); 205 } 206 207 $fileEntities = [ 208 'PartKeepr\FootprintBundle\Entity\FootprintAttachment', 209 'PartKeepr\FootprintBundle\Entity\FootprintImage', 210 'PartKeepr\ManufacturerBundle\Entity\ManufacturerICLogo', 211 'PartKeepr\PartBundle\Entity\PartAttachment', 212 'PartKeepr\ProjectBundle\Entity\ProjectAttachment', 213 'PartKeepr\StorageLocationBundle\Entity\StorageLocationImage', 214 ]; 215 216 $size = 0; 217 foreach ($fileEntities as $fileEntity) { 218 $qb = $this->container->get('doctrine.orm.default_entity_manager')->createQueryBuilder(); 219 $qb->select('SUM(a.size)')->from($fileEntity, 'a'); 220 221 $size += $qb->getQuery()->getSingleScalarResult(); 222 } 223 224 return $size; 225 } 226 227 /** 228 * @param $number 229 * 230 * @return bool 231 */ 232 protected function is_valid_value($number) 233 { 234 return is_numeric($number); 235 } 236 237 /** 238 * Filter for converting bytes to a human-readable format, as Unix command "ls -h" does. 239 * 240 * @param string|int $number A string or integer number value to format. 241 * @param bool $base2conversion Defines if the conversion has to be strictly performed as binary values or 242 * by using a decimal conversion such as 1 KByte = 1000 Bytes. 243 * 244 * @return string The number converted to human readable representation. 245 */ 246 public function format_bytes($number, $base2conversion = true) 247 { 248 if (!$this->is_valid_value($number)) { 249 return; 250 } 251 $unit = $base2conversion ? 1024 : 1000; 252 if ($number < $unit) { 253 return $number.' B'; 254 } 255 $exp = intval((log($number) / log($unit))); 256 $pre = ($base2conversion ? 'kMGTPE' : 'KMGTPE'); 257 $pre = $pre[$exp - 1].($base2conversion ? '' : 'i'); 258 259 return sprintf('%.1f %sB', $number / pow($unit, $exp), $pre); 260 } 261 262 /** 263 * Returns the effective size from a human-readable byte format. 264 * 265 * Example: 266 * getBytesFromHumanReadable("1M") will return 1048576. 267 * 268 * @param string $size_str The byte 269 * 270 * @return int The bytes 271 */ 272 public function getBytesFromHumanReadable($size_str) 273 { 274 switch (substr($size_str, -1)) { 275 case 'M': 276 case 'm': 277 return (int) $size_str * 1048576; 278 case 'K': 279 case 'k': 280 return (int) $size_str * 1024; 281 case 'G': 282 case 'g': 283 return (int) $size_str * 1073741824; 284 default: 285 return $size_str; 286 } 287 } 288 289 public function getPatreonStatus() 290 { 291 $statusURI = $this->container->getParameter("partkeepr.patreon.statusuri"); 292 293 if ($statusURI === false) { 294 return false; 295 } 296 297 try { 298 $client = new Client(); 299 $request = $client->createRequest('GET', $statusURI, ['timeout' => 3.14]); 300 $request->send(); 301 302 return json_decode($request->getResponse()->getBody(), true); 303 } catch (\Exception $e) { 304 return false; 305 } 306 } 307 }