Skip to content

opcache_is_script_cached_in_file_cache(string $filename) #16979

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
200193b
opcache_is_script_cached: file_cache option
iamacarpet Nov 28, 2024
798a07d
opcache_is_script_cached: file_cache: test
iamacarpet Nov 28, 2024
824e638
fix when file_cache_read_only
iamacarpet Nov 28, 2024
30641d6
remove file_cache_only
iamacarpet Nov 28, 2024
283844e
switch to zend_file_cache_script_validate(...)
iamacarpet Nov 29, 2024
8cc7293
fix build
iamacarpet Dec 2, 2024
fd93557
switch to opcache_is_script_cached_in_file_cache(...)
iamacarpet Dec 2, 2024
7e23285
remove double close(fd)
iamacarpet Dec 2, 2024
906b92d
zend_file_cache_open with struct
iamacarpet Dec 2, 2024
1cdf3fa
fix build
iamacarpet Dec 2, 2024
f620d9c
Unit Tests for GH-16551
iamacarpet Dec 3, 2024
6df1562
fix for file_cache_only
iamacarpet Dec 3, 2024
e282578
probe ARM test failure
iamacarpet Dec 4, 2024
17edbe6
remove unnecessary tests
iamacarpet Dec 4, 2024
cd0177f
Merge remote-tracking branch 'origin/master' into opcache/opcache_is_…
iamacarpet Feb 3, 2025
7cff70a
switch to validate_only method
iamacarpet Feb 3, 2025
59a4ecc
Merge remote-tracking branch 'origin/master' into opcache/opcache_is_…
iamacarpet Feb 20, 2025
b3b9501
zend_file_cache_script_load_ex
iamacarpet Feb 20, 2025
d1b74e9
Merge remote-tracking branch 'origin/master' into opcache/opcache_is_…
iamacarpet Apr 24, 2025
1b8311b
improve unit tests
iamacarpet Apr 24, 2025
3e573f3
Merge remote-tracking branch 'origin/master' into opcache/opcache_is_…
iamacarpet Jul 11, 2025
9346db9
reduce whitespace
iamacarpet Jul 11, 2025
f9c05a1
simplify tests & isolate to current change
iamacarpet Jul 11, 2025
d750ae0
Merge branch 'opcache/opcache_is_script_cached_file' of https://githu…
iamacarpet Jul 11, 2025
fbb4194
fix local test run
iamacarpet Jul 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ext/opcache/opcache.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ function opcache_jit_blacklist(Closure $closure): void {}
function opcache_get_configuration(): array|false {}

function opcache_is_script_cached(string $filename): bool {}

function opcache_is_script_cached_in_file_cache(string $filename): bool {}
6 changes: 5 additions & 1 deletion ext/opcache/opcache_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions ext/opcache/tests/gh16551_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
GH-16551: opcache file cache read only: generate file cache
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{TMP}"
--EXTENSIONS--
opcache
--CONFLICTS--
opcache_file_cache
--FILE--
<?php

$file = __DIR__ . '/gh16551_998.inc';
$uncached_file = __DIR__ . '/gh16551_999.inc';

opcache_compile_file($file);

var_dump(
opcache_is_script_cached($file)
);

var_dump(
opcache_is_script_cached_in_file_cache($file)
);

var_dump(
opcache_is_script_cached($uncached_file)
);

var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

?>
--EXPECT--
bool(true)
bool(true)
bool(false)
bool(false)
37 changes: 37 additions & 0 deletions ext/opcache/tests/gh16551_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
GH-16551: opcache file cache read only: read file cache
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{TMP}"
opcache.file_cache_read_only=1
--EXTENSIONS--
opcache
--CONFLICTS--
opcache_file_cache
--FILE--
<?php

$file = __DIR__ . '/gh16551_998.inc';
$uncached_file = __DIR__ . '/gh16551_999.inc';

var_dump(
opcache_is_script_cached_in_file_cache($file)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests look problematic to me. They will fail on first execution (confirmed locally).

========DIFF========
001- bool(true)
001+ bool(false)
     9
     bool(false)
     8
========DONE========
FAIL GH-16551: opcache file cache read only: read file cache with limited checks [ext/opcache/tests/gh16551_003.phpt]

========DIFF========
001- bool(true)
002- bool(true)
001+ bool(false)
002+ bool(false)
     9
========DONE========
FAIL GH-16551: opcache file cache read only: ensure we can't invalidate [ext/opcache/tests/gh16551_004.phpt]

I don't understand why they don't fail on CI. It's also possible they are order dependent, i.e. one job will prime the cache, another will check that the cache is not primed.

One solution might be to set opcache.file_cache to some sub-directory and clean it in the --CLEAN-- section to ensure a consistent state.

Copy link
Contributor Author

@iamacarpet iamacarpet Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iluuu1994 ,

It's also possible they are order dependent, i.e. one job will prime the cache, another will check that the cache is not primed.

Yes, they are order dependent - and using the conflict tag means they run one after another, even in parallel mode:

--CONFLICTS--
opcache_file_cache

However, I seem to have overlooked the fact that they expect the file-cache to be empty on first run, whereas yours already appears to be populated, potentially from a previous run - it won't be a problem on CI, as they always start with a fresh environment.

I think you are right about using --CLEAN-- - I'll look into implementing this, thank you!

EDIT: actually, no, my mistake - it's already using it's own file cache directly, as:

opcache.file_cache="{TMP}"

{TMP} should be a single-use temporary directory set up by the scripts that run the testing, so it shouldn't be pre-populated, even if you've run before on the same local machine.

We aren't cleaning it, as the script removes the temporary folder when it's finished running anyway.

I'm struggling to tell from your quoted output, but does it appear they are running out-of-order?

Copy link
Member

@iluuu1994 iluuu1994 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they are order dependent - and using the conflict tag means they run one after another, even in parallel mode:

Indeed, but these tests will still break with --shuffle. I think we should make an effort not to make tests order-dependent.

{TMP} should be a single-use temporary directory set up by the scripts that run the testing, so it shouldn't be pre-populated, even if you've run before on the same local machine.

{TMP} simply resolves to sys_get_temp_dir(), which usually is just /tmp. So it's not single-use. Actually, we depend on it not being single-use for the OPCACHE_VARIATION variation job in nightly, which runs test twice, first populating the file cache and then using it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification! I’ll shuffle things around, probably into the same test rather than multiple? And ensure we’re using a folder than is then subsequently cleaned up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind bigger tests if that's easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iluuu1994 , sorry about the delay!

I've refactored the tests following your suggestions, and they should now run in any order, without being interdependent, and support the OPCACHE_VARIATION run properly.

Do these look any better?

And if you are happy with everything, do you think there is still time for this to potentially make it into PHP 8.5?

);

require $file;

var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

require $uncached_file;

?>
--EXPECT--
bool(true)
9
bool(false)
8
41 changes: 41 additions & 0 deletions ext/opcache/tests/gh16551_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
GH-16551: opcache file cache read only: read file cache with limited checks
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{TMP}"
opcache.file_cache_read_only=1
opcache.revalidate_freq=0
opcache.enable_file_override=true
opcache.validate_timestamps=false
opcache.file_cache_consistency_checks=false
--EXTENSIONS--
opcache
--CONFLICTS--
opcache_file_cache
--FILE--
<?php

$file = __DIR__ . '/gh16551_998.inc';
$uncached_file = __DIR__ . '/gh16551_999.inc';

var_dump(
opcache_is_script_cached_in_file_cache($file)
);

require $file;

var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

require $uncached_file;

?>
--EXPECT--
bool(true)
9
bool(false)
8
36 changes: 36 additions & 0 deletions ext/opcache/tests/gh16551_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
GH-16551: opcache file cache read only: ensure we can't invalidate
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{TMP}"
opcache.file_cache_read_only=1
--EXTENSIONS--
opcache
--CONFLICTS--
opcache_file_cache
--FILE--
<?php

$file = __DIR__ . '/gh16551_998.inc';

var_dump(
opcache_is_script_cached_in_file_cache($file)
);

// invalidate SHM, but silently ignore file cache.
opcache_invalidate($file, true);

var_dump(
opcache_is_script_cached_in_file_cache($file)
);

require $file;

?>
--EXPECT--
bool(true)
bool(true)
9
45 changes: 45 additions & 0 deletions ext/opcache/tests/gh16551_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
GH-16551: opcache file cache read only: ensure cache files aren't created
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{TMP}"
opcache.file_cache_read_only=1
--EXTENSIONS--
opcache
--CONFLICTS--
opcache_file_cache
--FILE--
<?php

$uncached_file = __DIR__ . '/gh16551_999.inc';

// fix problem on ARM where it was already reporting as cached in SHM.
opcache_invalidate($uncached_file);

var_dump(
opcache_is_script_cached($uncached_file)
Copy link
Contributor Author

@iamacarpet iamacarpet Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused:

The CircleCI unit test for ARM is failing reliably on this line (it's returning true, when it should be false, as the file shouldn't have been cached yet).

image

I've added in the call to opcache_invalidate(...) above, which should remove it from the SHM if it's already there, and yet, it's still returning true.

Is this potentially a bug on ARM?

I'll sleep on it and see if I can figure out what's happening tomorrow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't re-create a problem with opcache_is_script_cached(...) in the 4 tests I created to probe this, so it's obviously something I was doing wrong.

I've removed the unnecessary tests and re-written the failing test to remove the part causing the issue.

);

var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

opcache_compile_file($uncached_file);

var_dump(
opcache_is_script_cached($uncached_file)
);

var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

?>
--EXPECT--
bool(false)
bool(false)
bool(true)
bool(false)
70 changes: 70 additions & 0 deletions ext/opcache/tests/gh16551_099.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
--TEST--
GH-16551: opcache file cache read only: double check file_cache_only
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{PWD}"
opcache.file_cache_only=1
--EXTENSIONS--
opcache
--CONFLICTS--
opcache_file_cache
--FILE--
<?php

$uncached_file = __DIR__ . '/gh16551_999.inc';

var_dump(
opcache_is_script_cached($uncached_file)
);

var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

opcache_compile_file($uncached_file);

var_dump(
opcache_is_script_cached($uncached_file)
);

// check the cache file itself exists...
if (substr(PHP_OS, 0, 3) !== 'WIN') {
$pattern = __DIR__ . '/*/' . __DIR__ . '/*16551_999.inc.bin';
} else {
$pattern = __DIR__ . '/*/*/' . str_replace(':', '', __DIR__) . '/*16551_999.inc.bin';
}
foreach (glob($pattern) as $p) {
var_dump($p !== false);
}

// check it is reported as existing...
var_dump(
opcache_is_script_cached_in_file_cache($uncached_file)
);

?>
--CLEAN--
<?php
if (substr(PHP_OS, 0, 3) !== 'WIN') {
$pattern = __DIR__ . '/*/' . __DIR__ . '/*16551*.bin';
} else {
$pattern = __DIR__ . '/*/*/' . str_replace(':', '', __DIR__) . '/*16551*.bin';
}
foreach (glob($pattern) as $p) {
unlink($p);
$p = dirname($p);
while(strlen($p) > strlen(__DIR__)) {
@rmdir($p);
$p = dirname($p);
}
}
?>
--EXPECT--
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
5 changes: 5 additions & 0 deletions ext/opcache/tests/gh16551_998.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

$a = 4+5;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this called 998? Also, why does it output 9, while 999 outputs 8. :D


echo $a . "\n";
5 changes: 5 additions & 0 deletions ext/opcache/tests/gh16551_999.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

$a = 3+5;

echo $a . "\n";
27 changes: 27 additions & 0 deletions ext/opcache/tests/gh16979.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
GH-16979: opcache_is_script_cached support file_cache argument
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.jit=disable
opcache.jit_buffer_size=0
opcache.file_cache="{TMP}"
--EXTENSIONS--
opcache
--FILE--
<?php

opcache_compile_file(__FILE__);

var_dump(
opcache_is_script_cached(__FILE__)
);

var_dump(
opcache_is_script_cached_in_file_cache(__FILE__)
);

?>
--EXPECT--
bool(true)
bool(true)
Loading
Loading