runopencode / backup
Requires
- php: >=5.4
- psr/log: 1.*
- symfony/event-dispatcher: ~2.7.6
Requires (Dev)
- league/flysystem: 1.*
- phpunit/phpunit: ~4.8
- symfony/filesystem: ~2.3|~3.0
- symfony/finder: ~2.5|~3.0
- symfony/process: 2.*
Suggests
- league/flysystem: 1.*
- monolog/monolog: 1.*
- symfony/filesystem: ~2.3|~3.0
- symfony/finder: ~2.5|~3.0
- symfony/process: 2.*
README
此库已过时,请使用https://phpbu.de
注意: 此库是对'kbond/php-backup'库的分支,进行了重大修改,使其与原版不兼容。
备份的每个过程都可以分解为几个活动
- 从各种来源(文件系统、数据库等)准备要备份的文件
- 以某种方式处理这些文件(例如创建zip存档)并按照某些约定命名
- 确定您的备份存储系统是否有足够的备份空间,并在必要时删除旧备份
- 将新备份复制到备份目标(通常是将当前日期和时间命名的文件夹中)
考虑到上述过程,我们可以识别备份系统的几个主要部分
- 源:需要备份的文件来源。即使数据库也属于此类,因为数据库备份是一种文件。
- 备份:从源检索的文件集合。
- 处理器:在备份中处理文件(通常是将它们压缩成一个存档,但不仅限于此)。
- 命名器:根据某些期望的规则(如时间戳)为备份提供新名称。
- 轮换器:检查备份存储中的旧备份,并在违反某些轮换约束(例如备份存储的最大大小)时删除旧备份。
- 目标:备份存储的抽象,其中包含备份。
- 工作流:通过一系列备份活动执行的备份过程的抽象。此库提供默认工作流,如果默认工作流不满足您的需求,则可以替换为您自己的。
- 配置文件:所有上述内容的集合,它定义了需要备份的内容、备份的处理方式、使用的名称、存储新备份的位置以及如何轮换旧备份。
- 管理器:配置文件的集合,它提供带有记录器和事件分派器的配置文件,并执行它们。
请注意,这个关于如何分解为基本备份系统组件的绝妙想法的作者是Kevin Bond。
通过评估简单示例来查看此库是否适合您
让我们通过创建一个将被crontab
执行的应用程序来备份我们的网站。假设我们的代码在某个app.php
文件中(不要担心代码量——每一行都有注释,这样您可以轻松理解概念)。
<?php
/**
* file: app.php
*/
require_once('vendor/autoload.php'); // We are going to use composer for autoloading.
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventDispatcher;
use RunOpenCode\Backup\Source\MySqlDumpSource;
use RunOpenCode\Backup\Source\SourceCollection;
use RunOpenCode\Backup\Processor\ZipArchiveProcessor;
use RunOpenCode\Backup\Namer\Timestamp;
use RunOpenCode\Backup\Rotator\NullRotator;
use RunOpenCode\Backup\Rotator\MaxCountRotator;
use RunOpenCode\Backup\Destination\FlysystemDestination;
use RunOpenCode\Backup\Backup\Profile;
use RunOpenCode\Backup\Workflow\Workflow;
use RunOpenCode\Backup\Manager;
use League\Flysystem\Dropbox\DropboxAdapter;
use League\Flysystem\Filesystem;
use Dropbox\Client;
$logger = new NullLogger(); // Or you can use concrete logger if you like.
$eventDispatcher = new EventDispatcher(); // We need event dispatcher as well.
// Let's define source of our website files, we will use glob source to fetch files
// Array keys are path to directories where files residue, while values are path prefixes which we would like to remove
// and work only with relative paths
$files = new GlobSource(array(
'/path/to/directory/with/files' => 'path/to/directory',
'/other/path/to/directory/with/files' => 'other/path'
));
// Let's backup database as well...
$settings = array(...);
$database = new MySqlDumpSource($settings['database'], $settings['username'], $settings['password'], $settings['host'], $settings['port']);
// Our files and databases are things that we are want to backup, so we have to use source collection...
$source = new SourceCollection(array(
$files,
$database
));
// We will zip our backup files in order to save storage space
$processor = new ZipArchiveProcessor('archive.zip');
// Note that processor requires event dispatcher...
$processor->setEventDispatcher($eventDispatcher);
// Our backups will get name based on current timestamp
$namer = new Timestamp();
// We will not use pre-rotator...
$preRotator = new NullRotator();
// But we will use post-rotator... (see docs below for difference, this is just example) limiting number of backups to some number.
$postRotator = new MaxCountRotator(5);
// And let's define our backup storage, per example, Dropbox via Flysystem (which is optional).
// @see http://flysystem.thephpleague.com/adapter/dropbox/
$client = new Client($accessToken, $appSecret);
$adapter = new DropboxAdapter($client, [$prefix]);
$filesystem = new Filesystem($adapter);
$destination = new FlysystemDestination($filesystem);
// When we have components, lets define our profile:
$profile = new Profile('my-profile', $source, $processor, $namer, $preRotator, $destination, $postRotator);
// We need workflow for manager, so we will build it and provide it with logger and event dispatcher
$workflow = Workflow::build();
$workflow->setEventDispatcher($eventDispatcher);
$workflow->setLogger($logger);
// Finally, we will create manager, and feed him with profile.
$manager = new Manager($workflow, array($profile));
// And we can now execute backup process...
$manager->execute('my-profile');
此库提供了非常灵活的方式来定义和执行您的备份。然而,库旨在在框架中使用,通过具体框架的各种配置选项来简化配置文件构建过程。
请继续阅读以了解如何使用和扩展库以满足您的需求。
源和文件
源在RunOpenCode\Backup\Contract\SourceInterface
中定义,只有一个方法定义:fetch()
。源实现预期的结果是RunOpenCode\Backup\Contract\FileInterface
集合。文件接口是备份过程中要备份的文件的抽象。此库中提供了具体的实现,即RunOpenCode\Backup\Backup\File
。
备份库目前为您提供几个 SourceInterface
实现方案。
RunOpenCode\Backup\Source\NullSource
是上述接口的空实现,用于测试目的,但在生产环境中也可以与事件(稍后解释)结合使用。RunOpenCode\Backup\Source\GlobSource
从本地驱动器使用glob表达式(详见 glob)获取备份文件。RunOpenCode\Backup\Source\MySqlDumpSource
从文件中获取MySQL转储输出,并允许您备份MySQL数据库。RunOpenCode\Backup\Source\SourceCollection
是几个SourceInterface
实现的集合。它允许您一次性使用多个源进行备份配置(例如,备份您的Web应用的文件和数据库)。
备份
备份是备份作业的抽象,它是一组备份文件,并且具有其唯一名称。
处理器
通常,当我们进行备份时,我们会处理我们的备份文件(例如 - 将它们压缩成一个单独的归档)。处理器定义在 RunOpenCode\Backup\Contract\ProcessorInterface
中,并且只定义了一个方法:process(array $files)
。
处理器的作用是修改预定备份的文件集合,并返回修改后的结果文件。
备份库目前为您提供几个 ProcessorInterface
实现方案。
RunOpenCode\Backup\Processor\NullProcessor
是上述接口的空实现。它用于测试目的,然而,当您不想处理备份文件时(例如,进行增量快照备份)也可以使用。RunOpenCode\Backup\Processor\GzipArchiveProcessor
将所有备份文件压缩成一个gzip归档。需要在系统上安装gzip,并且可以通过控制台访问。RunOpenCode\Backup\Processor\ZipArchiveProcessor
将所有备份文件压缩成一个zip归档。需要在系统上安装zip,并且可以通过控制台访问。RunOpenCode\Backup\Processor\ProcessorCollection
是几个ProcessorInterface
实现的集合,允许您对备份文件执行多个连续处理活动。
命名器
您的备份文件将存储在备份存储中的一个目录中。命名器将为该目录提供名称。命名器实现 RunOpenCode\Backup\Contract\NamerInterface
,它只包含一个方法:getName()
。
备份库为您提供两个默认的命名器实现。
RunOpenCode\Backup\Namer\Constant
将始终为您的新的备份目录提供相同的名称。RunOpenCode\Backup\Namer\Timestamp
将基于当前日期和时间提供目录名称。
根据命名策略,您的备份可以得到不同的结果。使用 RunOpenCode\Backup\Namer\Constant
,您可以实现增量备份、快照备份,其行为类似于Unix工具 rsync
。使用基于时间的命名器 RunOpenCode\Backup\Namer\Timestamp
,每个新的备份都可以存储在新目录中。
但是,您可以将基于时间的命名器配置为仅使用星期几,这样您就可以仅存储最后7个备份,而无需使用轮换器。
尽管如此,建议使用命名器来定义是否为增量备份或完整备份,而轮换器应用于旧备份的轮换。
轮换器
如果满足某些条件,旋转器将提名旧备份以从备份存储中删除。旋转器由RunOpenCode\Backup\Contract\RotatorInterface
定义,并且只有一个方法:nominate(array $backups)
。旋转器不能删除旧备份,它对备份存储不了解,其在过程中的唯一作用是提名旧备份从备份列表中删除。
备份库为您提供了几个旋转器
RunOpenCode\Backup\Rotator\NullRotator
永远不会提名任何备份进行删除。它用于测试目的,但是,如果您想保留所有备份,也可以在备份过程中使用它。RunOpenCode\Backup\Rotator\MaxCountRotator
将保留最多定义的备份数量。如果备份数量超过最大允许的数量,最旧的备份将被提名进行删除。RunOpenCode\Backup\Rotator\MaxSizeRotator
将保留所有备份的最大总大小。如果备份的大小超过最大允许的大小,最旧的备份将被提名进行删除。RunOpenCode\Backup\Rotator\MinCountMaxSizeRotator
使用与MaxSizeRotator
相同的方法提名备份进行删除,但是,即使违反了最大大小约束,旋转器也会保留定义的最小数量的备份。RunOpenCode\Backup\Rotator\RotatorCollection
可用于聚合多个不同旋转器的提名。
注意,该库中默认的备份工作流程定义了预旋转和后旋转。预旋转在备份被复制到备份存储(目标)之前执行,后旋转在备份被复制到备份存储后执行。
原因在于文件系统操作不是事务性的。如果在将新备份推送到备份存储之前执行删除旧备份的操作,且复制失败,您可能会因为该操作而少一个备份。然而,如果在将新备份推送到备份存储之后执行删除旧备份的操作,且复制失败,您仍然会保留旧备份。
预旋转和后旋转应该支持系统管理员更加谨慎或不太谨慎的愿望。
目标
目标是备份存储的抽象。每个目标都实现了RunOpenCode\Backup\Contract\Destination
接口,这是该库中最复杂的组件,并具有几个职责
- 从源将备份文件复制到备份目标的相关目录中。如果该目录不为空,它将同步源文件与该目录中的文件,从而使用户能够拥有增量备份(方法:
push(BackupInterface $backup)
)。目录名称应与清理后的备份名称相同。 - 提供备份的获取器和设置器,并允许遍历现有备份(方法:
get($name)
,has($name)
,all()
,并实现接口\IteratorAggregate
和\Countable
)。 - 支持删除现有备份(方法:
delete($name)
)。
备份库为您提供了几个默认实现
RunOpenCode\Backup\Destination\NullDestination
,用于测试目的,但也可以在生产环境中与事件一起使用(将在后面解释)。RunOpenCode\Backup\Destination\LocalDestination
是本地文件系统的抽象。您可以在备份存储位于可挂载设备上时使用本地目标。LocalDestination
需要安装symfony/filesystem
。RunOpenCode\Backup\Destination\FlysystemDestination
可以与可选包league/flysystem
一起使用。您可以在这里了解更多关于Flysystem的信息。一般来说,Flysystem是各种存储系统的抽象,包括但不限于Dropbox、Azure、AWS等。
此外,您可以通过使用来为备份提供多个目标
RunOpenCode\Backup\Destination\DestinationCollection
是由多个目的地组成的集合。这将允许您拥有备份的冗余副本。请注意,如果集合中的任何目的地在push()
方法中失败,则DestinationCollection
的push()
方法将失败。RunOpenCode\Backup\Destination\ReplicatedDestination
定义了主目的地和从目的地。与ReplicatedDestination
和DestinationCollection
的区别在于,如果ReplicatedDestination::push()
方法中的从目的地失败,备份不会失败,它将被视为成功。
请注意,您可以组合 ReplicatedDestination
和 DestinationCollection
以实现各种不同的备份存储系统,从简单的到非常复杂的。
工作流程
工作流程是备份过程的抽象,是一系列需要执行的活动,以成功完成备份过程。
工作流程由 RunOpenCode\Backup\Contract\WorkflowInterface
定义,而工作流程活动由 RunOpenCode\Backup\Contract\WorkflowActivityInterface
定义。在这方面,您可以将工作流程视为活动的集合,按顺序执行。
备份库为您提供默认的工作流程实现:RunOpenCode\Backup\Workflow\Workflow
,它具有静态方法 build()
,该方法将创建以下活动的默认工作流程:
RunOpenCode\Backup\Workflow\Fetch
活动,从源获取备份文件。RunOpenCode\Backup\Workflow\Process
活动,处理备份文件。RunOpenCode\Backup\Workflow\Name
活动,备份获取其名称。RunOpenCode\Backup\Workflow\PreRotate
活动,在目的地旋转现有的旧备份。RunOpenCode\Backup\Workflow\Push
活动,将备份推送到目的地。RunOpenCode\Backup\Workflow\PostRotate
活动,在目的地旋转现有的旧备份。
请注意,您可以根据需要修改此工作流程,如果提供的不是您期望的备份工作流程。然而,这可以被认为是边缘情况。
您应该注意,默认的工作流程实现 RunOpenCode\Backup\Workflow\Workflow
依赖于 Symfony\Component\EventDispatcher\EventDispatcherInterface
和 Psr\Log\LoggerInterface
,以及提供的工作流程活动。然而,工作流程及其活动在构建过程中都不会解决这些依赖。工作流程将通过设置器在工作流程活动执行之前提供事件调度器和记录器,而工作流程应在执行之前提供。
事件
事件和 Symfony EventDispatcher 是本库与原始 kbond/php-backup
库的主要区别。在此库和默认工作流程中分发的事件在 RunOpenCode\Backup\Event\BackupEvents
中定义,而分发的事件是 RunOpenCode\Backup\Event\BackupEvent
的实例。
事件用于跟踪每个定义的备份工作流程活动,允许您
- 修改和/或过滤每个工作流程活动的结果。 例如,您可以使用
RunOpenCode\Backup\Source\NullSource
,它将返回没有文件用于备份,并通过挂钩到BackupEvents::FETCH
事件手动添加文件用于备份。在这种情况下,此库中的每个“Null”实现都有意义,并且可以与事件调度器一起在生产环境中使用。 - 释放和清理不再需要的资源。 此库组件的一些实现需要某种清理。例如,MySQL数据库的备份需要使用临时文件,当备份过程结束时,应清理临时文件。通过挂钩到
BackupEvents::TERMINATE
事件,RunOpenCode\Backup\Source\MySqlDumpSource
会在不再使用临时文件时收到通知,并可以从系统中删除。
通过使用事件分发,简化了备份组件的API - 不需要cleanUp()
方法。
重要提示:将触发一些事件,但不会触发其他事件。然而,在您的应用程序中,您始终可以依赖以下事件
- 当为某些配置文件开始备份时,将触发
BackupEvents::BEGIN
。 - 当某些配置文件终止备份时,无论其执行结果如何,都将触发
BackupEvents::TERMINATE
。使用此事件作为清理所有使用的临时文件和释放所有资源的指示器。
其他事件取决于工作流程和每个工作流程活动的结果,以及执行过程中是否存在错误。
配置文件
配置文件使用RunOpenCode\Backup\Contract\ProfileInterface
定义,而默认实现为RunOpenCode\Backup\Backup\Profile
。备份配置文件定义
- 备份文件的来源。
- 处理这些文件的处理器。
- 为每个新备份提供名称的命名器。
- 预和后旋转器,将旋转现有的旧备份。
- 备份存储的目的地。
如果您考虑您的项目,您想要备份的应用程序,您应用程序的配置文件可能是,例如
- 每小时快照 - 配置文件将每小时执行一次。
- 每日备份 - 配置文件将每天执行一次。
- 每周备份 - 配置文件将每周执行一次。
- ...
管理器
管理器使用RunOpenCode\Backup\Contract\ManagerInterface
定义,默认实现为RunOpenCode\Backup\Manager
。它包含对所有配置文件的引用,允许遍历配置文件及其执行。如果您在项目中使用依赖注入或服务定位器,则管理器应该是库的唯一公共入口点,而其他组件应作为隐藏/私有依赖项注入到管理器中。
关于EventDispatcher、Logger和抛出异常的说明
请注意,库中的一些组件依赖于事件分发器和/或记录器。然而,依赖关系不是通过构造函数提供的,而是通过设置器提供的。一些组件依赖于分发器和/或记录器,而另一些则不依赖于它们。
为了确定某个类是否依赖于分发器,您可以检查该类是否实现了RunOpenCode\Backup\Contract\EventDispatcherAwareInterface
,而记录器依赖关系可以通过检查是否实现了RunOpenCode\Backup\Contract\LoggerAwareInterface
来调查。
RunOpenCode\Backup\Contract\WorkflowInterface
和RunOpenCode\Backup\Contract\WorkflowActivityInterface
的实例根据设计依赖于记录器和分发器。它们将记录备份过程的进度并分发备份进度事件。
此库中的一些其他类依赖于事件分发器以清理临时文件和释放资源。
然而,请注意,根据设计,工作流程及其活动应记录和分发事件。其他组件应仅订阅事件。如果它们必须通知错误,则应抛出异常。
记录器和事件分发器特质
为了简化您的实现,当实现RunOpenCode\Backup\Contract\EventDispatcherAwareInterface
和RunOpenCode\Backup\Contract\LoggerAwareInterface
时,请注意
RunOpenCode\Backup\Event\EventDispatcherAwareTrait
RunOpenCode\Backup\Log\LoggerAwareTrait
都在您的 disposal。
扩展库
您需要自己的源、目标、处理器吗?您可以轻松扩展库。
实现自己的源
您的类需要实现 RunOpenCode\Backup\Contract\SourceInterface
。方法 fetch()
应返回用于备份的文件集合 RunOpenCode\Backup\Contract\File
。
请注意,每个文件都有其路径和相对路径。路径是文件的绝对路径,因此备份库可以访问它并将其复制到备份目标。然而,在备份目录中,文件将以相对路径保存。相对路径由文件的根路径确定。
实现自己的处理器
您的类需要实现 RunOpenCode\Backup\Contract\ProcessorInterface
,并包含方法 process(array $files)
。您将获得需要备份的文件集合。您的处理器应对这些文件进行处理,并返回处理后的应备份的文件集合。
实现自己的轮转器
您的类需要实现 RunOpenCode\Backup\Contract\RotatorInterface
。轮转器将获取备份目标上的备份集合,并且应该只提名哪些备份应该被删除。
实现自己的目标
您的类需要实现 RunOpenCode\Backup\Contract\Destination
。目标是 RunOpenCode\Backup\Contract\BackupInterface
的集合,需要注意的是,在物理上,对于每个备份,目标将创建一个目录,并将所有备份文件存储在该目录中。
在实现方法 push(BackupInterface $backup)
时,目标应支持创建新的备份,以及维护增量备份。这意味着如果备份目录在目标上存在,在推送时,目标应将源文件与备份目录中存在的文件同步。为了加快实现速度,RunOpenCode\Backup\Destination\BaseDestination
可供您使用。
本库遵循 MIT 许可协议,与原始库相同的许可协议。有关原始库的许可信息,请访问:https://github.com/kbond/php-backup/blob/master/LICENSE。
有关本库的许可信息,请参阅本包中提供的 LICENSE 文件。