Introduction to Yabasi Framework
Yabasi is a powerful, flexible, and modern PHP framework designed for rapid application development. It combines the best practices of popular frameworks with innovative features to provide developers with a robust toolkit for building scalable and maintainable web applications.
Key Features
- Elegant MVC Architecture
- Powerful Routing System
- Intuitive ORM with advanced database abstraction
- Flexible Template Engine with Twig integration
- Built-in Security Features including CSRF protection and XSS prevention
- Dependency Injection Container for efficient service management
- Event-driven architecture for extensibility
- Command-line interface for automated tasks and migrations
- Comprehensive caching system
- RESTful API development tools
- WebSocket support for real-time applications
- Robust error handling and logging capabilities
Philosophy
Yabasi is built on the principles of simplicity, flexibility, and performance. It aims to provide developers with a clean and intuitive API while offering the power and features needed for complex applications. The framework encourages best practices in software design and promotes code reusability and maintainability.
Community and Ecosystem
Yabasi boasts a growing community of developers and a rich ecosystem of packages and extensions. The framework is open-source, encouraging collaboration and continuous improvement. Whether you're building a simple website or a complex enterprise application, Yabasi provides the tools and support you need to succeed.
Who is Yabasi For?
Yabasi is designed for developers of all skill levels:
- Beginners will appreciate its clear documentation and gentle learning curve
- Experienced developers will love its powerful features and flexibility
- Teams will benefit from its emphasis on clean architecture and maintainable code
Getting Started
Ready to dive in? The next sections will guide you through installing Yabasi and creating your first application. Whether you're new to PHP frameworks or an experienced developer, you'll find Yabasi intuitive and powerful.
Getting Started with Yabasi
This section will guide you through the process of setting up your first Yabasi project. We'll cover installation, configuration, and creating a basic application.
System Requirements
Before installing Yabasi, ensure your system meets the following requirements:
- PHP >= 8.1
- Composer
- PDO PHP Extension
- OpenSSL PHP Extension
- Mbstring PHP Extension
- Tokenizer PHP Extension
Installation
Yabasi utilizes Composer for dependency management. To create a new Yabasi project, run the following command in your terminal:
composer
create-project
yabasi/yabasi
your-project-name
This command will create a new Yabasi project in the `your-project-name` directory.
Configuration
After installation, you need to configure your application:
- Rename the `.env.example` file to `.env`
- Open the `.env` file and update the database credentials and other configuration options
- Generate an application key by running:
php
yabasi
key:generate
Directory Structure
Familiarize yourself with the Yabasi directory structure:
app/
- Contains the core code of your applicationconfig/
- Houses all of your application's configuration filespublic/
- The public directory contains the index.php file, which is the entry point for all requestsresources/
- Contains your views and raw, un-compiled assetsroutes/
- Contains all of the route definitions for your applicationstorage/
- Contains compiled Twig templates, file based sessions, file caches and other files generated by the frameworktests/
- Contains your automated testsvendor/
- Contains your Composer dependencies
Creating Your First Route
Let's create a simple route to test our installation. Open the `routes/web.php` file and add the following code:
use Yabasi\Routing\Router;
$router->get('/', function() {
return 'Hello, Yabasi!';
});
Running Your Application
You can use PHP's built-in server to run your application locally. Navigate to your project directory in the terminal and run:
php
-S
localhost:8000
-t
public
Now, open your web browser and visit `http://localhost:8000`. You should see "Hello, Yabasi!" displayed.
Next Steps
Congratulations! You've successfully set up your first Yabasi application. From here, you can:
- Explore the Routing system
- Learn about Controllers
- Dive into Views and templating
- Understand Models and database interactions
The following sections will guide you through these topics and more, helping you harness the full power of Yabasi framework.
Core Concepts
Yabasi Framework is built on several core concepts that form the foundation of its architecture and functionality. Understanding these concepts is crucial for effectively using the framework.
MVC Architecture
Yabasi follows the Model-View-Controller (MVC) architectural pattern:
- Models: Represent your data structure and handle database interactions.
- Views: Handle the presentation logic and render the user interface.
- Controllers: Act as intermediaries between Models and Views, processing requests and managing application logic.
Dependency Injection
Yabasi uses a powerful Dependency Injection Container to manage class dependencies and perform dependency injection, promoting loose coupling and easier testing.
Service Providers
Service Providers are the central place of all Yabasi application bootstrapping. They allow you to register services, event listeners, middleware, and even routes within your application.
Middleware
Middleware provide a convenient mechanism for filtering HTTP requests entering your application, allowing you to perform actions before a request reaches your route or controller.
ORM (Object-Relational Mapping)
Yabasi includes a powerful ORM that makes it easy to interact with your database using eloquent, intuitive syntax, without writing raw SQL queries.
Event System
The event system allows you to subscribe and listen to various events that occur within your application, providing a great way to decouple various aspects of your application.
Facades
Facades provide a static interface to classes that are available in the application's service container, offering the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
As you delve deeper into Yabasi, you'll see how these core concepts work together to create a robust, flexible framework for your web applications.
Routing
Routing is a core feature of Yabasi that allows you to map URLs to specific parts of your application. Yabasi provides a powerful and flexible routing system that can handle simple routes to complex patterns with ease.
Basic Routing
The most basic Yabasi routes accept a URI and a closure:
use Yabasi\Routing\Router;
$router->get('/greeting', function () {
return 'Hello, World!';
});
Available Router Methods
The router provides methods for all common HTTP verbs:
$router->get($uri, $callback);
$router->post($uri, $callback);
$router->put($uri, $callback);
$router->patch($uri, $callback);
$router->delete($uri, $callback);
$router->options($uri, $callback);
Route Groups
Route groups allow you to share route attributes across a large number of routes without needing to define those attributes on each individual route:
$router->group(['middleware' => [SessionMiddleware::class]], function ($router) {
$router->get('/dashboard', 'DashboardController@index');
$router->get('/account', 'AccountController@show');
});
Form Method Spoofing
HTML forms don't support PUT, PATCH or DELETE actions. To use these verb, add a hidden _method field to your form:
<form action="/foo/bar" method="POST">
<input type="hidden" name="_method" value="PUT">
{{ csrf_field()|raw }}
</form>
The routing system in Yabasi is both powerful and flexible, allowing you to structure your application's URLs in a way that makes sense for your project. As you build more complex applications, you'll find that mastering the routing system is key to creating clean, maintainable code.
Controllers
Controllers are a fundamental part of Yabasi's MVC architecture. They handle incoming HTTP requests, interact with models, and return responses. Let's dive deep into how controllers work in Yabasi.
Basic Controller Structure
Here's a basic example of a controller in Yabasi:
namespace Yabasi\Controllers;
use Exception;
use Yabasi\Container\Container;
use Yabasi\Controller\Controller;
use Yabasi\Http\Request;
use Yabasi\Http\Response;
use Yabasi\Logging\Logger;
use Yabasi\Models\Users;
use Yabasi\Requests\UserRegistrationRequest;
use Yabasi\Session\SessionManager;
class UserController extends Controller
{
protected SessionManager $session;
protected Logger $logger;
public function __construct(Container $container)
{
parent::__construct($container);
$this->logger = $this->container->get(Logger::class);
}
public function index(Request $request): Response
{
// Fetch users from the database
$users = Users::all();
// Return a view with the users
return $this->view('users', ['users' => $users]);
}
public function store(UserRegistrationRequest $request): Response
{
// Early return if validation fails
if (!$request->validate()) {
return (new Response())->redirect('/register');
}
// Input validation
$name = trim($request->input('name'));
$email = filter_var($request->input('email'), FILTER_SANITIZE_EMAIL);
$password = $request->input('password');
// Additional input checks
if (empty($name) || empty($email) || empty($password)) {
$this->session->flash('error', 'All fields are required.');
$this->session->flash('_old_input', $request->except(['password']));
return (new Response())->redirect('/register');
}
// Email format check
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->session->flash('error', 'Invalid email format.');
$this->session->flash('_old_input', $request->except(['password']));
return (new Response())->redirect('/register');
}
try {
// Check if email already exists
$existingUser = Users::query()->where('email', $email)->first();
if ($existingUser) {
$this->session->flash('error', 'Email already registered.');
$this->session->flash('_old_input', $request->except(['password']));
return (new Response())->redirect('/register');
}
// Create new user
$user = new Users();
$user->setName($request->input('name'));
$user->setEmail($request->input('email'));
$user->setPassword(password_hash($request->input('password'), PASSWORD_DEFAULT));
// Save user
if ($user->save()) {
return (new Response())->redirect('/');
} else {
throw new Exception("Failed to save user");
}
} catch (Exception $e) {
// Log the error with more details
$this->logger->error("User registration failed", [
'error' => $e->getMessage(),
'email' => $email,
'trace' => $e->getTraceAsString()
]);
// User friendly error message
$this->session->flash('error', 'Registration failed. Please try again later.');
return (new Response())->redirect('/register');
}
}
}
Controller Methods
Controller methods typically correspond to HTTP verbs and URIs. Common method names include:
index()
- Display a list of resourcesshow($id)
- Display a specific resourcecreate()
- Show a form to create a new resourcestore(Request $request)
- Store a new resourceedit($id)
- Show a form to edit an existing resourceupdate(Request $request, $id)
- Update an existing resourcedestroy($id)
- Delete a resource
Dependency Injection
Yabasi's controllers support automatic dependency injection. You can type-hint any dependencies in your controller's constructor or methods:
public function __construct(protected UserService $userService)
{
// UserService is automatically injected
}
public function show(Request $request, $id, Logger $logger)
{
// Request and Logger are automatically injected
$logger->info("Showing user with ID: {$id}");
$user = $this->userService->find($id);
return $this->view('users.show', ['user' => $user]);
}
Resource Controllers
Yabasi provides a convenient way to create resource controllers that handle all CRUD operations for a resource:
php
yabasi
make:controller
UserController
--resource
This command creates a controller with all the necessary methods for handling a resource.
Controller Middleware
You can apply middleware to your controllers using the middleware()
method:
public function __construct()
{
$this->middleware('auth')->only(['store', 'update', 'destroy']);
$this->middleware('log')->except('index');
}
This allows you to filter HTTP requests entering your application before they reach your controller methods.
Controllers are a crucial part of your Yabasi application, handling the core logic of your requests and responses. As you become more familiar with controllers, you'll be able to structure your application logic in a clean, organized manner.
Views
Views in Yabasi are responsible for containing the HTML served by your application. They separate your controller and application logic from your presentation logic, promoting cleaner, more maintainable code.
Creating Views
Views are stored in the resources/views
directory. A simple view might look like this:
<html>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
Returning Views from Controllers
You can return a view from your controller using the view()
helper method:
public function show($id)
{
$user = User::find($id);
return $this->view('user.profile', ['user' => $user]);
}
Passing Data to Views
You can pass data to views as an array:
return $this->view('user.profile', [
'user' => User::find($id),
'articles' => Article::latest()->get()
]);
Twig Templating Engine
Yabasi uses the Twig templating engine, which provides a powerful set of features for creating views:
Variables
{{ variable }}
{{ user.name }}
{{ user['name'] }}
Control Structures
{% if user %}
Hello, {{ user.name }}!
{% endif %}
{% for user in users %}
<li>{{ user.name }}</li>
{% endfor %}
Template Inheritance
Twig allows you to build a base "skeleton" template that contains all the common elements of your site:
<!-- layout.twig -->
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
<!-- child.twig -->
{% extends "layout.twig" %}
{% block title %}My Page{% endblock %}
{% block content %}
<h1>Welcome to my page!</h1>
{% endblock %}
View Composers
View composers are callbacks or class methods that are called when a view is rendered. They allow you to bind data to a view each time it's rendered:
View::composer('user.profile', function($view) {
$view->with('count', User::count());
});
Optimization
Yabasi can cache your views for faster rendering. To cache all your views, run:
php
yabasi
view:cache
To clear the view cache:
php
yabasi
view:clear
Custom Twig Extensions
Yabasi allows you to create custom Twig extensions to add extra functionality to your views:
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class MyCustomExtension extends AbstractExtension
{
public function getFilters()
{
return [
new TwigFilter('price', [$this, 'formatPrice']),
];
}
public function formatPrice($number, $decimals = 2, $decPoint = '.', $thousandsSep = ',')
{
return '$' . number_format($number, $decimals, $decPoint, $thousandsSep);
}
}
// In a service provider:
$this->app->singleton(Twig\Environment::class, function ($app) {
$twig = new Twig\Environment(...);
$twig->addExtension(new MyCustomExtension());
return $twig;
});
Views are an essential part of any web application, and Yabasi provides a flexible and powerful system for managing your application's presentation layer. By leveraging Twig's features and Yabasi's view helpers, you can create complex, dynamic user interfaces with ease.
Models
Models in Yabasi represent the data structure and business logic of your application. They interact with the database, handle relationships between different entities, and provide an elegant way to work with your data.
Defining Models
To create a model, extend the Yabasi\Database\Model
class:
namespace Yabasi\Models;
use Yabasi\Database\Model;
class User extends Model
{
protected static string $table = 'users';
protected array $fillable = ['name', 'email', 'password'];
}
Creating and Saving Models
You can create and save model instances like this:
$user = new User();
$user->setName("John Doe");
$user->setEmail("john@example.com");
$user->setPassword("secret");
$result = $user->save();
echo "User save result: " . ($result ? "true" : "false");
Retrieving Models
Yabasi provides various methods to retrieve models:
// Find by ID
$user = User::find(1);
// Find by email
$user = User::query()->where('email', 'john@example.com')->first();
// Get all users
$allUsers = User::all();
// Count users
$userCount = User::count();
Updating Models
You can update model attributes and save changes:
$user = User::find(1);
$user->setName("John Updated");
$updateResult = $user->save();
echo "User update result: " . ($updateResult ? "true" : "false");
Deleting Models
To delete a model:
$user = User::find(1);
$deleteResult = $user->delete();
Query Building
Yabasi provides a fluent query builder:
$recentUsers = User::query()
->where('name', 'LIKE', 'John%')
->orderBy('created_at', 'DESC')
->limit(5)
->get();
Relationships
Yabasi supports various types of relationships between models, allowing you to easily work with related data:
One-to-Many Relationship
class User extends Model
{
public function posts() {
return $this->hasMany(Post::class);
}
}
Belongs-To Relationship
class Post extends Model
{
public function author() {
return $this->belongsTo(User::class);
}
}
Using Relationships
You can easily access related models:
$user = User::find(1);
$posts = $user->posts;
foreach ($posts as $post) {
echo $post->title . " by " . $post->author->name . "\n";
}
$post = Post::find(1);
echo "Author: " . $post->author->name;
Eager Loading
To avoid the N+1 query problem, you can use eager loading:
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->name . " has " . $user->posts->count() . " posts.\n";
}
Mass Assignment
Yabasi allows you to set multiple attributes at once using the fill()
method:
$user = new User();
$user->fill([
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'password' => 'secret'
]);
$result = $user->save();
echo "User save result: " . ($result ? "true" : "false");
Accessors and Mutators
You can define accessors and mutators to format attribute values when retrieving or setting them:
class User extends Model
{
public function getName(): string
{
return ucfirst($this->attributes['name']);
}
public function setPassword($value): void
{
$this->attributes['password'] = password_hash($value, PASSWORD_DEFAULT);
}
}
These features of Yabasi's ORM allow you to work with your database in an object-oriented manner, making your code more readable and maintainable. The relationships feature, in particular, helps you to efficiently work with related data across multiple tables.
Database
Yabasi provides a robust and flexible database layer, supporting various database systems and offering an intuitive API for database operations.
Configuration
Database configuration is typically stored in the config/config.php
file:
return [
'database' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'yabasi_db',
'username' => 'root',
'password' => '',
],
];
Query Builder
Yabasi's query builder provides a convenient, fluent interface for creating and running database queries:
$users = User::query()
->where('active', true)
->where('age', '>', 18)
->orderBy('name', 'asc')
->limit(10)
->get();
Raw Queries
For more complex queries, you can use raw SQL:
$results = DB::select('SELECT * FROM users WHERE active = ?', [true]);
$affected = DB::update('UPDATE users SET status = ? WHERE id = ?', ['active', 1]);
Transactions
Yabasi supports database transactions to ensure data integrity:
DB::beginTransaction();
try {
DB::update('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1]);
DB::update('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 2]);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
Migrations
Yabasi provides a powerful migration system for managing database schema:
use Yabasi\Database\Schema\Blueprint;
use Yabasi\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
}
These features provide a solid foundation for working with databases in your Yabasi applications. The query builder and ORM work together to offer a powerful yet intuitive interface for database operations, while migrations ensure that your database schema can be easily version-controlled and shared across different environments.
Forms & Validation
Yabasi framework provides a powerful and flexible system for handling forms and validating data. This system allows you to process and validate user inputs securely and efficiently.
Creating Forms
In Yabasi, forms are typically created within Twig templates. Here's an example of a simple form:
<form method="POST" action="{{ url('user.store') }}">
{{ csrf_field() }}
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="{{ old('name') }}">
</div>
<button type="submit">Submit</button>
</form>
Handling Form Submissions
Form submissions are typically handled in controller methods. Here's an example of how to handle a form submission:
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
]);
// Process the validated data
User::create($validated);
}
Validation
Yabasi provides a powerful validation system. You can define validation rules for your form data in various ways:
Using the Validator Class
use Yabasi\Validation\Validator;
public function store(Request $request, Validator $validator)
{
$rules = [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
];
if (!$validator->make($request->all(), $rules)) {
return redirect()->back()->withErrors($validator)->withInput();
}
// Process validated data
}
Form Request Validation
For more complex validation scenarios, you can create dedicated Form Request classes:
use Yabasi\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
];
}
}
Then, you can use this Form Request in your controller:
public function store(StoreUserRequest $request)
{
User::create($request->validated());
}
Displaying Validation Errors
Validation errors can be displayed in your Twig templates:
{% if errors.has('name') %}
<div class="error">
{{ errors.first('name') }}
</div>
{% endif %}
Yabasi's form and validation system provides a robust way to handle user input, ensuring data integrity and improving the overall security of your application.
Authentication
Yabasi framework provides a robust and flexible authentication system. It allows you to easily implement user authentication in your applications.
Configuration
Authentication configuration is typically stored in the config/config.php
file:
return [
'auth' => [
'model' => Yabasi\Models\User::class,
'table' => 'users',
'username' => 'email',
'password' => 'password',
],
];
User Registration
Here's an example of how to implement user registration:
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create($validated);
Auth::login($user);
return redirect()->intended('/dashboard');
}
User Login
Here's an example of how to implement user login:
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('/dashboard');
}
return back()->withErrors(['email' => 'The provided credentials do not match our records.']);
}
Middleware
Yabasi provides middleware to protect routes that require authentication:
$router->group(['middleware' => ['auth']], function ($router) {
$router->get('/dashboard', 'DashboardController@index');
$router->get('/profile', 'ProfileController@show');
// Add more protected routes here
});
Yabasi's authentication system provides a secure and flexible way to manage user authentication in your application. It integrates seamlessly with the framework's other features like middleware and session management.
Caching
Yabasi framework provides a powerful and flexible caching system to improve your application's performance. The caching system supports multiple cache drivers and offers a clean, unified API.
Configuration
Caching configuration is typically stored in the config/config.php
file:
return [
'cache' => [
'default' => 'file', // Primary cache store
'path' => BASE_PATH . '/storage/cache',
'stores' => [
'file' => [
'driver' => 'file',
'path' => BASE_PATH . '/storage/cache/data',
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
],
],
];
Basic Usage
Here are some basic operations you can perform with the Yabasi caching system:
Setting and Getting Cache Values
// Set a value in the cache for 1 hour
Cache::set('user_count', 100, 3600);
// Get a value from the cache
$userCount = Cache::get('user_count', 0);
Checking for Existence and Removing Cache Items
// Check if a key exists in the cache
if (Cache::has('user_count')) {
echo "User count is cached.";
}
// Remove an item from the cache
Cache::delete('user_count');
Incrementing and Decrementing Values
// Increment a value
$newCount = Cache::increment('user_count', 5);
// Decrement a value
$decrementedCount = Cache::decrement('user_count', 2);
Working with Multiple Cache Items
// Set multiple values
Cache::setMany([
'total_posts' => 1000,
'total_comments' => 5000
], 1800); // Cache for 30 minutes
// Get multiple values
$stats = Cache::many(['total_posts', 'total_comments', 'nonexistent_key']);
// Delete multiple values
Cache::deleteMany(['total_posts', 'total_comments']);
Advanced Usage
Using Different Cache Stores
You can specify which cache store to use for a particular operation:
$value = Cache::store('redis')->get('key');
Cache::store('file')->put('key', 'value', 600);
Atomic Locks
Yabasi provides atomic locks to manage concurrent access to resources:
$lock = Cache::lock('processing-report', 10);
if ($lock->get()) {
// Process the report...
$lock->release();
} else {
// Could not obtain lock...
return 'Try again later.';
}
Cache Tags
Cache tags allow you to tag related items in the cache and then flush all cached values tagged with a given name:
Cache::tags(['people', 'artists'])->put('John', $johnData, 3600);
Cache::tags(['people', 'authors'])->put('Anne', $anneData, 3600);
$john = Cache::tags(['people', 'artists'])->get('John');
$anne = Cache::tags(['people', 'authors'])->get('Anne');
// Remove all entries tagged with 'people'
Cache::tags('people')->flush();
Cache Helpers
Yabasi provides several helper functions to make working with the cache easier:
// Cache a value if it doesn't exist
$value = Cache::remember('key', 3600, function () {
return computeExpensiveValue();
});
// Cache a value forever
Cache::forever('key', 'value');
// Get and forget a value
$value = Cache::pull('key');
// Clear all items from the cache
Cache::flush();
Best Practices
- Use meaningful keys for your cached items to avoid conflicts.
- Set appropriate TTL (Time To Live) values for your cached data to ensure freshness.
- Use cache tags to organize and manage related cache entries.
- Be mindful of cache size, especially when using file-based caching.
- Use atomic locks for operations that require exclusive access.
- Implement cache warming strategies for critical data.
Yabasi's caching system provides a powerful tool to improve your application's performance. By strategically caching data and computationally expensive operations, you can significantly reduce load times and server load.
Session Management
Yabasi framework provides a robust and flexible session management system. It allows you to easily handle user sessions, store and retrieve data, and implement security measures.
Configuration
Session configuration is typically stored in the config/config.php
file:
return [
'session' => [
'driver' => 'file',
'lifetime' => 120,
'path' => '/tmp',
'domain' => null,
'secure' => false,
'httponly' => true,
],
];
Basic Usage
Here are some basic operations you can perform with the Yabasi session management system:
Starting a Session
use Yabasi\Session\SessionManager;
$sessionManager = new SessionManager($config, $securityHandler);
$sessionManager->start();
Setting and Getting Session Data
// Set a session value
$sessionManager->set('user_id', 123);
// Get a session value
$userId = $sessionManager->get('user_id');
Checking and Removing Session Data
if ($sessionManager->has('user_id')) {
// The 'user_id' exists in the session
$sessionManager->remove('user_id');
}
Advanced Features
Flash Messages
Flash messages are temporary session data, typically used for one-time notifications:
// Set a flash message
$sessionManager->flash('success', 'Operation completed successfully.');
// Retrieve a flash message
$flashMessage = $sessionManager->getFlash('success');
Session Security
Yabasi provides built-in security features for sessions:
use Yabasi\Session\SecurityHandler;
$securityHandler = new SecurityHandler();
$securityHandler->setSessionIdentifiers();
SecurityHandler::preventSessionFixation();
Best Practices
- Always start the session before using any session-related functions.
- Use flash messages for temporary data that should only be available for the next request.
- Implement proper session security measures to prevent attacks like session fixation.
- Regularly regenerate session IDs to enhance security.
- Clear sensitive session data when it's no longer needed.
Yabasi's session management system provides a secure and flexible way to handle user sessions in your application. By leveraging these features, you can create robust and secure web applications.
Error Handling & Logging
Yabasi framework provides robust error handling and logging capabilities to help you manage and debug your application effectively.
Error Handling
Yabasi uses a custom exception handler to manage errors gracefully. Here's how you can configure and use it:
use Yabasi\Logging\CustomExceptionHandler;
$config = $this->container->get('config');
$this->exceptionHandler = new CustomExceptionHandler(
$config->get('app.debug', false)
);
Handling Exceptions
You can use the custom exception handler to manage exceptions in your application:
public function exampleAction()
{
try {
// Your code here
} catch (\Exception $e) {
$this->exceptionHandler->handle($e);
}
}
Logging
Yabasi provides a powerful logging system through the Logger class. Here's how to use it:
use Yabasi\Logging\Logger;
$logger = new Logger($config);
$logger->info('This is an informational message');
$logger->warning('This is a warning');
$logger->error('An error occurred', ['error_code' => 500]);
$logger->critical('A critical error occurred');
Log Levels
Yabasi supports various log levels, allowing you to categorize your log messages:
- emergency
- alert
- critical
- error
- warning
- notice
- info
- debug
Custom Log Files
You can create custom log files for different parts of your application:
$apiLogger = new Logger($config, 'api');
$apiLogger->info('API request received', ['endpoint' => '/users']);
Best Practices
- Use appropriate log levels to categorize your messages.
- Include relevant context data in your log messages.
- Implement proper error handling to catch and log exceptions.
- Regularly review and analyze your log files.
- Use custom log files for different components or services in your application.
- Configure log rotation to manage log file sizes.
Proper error handling and logging are crucial for maintaining and debugging your Yabasi application. By leveraging these features, you can quickly identify and resolve issues, improving the overall reliability of your application.
Security
Yabasi framework provides robust security features to help protect your application against common web vulnerabilities. This section covers the key security components and best practices.
CSRF Protection
Cross-Site Request Forgery (CSRF) protection is built into Yabasi to prevent unauthorized commands from being transmitted from a user that the web application trusts.
Generating CSRF Tokens
use Yabasi\Security\CsrfProtection;
$csrfProtection = new CsrfProtection($session);
$token = $csrfProtection->generateToken();
Validating CSRF Tokens
public function handle(Request $request, Closure $next): Response
{
$token = $this->csrfProtection->getTokenFromRequest($request);
if (!$token || !$this->csrfProtection->validateToken($token)) {
return new Response('CSRF token mismatch', 403);
}
return $next($request);
}
XSS Protection
Yabasi includes built-in protection against Cross-Site Scripting (XSS) attacks by automatically escaping output.
$userInput = "<script>alert('XSS');</script>";
$cleanInput = XssProtection::clean($userInput);
// Output: <script>alert('XSS');</script>
Session Security
Yabasi provides robust session security features to protect against session hijacking and fixation attacks.
Secure Session Configuration
SecurityHandler::setSecureCookieParams();
SecurityHandler::preventSessionFixation();
Session Validation
$securityHandler = new SecurityHandler();
if (!$securityHandler->validateSession()) {
// Session is invalid, handle accordingly
$session->regenerate();
}
Password Hashing
Yabasi uses secure password hashing by default when working with user models.
public function setPassword($value): void
{
$this->attributes['password'] = password_hash($value, PASSWORD_DEFAULT);
}
Database Security
Yabasi uses prepared statements to prevent SQL injection attacks.
$users = User::query()
->where('email', '=', $email)
->get();
API Security
For API security, Yabasi includes rate limiting to prevent abuse.
CLI & Artisan
Yabasi framework provides a powerful Command Line Interface (CLI) tool, similar to Laravel's Artisan, to help you manage and interact with your application. This tool simplifies many common tasks and allows you to create custom commands for your specific needs.
1. Yabasi CLI Overview
The Yabasi CLI is a versatile tool that automates many development tasks, from generating boilerplate code to managing database migrations. To use the Yabasi CLI, you typically run commands in your terminal from your project's root directory.
php
yabasi
command:name
2. Basic Commands
Yabasi comes with several built-in commands to help you generate common components of your application:
make:controller
Creates a new controller class.
php
yabasi
make:controller
UserController
make:model
Generates a new model class.
php
yabasi
make:model
User
make:middleware
Creates a new middleware class.
php
yabasi
make:middleware
AuthMiddleware
make:migration
Generates a new database migration file.
php
yabasi
make:migration
create_users_table
3. Database Commands
Yabasi provides several commands to manage your database migrations:
migrate
: Run all pending migrationsmigrate:rollback
: Rollback the last database migrationmigrate:reset
: Rollback all database migrationsmigrate:refresh
: Reset and re-run all migrationsmigrate:status
: Show the status of each migrationdb:dump
: Create a database dumpdb:restore
: Restore a database from a dump
4. Creating Custom Commands
You can create your own custom commands in Yabasi. Here's an example of how to structure a custom command:
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CustomCommand extends Command
{
protected static $defaultName = 'app:custom-command';
protected function configure()
{
$this->setDescription('A custom command description');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Custom command executed!');
return Command::SUCCESS;
}
}
After creating your custom command, you need to register it in the Console class:
protected function registerCommands(): void
{
$this->console->add(new CustomCommand());
}
5. Console Class
The Console class in Yabasi is responsible for registering and running CLI commands. It uses Symfony's Console component to manage commands.
class Console
{
protected Container $container;
protected SymfonyConsole $console;
public function __construct(Container $container)
{
$this->container = $container;
$this->console = new SymfonyConsole('Yabasi', '1.0.0');
$this->registerCommands();
}
public function run(array $argv): int
{
return $this->console->run();
}
}
6. Arguments and Options
You can add arguments and options to your custom commands to make them more flexible and powerful. Here's an example of how to define and use arguments and options:
protected function configure()
{
$this
->setName('app:greet')
->setDescription('Greet someone')
->addArgument('name', InputArgument::REQUIRED, 'Who do you want to greet?')
->addOption('yell', 'y', InputOption::VALUE_NONE, 'Yell in uppercase letters');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$greeting = "Hello, $name!";
if ($input->getOption('yell')) {
$greeting = strtoupper($greeting);
}
$output->writeln($greeting);
return Command::SUCCESS;
}
7. Output Formatting
Yabasi CLI commands support colorized and formatted output to enhance readability and user experience. Here's how you can use various output styles:
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('<info>This is a success message</info>');
$output->writeln('<comment>This is a comment</comment>');
$output->writeln('<question>This is a question</question>');
$output->writeln('<error>This is an error</error>');
return Command::SUCCESS;
}
8. Interactive Commands
You can create interactive commands that ask for user input or confirmation. Here's an example:
protected function execute(InputInterface $input, OutputInterface $output)
{
$helper = $this->getHelper('question');
$question = new Question('Please enter your name: ', 'User');
$name = $helper->ask($input, $output, $question);
$question = new ConfirmationQuestion('Do you want to continue? (y/n) ', false);
if (!$helper->ask($input, $output, $question)) {
return Command::SUCCESS;
}
$output->writeln("Hello, $name! You chose to continue.");
return Command::SUCCESS;
}
9. Help and Documentation
Yabasi automatically generates help documentation for your commands based on the descriptions and configurations you provide. You can view help for a command by running:
php
yabasi
command:name
--help
To provide more detailed help for your custom commands, you can override the configure()
method:
protected function configure()
{
$this
->setName('app:custom-command')
->setDescription('A custom command description')
->setHelp('This command allows you to...')
->addArgument('name', InputArgument::REQUIRED, 'Name description')
->addOption('option', null, InputOption::VALUE_NONE, 'Option description')
;
}
10. Best Practices
When writing CLI commands for Yabasi, consider the following best practices:
- Keep commands focused on a single task
- Use meaningful names for your commands, arguments, and options
- Provide clear and concise descriptions for your commands and their parameters
- Use input validation to prevent errors
- Implement proper error handling and provide informative error messages
- Use output formatting to improve readability
- For long-running commands, consider adding progress indicators
- Write unit tests for your commands to ensure they work as expected
Example: Progress Bar
For long-running tasks, you can use a progress bar to keep the user informed:
use Symfony\Component\Console\Helper\ProgressBar;
protected function execute(InputInterface $input, OutputInterface $output)
{
$progressBar = new ProgressBar($output, 100);
$progressBar->start();
for ($i = 0; $i < 100; $i++) {
// ... do some work here
$progressBar->advance();
}
$progressBar->finish();
return Command::SUCCESS;
}
By following these best practices and utilizing the features provided by Yabasi's CLI tools, you can create powerful, user-friendly command-line interfaces for your application. These tools can significantly improve your development workflow and provide valuable utilities for managing your Yabasi projects.
Testing
Yabasi framework provides a robust testing infrastructure to ensure the reliability and stability of your applications. It uses PHPUnit as the testing framework, allowing you to write unit tests, integration tests, and feature tests with ease.
Setting Up the Testing Environment
Yabasi comes with a pre-configured phpunit.xml
file that defines test suites and environment variables for testing.
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="../vendor/autoload.php"
colors="true"
verbose="true"
stopOnFailure="false">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./Unit</directory>
</testsuite>
<testsuite name="Integration">
<directory suffix="Test.php">./Integration</directory>
</testsuite>
</testsuites>
<php>
<env name="APP_ENV" value="testing"/>
</php>
</phpunit>
Base Test Case
Yabasi provides a base test case class that sets up the testing environment and offers helpful utility methods for your tests.
namespace Yabasi\Tests;
use PHPUnit\Framework\TestCase;
use Yabasi\Application;
use Yabasi\Container\Container;
class YabasiTestCase extends TestCase
{
protected $app;
protected $container;
protected function setUp(): void
{
parent::setUp();
$this->container = new Container();
$this->app = new Application($this->container);
}
protected function tearDown(): void
{
$this->app = null;
$this->container = null;
parent::tearDown();
}
}
Writing Unit Tests
Unit tests are used to test the smallest parts of your application in isolation. Here's an example of a simple unit test:
namespace Yabasi\Tests\Unit;
use Yabasi\Application;
use Yabasi\Tests\YabasiTestCase;
class ExampleTest extends YabasiTestCase
{
public function testExample()
{
$this->assertTrue(true);
}
public function testApplicationInstance()
{
$this->assertInstanceOf(Application::class, $this->app);
}
}
Integration Tests
Integration tests are used to test how different components of your application work together. Here's an example of a router integration test:
namespace Yabasi\Tests\Integration;
use Yabasi\Http\Request;
use Yabasi\Routing\Router;
use Yabasi\Tests\YabasiTestCase;
class RouterTest extends YabasiTestCase
{
public function testBasicRouting()
{
$router = $this->app->make(Router::class);
$router->get('/test', function() {
return 'Hello Test';
});
$request = $this->createRequest('/test', 'GET');
$response = $router->dispatch($request);
$this->assertEquals('Hello Test', $response->getContent());
}
private function createRequest($uri, $method, $parameters = [], $cookies = [], $files = [], $server = [])
{
// Implementation of createRequest method
}
}
Running Tests
To run your tests, use the PHPUnit command from your project root:
./vendor/bin/phpunit
You can also run specific test suites or individual test files:
./vendor/bin/phpunit
--testsuite Unit
./vendor/bin/phpunit
tests/Unit/ExampleTest.php
Best Practices
- Write tests for all new features and bug fixes
- Aim for high test coverage, but focus on critical paths
- Keep tests fast and independent of each other
- Use descriptive test method names
- Follow the Arrange-Act-Assert (AAA) pattern in your tests
- Use data providers for testing multiple scenarios
- Mock external dependencies to isolate the code being tested
By following these testing practices and utilizing Yabasi's testing infrastructure, you can ensure the reliability and maintainability of your applications.
Events & Listeners
Yabasi framework provides a robust event system that allows you to easily implement and manage application events. This system helps in decoupling various aspects of your application, making it more maintainable and extensible.
Event Dispatcher
The core of Yabasi's event system is the EventDispatcher
class. It manages event registration and dispatching.
namespace Yabasi\Events;
use Closure;
class EventDispatcher
{
protected array $listeners = [];
public function listen(string $eventName, $listener): void
{
$this->listeners[$eventName][] = $listener;
}
public function dispatch(Event $event): void
{
$eventName = $event->getName();
if (isset($this->listeners[$eventName])) {
foreach ($this->listeners[$eventName] as $listener) {
if ($listener instanceof Closure) {
$listener($event);
} elseif (is_array($listener) && count($listener) == 2) {
[$class, $method] = $listener;
if (is_string($class)) {
$class = new $class();
}
$class->$method($event);
}
}
}
}
}
Creating Events
Events in Yabasi are simple classes that extend the base Event
class. They typically contain data relevant to the event.
namespace App\Events;
use Yabasi\Events\Event;
use App\Models\User;
class UserRegisteredEvent extends Event
{
public function __construct(public User $user)
{
parent::__construct('user.registered');
}
}
Creating Listeners
Listeners are classes or closures that respond to specific events. They perform actions based on the event data.
namespace App\Listeners;
use App\Events\UserRegisteredEvent;
class SendWelcomeEmail
{
public function handle(UserRegisteredEvent $event)
{
// Send welcome email to $event->user
}
}
Registering Events and Listeners
You can register events and listeners in your service providers or anywhere you have access to the EventDispatcher.
namespace App\Providers;
use Yabasi\Events\EventDispatcher;
use Yabasi\ServiceProvider\ServiceProvider;
use App\Events\UserRegisteredEvent;
use App\Listeners\SendWelcomeEmail;
class EventServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->container->get(EventDispatcher::class)->listen(UserRegisteredEvent::class, [SendWelcomeEmail::class, 'handle']);
}
}
Dispatching Events
To dispatch an event, you can use the EventDispatcher's dispatch method.
use App\Events\UserRegisteredEvent;
use Yabasi\Events\EventDispatcher;
class UserController
{
public function register(Request $request, EventDispatcher $eventDispatcher)
{
// User registration logic
$user = User::create($request->all());
// Dispatch the event
$eventDispatcher->dispatch(new UserRegisteredEvent($user));
return response()->json(['message' => 'User registered successfully']);
}
}
Event Subscribers
Event subscribers are classes that can listen to multiple events. They provide a more organized way to handle related events.
namespace App\Listeners;
use Yabasi\Events\EventDispatcher;
class UserEventSubscriber
{
public function subscribe(EventDispatcher $events): void
{
$events->listen(
'user.registered',
[$this, 'handleUserRegistered']
);
$events->listen(
'user.login',
[$this, 'handleUserLogin']
);
}
public function handleUserRegistered($event) { /* ... */ }
public function handleUserLogin($event) { /* ... */ }
}
Best Practices
- Keep events and listeners small and focused on a single responsibility.
- Use event names that clearly describe what happened (e.g., 'user.registered', 'order.shipped').
- Consider using event subscribers for closely related events.
- Avoid performing time-consuming tasks directly in listeners. Instead, dispatch jobs to be processed in the background.
- Use type-hinting in your listener methods to ensure you're working with the correct event type.
By leveraging Yabasi's event system, you can create more modular and maintainable applications. Events allow you to decouple various parts of your application, making it easier to add new functionality without modifying existing code.
Queues & Jobs
Yabasi provides a robust queue system for handling time-consuming tasks asynchronously. The queue system supports multiple drivers (with Redis as the default), job retries, batch processing, and event handling.
Creating Jobs
To create a job, extend the base Job class:
namespace App\Jobs;
use Yabasi\Queue\Job;
class ProcessVideoJob extends Job
{
protected $maxAttempts = 3;
protected $delay = 60;
public function handle()
{
// Process video logic here
$this->setProgress(50);
// More processing...
}
}
Dispatching Jobs
You can dispatch jobs to the queue in several ways:
use App\Jobs\ProcessVideoJob;
// Dispatch immediately
app()->get('queue')->push(new ProcessVideoJob($data));
// Dispatch with delay
app()->get('queue')->later(60, new ProcessVideoJob($data));
Job Configuration
Jobs can be configured with several options:
$maxAttempts
: Maximum number of retry attempts$delay
: Delay between retry attempts in seconds$progress
: Track job progress
Running the Queue Worker
To process jobs, you need to run the queue worker:
php
yabasi
queue:work
Handling Failed Jobs
You can implement custom failure handling in your jobs:
public function failed(Exception $exception)
{
// Log the failure
$this->logger->error("Job failed: " . $exception->getMessage());
// Notify or perform cleanup
}
Monitoring Progress
Jobs can report their progress during execution:
public function handle()
{
$this->setProgress(25);
// Process first stage...
$this->setProgress(50);
// Process second stage...
$this->setProgress(100);
}
Queue Events
The queue system dispatches various events that you can listen to:
- Job started
- Job processed
- Job failed
- Job retrying
Note
Make sure Redis is installed and properly configured in your config/config.php
file before using the queue system.
API Development
Yabasi provides a robust set of tools for building RESTful APIs, including versioning, rate limiting, CORS support, and standardized response formatting. The framework makes it easy to create secure and scalable APIs while following REST best practices.
API Routes
Define API routes using the ApiRouter with automatic prefix and versioning:
use Yabasi\Routing\ApiRouter;
$apiRouter = new ApiRouter($router);
$apiRouter->setVersion('v1')->group([], function($router) {
$router->resource('users', 'UserApiController');
$router->get('posts', 'PostApiController@index');
$router->post('posts', 'PostApiController@store');
});
API Controllers
Extend the ApiController base class to get standardized response methods:
namespace App\Controllers;
use Yabasi\Http\Controllers\ApiController;
use Yabasi\Http\Request;
class UserApiController extends ApiController
{
public function index()
{
$users = User::all();
return $this->respondWithData($users);
}
public function store(Request $request)
{
// Validation and creation logic
return $this->respondWithSuccess('User created successfully', 201);
}
}
Rate Limiting
Protect your API endpoints with built-in rate limiting:
$apiRouter->group([
'middleware' => [
RateLimitMiddleware::class
]
], function($router) {
// Your rate-limited routes here
});
API Versioning
API versioning is handled automatically through the URL or Accept header:
- URL-based:
/api/v1/users
- Header-based:
Accept-Version: v1
CORS Configuration
Configure CORS for your API in the config/config.php file:
'cors' => [
'allowed_origins' => ['*'],
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],
'allow_credentials' => false,
'max_age' => 0,
],
Standardized API Responses
ApiController provides methods for consistent response formatting:
respondWithData($data, $statusCode = 200)
respondWithError($message, $statusCode)
respondWithSuccess($message, $statusCode = 200)
respondWithValidationError($errors)
respondWithPaginatedData($data, $page, $perPage, $total)
Response Examples
Example of standardized JSON responses:
{
"data": {
"id": 1,
"name": "John Doe",
"email": "john@example.com"
},
"meta": {
"version": "v1"
}
}
Best Practices
- Always version your APIs
- Implement rate limiting for public endpoints
- Use appropriate HTTP status codes
- Include proper error messages in responses
- Document your API endpoints
WebSockets
Yabasi provides real-time communication capabilities through WebSockets using the Ratchet WebSocket server. This enables you to build real-time features like chat applications, live notifications, and real-time dashboards.
Creating a WebSocket Server
Create a WebSocket server by extending the BaseWebSocketServer class:
namespace App\WebSocket;
use Ratchet\ConnectionInterface;
use Yabasi\WebSocket\BaseWebSocketServer;
class NotificationServer extends BaseWebSocketServer
{
public function onMessage(ConnectionInterface $from, $msg)
{
$data = json_decode($msg, true);
// Broadcast message to all connected clients
foreach ($this->clients as $client) {
$client->send(json_encode([
'type' => 'notification',
'data' => $data
]));
}
}
}
Running the WebSocket Server
Run your WebSocket server using the command line:
php
yabasi
websocket:serve
Client-Side Connection
Connect to your WebSocket server from the client side:
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = function() {
console.log('Connected to WebSocket server');
};
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.type === 'notification') {
console.log('New notification:', data.data);
}
};
Handling Events
The BaseWebSocketServer provides several events you can handle:
class GameServer extends BaseWebSocketServer
{
public function onOpen(ConnectionInterface $conn)
{
// New connection established
parent::onOpen($conn);
}
public function onClose(ConnectionInterface $conn)
{
// Connection closed
parent::onClose($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
// Handle error
parent::onError($conn, $e);
}
}
Broadcasting Messages
Send messages to specific clients or broadcast to all connected clients:
// Broadcast to all clients except sender
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
// Send to specific client
$targetClient->send(json_encode([
'type' => 'private',
'message' => 'Private message'
]));
Note
- WebSocket server runs on port 8080 by default
- Make sure your firewall allows WebSocket connections
- Consider using a process manager like Supervisor in production
- Use secure WebSocket (wss://) in production environments
Localization
Yabasi provides a powerful and flexible localization system for creating multilingual applications. The framework uses JSON-based language files and offers an intuitive API for managing translations.
Configuration
First, set your application's default and fallback locales in your configuration file:
return [
'app' => [
'locale' => 'en',
'fallback_locale' => 'en',
],
];
Language Files
Create JSON language files in the storage/lang
directory. Each language should have its own file:
{
"welcome": "Welcome to our application",
"auth.failed": "These credentials do not match our records",
"users.greeting": "Hello, :name!",
"items.count": "You have :count items"
}
Basic Usage
You can use the translation helper function __()
or the Translator class directly:
// Using helper function
echo __('welcome');
// Using with parameters
echo __('users.greeting', ['name' => 'John']);
// Using Translator class
$translator = $container->get(Translator::class);
echo $translator->get('welcome');
Using in Twig Templates
The translation function is also available in Twig templates:
<h1>{{ __('welcome') }}</h1>
<p>{{ __('users.greeting', {'name': user.name}) }}</p>
Switching Locales
You can change the application's locale at runtime:
$translator = $container->get(Translator::class);
// Switch to Spanish
$translator->setLocale('es');
Pluralization
Handle pluralization in your translations using the count parameter:
{
"items.zero": "No items found",
"items.one": "One item found",
"items.many": ":count items found"
}
Best Practices
- Use dot notation to organize translations hierarchically
- Always provide a fallback locale for missing translations
- Keep translation keys lowercase and use dots for namespacing
- Use meaningful key names that describe the content
- Group related translations together
Remember to create language files for all supported locales before deploying your application. Missing translations will fall back to the default locale.