core-bundle/src/Resources/contao/library/Contao/Dbafs.php
public static function syncFiles()
	{
		@ini_set('max_execution_time', 0);

		// Consider the suhosin.memory_limit (see #7035)
		if (\extension_loaded('suhosin'))
		{
			if (($limit = ini_get('suhosin.memory_limit')) !== '')
			{
				@ini_set('memory_limit', $limit);
			}
		}
		else
		{
			@ini_set('memory_limit', -1);
		}

		$objDatabase = Database::getInstance();

		// Lock the files table
		$objDatabase->lockTables(array('tl_files'=>'WRITE'));

		// Reset the "found" flag
		$objDatabase->query("UPDATE tl_files SET found=''");

		$rootDir = System::getContainer()->getParameter('kernel.project_dir');

		/** @var \SplFileInfo[] $objFiles */
		$objFiles = new \RecursiveIteratorIterator(
			new SyncExclude(
				new \RecursiveDirectoryIterator(
					$rootDir . '/' . Config::get('uploadPath'),
					\FilesystemIterator::UNIX_PATHS|\FilesystemIterator::FOLLOW_SYMLINKS|\FilesystemIterator::SKIP_DOTS
				)
			), \RecursiveIteratorIterator::SELF_FIRST
		);

		$strLog = 'system/tmp/' . md5(uniqid(mt_rand(), true));

		// Open the log file
		$objLog = new File($strLog);
		$objLog->truncate();

		$arrModels = array();
		$arrFoldersToHash = array();
		$arrFoldersToCompare = array();

		// Create or update the database entries
		foreach ($objFiles as $objFile)
		{
			$strRelpath = StringUtil::stripRootDir($objFile->getPathname());

			// Get all subfiles in a single query
			if ($objFile->isDir())
			{
				$objSubfiles = FilesModel::findMultipleFilesByFolder($strRelpath);

				if ($objSubfiles !== null)
				{
					while ($objSubfiles->next())
					{
						$arrModels[$objSubfiles->path] = $objSubfiles->current();
					}
				}
			}

			// Get the model
			if (isset($arrModels[$strRelpath]))
			{
				/** @var Model $objModel */
				$objModel = $arrModels[$strRelpath];
			}
			else
			{
				$objModel = FilesModel::findByPath($strRelpath);
			}

			if ($objModel === null)
			{
				// Add a log entry
				$objLog->append("[Added] $strRelpath");

				// Get the parent folder
				$strParent = \dirname($strRelpath);

				// Get the parent ID
				if ($strParent == Config::get('uploadPath'))
				{
					$strPid = null;
				}
				else
				{
					$objParent = FilesModel::findByPath($strParent);

					if ($objParent === null)
					{
						throw new \Exception("No parent entry for $strParent");
					}

					$strPid = $objParent->uuid;
				}

				// Create the file or folder
				if (is_file($rootDir . '/' . $strRelpath))
				{
					$objFile = new File($strRelpath);

					$objModel = new FilesModel();
					$objModel->pid       = $strPid;
					$objModel->tstamp    = time();
					$objModel->name      = $objFile->name;
					$objModel->type      = 'file';
					$objModel->path      = $objFile->path;
					$objModel->extension = $objFile->extension;
					$objModel->found     = 2;
					$objModel->hash      = $objFile->hash;
					$objModel->uuid      = $objDatabase->getUuid();
					$objModel->save();
				}
				else
				{
					$objFolder = new Folder($strRelpath);

					$objModel = new FilesModel();
					$objModel->pid       = $strPid;
					$objModel->tstamp    = time();
					$objModel->name      = $objFolder->name;
					$objModel->type      = 'folder';
					$objModel->path      = $objFolder->path;
					$objModel->extension = '';
					$objModel->found     = 2;
					$objModel->uuid      = $objDatabase->getUuid();
					$objModel->save();

					$arrFoldersToHash[] = $strRelpath;
				}
			}
			else
			{
				if ($objFile->isDir())
				{
					$arrFoldersToCompare[] = $objModel;
				}
				else
				{
					// Check whether the MD5 hash has changed
					$strHash = (new File($strRelpath))->hash;
					$strType = ($objModel->hash != $strHash) ? 'Changed' : 'Unchanged';

					// Add a log entry
					$objLog->append("[$strType] $strRelpath");

					// Update the record
					$objModel->found = 1;
					$objModel->hash  = $strHash;
					$objModel->save();
				}
			}
		}

		// Update the folder hashes from bottom up after all file hashes are set
		foreach (array_reverse($arrFoldersToHash) as $strPath)
		{
			$objModel = FilesModel::findByPath($strPath);
			$objModel->hash = static::getFolderHash($strPath);
			$objModel->save();
		}

		// Compare the folders after all hashes are set
		foreach (array_reverse($arrFoldersToCompare) as $objModel)
		{
			// Check whether the MD5 hash has changed
			$strHash = static::getFolderHash($objModel->path);
			$strType = ($objModel->hash != $strHash) ? 'Changed' : 'Unchanged';

			// Add a log entry
			$objLog->append("[$strType] {$objModel->path}");

			// Update the record
			$objModel->found = 1;
			$objModel->hash  = $strHash;
			$objModel->save();
		}

		// Check for left-over entries in the DB
		$objFiles = FilesModel::findByFound('');

		if ($objFiles !== null)
		{
			$arrMapped = array();
			$arrPidUpdate = array();

			/** @var Collection|FilesModel $objFiles */
			while ($objFiles->next())
			{
				$objFound = FilesModel::findBy(array('hash=?', 'found=2'), $objFiles->hash);

				if ($objFound !== null)
				{
					// Check for matching file names if the result is ambiguous (see #5644)
					if ($objFound->count() > 1)
					{
						while ($objFound->next())
						{
							if ($objFound->name == $objFiles->name)
							{
								$objFound = $objFound->current();
								break;
							}
						}
					}

					// If another file has been mapped already, delete the entry (see #6008)
					if (\in_array($objFound->path, $arrMapped))
					{
						$objLog->append("[Deleted] {$objFiles->path}");
						$objFiles->delete();
						continue;
					}

					$arrMapped[] = $objFound->path;

					// Store the PID change
					if ($objFiles->type == 'folder')
					{
						$arrPidUpdate[$objFound->uuid] = $objFiles->uuid;
					}

					// Add a log entry BEFORE changing the object
					$objLog->append("[Moved] {$objFiles->path} to {$objFound->path}");

					// Update the original entry
					$objFiles->pid    = $objFound->pid;
					$objFiles->tstamp = $objFound->tstamp;
					$objFiles->name   = $objFound->name;
					$objFiles->type   = $objFound->type;
					$objFiles->path   = $objFound->path;
					$objFiles->found  = 1;

					// Delete the newer (duplicate) entry
					$objFound->delete();

					// Then save the modified original entry (prevents duplicate key errors)
					$objFiles->save();
				}
				else
				{
					// Add a log entry BEFORE changing the object
					$objLog->append("[Deleted] {$objFiles->path}");

					// Delete the entry if the resource has gone
					$objFiles->delete();
				}
			}

			// Update the PID of the child records
			if (!empty($arrPidUpdate))
			{
				foreach ($arrPidUpdate as $from=>$to)
				{
					$objChildren = FilesModel::findByPid($from);

					if ($objChildren !== null)
					{
						while ($objChildren->next())
						{
							$objChildren->pid = $to;
							$objChildren->save();
						}
					}
				}
			}
		}

		// Close the log file
		$objLog->close();

		// Reset the found flag
		$objDatabase->query("UPDATE tl_files SET found=1 WHERE found=2");

		// Unlock the tables
		$objDatabase->unlockTables();

		// Return the path to the log file
		return $strLog;
	}
public static function syncFiles()
	{
		@ini_set('max_execution_time', 0);

		// Consider the suhosin.memory_limit (see #7035)
		if (\extension_loaded('suhosin'))
		{
			if (($limit = ini_get('suhosin.memory_limit')) !== '')
			{
				@ini_set('memory_limit', $limit);
			}
		}
		else
		{
			@ini_set('memory_limit', -1);
		}

		$objDatabase = Database::getInstance();

		// Lock the files table
		$objDatabase->lockTables(array('tl_files'=>'WRITE'));

		// Reset the "found" flag
		$objDatabase->query("UPDATE tl_files SET found=''");

		$rootDir = System::getContainer()->getParameter('kernel.project_dir');

		/** @var \SplFileInfo[] $objFiles */
		$objFiles = new \RecursiveIteratorIterator(
			new SyncExclude(
				new \RecursiveDirectoryIterator(
					$rootDir . '/' . Config::get('uploadPath'),
					\FilesystemIterator::UNIX_PATHS|\FilesystemIterator::FOLLOW_SYMLINKS|\FilesystemIterator::SKIP_DOTS
				)
			), \RecursiveIteratorIterator::SELF_FIRST
		);

		$strLog = 'system/tmp/' . md5(uniqid(mt_rand(), true));

		// Open the log file
		$objLog = new File($strLog);
		$objLog->truncate();

		$arrModels = array();
		$arrFoldersToHash = array();
		$arrFoldersToCompare = array();

		// Create or update the database entries
		foreach ($objFiles as $objFile)
		{
			$strRelpath = StringUtil::stripRootDir($objFile->getPathname());

			// Get all subfiles in a single query
			if ($objFile->isDir())
			{
				$objSubfiles = FilesModel::findMultipleFilesByFolder($strRelpath);

				if ($objSubfiles !== null)
				{
					while ($objSubfiles->next())
					{
						$arrModels[$objSubfiles->path] = $objSubfiles->current();
					}
				}
			}

			// Get the model
			if (isset($arrModels[$strRelpath]))
			{
				/** @var Model $objModel */
				$objModel = $arrModels[$strRelpath];
			}
			else
			{
				$objModel = FilesModel::findByPath($strRelpath);
			}

			if ($objModel === null)
			{
				// Add a log entry
				$objLog->append("[Added] $strRelpath");

				// Get the parent folder
				$strParent = \dirname($strRelpath);

				// Get the parent ID
				if ($strParent == Config::get('uploadPath'))
				{
					$strPid = null;
				}
				else
				{
					$objParent = FilesModel::findByPath($strParent);

					if ($objParent === null)
					{
						throw new \Exception("No parent entry for $strParent");
					}

					$strPid = $objParent->uuid;
				}

				// Create the file or folder
				if (is_file($rootDir . '/' . $strRelpath))
				{
					$objFile = new File($strRelpath);

					$objModel = new FilesModel();
					$objModel->pid       = $strPid;
					$objModel->tstamp    = time();
					$objModel->name      = $objFile->name;
					$objModel->type      = 'file';
					$objModel->path      = $objFile->path;
					$objModel->extension = $objFile->extension;
					$objModel->found     = 2;
					$objModel->hash      = $objFile->hash;
					$objModel->uuid      = $objDatabase->getUuid();
					$objModel->save();
				}
				else
				{
					$objFolder = new Folder($strRelpath);

					$objModel = new FilesModel();
					$objModel->pid       = $strPid;
					$objModel->tstamp    = time();
					$objModel->name      = $objFolder->name;
					$objModel->type      = 'folder';
					$objModel->path      = $objFolder->path;
					$objModel->extension = '';
					$objModel->found     = 2;
					$objModel->uuid      = $objDatabase->getUuid();
					$objModel->save();

					$arrFoldersToHash[] = $strRelpath;
				}
			}
			else
			{
				if ($objFile->isDir())
				{
					$arrFoldersToCompare[] = $objModel;
				}
				else
				{
					// Check whether the MD5 hash has changed
					$strHash = (new File($strRelpath))->hash;
					$strType = ($objModel->hash != $strHash) ? 'Changed' : 'Unchanged';

					// Add a log entry
					$objLog->append("[$strType] $strRelpath");

					// Update the record
					$objModel->found = 1;
					$objModel->hash  = $strHash;
					$objModel->save();
				}
			}
		}

		// Update the folder hashes from bottom up after all file hashes are set
		foreach (array_reverse($arrFoldersToHash) as $strPath)
		{
			$objModel = FilesModel::findByPath($strPath);
			$objModel->hash = static::getFolderHash($strPath);
			$objModel->save();
		}

		// Compare the folders after all hashes are set
		foreach (array_reverse($arrFoldersToCompare) as $objModel)
		{
			// Check whether the MD5 hash has changed
			$strHash = static::getFolderHash($objModel->path);
			$strType = ($objModel->hash != $strHash) ? 'Changed' : 'Unchanged';

			// Add a log entry
			$objLog->append("[$strType] {$objModel->path}");

			// Update the record
			$objModel->found = 1;
			$objModel->hash  = $strHash;
			$objModel->save();
		}

		// Check for left-over entries in the DB
		$objFiles = FilesModel::findByFound('');

		if ($objFiles !== null)
		{
			$arrMapped = array();
			$arrPidUpdate = array();

			/** @var Collection|FilesModel $objFiles */
			while ($objFiles->next())
			{
				$objFound = FilesModel::findBy(array('hash=?', 'found=2'), $objFiles->hash);

				if ($objFound !== null)
				{
					// Check for matching file names if the result is ambiguous (see #5644)
					if ($objFound->count() > 1)
					{
						while ($objFound->next())
						{
							if ($objFound->name == $objFiles->name)
							{
								$objFound = $objFound->current();
								break;
							}
						}
					}

					// If another file has been mapped already, delete the entry (see #6008)
					if (\in_array($objFound->path, $arrMapped))
					{
						$objLog->append("[Deleted] {$objFiles->path}");
						$objFiles->delete();
						continue;
					}

					$arrMapped[] = $objFound->path;

					// Store the PID change
					if ($objFiles->type == 'folder')
					{
						$arrPidUpdate[$objFound->uuid] = $objFiles->uuid;
					}

					// Add a log entry BEFORE changing the object
					$objLog->append("[Moved] {$objFiles->path} to {$objFound->path}");

					// Update the original entry
					$objFiles->pid    = $objFound->pid;
					$objFiles->tstamp = $objFound->tstamp;
					$objFiles->name   = $objFound->name;
					$objFiles->type   = $objFound->type;
					$objFiles->path   = $objFound->path;
					$objFiles->found  = 1;

					// Delete the newer (duplicate) entry
					$objFound->delete();

					// Then save the modified original entry (prevents duplicate key errors)
					$objFiles->save();
				}
				else
				{
					// Add a log entry BEFORE changing the object
					$objLog->append("[Deleted] {$objFiles->path}");

					// Delete the entry if the resource has gone
					$objFiles->delete();
				}
			}

			// Update the PID of the child records
			if (!empty($arrPidUpdate))
			{
				foreach ($arrPidUpdate as $from=>$to)
				{
					$objChildren = FilesModel::findByPid($from);

					if ($objChildren !== null)
					{
						while ($objChildren->next())
						{
							$objChildren->pid = $to;
							$objChildren->save();
						}
					}
				}
			}
		}

		// Close the log file
		$objLog->close();

		// Reset the found flag
		$objDatabase->query("UPDATE tl_files SET found=1 WHERE found=2");

		// Unlock the tables
		$objDatabase->unlockTables();

		// Return the path to the log file
		return $strLog;
	}
public static function syncFiles ( ) { @ ini_set ( 'max_execution_time' , 0 ) ; // Consider the suhosin.memory_limit (see #7035) if ( \ extension_loaded ( 'suhosin' ) ) { if ( ( $ limit = ini_get ( 'suhosin.memory_limit' ) ) !== '' ) { @ ini_set ( 'memory_limit' , $ limit ) ; } } else { @ ini_set ( 'memory_limit' , - 1 ) ; } $ objDatabase = Database :: getInstance ( ) ; // Lock the files table $ objDatabase -> lockTables ( array ( 'tl_files' => 'WRITE' ) ) ; // Reset the "found" flag $ objDatabase -> query ( "UPDATE tl_files SET found=''" ) ; $ rootDir = System :: getContainer ( ) -> getParameter ( 'kernel.project_dir' ) ; /** @var \SplFileInfo[] $objFiles */ $ objFiles = new \ RecursiveIteratorIterator ( new SyncExclude ( new \ RecursiveDirectoryIterator ( $ rootDir . '/' . Config :: get ( 'uploadPath' ) , \ FilesystemIterator :: UNIX_PATHS | \ FilesystemIterator :: FOLLOW_SYMLINKS | \ FilesystemIterator :: SKIP_DOTS ) ) , \ RecursiveIteratorIterator :: SELF_FIRST ) ; $ strLog = 'system/tmp/' . md5 ( uniqid ( mt_rand ( ) , true ) ) ; // Open the log file $ objLog = new File ( $ strLog ) ; $ objLog -> truncate ( ) ; $ arrModels = array ( ) ; $ arrFoldersToHash = array ( ) ; $ arrFoldersToCompare = array ( ) ; // Create or update the database entries foreach ( $ objFiles as $ objFile ) { $ strRelpath = StringUtil :: stripRootDir ( $ objFile -> getPathname ( ) ) ; // Get all subfiles in a single query if ( $ objFile -> isDir ( ) ) { $ objSubfiles = FilesModel :: findMultipleFilesByFolder ( $ strRelpath ) ; if ( $ objSubfiles !== null ) { while ( $ objSubfiles -> next ( ) ) { $ arrModels [ $ objSubfiles -> path ] = $ objSubfiles -> current ( ) ; } } } // Get the model if ( isset ( $ arrModels [ $ strRelpath ] ) ) { /** @var Model $objModel */ $ objModel = $ arrModels [ $ strRelpath ] ; } else { $ objModel = FilesModel :: findByPath ( $ strRelpath ) ; } if ( $ objModel === null ) { // Add a log entry $ objLog -> append ( "[Added] $strRelpath" ) ; // Get the parent folder $ strParent = \ dirname ( $ strRelpath ) ; // Get the parent ID if ( $ strParent == Config :: get ( 'uploadPath' ) ) { $ strPid = null ; } else { $ objParent = FilesModel :: findByPath ( $ strParent ) ; if ( $ objParent === null ) { throw new \ Exception ( "No parent entry for $strParent" ) ; } $ strPid = $ objParent -> uuid ; } // Create the file or folder if ( is_file ( $ rootDir . '/' . $ strRelpath ) ) { $ objFile = new File ( $ strRelpath ) ; $ objModel = new FilesModel ( ) ; $ objModel -> pid = $ strPid ; $ objModel -> tstamp = time ( ) ; $ objModel -> name = $ objFile -> name ; $ objModel -> type = 'file' ; $ objModel -> path = $ objFile -> path ; $ objModel -> extension = $ objFile -> extension ; $ objModel -> found = 2 ; $ objModel -> hash = $ objFile -> hash ; $ objModel -> uuid = $ objDatabase -> getUuid ( ) ; $ objModel -> save ( ) ; } else { $ objFolder = new Folder ( $ strRelpath ) ; $ objModel = new FilesModel ( ) ; $ objModel -> pid = $ strPid ; $ objModel -> tstamp = time ( ) ; $ objModel -> name = $ objFolder -> name ; $ objModel -> type = 'folder' ; $ objModel -> path = $ objFolder -> path ; $ objModel -> extension = '' ; $ objModel -> found = 2 ; $ objModel -> uuid = $ objDatabase -> getUuid ( ) ; $ objModel -> save ( ) ; $ arrFoldersToHash [ ] = $ strRelpath ; } } else { if ( $ objFile -> isDir ( ) ) { $ arrFoldersToCompare [ ] = $ objModel ; } else { // Check whether the MD5 hash has changed $ strHash = ( new File ( $ strRelpath ) ) -> hash ; $ strType = ( $ objModel -> hash != $ strHash ) ? 'Changed' : 'Unchanged' ; // Add a log entry $ objLog -> append ( "[$strType] $strRelpath" ) ; // Update the record $ objModel -> found = 1 ; $ objModel -> hash = $ strHash ; $ objModel -> save ( ) ; } } } // Update the folder hashes from bottom up after all file hashes are set foreach ( array_reverse ( $ arrFoldersToHash ) as $ strPath ) { $ objModel = FilesModel :: findByPath ( $ strPath ) ; $ objModel -> hash = static :: getFolderHash ( $ strPath ) ; $ objModel -> save ( ) ; } // Compare the folders after all hashes are set foreach ( array_reverse ( $ arrFoldersToCompare ) as $ objModel ) { // Check whether the MD5 hash has changed $ strHash = static :: getFolderHash ( $ objModel -> path ) ; $ strType = ( $ objModel -> hash != $ strHash ) ? 'Changed' : 'Unchanged' ; // Add a log entry $ objLog -> append ( "[$strType] {$objModel->path}" ) ; // Update the record $ objModel -> found = 1 ; $ objModel -> hash = $ strHash ; $ objModel -> save ( ) ; } // Check for left-over entries in the DB $ objFiles = FilesModel :: findByFound ( '' ) ; if ( $ objFiles !== null ) { $ arrMapped = array ( ) ; $ arrPidUpdate = array ( ) ; /** @var Collection|FilesModel $objFiles */ while ( $ objFiles -> next ( ) ) { $ objFound = FilesModel :: findBy ( array ( 'hash=?' , 'found=2' ) , $ objFiles -> hash ) ; if ( $ objFound !== null ) { // Check for matching file names if the result is ambiguous (see #5644) if ( $ objFound -> count ( ) > 1 ) { while ( $ objFound -> next ( ) ) { if ( $ objFound -> name == $ objFiles -> name ) { $ objFound = $ objFound -> current ( ) ; break ; } } } // If another file has been mapped already, delete the entry (see #6008) if ( \ in_array ( $ objFound -> path , $ arrMapped ) ) { $ objLog -> append ( "[Deleted] {$objFiles->path}" ) ; $ objFiles -> delete ( ) ; continue ; } $ arrMapped [ ] = $ objFound -> path ; // Store the PID change if ( $ objFiles -> type == 'folder' ) { $ arrPidUpdate [ $ objFound -> uuid ] = $ objFiles -> uuid ; } // Add a log entry BEFORE changing the object $ objLog -> append ( "[Moved] {$objFiles->path} to {$objFound->path}" ) ; // Update the original entry $ objFiles -> pid = $ objFound -> pid ; $ objFiles -> tstamp = $ objFound -> tstamp ; $ objFiles -> name = $ objFound -> name ; $ objFiles -> type = $ objFound -> type ; $ objFiles -> path = $ objFound -> path ; $ objFiles -> found = 1 ; // Delete the newer (duplicate) entry $ objFound -> delete ( ) ; // Then save the modified original entry (prevents duplicate key errors) $ objFiles -> save ( ) ; } else { // Add a log entry BEFORE changing the object $ objLog -> append ( "[Deleted] {$objFiles->path}" ) ; // Delete the entry if the resource has gone $ objFiles -> delete ( ) ; } } // Update the PID of the child records if ( ! empty ( $ arrPidUpdate ) ) { foreach ( $ arrPidUpdate as $ from => $ to ) { $ objChildren = FilesModel :: findByPid ( $ from ) ; if ( $ objChildren !== null ) { while ( $ objChildren -> next ( ) ) { $ objChildren -> pid = $ to ; $ objChildren -> save ( ) ; } } } } } // Close the log file $ objLog -> close ( ) ; // Reset the found flag $ objDatabase -> query ( "UPDATE tl_files SET found=1 WHERE found=2" ) ; // Unlock the tables $ objDatabase -> unlockTables ( ) ; // Return the path to the log file return $ strLog ; }
Synchronize the file system with the database

@return string The path to the synchronization log file

@throws \Exception If a parent ID entry is missing
Synchronize the file system with the database