Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Improve performance of folder hashing #8856

Merged
merged 3 commits into from
Feb 22, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion system/modules/core/drivers/DC_Folder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2227,7 +2227,7 @@ public function sync()
</div>

<div class="tl_submit_container">
<a href="'.$this->getReferer(true).'" class="tl_submit" style="display:inline-block">'.$GLOBALS['TL_LANG']['MSC']['continue'].'</a>
<a href="'.$this->getReferer(true).'" class="tl_submit" style="display:inline-block">'.$GLOBALS['TL_LANG']['MSC']['backBT'].'</a>
</div>
';

Expand Down
7 changes: 3 additions & 4 deletions system/modules/core/library/Contao/Database/Updater.php
Original file line number Diff line number Diff line change
Expand Up @@ -627,13 +627,12 @@ public function scanUploadFolder($strPath=null, $pid=null)
// Folders
foreach ($arrFolders as $strFolder)
{
$objFolder = new \Folder($strFolder);
$strUuid = $this->Database->getUuid();

$this->Database->prepare("INSERT INTO tl_files (pid, tstamp, uuid, name, type, path, hash) VALUES (?, ?, ?, ?, 'folder', ?, ?)")
->execute($pid, time(), $strUuid, basename($strFolder), $strFolder, $objFolder->hash);

$this->scanUploadFolder($strFolder, $strUuid);

$this->Database->prepare("INSERT INTO tl_files (pid, tstamp, uuid, name, type, path, hash) VALUES (?, ?, ?, ?, 'folder', ?, ?)")
->execute($pid, time(), $strUuid, basename($strFolder), $strFolder, \Dbafs::getFolderHash($strFolder));
}

// Files
Expand Down
93 changes: 81 additions & 12 deletions system/modules/core/library/Contao/Dbafs.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ public static function addResource($strResource, $blnUpdateFolders=true)
$objModel->type = 'folder';
$objModel->path = $objFolder->path;
$objModel->extension = '';
$objModel->hash = $objFolder->hash;
$objModel->uuid = $objDatabase->getUuid();
$objModel->save();

Expand All @@ -197,6 +196,17 @@ public static function addResource($strResource, $blnUpdateFolders=true)
}
}

// Update folder hashes from bottom up after all file hashes are set
foreach (array_reverse($arrPaths) as $strPath)
{
if (is_dir(TL_ROOT . '/' . $strPath))
{
$objModel = \FilesModel::findByPath($strPath);
$objModel->hash = static::getFolderHash($strPath);
$objModel->save();
}
}

// Update the folder hashes
if ($blnUpdateFolders)
{
Expand Down Expand Up @@ -445,7 +455,7 @@ public static function updateFolderHashes($varResource)
$objModel = static::addResource($strPath, false);
}

$objModel->hash = $objFolder->hash;
$objModel->hash = static::getFolderHash($strPath);
$objModel->save();
}
}
Expand Down Expand Up @@ -500,6 +510,8 @@ public static function syncFiles()
$objLog->truncate();

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

// Create or update the database entries
foreach ($objFiles as $objFile)
Expand Down Expand Up @@ -584,27 +596,57 @@ public static function syncFiles()
$objModel->path = $objFolder->path;
$objModel->extension = '';
$objModel->found = 2;
$objModel->hash = $objFolder->hash;
$objModel->uuid = $objDatabase->getUuid();
$objModel->save();

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

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

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

// Update 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('');

Expand Down Expand Up @@ -708,6 +750,33 @@ public static function syncFiles()
return $strLog;
}

/**
* Get the folder hash from the databse by combining the hashes of all children
*
* @param string $strPath The relative path
*
* @return string MD5 hash
*/
public static function getFolderHash($strPath)
{
$strPath = str_replace(array('\\', '%', '_'), array('\\\\', '\\%', '\\_'), $strPath);
$arrHash = [];

$objChildren = \Database::getInstance()
->prepare("SELECT hash, name FROM tl_files WHERE path LIKE ? AND path NOT LIKE ? ORDER BY name")
->execute($strPath.'/%', $strPath.'/%/%');

if ($objChildren !== null)
{
while ($objChildren->next())
{
$arrHash[] = $objChildren->hash . $objChildren->name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why you have not added a separator here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hash has always the same length, so there is no need for a seperator.

}
}

return md5(implode("\0", $arrHash));
}


/**
* Check if the current resource should be synchronized with the database
Expand Down
4 changes: 4 additions & 0 deletions system/modules/core/library/Contao/Folder.php
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,13 @@ public function getModel()
* Return the MD5 hash of the folder
*
* @return string The MD5 has
*
* @deprecated Use Dbafs::getFolderHash() instead
*/
protected function getHash()
{
@trigger_error('Using Folder::getHash() has been deprecated and will no longer work in Contao 5.0. Use Dbafs::getFolderHash() instead.', E_USER_DEPRECATED);

$arrFiles = array();

/** @var \SplFileInfo[] $it */
Expand Down