14 KiB
Conventions & Quality
Analysis Date: 2026-04-30 Framework: Kohana (legacy PHP MVC, version ~2.x)
Naming Conventions
Classes
- PascalCase with framework-mandated
_Controller,_Modelsuffix:- Controllers:
Page_Controller,User_Controller,Base_Admin_Controller - Models:
Page_Model,User_Model,Gallery_Model,News_Model
- Controllers:
- Abstract base controllers:
Base_Admin_Controller,Base_Front_Controller - Helper extensions:
MY_prefix on filename and class — e.g.MY_form.php→class form extends form_Core
Methods
- camelCase throughout:
uniqueKey(),uniqueKeyExists(),nameNotExists(),emailNotExists() - Exception: Kohana routing maps URL segments to method names, so public controller action methods use lowercase:
index(),login(),logout(),edit(),show(),contact(),password() - Fluent/utility methods follow framework conventions:
set_flash(),get_once()
Variables & Properties
- snake_case for local variables and view variables:
$page_view,$password_view,$path_arrow,$admin_title
- camelCase for object properties that mirror DB columns (Kohana ORM maps directly to column names):
$user->sha1_password,$user->last_success,$user->is_active
- Session data keys: lowercase with underscores —
'admin','admin_redirect','message','tiny_mce_public_html_dir'
Files
- Controllers:
lowercase.phpmatching class name without_Controllersuffixapplication/controllers/admin/page.php→Page_Controllerapplication/controllers/front/page.php→Page_Controller(different namespace via directory)
- Models:
lowercase.phpmatching class name without_Modelsuffixapplication/models/page.php→Page_Model
- Views:
lowercase_with_underscores.phpapplication/views/admin/page_edit.phpapplication/views/front/page_show.php
- Helper/library extensions:
MY_prefixapplication/helpers/MY_form.php,application/helpers/MY_html.php,application/helpers/MY_valid.phpapplication/libraries/MY_Database.php
- Layout views:
*_layout.php—default_layout.php,admin_layout.php
Database Columns (ORM)
- snake_case:
sha1_password,last_success,last_failed,password_date,is_active,parent_id,created_at
Code Style
Indentation
- 2-space indentation throughout — consistent across all application files
- Example from
application/controllers/admin/page.php:if($this->input->post()) { $page->title = $this->input->post('page_title');
Brace Style
- Allman style (opening brace on new line) for class and method bodies
- Kohana-era convention — does NOT follow PSR-2/PSR-12 which requires same-line braces
PSR Compliance
- Not PSR-12 compliant. The codebase predates PSR and uses Kohana framework's own style:
- Allman braces (PSR-12 requires same-line)
- 2-space indent (PSR-12 requires 4-space)
- No strict types declaration
- CLAUDE.md states PSR-12 as a goal — this is aspirational, not current practice
Comments
#style used for inline disabled code and short notes (Kohana convention):#$this->profiler = new Profiler(); # TODO ? zastosowac parametr GET...//style for explanatory comments:// Load the class extension/* */for blocks of temporarily disabled code- PHPDoc (
/** */) only in framework library files, not in application code - Comments explain "why" rarely — most comments are disabled code blocks
Direct Access Guard
Every PHP file starts with:
<?php defined('SYSPATH') OR die('No direct access allowed.');
Common Patterns
Controller Structure
Controllers always extend a base class (never Controller directly for app controllers):
class Page_Controller extends Base_Admin_Controller
{
public function __construct()
{
parent::__construct();
$this->view->path = 'Strony'; // breadcrumb label
}
public function edit($name = null)
{
// 1. Build view object
$page_view = new View('admin/page_edit');
// 2. Load model via ORM
$page = ORM::factory('page')->where('name', $name)->find();
// 3. Guard - 404 if not found
if (!$page->loaded) { return $this->error404(); }
// 4. Handle POST
if($this->input->post()) { ... $page->save(); url::redirect(...); }
// 5. Pass data to view, render
$page_view->page = $page;
$this->view->content = $page_view;
$this->view->render(true);
}
}
ORM / Database Queries
The app uses Kohana ORM (Active Record pattern). All queries go through ORM::factory():
// Find by primary key (integer) or unique key (string, via unique_key() override)
ORM::factory('page')->where('name', $name)->find();
ORM::factory('user')->find($this->input->post('username'));
ORM::factory('user', 1); // find by PK integer
// Save after setting properties
$page->title = $this->input->post('page_title');
$page->save();
if ($page->saved) { ... } // check success
Raw DB builder (used in install.php and models):
$this->db->set(array('col' => $val))->insert('table');
$this->db->where('id', 1)->update('user')->count();
$this->db->where($this->unique_key($value), $value)->count_records($this->table_name);
Models define uniqueness helpers to support string-based lookup:
unique_key($id)— returns column name for string vs. integer lookupunique_key_exists($value)— boolean uniqueness checkname_not_exists(),username_not_exists(),email_not_exists()— used before save
View Rendering Pattern
Two-layer view system: content view embedded in layout view.
// In controller:
$page_view = new View('admin/page_edit'); // inner/content view
$page_view->page = $page; // pass data to inner view
$this->view->content = $page_view; // embed in layout
$this->view->render(true); // render layout with output buffering
In layout view (admin_layout.php, default_layout.php):
<?php echo $content ?> // renders the inner view
Form Handling Pattern
Forms use Kohana's form:: helper class for HTML generation:
echo form::open(null, array('id' => 'edit_form'), array('id' => $page->id));
echo form::label('page_title', 'Tytuł strony: ');
echo form::input(array('name'=>'page_title', 'size'=>75, 'maxlength' => 95), $page->title);
echo form::textarea(array('name' => 'page_content', 'class' => 'mceEditor'), $page->content);
echo form::submit('save','Zapisz');
echo form::close();
POST handling in controller (pattern: check → validate → save → flash message → redirect):
if($this->input->post())
{
$page->title = $this->input->post('page_title');
$page->save();
if ($page->saved) {
$this->session->set_flash('message','Strona została zapisana.');
}
url::redirect(url::current()); // PRG pattern
}
Flash Messages
Flash messages are set in the controller and displayed in the layout:
// Set (controller):
$this->session->set_flash('message', 'Strona została zapisana.');
// Read (base controller constructor):
$this->view->message = $this->session->get('message');
// Display (layout view):
<?php if ($message): ?><div class="message"><?php echo $message ?></div><?php endif; ?>
Validation Pattern
For complex forms, Kohana Validation object is used:
$post = new Validation($this->input->post());
$post->pre_filter('trim')
->add_rules('username', 'required', 'length[3,20]', 'chars[a-zA-Z0-9_.]')
->add_rules('email', 'required', 'length[5,50]', 'valid::email')
->add_rules('password', 'required', 'length[3,40]')
->add_rules('password2', 'matches[password]');
if($post->validate()) { ... }
For simple forms (page editing), no server-side validation — JavaScript regex validation only.
URL/Routing
Routes defined in application/config/routes.php as regex → controller/method:
$config['admin/page/(.*)'] = 'admin/page/edit/$1';
$config['(.+)'] = 'front/page/$1'; // catch-all for front pages
Redirects: url::redirect('admin/login') — always relative path strings.
Error Handling
Strategy
- No try/catch in application code. Error handling is framework-delegated.
- Kohana catches exceptions via its own error handler and logs them.
- 404 is handled explicitly via controller method:
public function error404()
{
header('HTTP/1.1 404 File Not Found');
$error_view = new View('admin/error404');
$error_view->page_name = Router::$current_uri . Router::$url_suffix;
$this->view->content = $error_view;
$this->view->render(true);
}
// Magic method catches all undefined actions:
public function __call($method, $arguments)
{
return $this->error404();
}
Logging
- Kohana framework logging used in system/library code (
Kohana::log('debug', '...')) - Application log threshold set to
1(errors and exceptions only) inapplication/config/config.php - Log files written to
application/logs/ - No application-level
Kohana::log()calls in custom code
Model Not Found
Pattern: check $model->loaded (boolean) after ->find():
$page = ORM::factory('page')->where('name', $name)->find();
if (!$page->loaded) {
return $this->error404();
}
Authentication & Sessions
Auth Mechanism
Custom session-based authentication. No auth module used (Kohana auth module is commented out in config).
Login flow (application/controllers/admin/user.php):
- Find user by username via ORM
- Check
$user->is_activeflag - Compare
sha1($user->salt . $plaintext_password)against stored$user->sha1_password - On success: store user data array in session under key
'admin' - Redirect to saved
admin_redirectURL
Password storage: SHA1 with random MD5 salt — weak by modern standards (SHA1 is not suitable for passwords; bcrypt/argon2 should be used).
Auth check (in Base_Admin_Controller::__construct()):
if(!$this->session->get('admin') && Router::$method != 'login' && Router::$method != 'logout')
{
url::redirect('admin/login');
}
Admin session payload:
$admin = array(
'id' => $user->id,
'role' => $user->role,
'username' => $user->username,
'email' => $user->email,
'last_success' => $user->last_success,
'last_failed' => $user->last_failed,
);
$this->session->set('admin', $admin);
Session Configuration (application/config/session.php)
- Driver:
native(PHP native sessions) - Session name:
Frisson_session - Expiration: 1800 seconds (30 minutes)
- Validation:
user_agent(ties session to browser UA) - Encryption: disabled
- Session regeneration: disabled (
'regenerate' => 0) — CSRF risk
Redirect After Login
// Save intended URL before redirect to login:
$this->session->set('admin_redirect', url::current());
// After login, consume and redirect:
$redirect = $this->session->get_once('admin_redirect', 'admin');
url::redirect($redirect);
Security Gap: Force Login Controller
application/controllers/admin/force.php contains a Force_Controller::login() method that logs in user ID 1 without any password check. This is a development backdoor left in production code.
Testing
No test framework is configured or used.
- Kohana's
unit_testmodule exists inmodules/but is commented out inapplication/config/config.php:// MODPATH.'unit_test', // Unit testing - No test files found (no
*.test.php,*_test.php,*Test.php,*spec*files) - No PHPUnit, Pest, or any other testing tool detected
- No test directory (
tests/,spec/, etc.) exists
Current state: Zero automated test coverage.
Security Practices
Input Handling
- POST data accessed via
$this->input->post('field')— Kohana's Input class applies global XSS filtering whenglobal_xss_filtering = true(set inapplication/config/config.php):$config['global_xss_filtering'] = true; - This provides baseline XSS protection on GET/POST/SERVER data
Output Escaping
- Layout files escape page metadata with
html::specialchars():echo html::specialchars($title); echo html::specialchars($meta_description); - Content output in views is unescaped —
echo $page->content— intentional for HTML content managed via TinyMCE editor, but trusts admin input fully - Front-end user-controlled output: none currently (public site is read-only display)
SQL Injection Prevention
- ORM query builder with
'escape' => TRUEin database config handles escaping automatically - Raw
$this->db->query()calls exist only ininstall.phpwith hardcoded strings - No prepared statements with
bindParam()in app code — reliance on ORM escaping
CSRF
- No CSRF protection. No CSRF tokens on any form. Kohana's form helpers do not add tokens automatically.
- Admin panel forms are vulnerable to CSRF attacks
Cookie Security (application/config/cookie.php)
'secure' => FALSE— cookies sent over HTTP as well as HTTPS'httponly' => FALSE— cookies accessible to JavaScript (XSS risk)- Both should be
TRUEin production
Session Security
- Session ID regeneration disabled (
'regenerate' => 0) — increases session fixation risk - No HTTPS enforcement detected in application code
Database Credentials
- Plaintext credentials stored in
application/config/database.php— committed to git (critical issue)
Significant Security Issues (summary)
| Issue | Location |
|---|---|
| Development backdoor login (no password) | application/controllers/admin/force.php |
| SHA1 password hashing (not bcrypt/argon2) | application/controllers/admin/user.php |
| No CSRF tokens on admin forms | All admin views |
print_r($_POST) left in production code |
application/controllers/admin/user.php:29 |
| DB credentials in version control | application/config/database.php |
| HttpOnly and Secure cookie flags disabled | application/config/cookie.php |
| Session ID never regenerated | application/config/session.php |