Open
Description
Hi! i Just made this controller to analyze wheter a system class has dynamically declared properties. Ithink this may help to adapt CI3 to the neew PHP 8.3 without using [#AllowDynamicProperties] argument that can be depecated in future versions.
Cannot detect if a property is inherited! so in some cases, there can be false positives.
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Analyze_dynamic_properties extends CI_Controller {
public function __construct()
{
parent::__construct();
}
public function index()
{
$directory = SYSDIR;
$dynamicProperties = $this->findDynamicProperties($directory);
// Renderizar la vista directamente aquí
echo $this->renderView($dynamicProperties);
}
private function findDynamicProperties($dir)
{
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
$dynamicProperties = [];
foreach ($files as $file) {
if ($file->isDir() || $file->getExtension() !== 'php') {
continue;
}
$content = file_get_contents($file->getRealPath());
$tokens = token_get_all($content);
$classes = [];
$currentClass = '';
$collectingClass = false;
$declaredProperties = [];
for ($i = 0; $i < count($tokens); $i++) {
$token = $tokens[$i];
if (is_array($token)) {
if ($token[0] === T_CLASS) {
$collectingClass = true;
} elseif ($collectingClass && $token[0] === T_STRING) {
$currentClass = $token[1];
$classes[$currentClass] = ['file' => $file->getRealPath(), 'properties' => [], 'declared' => []];
$collectingClass = false;
} elseif ($currentClass && $token[0] === T_VARIABLE && $token[1] === '$this') {
$nextToken = $tokens[$i + 1] ?? null;
if (is_array($nextToken) && $nextToken[0] === T_OBJECT_OPERATOR) {
$propertyToken = $tokens[$i + 2] ?? null;
$assignmentToken = $tokens[$i + 4] ?? null;
if (is_array($propertyToken) && $propertyToken[0] === T_STRING && $assignmentToken === '=') {
$propertyName = $propertyToken[1];
if (!in_array($propertyName, $declaredProperties) && !in_array($propertyName, $classes[$currentClass]['declared'])) {
$classes[$currentClass]['properties'][] = [
'name' => $propertyName,
'declared' => false
];
}
}
}
} elseif ($currentClass && $token[0] === T_VARIABLE) {
$declaredProperties[] = substr($token[1], 1);
} elseif ($currentClass && $token[0] === T_PUBLIC) {
$nextToken = $tokens[$i + 2] ?? null;
if (is_array($nextToken) && $nextToken[0] === T_VARIABLE) {
$declaredPropertyName = substr($nextToken[1], 1);
$classes[$currentClass]['declared'][] = $declaredPropertyName;
$classes[$currentClass]['properties'][] = [
'name' => $declaredPropertyName,
'declared' => true
];
}
}
}
}
foreach ($classes as $className => $data) {
if (!empty($data['properties'])) {
$dynamicProperties[$className]['file'] = $data['file'];
$dynamicProperties[$className]['properties'] = $data['properties'];
}
}
}
return $dynamicProperties;
}
private function renderView($dynamicProperties)
{
ob_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PHP 8 Compatibility Analysis</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style>
.table td.success {
background-color: #dff0d8;
}
.table td.danger {
background-color: #f2dede;
}
</style>
</head>
<body>
<div class="container">
<h1>PHP 8 Compatibility Analysis</h1><hr>
<div class="btn-group" role="group" aria-label="Filter Buttons">
<button type="button" class="btn btn-success" >Good Classes</button>
<button type="button" class="btn btn-danger" >Wrong Classes</button>
<button type="button" class="btn btn-primary" >All Classes</button>
</div>
<hr>
<table class="table table-bordered table-striped" id="resultsTable">
<thead>
<tr>
<th>Class</th>
<th>File</th>
<th>Property</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($dynamicProperties as $className => $data): ?>
<?php
$classStatus = 'good';
foreach ($data['properties'] as $property):
if (!$property['declared']) {
$classStatus = 'bad';
break;
}
endforeach;
?>
<?php foreach ($data['properties'] as $index => $property): ?>
<tr class="<?= $classStatus ?>">
<?php if ($index === 0): ?>
<td rowspan="<?= count($data['properties']) ?>"><?= $className ?></td>
<td rowspan="<?= count($data['properties']) ?>"><?= $data['file'] ?></td>
<?php endif; ?>
<td class="<?= $property['declared'] ? 'success' : 'danger' ?>"><?= $property['name'] ?></td>
<td class="<?= $property['declared'] ? 'success' : 'danger' ?>">
<strong class="text-<?= $property['declared'] ? 'success' : 'danger' ?>"><?= $property['declared'] ? 'Declared =)' : 'Not Declared !!!' ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
function filterClasses(status) {
var rows = document.querySelectorAll('#resultsTable tbody tr');
rows.forEach(function (row) {
if (status === 'all') {
row.st
5258
yle.display = '';
} else if (status === 'good' && row.classList.contains('good')) {
row.style.display = '';
} else if (status === 'bad' && row.classList.contains('bad')) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
}
</script>
</body>
</html>
<?php
return ob_get_clean();
}
}
`
Metadata
Metadata
Assignees
Labels
No labels