PHP code example of neophp-dev / neophp

1. Go to this page and download the library: Download neophp-dev/neophp library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

neophp-dev / neophp example snippets



declare(strict_types=1);

namespace Neo\Src\Blog\App\Services;

use Neo\Core\Utils\Cache\CacheManager;
use Neo\Core\Utils\Logger\LoggerManager;

final class ReportService
{
    public function __construct(
        private CacheManager $cache,
        private LoggerManager $logger
    ) {
    }

    public function build(): array
    {
        $this->logger->info('Generating the report');

        return $this->cache->get('report.latest', []);
    }
}

$appName = $this->getConfig()->from('app')->get('general.name');
$timezone = $this->getConfig()->from('app')->get('date.timezone', 'UTC');
$twigOptions = $this->getConfig()->from('twig')->all();


declare(strict_types=1);

return [
    'general' => [
        'name' => 'Blog',
        'description' => 'My NeoPHP project',
    ],
    'environment' => 'dev',
    'access' => 'localhost:8000',
    'date' => [
        'timezone' => 'Europe/Paris',
    ],
];

#[Route(path: '/search', name: 'search', methods: ['GET'])]
public function search(): Response
{
    $term = (string) $this->request->query('q', '');

    return $this->render('pages/search/index.html.twig', [
        'term' => $term,
        'ip' => $this->request->getIp(),
    ]);
}

$response = new Response();
$response->setStatusCode(200);
$response->setHeader('Content-Type', 'text/plain; charset=UTF-8');
$response->setContent('OK');
return $response;

return $this->jsonSuccess(['saved' => true], 201);
return $this->jsonError('Not found', 404);
return $this->redirectToRoute('posts.index');
return $this->redirectToPath('/maintenance', 302);

$client = $container->get(HttpClientManager::class);

// Simple GET request
$data = $client->request('GET', 'https://api.example.com/users')->toArray();

// JSON POST request with Bearer token
$response = $client->request('POST', '/api/articles', [
    'base_uri' => 'https://api.example.com',
    'bearer'   => $token,
    'json'     => ['title' => 'My article'],
]);
$response->getStatusCode(); // 201
$response->toArray();       // ['id' => 42, ...]

$this->getSession()->set('wizard.step', 2);
$step = $this->getSession()->get('wizard.step', 1);

$this->getCookie()->set('theme', 'dark');
$theme = $this->getCookie()->get('theme', 'light');

$this->getFlash()->add('success', 'Operation completed');

#[MainRoute(path: '/posts', name: 'posts')]
final class PostController extends AbstractController
{
    #[Route(path: '/', name: 'index', methods: ['GET'])]
    public function index(): Response
    {
        return $this->render('pages/posts/index.html.twig');
    }
}


declare(strict_types=1);

namespace Neo\Src\Blog\App\Controllers;

use Neo\Core\Controller\AbstractController;use Neo\Core\Http\Response\Types\Response;use Neo\Core\Routing\Attribute\MainRoute;use Neo\Core\Routing\Attribute\Route;use Neo\Src\Blog\Database\Repository\PostRepository;

#[MainRoute(path: '/posts', name: 'posts')]
final class PostController extends AbstractController
{
    public function __construct(private PostRepository $postRepository)
    {
    }

    #[Route(path: '/', name: 'index', methods: ['GET'])]
    public function index(): Response
    {
        return $this->render('pages/posts/index.html.twig', [
            'posts' => $this->postRepository->findAll(),
        ]);
    }

    #[Route(path: '/{id}', name: 'show', methods: ['GET'], 



return [
    'Bienvenue sur le blog' => 'Bienvenue sur le blog',
    'Enregistrer' => 'Enregistrer',
];

// en.php
return [
    'Bienvenue sur le blog' => 'Welcome to the blog',
    'Enregistrer' => 'Save',
];

#[Route(path: '/change-locale/{locale}', name: 'change.locale', methods: ['GET'])]
public function changeLocale(string $locale, TranslationManager $translator): Response
{
    $translator->setLocale($locale);
    return $this->redirectBack('home.index');
}

$slug = $this->getString()->slugify('My Example Title');
$price = $this->getNumber()->currency(19.99, 'EUR');

return [
    'enabled' => true,
    'use' => 'default',
    'connections' => [
        'default' => [
            'driver' => 'mysql',
            'host' => 'localhost',
            'port' => 3306,
            'dbname' => 'blog',
            'user' => 'root',
            'pass' => '',
            'charset' => 'utf8mb4',
        ],
    ],
];


declare(strict_types=1);

use Neo\Core\Database\Builder\QueryBuilder;

$qb = (new QueryBuilder())
    ->table('posts')
    ->select(['posts.id', 'posts.title'])
    ->where('posts.user_id', '=', 1)
    ->whereLike('posts.title', 'neo')
    ->orderBy('posts.id', 'DESC')
    ->limit(10);

$rows = $qb->get();

(new QueryBuilder())
    ->table('posts')
    ->transaction(function (QueryBuilder $qb): void {
        $qb->table('posts')->insert([
            'user_id' => 1,
            'title' => 'Transactional post',
            'content' => 'Content',
        ]);
    });


declare(strict_types=1);

final class MigrationVersion_20260606120000
{
    public function up(DatabaseManager $db): void
    {
        $db->execute('CREATE TABLE posts (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY)');
    }

    public function down(DatabaseManager $db): void
    {
        $db->execute('DROP TABLE posts');
    }
}

#[Route(path: '/', name: 'store', methods: ['POST'])]
public function store(): Response
{
    $post = new Post();
    $post->setTitle((string) $this->request->body('title'));

    $this->entityManager->persist($post);
    $this->entityManager->flush();

    return $this->jsonSuccess(['id' => $post->getId()], 201);
}


declare(strict_types=1);

namespace Neo\Src\Blog\Database\Repository;

use Neo\Core\Database\ORM\Persistence\EntityRepository;
use Neo\Src\Blog\Database\Entity\Post;

/**
 * @extends EntityRepository<Post>
 */
final class PostRepository extends EntityRepository
{
}

public function __construct(private PostRepository $posts) {}

public function index(): Response
{
    return $this->render('pages/posts/index.html.twig', [
        'posts' => $this->posts->findAll(),
    ]);
}

public function show(int $id): Response
{
    $post = $this->posts->find($id);

    return $this->render('pages/posts/show.html.twig', ['post' => $post]);
}


declare(strict_types=1);

namespace Neo\Src\Blog\Database\Entity;

use Neo\Core\Database\ORM\Collection\Collection;
use Neo\Core\Database\ORM\Mapping\Attribute\Column;
use Neo\Core\Database\ORM\Mapping\Attribute\Entity;
use Neo\Core\Database\ORM\Mapping\Attribute\GeneratedValue;
use Neo\Core\Database\ORM\Mapping\Attribute\Id;
use Neo\Core\Database\ORM\Mapping\Attribute\JoinColumn;
use Neo\Core\Database\ORM\Mapping\Attribute\ManyToOne;
use Neo\Core\Database\ORM\Mapping\Attribute\OneToMany;
use Neo\Core\Database\ORM\Mapping\Attribute\Table;
use Neo\Src\Blog\Database\Repository\PostRepository;

#[Entity(repositoryClass: PostRepository::class)]
#[Table(name: 'posts')]
final class Post
{
    #[Id]
    #[GeneratedValue]
    #[Column(type: 'integer', unsigned: true)]
    private ?int $id = null;

    #[Column(type: 'string', length: 255)]
    private string $title;

    #[ManyToOne(targetEntity: User::class, inversedBy: 'posts')]
    #[JoinColumn(name: 'user_id', nullable: false)]
    private User $author;

    /** @var Collection<Comment> */
    #[OneToMany(targetEntity: Comment::class, mappedBy: 'post')]
    private Collection $comments;

    public function __construct()
    {
        $this->comments = new Collection();
    }

    public function getId(): ?int { return $this->id; }

    public function getTitle(): string { return $this->title; }
    public function setTitle(string $title): static { $this->title = $title; return $this; }

    public function getAuthor(): User { return $this->author; }
    public function setAuthor(User $author): static { $this->author = $author; return $this; }

    /** @return Collection<Comment> */
    public function getComments(): Collection { return $this->comments; }
}


declare(strict_types=1);

namespace Neo\Src\Blog\Database\Entity;

use Neo\Core\Database\ORM\Mapping\Attribute\Column;
use Neo\Core\Database\ORM\Mapping\Attribute\Entity;
use Neo\Core\Database\ORM\Mapping\Attribute\GeneratedValue;
use Neo\Core\Database\ORM\Mapping\Attribute\Id;
use Neo\Core\Database\ORM\Mapping\Attribute\Table;
use Neo\Src\Blog\Database\Repository\PostRepository;

#[Entity(repositoryClass: PostRepository::class)]
#[Table(name: 'posts')]
final class Post
{
    #[Id]
    #[GeneratedValue]
    #[Column(type: 'integer', unsigned: true)]
    private ?int $id = null;

    #[Column(type: 'string', length: 255)]
    private string $title;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle(string $title): static
    {
        $this->title = $title;

        return $this;
    }
}

$article = $em->find(Article::class, 1);
$article->getTags()->add($em->find(Tag::class, 5)); // Add
$article->getTags()->remove($existingTag);           // Remove
$em->flush(); // Automatically syncs the article_tag table


declare(strict_types=1);

namespace Neo\Src\Blog\Database\Repository;

use Neo\Core\Database\ORM\Persistence\EntityRepository;
use Neo\Src\Blog\Database\Entity\Post;

/**
 * @extends EntityRepository<Post>
 */
final class PostRepository extends EntityRepository
{
}


declare(strict_types=1);

namespace Neo\Src\Blog\App\Services;

use Neo\Core\Database\Form\Form;
use Neo\Core\Database\Form\FormFactory;
use Neo\Src\Blog\Database\Entity\User;

final class UserFormService
{
    public function __construct(private FormFactory $factory) {}

    public function build(?User $user = null): Form
    {
        $user ??= new User();

        return $this->factory->createFor($user)
            ->add('firstname', 'text', ['label' => 'First name', '

$filename = $this->upload(
    string $field,
    string $name,
    array $extensions,
    string $directory
);

#[Route(path: '/profile/avatar', name: 'avatar.upload', methods: ['POST'])]
public function uploadAvatar(): Response
{
    $filename = $this->upload(
        field: 'avatar',
        name: 'user_' . (string) $this->auth()->user()?->getId(),
        extensions: ['jpg', 'jpeg', 'png', 'webp'],
        directory: 'uploads/avatars'
    );

    return $this->jsonSuccess([
        'filename' => $filename,
        'path' => 'uploads/avatars/' . $filename,
    ]);
}


declare(strict_types=1);

namespace Neo\Src\Blog\App\Dto;

use Neo\Core\Validator\Assert\Email;
use Neo\Core\Validator\Assert\EqualToField;
use Neo\Core\Validator\Assert\Length;
use Neo\Core\Validator\Assert\NotBlank;

final class RegisterDto
{
    #[NotBlank(message: 'First name is ld: 'password', message: 'Passwords must match.')]
    public string $password_confirm = '';
}


declare(strict_types=1);

namespace Neo\Src\Blog\Database\Seeder;

use Neo\Core\Database\ORM\Persistence\EntityManager;
use Neo\Core\Database\Seeder\Attribute\Seeder;
use Neo\Core\Database\Seeder\Interface\SeedInterface;
use Neo\Src\Blog\Database\Entity\Country;

#[Seeder(order: 10, group: 'reference')]
final class CountrySeeder implements SeedInterface
{
    public function run(EntityManager $entityManager): void
    {
        $country = new Country();
        $country->setCode('FR')->setName('France');
        $entityManager->persist($country);
        $entityManager->flush();
    }
}

'auth' => [
    'enabled' => true,
    'model' => User::class,
    'identifier' => 'email',
    'password' => 'password',
    'guard' => 'session',
    'role' => [
        'model' => Role::class,
        'foreign_key' => 'role_id',
        'field' => 'slug',
    ],
    'options' => [
        'secret' => 'change-me',
        'expiration' => 3600,
        'algorithm' => 'HS256',
    ],
],

#[MainRoute(path: '/login', name: 'login')]
final class LoginController extends AbstractController
{
    #[Route(path: '/', name: 'index', methods: ['GET', 'POST'])]
    public function index(): Response
    {
        if ($this->request->getMethod() === 'GET') {
            return $this->render('pages/auth/login.html.twig');
        }

        $ok = $this->auth()->attempt([
            'email' => (string) $this->request->body('email'),
            'password' => (string) $this->request->body('password'),
        ]);

        if (!$ok) {
            return $this->jsonError('Invalid credentials', 401);
        }

        return $this->redirectToRoute('dashboard.index');
    }
}

#[MainRoute(path: '/api', name: 'api')]
final class ApiAuthController extends AbstractController
{
    public function __construct(private UserRepository $userRepository)
    {
    }

    #[Route(path: '/login', name: 'login', methods: ['POST'])]
    public function login(): Response
    {
        $email = (string) $this->request->body('email');
        $password = (string) $this->request->body('password');

        $ok = $this->auth()->attempt([
            'email' => $email,
            'password' => $password,
        ]);

        if (!$ok) {
            return $this->jsonError('Invalid credentials', 401);
        }

        $user = $this->userRepository->findOneBy(['email' => $email]);

        if ($user === null) {
            return $this->jsonError('User not found', 401);
        }

        return $this->jsonSuccess([
            'token' => $this->auth()->generateToken($user),
        ]);
    }
}

$hash = $this->getPasswordManager()->hash('secret123');
$ok = $this->getPasswordManager()->verify('secret123', $hash);


declare(strict_types=1);

namespace Neo\Src\Blog\App\Middlewares;

use Neo\Core\DI\Container;
use Neo\Core\Security\Auth\AuthManager;
use Neo\Core\Security\Middleware\Interface\MiddlewareInterface;

final class AdminAccessMiddleware implements MiddlewareInterface
{
    private AuthManager $auth;

    public function __construct(Container $container)
    {
        $this->auth = $container->get(AuthManager::class);
    }

    public function handle(): bool
    {
        return $this->auth->check() && $this->auth->hasRole('admin');
    }
}

#[MainRoute(path: '/admin', name: 'admin')]
#[Middleware(use: AuthMiddleware::class, redirect: 'login.index')]
#[Middleware(use: RoleMiddleware::class, params: ['role' => 'admin'])]
final class DashboardController extends AbstractController
{
    #[Route(path: '/', name: 'index', methods: ['GET'])]
    #[RateLimit(maxAttempts: 20, decaySeconds: 60)]
    public function index(): Response
    {
        return $this->render('pages/admin/index.html.twig');
    }
}

#[MainRoute(path: '/admin', name: 'admin')]
#[IsGranted(roles: ['admin'])]
final class DashboardController extends AbstractController
{
    #[Route(path: '/users', name: 'users', methods: ['GET'])]
    #[IsGranted(roles: ['admin', 'superadmin'])]
    public function users(): Response
    {
        return $this->render('pages/admin/users.html.twig');
    }
}


declare(strict_types=1);

namespace Neo\Src\Blog\App\Event;

use Neo\Core\Event\Abstract\AbstractEvent;

final class UserRegisteredEvent extends AbstractEvent
{
    public function __construct(public readonly int $userId)
    {
    }
}


declare(strict_types=1);

namespace Neo\Src\Blog\App\Event\Listener;

use Neo\Core\Event\Attribute\AsListener;
use Neo\Src\Blog\App\Event\UserRegisteredEvent;

#[AsListener(event: UserRegisteredEvent::class, priority: 0)]
final class SendWelcomeEmailListener
{
    public function handle(UserRegisteredEvent $event): void
    {
        $userId = $event->userId;
    }
}

#[Route(path: '/register', name: 'register', methods: ['POST'])]
public function register(): Response
{
    $user = new \Neo\Src\Blog\Database\Entity\User();
    $user->setFirstname((string) $this->request->body('firstname'));
    $user->setEmail((string) $this->request->body('email'));
    $user->setPassword($this->getPasswordManager()->hash(
        (string) $this->request->body('password')
    ));

    $em = $this->entityManager();
    $em->persist($user);
    $em->flush();

    $this->dispatch(new \Neo\Src\Blog\App\Event\UserRegisteredEvent((int) $user->getId()));

    return $this->jsonSuccess([
        'id' => $user->getId(),
    ], 201);
}

$this->getCache()->set('homepage.posts', $posts, 600);
$posts = $this->getCache()->get('homepage.posts', []);
$stats = $this->getCache()->remember('stats.daily', 300, fn() => $service->buildStats());

$this->getLogger()->channel('framework')->error(
    'Business error',
    ['post_id' => 12],
    'PostController::show'
);

$sent = $this->getMailer()
    ->to('[email protected]', 'John Doe')
    ->subject('Welcome')
    ->template('emails/welcome.html.twig', [
        'user' => $user,
    ])
    ->send();

$manager = $container->get(MarkdownManager::class);

// From a file
$blocks = $manager->blocks('docs/guide.md');

// From a string
$blocks = $manager->parse("## Title\n\nContent.");

#[Test(
    type: 'auto',
    cases: [],
    route: null,
    httpMethod: 'GET',
    dataset: [],
    skip: false,
    extends: null
)]


declare(strict_types=1);

namespace Neo\Src\Blog\App\Services;

use Neo\Core\Testing\Attribute\Test;

#[Test(type: 'unit', cases: ['it_works', 'returns_slug'])]
final class SlugService
{
    public function slugify(string $value): string
    {
        return strtolower(trim(str_replace(' ', '-', $value)));
    }
}


declare(strict_types=1);

namespace Neo\Src\Blog\Database\Repository;

use Neo\Core\Database\ORM\Persistence\EntityRepository;
use Neo\Core\Testing\Attribute\Test;
use Neo\Src\Blog\Database\Entity\User;

#[Test(
    type: 'database',
    cases: ['find_by_email', 'create'],
    dataset: [
        'table' => 'users',
        'data' => [
            'firstname' => 'John',
            'email' => '[email protected]',
        ],
    ],
)]
final class UserRepository extends EntityRepository
{
}


declare(strict_types=1);

namespace Neo\Src\Blog\App\Controllers;

use Neo\Core\Controller\AbstractController;use Neo\Core\Http\Response\Types\Response;use Neo\Core\Routing\Attribute\MainRoute;use Neo\Core\Testing\Attribute\Test;

#[MainRoute(path: '/login', name: 'login')]
final class AuthController extends AbstractController
{
    #[Test(
        route: '/login',
        httpMethod: 'POST',
        cases: ['returns_success', 'rejects_invalid_credentials']
    )]
    public function login(): Response
    {
        return $this->jsonSuccess();
    }
}


declare(strict_types=1);

return [
    'ftp' => [
        'host' => 'ftp.example.com',
        'user' => 'my-user',
        'pass' => 'my-pass',
    ],
    'remote' => [
        'domain' => 'example.com',
        'framework_dir' => 'domains/example.com/neo',
        'public_dir' => 'domains/example.com/public_html',
    ],
];
bash
php bin/neo cache:clear --project=Test
text
src/Blog/
|-- .gitignore
|-- composer.json
|-- App/
|   |-- Controllers/
|   |-- Middlewares/
|   |-- Services/
|   `-- Views/
|-- Assets/
|-- Config/
|   |-- api.config.php
|   |-- app.config.php
|   |-- cache.config.php
|   |-- database.config.php
|   |-- deploy.config.php
|   |-- logger.config.php
|   |-- mailer.config.php
|   |-- session.config.php
|   `-- twig.config.php
|-- Database/
|   |-- Entity/
|   |-- Migrations/
|   |-- Repository/
|-- Storage/
`-- Translations/
text
src/Blog/
|-- Assets/
|   |-- css/
|   `-- js/
|-- App/Views/
|   |-- errors/
|   |-- layouts/
|   |-- pages/default/
|   `-- partials/
`-- Translations/
    |-- fr.php
    `-- en.php
bash
php bin/neo database:create --project=Blog
php bin/neo make:entity Post --project=Blog
php bin/neo database:orm:diff --project=Blog --name=add_posts_table
php bin/neo database:orm:diff --project=Blog --name=add_posts_table --dry-run
bash
php bin/neo make:entity Post --project=Blog
php bin/neo database:orm:diff --project=Blog --name=initial_schema
php bin/neo database:migration:status --project=Blog
php bin/neo database:migration:migrate --project=Blog
php bin/neo database:migration:rollback --project=Blog
bash
php bin/neo make:entity Post --project=Blog
bash
php bin/neo make:cron <CronName> --project=Blog
bash
php bin/neo make:cron CleanupTempFiles --project=Blog
bash
php bin/neo cron:list --project=Blog
bash
php bin/neo cron:run --project=Blog
bash
* * * * * php /var/www/neophp/bin/neo cron:run --project=Blog
bash
* * * * * php /Users/benjamin/Sites/neophp/bin/neo cron:run --project=Blog
bash
php C:\Sites\NeoPHP\bin\neo cron:run --project=Blog
bash
php bin/neo
bash
php bin/neo <command> --help
bash
php bin/neo project:create Blog
php bin/neo make:controller PostController --project=Blog
php bin/neo make:controller ApiPostController --api --project=Blog
php bin/neo app:make:command CleanupLogs --name=logs:clean --project=Blog
php bin/neo app:make:service Mail --project=Blog
php bin/neo make:middleware AdminAccess --project=Blog
php bin/neo make:event UserRegistered --project=Blog
php bin/neo make:event:listener SendWelcomeEmail --event=UserRegistered --project=Blog
php bin/neo make:cron CleanupTempFiles --project=Blog
php bin/neo make:entity Post --project=Blog
php bin/neo make:config mail --project=Blog
php bin/neo database:create --project=Blog
php bin/neo database:orm:diff --project=Blog --name=initial_schema
bash
php bin/neo make:config mail --project=Blog
bash
php bin/neo app:make:command CleanupLogs --name=logs:clean --category=maintenance --project=Blog
php bin/neo logs:clean --project=Blog
bash
php bin/neo generate:default:config --project=Blog
php bin/neo app:composer:project=Blog
php bin/neo cache:clear --project=Blog
php bin/neo asset:reload --project=Blog
php bin/neo database:migration:status --project=Blog
php bin/neo database:migration:migrate --project=Blog
php bin/neo translation:sync --project=Blog
php bin/neo translation:sync --project=Blog --dry-run
bash
php bin/neo make:test UserServiceTest --type=unit --project=Blog
php bin/neo make:test UserControllerTest --type=feature --project=Blog
php bin/neo make:test UserRepositoryTest --type=database --project=Blog
php bin/neo make:test AuthMiddlewareTest --type=middleware --project=Blog