定制 northrook/php-filesystem 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

northrook/php-filesystem

Composer 安装命令:

composer require northrook/php-filesystem

包简介

Filesystem operations with typed exceptions, atomic writes, and directory sync

README 文档

README

Filesystem operations for Northrook projects.

Implements FilesystemInterface with typed exceptions, atomic writes, directory sync, and predictable semantics around paths, symlinks, and removal.

Requires PHP >=8.4 and northrook/core-contracts.

Installation

composer require northrook/php-filesystem
use Northrook\Filesystem;

$filesystem = new Filesystem();

An optional ErrorHandlerInterface may be passed to the constructor so native calls routed through box() integrate with your application error handler.

Basic usage

$filesystem->createDirectory('/var/app/cache');
$filesystem->writeFileAtomically('/var/app/config.json', '{"enabled":true}');
$filesystem->appendToFile('/var/app/log.txt', "line\n", lock: true);

$content = $filesystem->readFile('/var/app/config.json');

$filesystem->copyFile('/var/app/config.json', '/var/app/config.backup.json');
$filesystem->move('/var/app/config.backup.json', '/var/app/archive/config.json');

$filesystem->remove('/var/app/archive');

Semantics

Exceptions

Failed operations throw Northrook\Contracts\Exceptions\FilesystemException or FileNotFoundException.

Invalid use of contract-backed helpers throw Northrook\Contracts\Exceptions\RuntimeException.

Path length

Paths are validated against MAX_PATH_LENGTH of 4094 from core-contracts.

Predicates are assertive; an overlong path throws rather than returning false, unlike PHP's native file_exists()-style functions.

Atomic writes

writeFileAtomically() writes to a temporary file in the target directory, preserves existing permissions when possible, then moves the temp file into place.

When the target path is a symlink, the link is followed to its ultimate referent (cycle-safe), and the content is written there; the symlink at the original path remains.

// Safe to call repeatedly; last write wins without a torn read.
$filesystem->writeFileAtomically($path, $json);

syncDirectory

Mirrors a source directory tree into a destination.

$alwaysOverwrite

When false (default), files are copied only if the source is newer than the destination.

$deleteMissingFiles

When true, entries present in the destination but absent from the source are removed (mirror mode).

Orphan scanning uses the destination tree separately; a custom $entries iterator only drives the source walk.

Type mismatches

If a relative path exists as different types in source and destination (e.g. file vs directory), an exception is thrown unless mirror mode removes the conflicting destination entry first.

Nested destinations

Copying is skipped when the destination lies inside the source tree.

Symlinks

Symlink targets are copied verbatim unless $copyLinksOnWindows is enabled.

// Mirror deploy: copy tree and delete files no longer in source.
$filesystem->syncDirectory($buildDir, $publicDir, deleteMissingFiles: true);

move fallbacks

move() prefers a native rename(). When that fails:

  • Symlinks the link is recreated at the target; the referent is not dereferenced.
  • Directories contents are synced via relocation aside, with rollback on failure.
  • Files copy to target, then remove the source.

Parent directories for the target are created as needed.

Unless $overwrite is true, an existing file or symlink at the target is rejected.

Symlinks: copy vs move

copyFile() and move() treat symlinks at the destination differently:

  • copyFile() writes through an existing destination symlink to its referent (same inode short-circuit applies when source and referent share an inode).
  • move() replaces a symlink entry at the target, consistent with rename() semantics rather than writing through the link.

Removal and .temp!<hash>

Removing a top-level directory may first rename it aside to .temp!<hash> in the same parent directory, freeing the original path immediately while contents are deleted recursively.

A lock file (.temp!<hash>.lock) is held for the duration of that work.

Interrupted removals or cross-device moves can leave stale .temp!<hash> directories behind.

Use purgeRelocationTempDirectories() for periodic clean-up; it respects active locks, supports a minimum age filter, and can scan recursively.

// Cron-friendly: remove relocation temps older than one hour.
$removed = $filesystem->purgeRelocationTempDirectories(
    '/var/app',
    recursive: true,
    minimumAgeSeconds: 3600,
);

Windows

Creating symbolic or hard links may require elevated privileges.

When the OS reports error code 1314, the exception message includes a plain-language hint.

  • createSymlink(..., $copyDirectoryOnWindows) optionally syncs a directory instead of creating a symlink.
  • syncDirectory(..., $copyLinksOnWindows) optionally follows symlinks when iterating the source on Windows.

Ownership and recursive chmod tests are platform-dependent; see the test suite skip conditions for what applies on Windows.

Error handler injection

use Northrook\ErrorHandler;
use Northrook\Filesystem;

$errorHandler = ErrorHandler::register( $logger );
$filesystem   = new Filesystem($errorHandler);

When an ErrorHandlerInterface is provided, fallible native calls in box() are delegated to $errorHandler->box().

Without one, a lightweight internal error handler captures the last PHP error for exception messages.

Development

composer test
composer phpstan
composer test:stress   # optional; large batch / tree scenarios

License

BSD-3-Clause. See LICENSE.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: BSD-3-Clause
  • 更新时间: 2026-07-02

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固