Я бы создал из этого больше функций, каждая из которых выполняет отличительную работу, вероятно, инкапсулированную в класс, как
empty_directory
copy_directory
Вы все еще можете поддерживать свою единственную функцию, а затем использовать подпрограммы / объекты для обеспечения фасада в вашем приложении, например, иметь дело с исключениями для обработки ошибок и т. Д.
Рядом с этим выНеплохой код.Это рекурсивная функция для копирования данных, которая может немного нагружать файловую систему - которой, вероятно, можно пренебречь.Если вы перемещаете приличную функциональность в свои собственные единицы, вы можете со временем изменить это, если столкнетесь с реальными проблемами.
Но первое преимущество будет состоять в том, чтобы использовать исключения в подпрограммах, которые я считаю:
function backupOrRestoreWorld($source, $target)
{
empty_directory($target);
copy_directory($source, $target);
if($source == "server/world"){
return "World backed up.";
}
else {
return "World restored from backup.";
}
}
function empty_directory($path)
{
$path = rtrim($path, '/');
if (!is_dir($path))
{
throw new InvalidArgumentException(sprintf('Not a directory ("%s").', $path));
}
if (!is_writable($path))
{
throw new InvalidArgumentException(sprintf('Directory ("%s") is not a writeable.', $path));
}
$paths = glob($path.'/*.*');
if (false === $paths)
{
throw new Exception(sprintf('Unable to get path list on path "%s" (glob failed).', $path));
}
foreach ($paths as $v)
{
unlink($v);
}
}
function copy_directory($source, $target)
{
$source = rtrim($source, '/');
$target = rtrim($target, '/');
if (!is_dir($source))
{
throw new InvalidArgumentException(sprintf('Source ("%s") is not a valid directory.', $source));
}
if (!is_readable($source))
{
throw new InvalidArgumentException(sprintf('Source ("%s") is not readable.', $source));
}
if (!is_dir($target))
$r = mkdir($target);
if (!is_dir($target))
{
throw new InvalidArgumentException(sprintf('Target ("%s") is not a valid directory.', $target));
}
if (!is_writable($target))
{
throw new InvalidArgumentException(sprintf('Target ("%s") is not a writeable.', $target));
}
$dirs = array('');
while(count($dirs))
{
$dir = array_shift($dirs)
$base = $source.'/'.$dir;
$d = dir($base);
if (!$d)
{
throw new Exception(sprintf('Unable to open directory "%s".', $base));
}
while(false !== ($entry = $d->read()))
{
// skip self and parent directories
if (in_array($entry, array('.', '..'))
{
continue;
}
// put subdirectories on stack
if (is_dir($base.'/'.$entry))
{
$dirs[] = $dir.'/'.$entry;
continue;
}
// copy file
$from = $base.'/'.$entry;
$to = $target.'/'.$dir.'/'.$entry;
$result = copy($from, $to);
if (!$result)
{
throw new Exception(sprintf('Failed to copy file (from "%s" to "%s").', $from, $to);
}
}
$d->close();
}
}
В этом примере в основном представлены две функции: одна для очистки каталога, а другая - для копирования содержимого одного каталога в другой.Обе функции теперь генерируют исключения с более или менее полезным описанием того, что происходит.Я пытался выявить ошибки на ранних стадиях, проверяя входные параметры и выполняя некоторые дополнительные тесты.
Функция empty_directory
может быть немного короткой.Например, я не знаю, есть ли подкаталоги и они не пусты, если бы unlink
работал.Я оставляю это для упражнения для вас.
Функция copy_directory
работает нерекурсивно.Это делается путем предоставления стека каталогов для обработки.Если есть подкаталог, каталог помещается в стек и обрабатывается , после копируются все файлы текущего каталога.Это помогает предотвратить слишком частое переключение каталогов и, как правило, быстрее.Но, как вы видите, он очень похож на ваш код.
Так что эти функции концентрируются на работе файловой системы.Поскольку ясно, что они делают и кем они являются, вы можете сосредоточиться внутри своей функции на основной работе, например, на логике, чтобы определить, в каком направлении было выполнено копирование.Поскольку вспомогательные функции теперь генерируют исключения, вы также можете их перехватывать.Кроме того, вы можете убедиться, что $source
и $target
действительно содержат значения, которые вы явно разрешаете.Например, вы, вероятно, не хотите, чтобы внутри них были ..
или /
, а просто символы из a-z
.
Это также поможет вам найти другие причины ошибок, напримерпопытки перезаписи и т. д.:
function backupOrRestoreWorld($source, $target)
{
$basePath = 'path/to/server';
$world = 'world';
$pattern = '[a-z]+';
try
{
if (!preg_match("/^{$pattern}\$/", $source))
{
throw new InvalidArgumentException('Invalid source path.');
}
if (!preg_match("/^{$pattern}\$/", $target))
{
throw new InvalidArgumentException('Invalid target path.');
}
if ($source === $target)
{
throw new InvalidArgumentException('Can not backup or restore to itself.');
}
$targetPath = $basePath.'/'.$target;
if (is_dir($targetPath))
empty_directory($targetPath);
copy_directory($basePath.'/'.$source, $targetPath);
if($source === $world)
{
return "World backed up.";
}
else
{
return "World restored from backup.";
}
}
catch(Exception $e)
{
return 'World not backed up. Error: '.$e->getMessage();
}
}
В примере backupOrRestoreWorld
по-прежнему действует как исходная функция, но теперь возвращает сообщение об ошибке и, в частности, проверяет условия ошибки в своей собственной логике.Это немного расточительно, потому что он преобразует исключения в возвращаемое значение, которые представляют собой два вида обработки ошибок (которые вы, возможно, не захотите), но это компромисс для взаимодействия с существующим кодом (фасадом), в то же время покрывая саму проверку входных данных исключениями.
Кроме того, значения, с которыми он работает (параметры), указываются в верхней части функции, а не позже в коде функции.
Надеюсь, это поможет.Что осталось?
- Функция
copy_directory
может проверить, существует ли каталог, является ли он пустым.В противном случае он действительно не скопировал бы мир, но смешал бы два мира. - Функция
empty_directory
должна быть правильно проверена, если она действительно выполняет работу по очистке каталога в отказоустойчивой манере, особенно при работе сподкаталоги. - Вы можете принять решение об общем способе обработки ошибок в вашем приложении, чтобы вам было легче справляться с ошибками внутри приложения.
- Вы можете подумать о создании объектовиметь дело с хранением и извлечением миров, чтобы в конечном итоге их можно было легко расширять.