Skip to content

Commit 74a2728

Browse files
moufmoufGirgias
authored andcommitted
Throw specific exceptions depending on the errno in IO.
Merges #3
1 parent c265d6a commit 74a2728

8 files changed

+122
-7
lines changed

ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt

+9-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $standard = new ReflectionExtension('standard');
88
var_dump($standard->getClassNames());
99
?>
1010
--EXPECT--
11-
array(9) {
11+
array(11) {
1212
[0]=>
1313
string(22) "__PHP_Incomplete_Class"
1414
[1]=>
@@ -18,13 +18,17 @@ array(9) {
1818
[3]=>
1919
string(15) "FileSystemError"
2020
[4]=>
21-
string(23) "InsufficientPermissions"
21+
string(12) "FileNotFound"
2222
[5]=>
23-
string(16) "TemporaryFailure"
23+
string(12) "NotDirectory"
2424
[6]=>
25-
string(15) "php_user_filter"
25+
string(23) "InsufficientPermissions"
2626
[7]=>
27-
string(9) "Directory"
27+
string(16) "TemporaryFailure"
2828
[8]=>
29+
string(15) "php_user_filter"
30+
[9]=>
31+
string(9) "Directory"
32+
[10]=>
2933
string(14) "AssertionError"
3034
}

ext/standard/dir.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ PHP_FUNCTION(chroot)
279279

280280
ret = chroot(str);
281281
if (ret != 0) {
282-
php_exception_or_warning_docref(NULL, zend_ce_filesystem_error, "%s (errno %d)", strerror(errno), errno);
282+
handle_io_error(errno, str);
283283
RETURN_FALSE;
284284
}
285285

ext/standard/io_exceptions.c

+27
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
PHPAPI zend_class_entry *zend_ce_filesystem;
2525
PHPAPI zend_class_entry *zend_ce_network;
2626
PHPAPI zend_class_entry *zend_ce_filesystem_error;
27+
PHPAPI zend_class_entry *zend_ce_file_not_found;
28+
PHPAPI zend_class_entry *zend_ce_not_directory;
2729
PHPAPI zend_class_entry *zend_ce_insufficient_permissions;
2830
PHPAPI zend_class_entry *zend_ce_temporary_failure;
2931

@@ -42,6 +44,14 @@ PHP_MINIT_FUNCTION(io_exceptions) {
4244
zend_ce_filesystem_error = zend_register_internal_class_ex(&ce, zend_ce_exception);
4345
zend_class_implements(zend_ce_filesystem_error, 1, zend_ce_filesystem);
4446

47+
INIT_CLASS_ENTRY(ce, "FileNotFound", class_FileNotFound_methods);
48+
zend_ce_file_not_found = zend_register_internal_class_ex(&ce, zend_ce_exception);
49+
zend_class_implements(zend_ce_file_not_found, 1, zend_ce_filesystem);
50+
51+
INIT_CLASS_ENTRY(ce, "NotDirectory", class_FileNotFound_methods);
52+
zend_ce_not_directory = zend_register_internal_class_ex(&ce, zend_ce_exception);
53+
zend_class_implements(zend_ce_not_directory, 1, zend_ce_filesystem);
54+
4555
INIT_CLASS_ENTRY(ce, "InsufficientPermissions", class_InsufficientPermissions_methods);
4656
zend_ce_insufficient_permissions = zend_register_internal_class_ex(&ce, zend_ce_exception);
4757
zend_class_implements(zend_ce_insufficient_permissions, 1, zend_ce_filesystem);
@@ -52,3 +62,20 @@ PHP_MINIT_FUNCTION(io_exceptions) {
5262

5363
return SUCCESS;
5464
}
65+
66+
PHPAPI void handle_io_error(int error, const char *path) {
67+
if (path == NULL) {
68+
path = "[unknown]";
69+
}
70+
switch (error) {
71+
case ENOENT:
72+
php_exception_or_warning_docref(NULL, zend_ce_file_not_found, "File not found: \"%s\"", path);
73+
break;
74+
case ENOTDIR:
75+
php_exception_or_warning_docref(NULL, zend_ce_not_directory, "\"%s\" is not a directory", path);
76+
break;
77+
default:
78+
php_exception_or_warning_docref(NULL, zend_ce_filesystem_error, "%s (path: \"%s\", errno %d)", strerror(errno), path, errno);
79+
break;
80+
}
81+
}

ext/standard/io_exceptions.h

+4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ BEGIN_EXTERN_C()
2424
extern PHPAPI zend_class_entry *zend_ce_filesystem;
2525
extern PHPAPI zend_class_entry *zend_ce_network;
2626
extern PHPAPI zend_class_entry *zend_ce_filesystem_error;
27+
extern PHPAPI zend_class_entry *zend_ce_file_not_found;
28+
extern PHPAPI zend_class_entry *zend_ce_not_directory;
2729
extern PHPAPI zend_class_entry *zend_ce_insufficient_permissions;
2830
extern PHPAPI zend_class_entry *zend_ce_temporary_failure;
2931

3032
END_EXTERN_C()
3133

34+
PHPAPI void handle_io_error(int error, const char *path);
35+
3236
#endif /* PHP_IO_EXCEPTION */

ext/standard/io_exceptions.stub.php

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ interface Network extends IO {}
99
/* Should use more specialized ones instead but for PoC will use this instead */
1010
class FileSystemError extends Exception implements FileSystem {}
1111

12+
class FileNotFound extends Exception implements FileSystem {}
13+
14+
class NotDirectory extends Exception implements FileSystem {}
15+
1216
class InsufficientPermissions extends Exception implements FileSystem {}
1317

1418
class TemporaryFailure extends Exception implements Network {}

ext/standard/io_exceptions_arginfo.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 68880a94d8393c0d367cee9e9c18c8ac6d16d9f2 */
2+
* Stub hash: c4ecc9d6fb58a424dc63d5c5a039843b89273142 */
33

44

55

@@ -19,6 +19,16 @@ static const zend_function_entry class_FileSystemError_methods[] = {
1919
};
2020

2121

22+
static const zend_function_entry class_FileNotFound_methods[] = {
23+
ZEND_FE_END
24+
};
25+
26+
27+
static const zend_function_entry class_NotDirectory_methods[] = {
28+
ZEND_FE_END
29+
};
30+
31+
2232
static const zend_function_entry class_InsufficientPermissions_methods[] = {
2333
ZEND_FE_END
2434
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Test chroot() function error conditions with throw on error declare enabled - Non-existent directory
3+
--SKIPIF--
4+
<?php
5+
if (substr(PHP_OS, 0, 3) == 'WIN') {
6+
die('skip.. Not valid for Windows');
7+
}
8+
9+
if (!function_exists('chroot')) {
10+
die('skip.. chroot() not defined in this build');
11+
}
12+
?>
13+
--FILE--
14+
<?php
15+
16+
declare(throw_on_error=1);
17+
18+
echo "*** Testing chroot() : error conditions ***\n";
19+
$directory = __DIR__ . '/idonotexist';
20+
21+
echo "\n-- Pass chroot() an absolute path that does not exist --\n";
22+
try {
23+
chroot($directory);
24+
} catch (\FileNotFound $e) {
25+
echo $e->getMessage() . \PHP_EOL;
26+
}
27+
28+
?>
29+
--EXPECTF--
30+
*** Testing chroot() : error conditions ***
31+
32+
-- Pass chroot() an absolute path that does not exist --
33+
File not found: "%sidonotexist"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Test chroot() function error conditions with throw on error declare enabled - Non-existent directory
3+
--SKIPIF--
4+
<?php
5+
if (substr(PHP_OS, 0, 3) == 'WIN') {
6+
die('skip.. Not valid for Windows');
7+
}
8+
9+
if (!function_exists('chroot')) {
10+
die('skip.. chroot() not defined in this build');
11+
}
12+
?>
13+
--FILE--
14+
<?php
15+
16+
declare(throw_on_error=1);
17+
18+
echo "*** Testing chroot() : error conditions ***\n";
19+
$directory = __FILE__;
20+
21+
echo "\n-- Pass chroot() a non directory --\n";
22+
try {
23+
chroot($directory);
24+
} catch (\NotDirectory $e) {
25+
echo $e->getMessage() . \PHP_EOL;
26+
}
27+
28+
?>
29+
--EXPECTF--
30+
*** Testing chroot() : error conditions ***
31+
32+
-- Pass chroot() a non directory --
33+
"%schroot_throw_on_error_file_in_path.php" is not a directory

0 commit comments

Comments
 (0)