jaredclemence / hash-chain-file
受区块链启发的文件格式。
Requires
Requires (Dev)
- fzaninotto/faker: ^1.8@dev
This package is auto-updated.
Last update: 2024-09-18 07:24:03 UTC
README
哈希链文件旨在使用类似于区块链的头信息来按顺序链接文件版本。每个被修改的文件都存储为一个新的哈希值。文件头指向前一个文件版本。此实现将所有文件存储在同一个目录下。
优点
- HashChainFile 使用文件哈希作为文件名,这允许 更快的文件访问。请求的文件要么存在,要么不存在。不需要搜索。
- HashChainFile 将先前版本存储在单独的文件中,这 减少了当前文件版本的读取时间和传输时间。
- HashChainFile 使用 MerkleRoot 来
文件头哈希
定义头字段。
用户可以定义用于识别唯一文件的头字段。例如,在一个实现中,患者姓名、出生日期和就诊日期可以识别相关文件。一旦设置,此值不应更改。例如,在患者姓名示例中,患者更正姓名的拼写是常见的情况。在这种情况下,患者姓名将保留在文件头中原始的写法,即使它在文件正文中可能已更改。
头字段在文件初始化时使用数组定义。以下示例使用上面提到的患者姓名概念作为示例。
//This is the data that I choose to use in the header.
$originalLastName = "Javier";
$originalFirstName = "Xavier";
$originalDateOfBirth = new \DateTime( "2010-10-02 00:00:00-07:00", new \DateTimeZone("America/Los_Angeles") );
$originalDateOfVisit = new \DateTime( "2018-07-28 10:30:00-07:00", new \DateTimeZone("America/Los_Angeles") );
//I package it in an array.
$customHeader = compact( "originalLastName", "originalFirstName", "originalDateOfBirth", "originalDateOfVisit" );
//I create a new file instance.
$hashFile = new \JRC\HashChainFile\HashChainFile( $customHeader );
//All versions of this file will contain a header with the fields "originalLastName", "originalFirstName", "originalDateOfBirth", and "originalDateOfVisit".
//The original reference in previous_hash will be a 32 Byte string of null bytes.
//For newer PHP coders, this means that there will be a string where each character is "\x00" (that is one character not four---check the PHP manuals).
安全担忧
注意,文件格式不处理安全担忧。如果多个人编辑文件的旧版本然后保存,新版本的文件将指向旧版本。因此,追踪哪个文件版本反映了正确的当前版本的责任在于您。
通过指向“正确”的当前版本(“正确”由您定义),您将创建一个追踪文件历史的链。该链之外的文件版本(在区块链中称为“叔叔”)在文件迭代中找不到,因为没有前向指针。要找到叔叔,必须识别那些不被当前文件链引用的文件。
外观使用
为了使这个库更容易使用,我创建了一个外观,使它更直观。可以直接使用类,但直到您熟悉库之前,请使用外观使事情更简单。
注意:查看 FacadeTest.php 文件,了解如何使用外观与 HashChainFile 实例一起使用。
初始化外观
在下面的示例中,我们将使用一个 $facade
变量,它在此初始化
$facade = new \JRC\HashChainFile\Facade();
初始化新文件
文件作为新实例创建,或者从现有的文件内容中读取。要创建新的文件链,请以下代码作为参考
$headers = [];
$headers["Content-Type"] = "text/html";
$headers["Company"] = "Pediatric Heart Center";
//Headers can be set to any value. They are used to make empty files unique, and once
//set they cannot change.
//NOTE: If the header values are left empty, a timestamp value is set automatically to prevent duplication.
$file = $facade->makeHashFile( $header );
//new files are automatically set to be writable objects
从现有内容中读取文件
HashChainFile 内容是 BINN 格式的对象。您可以使用任何 BINN 格式解析器来解包文件。BINN 读取器将返回一个嵌套的 stdClass 对象。
为了充分利用此库,您应该使用外观将 BINN 数据打包到 HashChainFile 实例中。
//read file data into a content variable. We will use $fileContent.
$fileInstance = $facade->parseFileData( $fileContent );
//files are READONLY by default. To edit the file, use this method:
$facade->makeFileWriteable( $fileInstance );
写入文件
文件实例自动将头内容与主体内容分开。我使用魔法方法来动态捕获属性。您尝试定义的所有属性都将存储在 body
对象上,并保持与文件头分开。
简要来说:您无需担心从文件实例进行写入和读取。只需将文件视为stdClass对象即可,但请注意,您的特定类实例将被重写为stdClass对象并写入文件。所有自定义类将被移除,只将公开可访问的值写入文件。
提示:将通用数据从文件读取到您的自定义类中。保存时,使用通用对象将类写入文件。不要直接使用文件存储对象实例。
//data can be written to the file. All data written to the file is appended to the file body.
$file->content = "Some text here.";
$file->separatedContent = "Other text here.";
$file->encryptedContent = "put encrypted content here";
//these attributes are arbitrary, call them whatever you like.
$newHeaderHash = $facade->getFileReferenceId( $file );
$newFileContent = $facade->getBinaryContentForFileStorage( $file );
//Any change to the file content will produce a new hash reference...use the reference id to store the file for quick access.
//The file content generated must not be altered if it is to be used.
//Atering the file content will cause the merkle root to change, which will invalidate the file structure.
读取标题值
当您必须读取标题值时,请使用以下方法
$headerValue = $facade->getFileHeaderValue( $file, $attributeName );
获取先前文件版本的引用
文件通过其标题的哈希值相互链接。
$previousFileHash = $facade->readPreviousFileHash( $file );
$previousFileContent = ""; //load this value with the file content referenced by $previousFileHash
$previousFile = $facade->parseFileData( $previousFileContent );
$previousFileHash2 = $facade->readPreviousFileHash( $previousFile );
$nextFileContent2 = ""; //load the next value again.
//using this method, you can iterate from a current file version back through all the versions to the initial file.