Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/php-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ jobs:
if: ${{ matrix.coverage }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f
with:
file: build/logs/*.xml
files: build/logs/*.xml
flags: unit
token: ${{ secrets.CODECOV_TOKEN }}

Expand Down Expand Up @@ -158,6 +158,6 @@ jobs:
if: ${{ matrix.coverage }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f
with:
file: build/logs/*.xml
files: build/logs/*.xml
flags: phpcs-sniffs
token: ${{ secrets.CODECOV_TOKEN }}
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,9 @@ private function is_ast_valid_for_direct_access( array $ast ) {
Stmt\Enum_::class,
);

$has_assignments = false;
$has_returns = false;
$has_assignments = false;
$has_returns = false;
$has_structural_declaration = $this->has_structural_declaration( $ast );

foreach ( $ast as $node ) {
$node_class = get_class( $node );
Expand All @@ -428,6 +429,10 @@ private function is_ast_valid_for_direct_access( array $ast ) {
}

if ( $node instanceof Stmt\Expression ) {
if ( $node->expr instanceof Expr\Include_ && $has_structural_declaration ) {
continue;
}

if ( $this->is_safe_expression( $node->expr ) ) {
continue;
}
Expand Down Expand Up @@ -455,6 +460,33 @@ private function is_ast_valid_for_direct_access( array $ast ) {
return true;
}

/**
* Checks whether the AST contains structural declarations.
*
* @since n.e.x.t
*
* @param array $ast The parsed AST nodes.
* @return bool True if the AST contains class/interface/trait/enum declarations, false otherwise.
*/
private function has_structural_declaration( array $ast ) {
foreach ( $ast as $node ) {
if (
$node instanceof Stmt\Class_ ||
$node instanceof Stmt\Interface_ ||
$node instanceof Stmt\Trait_ ||
$node instanceof Stmt\Enum_
) {
return true;
}

if ( $node instanceof Stmt\Namespace_ && ! empty( $node->stmts ) ) {
return $this->has_structural_declaration( $node->stmts );
}
}

return false;
}

/**
* Checks if an expression is an asset file assignment (variable = array/string).
*
Expand Down Expand Up @@ -895,7 +927,7 @@ private function has_procedural_code( $contents ) {
private function has_only_safe_function_calls( $contents ) {
$safe_if_count = preg_match_all( '/if\s*\([^)]*(?:class_exists|function_exists|interface_exists|trait_exists|defined)\s*\(/i', $contents );
$return_count = preg_match_all( '/return\s*;/', $contents );
$all_function_calls = preg_match_all( '/\b(?!class_exists|function_exists|interface_exists|trait_exists|defined|return|if|else|elseif|isset|empty|unset|array|list|echo|print)\w+\s*\(/i', $contents );
$all_function_calls = preg_match_all( '/\b(?!class_exists|function_exists|interface_exists|trait_exists|defined|return|if|else|elseif|isset|empty|unset|array|list|echo|print|require|require_once|include|include_once)\w+\s*\(/i', $contents );

return $safe_if_count > 0 && $return_count >= $safe_if_count && 0 === $all_function_calls;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
/**
* File with require_once and class - safe for direct access.
*/

require_once __DIR__ . '/class-parent.php';

/**
* Test class.
*/
class My_Class extends Parent_Class {
/**
* Property.
*
* @var string
*/
private $property = 'test';
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public function test_run_allows_class_only_files() {
// Files with only class definitions should not produce errors.
$this->assertArrayNotHasKey( 'includes/class-only-file.php', $errors );
$this->assertArrayNotHasKey( 'includes/namespace-class-only.php', $errors );
$this->assertArrayNotHasKey( 'includes/require-once-class-only.php', $errors );
}

/**
Expand Down