Skip to content

Commit 10d0394

Browse files
authored
[k2] add file, is_file builtins (#1483)
1 parent 06291e5 commit 10d0394

File tree

6 files changed

+96
-13
lines changed

6 files changed

+96
-13
lines changed

builtin-functions/kphp-light/stdlib/file-functions.txt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,25 +45,24 @@ function vsprintf ($format ::: string, $args ::: array) ::: string;
4545

4646
function file_exists ($name ::: string) ::: bool;
4747

48-
// === UNSUPPORTED ===
48+
function file ($name ::: string) ::: string[] | false;
4949

50+
function is_file ($name ::: string) ::: bool;
51+
52+
// === UNSUPPORTED ===
5053
/** @kphp-extern-func-info stub generation-required */
5154
function chmod ($name ::: string, $mode ::: int) ::: bool;
5255
/** @kphp-extern-func-info stub generation-required */
5356
function copy ($from ::: string, $to ::: string) ::: bool;
5457
/** @kphp-extern-func-info stub generation-required */
5558
function dirname ($name ::: string) ::: string;
5659
/** @kphp-extern-func-info stub generation-required */
57-
function file ($name ::: string) ::: string[] | false;
58-
/** @kphp-extern-func-info stub generation-required */
5960
function filectime ($name ::: string) ::: int | false;
6061
/** @kphp-extern-func-info stub generation-required */
6162
function filemtime ($name ::: string) ::: int | false;
6263
/** @kphp-extern-func-info stub generation-required */
6364
function is_dir ($name ::: string) ::: bool;
6465
/** @kphp-extern-func-info stub generation-required */
65-
function is_file ($name ::: string) ::: bool;
66-
/** @kphp-extern-func-info stub generation-required */
6766
function is_readable ($name ::: string) ::: bool;
6867
/** @kphp-extern-func-info stub generation-required */
6968
function mkdir ($name ::: string, $mode ::: int = 0777, $recursive ::: bool = false) ::: bool;

runtime-light/k2-platform/k2-api.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,11 +415,18 @@ inline auto canonicalize(std::string_view path) noexcept {
415415
return return_type{{unique_ptr_type{resolved_path, std::invoke(deleter_creator, resolved_path_len, resolved_path_align)}, resolved_path_len}};
416416
}
417417

418-
inline int32_t stat(std::string_view path, struct stat* stat) noexcept {
418+
inline std::expected<void, int32_t> stat(std::string_view path, struct stat* stat) noexcept {
419419
if (auto error_code{k2_stat(path.data(), path.size(), stat)}; error_code != k2::errno_ok) [[unlikely]] {
420-
return error_code;
420+
return std::unexpected{error_code};
421421
}
422-
return k2::errno_ok;
422+
return {};
423+
}
424+
425+
inline std::expected<void, int32_t> lstat(std::string_view path, struct stat* stat) noexcept {
426+
if (auto error_code{k2_lstat(path.data(), path.size(), stat)}; error_code != k2::errno_ok) [[unlikely]] {
427+
return std::unexpected{error_code};
428+
}
429+
return {};
423430
}
424431

425432
using CommandArg = CommandArg;

runtime-light/k2-platform/k2-header.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,29 @@ int32_t k2_canonicalize(const char* path, size_t pathlen, char* const* resolved_
742742
*/
743743
int32_t k2_stat(const char* pathname, size_t pathname_len, struct stat* statbuf);
744744

745+
/**
746+
* Semantically equivalent to libc's `lstat`.
747+
*
748+
* Possible `errno`:
749+
* `EACCES` => Search permission is denied for one of the directories in the path prefix of `pathname`.
750+
* `EINVAL` => `pathname` or `statbuf` is `NULL`.
751+
* `EFAULT` => Bad address.
752+
* `ELOOP` => Too many symbolic links encountered while traversing the path.
753+
* `ENAMETOOLONG` => `pathname` is too long.
754+
* `ENOENT` => A component of `pathname` does not exist or is a dangling symbolic link.
755+
* `ENOMEM` => Out of memory (i.e., kernel memory).
756+
* `ENOTDIR` => A component of the path prefix of `pathname` is not a directory.
757+
* `EOVERFLOW` => `pathname` refers to a file whose size, inode number,
758+
* or number of blocks cannot be represented in, respectively,
759+
* the types `off_t`, `ino_t`, or `blkcnt_t`. This error can occur
760+
* when, for example, an application compiled on a 32-bit
761+
* platform without `-D_FILE_OFFSET_BITS=64` calls `lstat()` on a
762+
* file whose size exceeds `(1<<31)-1` bytes.
763+
* `ERANGE` => Failed to convert `st_size`, `st_blksize`, or `st_blocks` to `int64_t`.
764+
* `ENOSYS` => Internal error.
765+
*/
766+
int32_t k2_lstat(const char* pathname, size_t pathname_len, struct stat* statbuf);
767+
745768
struct CommandArg {
746769
const char* arg;
747770
size_t arg_len;

runtime-light/state/component-state.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ void ComponentState::parse_runtime_config_arg(std::string_view value_view) noexc
5454
}
5555

5656
struct stat stat {};
57-
if (auto error_code{k2::stat({runtime_config_path.get(), runtime_config_path_size}, std::addressof(stat))}; error_code != k2::errno_ok) [[unlikely]] {
58-
return kphp::log::warning("error getting runtime-config stat: error code -> {}", error_code);
57+
if (auto expected_stat_result{k2::stat({runtime_config_path.get(), runtime_config_path_size}, std::addressof(stat))}; !expected_stat_result.has_value())
58+
[[unlikely]] {
59+
return kphp::log::warning("error getting runtime-config stat: error code -> {}", expected_stat_result.error());
5960
}
6061

6162
const auto runtime_config_mem{std::unique_ptr<char, decltype(std::addressof(kphp::memory::script::free))>{

runtime-light/stdlib/file/file-system-functions.h

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include <unistd.h>
1717
#include <utility>
1818

19+
#include "runtime-common/core/allocator/script-allocator.h"
1920
#include "runtime-common/core/runtime-core.h"
21+
#include "runtime-common/core/std/containers.h"
2022
#include "runtime-common/stdlib/array/array-functions.h"
2123
#include "runtime-common/stdlib/string/string-functions.h"
2224
#include "runtime-light/coroutine/task.h"
@@ -61,7 +63,7 @@ inline string f$basename(const string& path, const string& suffix = {}) noexcept
6163

6264
inline Optional<int64_t> f$filesize(const string& filename) noexcept {
6365
struct stat stat {};
64-
if (auto errc{k2::stat({filename.c_str(), filename.size()}, std::addressof(stat))}; errc != k2::errno_ok) [[unlikely]] {
66+
if (auto stat_result{k2::stat({filename.c_str(), filename.size()}, std::addressof(stat))}; !stat_result.has_value()) [[unlikely]] {
6567
return false;
6668
}
6769
return static_cast<int64_t>(stat.st_size);
@@ -201,6 +203,57 @@ inline Optional<string> f$file_get_contents(const string& stream) noexcept {
201203
return false;
202204
}
203205

206+
inline Optional<array<string>> f$file(const string& name) noexcept {
207+
struct stat stat_buf {};
208+
209+
auto expected_file{kphp::fs::file::open(name.c_str(), "r")};
210+
if (!expected_file.has_value()) {
211+
return false;
212+
}
213+
if (!k2::stat(name.c_str(), std::addressof(stat_buf)).has_value()) {
214+
return false;
215+
}
216+
if (!S_ISREG(stat_buf.st_mode)) {
217+
kphp::log::warning("regular file expected as first argument in function file, \"{}\" is given", name.c_str());
218+
return false;
219+
}
220+
221+
const size_t size{static_cast<size_t>(stat_buf.st_size)};
222+
if (size > string::max_size()) {
223+
kphp::log::warning("file \"{}\" is too large", name.c_str());
224+
return false;
225+
}
226+
227+
kphp::stl::vector<std::byte, kphp::memory::script_allocator> file_content;
228+
file_content.resize(size);
229+
{
230+
auto file{std::move(*expected_file)};
231+
if (auto expected_read_result{file.read(file_content)}; !expected_read_result.has_value() || *expected_read_result < size) {
232+
return false;
233+
}
234+
}
235+
236+
array<string> result;
237+
int32_t prev{-1};
238+
for (size_t i{0}; i < size; i++) {
239+
if (static_cast<char>(file_content[i]) == '\n' || i + 1 == size) {
240+
result.push_back(string{reinterpret_cast<char*>(file_content.data()) + prev + 1, static_cast<string::size_type>(i - prev)});
241+
prev = i;
242+
}
243+
}
244+
245+
return result;
246+
}
247+
248+
inline bool f$is_file(const string& name) noexcept {
249+
struct stat stat_buf {};
250+
// TODO: the semantics in PHP are different: PHP expects stat
251+
if (!k2::lstat(name.c_str(), std::addressof(stat_buf)).has_value()) {
252+
return false;
253+
}
254+
return S_ISREG(stat_buf.st_mode);
255+
}
256+
204257
inline Optional<int64_t> f$file_put_contents(const string& stream, const mixed& content_var, int64_t flags = 0) noexcept {
205258
string content{content_var.is_array() ? f$implode(string{}, content_var.to_array()) : content_var.to_string()};
206259

runtime-light/stdlib/kml/kml-file-api.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ class dir_traverser final : public kphp::kml::dir_traverser_interface<dir_traver
120120
}
121121

122122
struct stat stat {};
123-
if (auto error_code{k2::stat(direntry_path, std::addressof(stat))}; error_code != k2::errno_ok) [[unlikely]] {
124-
kphp::log::warning("[kml] failed to get stat: error code -> {}, path -> {}", error_code, direntry_path);
123+
if (auto expected_stat_result{k2::stat(direntry_path, std::addressof(stat))}; !expected_stat_result.has_value()) [[unlikely]] {
124+
kphp::log::warning("[kml] failed to get stat: error code -> {}, path -> {}", expected_stat_result.error(), direntry_path);
125125
return std::nullopt;
126126
}
127127

0 commit comments

Comments
 (0)