Add CLI command to initialize a new project

This commit is contained in:
Benjamin Renard 2023-02-26 23:48:53 +01:00
parent 4fae41d1ea
commit 02b0bbcfad
17 changed files with 496 additions and 2 deletions

17
docs/getting-start.md Normal file
View file

@ -0,0 +1,17 @@
# Getting start
To start using the EesyPHP framework on your project, you have to firstly install it using [composer](https://getcomposer.org/) :
```bash
mkdir my_project
cd my_project
composer init --stability dev
# When asking for dependency, search brenard/eesyphp
composer update
```
With the provided `eesyphp` CLI tool, you can quickly init a new project:
```bash
vendor/bin/eesyphp new_project
```

4
example/locales/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
headers.pot
js-*-messages.pot
php-messages.pot
templates-*-messages.pot

15
skel/.gitignore vendored Normal file
View file

@ -0,0 +1,15 @@
# Ignore vim temporary/backup files
*~
.*.swp
# Exclude composer installed libs
/vendor
/composer.lock
# Common UNIX user home directory files
/.bash*
/.vim*
/.composer
/.gitconfig
/.vim*
/.less*
/.ssh
/.phplint-cache

7
skel/bin/manage Normal file
View file

@ -0,0 +1,7 @@
#!/usr/bin/php
<?php
use EesyPHP\Cli;
require realpath(dirname(__FILE__).'/..')."/includes/core.php";
Cli :: handle_args();

303
skel/config.yml Normal file
View file

@ -0,0 +1,303 @@
# Public root URL
public_root_url: "/"
# Application root data directory
data_directory: "${root_directory_path}/data"
# Temporary files root directory
tmp_root_directory: "${data_directory}/tmp"
# Temporary uploading files directory
upload_tmp_directory: ${tmp_root_directory}/uploading"
# Main pagetitle
main_pagetitle: "Eesyphp"
# Debug Ajax request/response
debug_ajax: false
#
# Log configuration
#
log:
# Logs directory path
directory_path: "${data_directory}/logs"
# CLI log file path
cli_file_path: "${log.directory_path}/cli.log"
# Log file path
file_path: "${log.directory_path}/app.log"
# Log level (TRACE / DEBUG / INFO / WARNING / ERROR / FATAL)
level: 'INFO'
# Log PHP errors levels (as specified to set_error_handler())
# Note: expected a list of PHP error level constants that will be resolved and combine
# with a bitwise operator. After the first value, the bitwise operator "|" (OR) or "^" (XOR)
# could be specified as contanst name prefix, otherwise the "&" (AND) operator will be used.
# The constant name could also be prefix with "~" or "!" (NOT) bitwise operator.
#
# Default:
# - In TRACE or DEBUG: E_ALL & ~E_STRICT
# - Otherwise: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
#log_php_errors_levels:
# - E_ALL
# - ~E_NOTICE
# - ~E_STRICT
# - ~E_DEPRECATED
#
# Sentry configuration
#
sentry:
# Sentry DSN
#dsn: null
# Log PHP errors in Sentry: list of errors types to logs
# See: https://www.php.net/manual/fr/errorfunc.constants.php
php_error_types:
- "E_ERROR"
- "E_PARSE"
- "E_CORE_ERROR"
- "E_COMPILE_ERROR"
- "E_USER_ERROR"
- "E_RECOVERABLE_ERROR"
- "E_DEPRECATED"
# Traces sample rate (between 0 and 1)
# Note: this parameter permit to determine how many transactions (=~ access) are traced and
# sent to Sentry, for instance, 0.2 meen that 20% of the transactions will be traced. In dev
# mode, set to 1 if you want all transactions are sent to Sentry.
# Default: 0.2
traces_sample_rate: 0.2
#
# Smarty templates configuration
#
templates:
# Smarty directories
directory: "${root_directory_path}/templates"
cache_directory: "${tmp_root_directory}/templates_c"
# Theme CSS file
included_css_files:
#- css/custom.css
included_js_files:
#- js/custom.js
#
# Translations
#
i18n:
# Default locale (see locales directory for available languages list)
default_locale: 'en_US.UTF8'
#
# Session
#
session:
timeout: 1800 # Session timeout dur to inactivity (in seconds)
max_duration: 43200 # Session max duration (in seconds, default : 12h)
#
# Database configuration
#
db:
# Sqlite
#dsn: "sqlite:${data_directory}/db.sqlite3"
#options: null
# Date/Datetime format in database (strptime format)
#date_format: '%s'
#datetime_format: '%s'
# Postgresql
#dsn: "pgsql:host=localhost;port=5432;dbname=items"
#user: "items"
#pwd: "items"
#options: null
# Date/Datetime format in database (strptime format)
#date_format: '%Y-%m-%d' # Exemple : 2018-10-12
#datetime_format: '%Y-%m-%d %H:%M:%S' # Exemple : 2018-10-12 18:06:59
# MariaDB / MySQL
#dsn: "mysql:host=localhost;dbname=items"
#user: "items"
#pwd: "items"
#options: null
# Date/Datetime format in database (strptime format)
#date_format: '%Y-%m-%d' # Exemple : 2018-10-12
#datetime_format: '%Y-%m-%d %H:%M:%S' # Exemple : 2018-10-12 18:06:59
#
# Authentication
#
auth:
# Enabled authentication
enabled: false
# Methods to authenticate users
methods:
- form
- http
#- cas
# User backends
backends:
#- ldap
#
# Login form
#
login_form:
# Display link for other authentication methods
# Note: method as key and label as value
display_other_methods:
http: "HTTP"
cas: "SSO"
#
# HTTP Authentication Configuration
#
http:
# HTTP Auth methods :
# * AUTHORIZATION : use HTTP_AUTHORIZATION environnement. This mode could be use with PHP FPM.
# Specific configuration is need in Apache to use this mode :
# RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# * REMOTE_USER : use REMOTE_USER environnement variable to retreive authenticated user's login
# This method could be only used with $http_auth_trust_without_password_challenge
# enabled.
# * PHP_AUTH : use PHP_AUTH_USER and PHP_AUTH_PW environnement variables (only available
# using mod_php with Apache. (default)
method: 'PHP_AUTH'
# Trust HTTP server authentication
# If enabled, the application will no check user credentials and only retreive user's login
# from HTTP server.
#trust_without_password_challenge: true
# Realm (use when force HTTP login, optional)
#realm: "Authentication required"
#
# CAS Configuration
#
cas:
# CAS host (just the domain name)
host: 'idp.example.com'
# CAS context (the root path, default: '/idp/cas')
# Example: for 'http://idp.example.com/idp/cas', put '/idp/cas'
context: '/idp/cas'
# CAS HTTP port (default: 443)
#port: 443
# CAS procotol version
# Posssible values: "1.0", "2.0" (default), "3.0" or "S1" (SAML1)
#version: '2.0'
# CAS server SSL certificate validation (set to false to disable)
ca_cert_certificate_path: "/etc/ssl/certs/ca-certificates.crt"
# CAS Debug log file
#debug_log_file: "${root_directory_path}/data/logs/cas.log"
# CAS Logout
#logout: true # Enable CAS logout on app logout
#logout_url: "https://my.example.fr/logout/" # Specify custom CAS logout URL
# CAS Fake authenticated user
#fake_authenticated_user: 'myusername'
#
# LDAP user backend
#
ldap:
# LDAP host (required, multiple hosts could be specified with comma separator)
host: 'ldap://localhost'
# LDAP port (optional)
#port: 389
# Enable STARTTLS (optional, default: false)
#starttls: false
# LDAP directory base DN (required)
basedn: 'o=example'
# LDAP bind DN (optional)
#bind_dn: 'uid=eesyphp,ou=sysaccounts,${auth.ldap.basedn}'
# LDAP bind password (optional)
#bind_password: 'secret'
# User search filter by username. The keyword "[username]" will be replace before search by
# the looked username (default: "uid=[username]")
#user_filter_by_uid: 'uid=[username]'
# User base DN
user_basedn: 'ou=people,${auth.ldap.basedn}'
# Bind with username instead of user DN (optional, default: false)
#bind_with_username: true
# LDAP user attributes to retreive with their properties:
# [LDAP attr name]:
# name: [map name] # optional, default: LDAP attr name
# type: [type of value] # optional, default: 'string', possible values: string, bool, int, float
# multivalued: true # optional, default: false
# default: null # optional, default: null
user_attributes:
uid:
name: 'login'
multivalued: false
default: null
cn:
name: 'name'
multivalued: false
default: null
mail:
type: 'string'
# PEAR Net_LDAP2 library path (optional, default: Net/LDAP2.php)
#netldap2_path: 'Net/LDAP2.php'
#
# Email configuration
#
email:
# PHP PEAR Mail and Mail_Mine paths
php_mail_path: 'Mail.php'
php_mail_mime_path: 'Mail/mime.php'
# Sending method :
# - mail : use PHP mail function
# - sendmail : use sendmail system command
# - smtp : use an SMTP server (PHP PEAR Net_SMTP required)
send_method: 'smtp'
# Sending parameters
# See : http:#pear.php.net/manual/en/package.mail.mail.factory.php
send_params: NULL
# Headers add to all e-mails sent
headers:
#- "CC: support@example.com"
# Email sender address (for all emails sent by the application)
sender: "noreply@example.org"
# Catch all e-mails sent to a configured e-mail address
catch_all: false
# Web Stats JS code
webstats_js_code: ''
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

2
skel/data/logs/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.log
*.log.*

1
skel/data/tmp/templates_c/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.tpl.php

53
skel/includes/core.php Normal file
View file

@ -0,0 +1,53 @@
<?php
use EesyPHP\App;
use EesyPHP\I18n;
use EesyPHP\SentrySpan;
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
// Root directory path
$script = null;
if (defined('__FILE__') && constant('__FILE__')) {
$script = __FILE__;
}
else {
// Fallback method : detect path from core.php path
foreach(get_included_files() as $script)
if (basename($script) == 'core.php')
break;
}
if (!$script) die('Fail to detect root directory path');
$root_dir_path = realpath(dirname($script).'/../');
// Include App's includes and vendor directories to PHP include paths
set_include_path($root_dir_path.'/includes' . PATH_SEPARATOR . get_include_path());
// Load composer autoload.php
require("$root_dir_path/vendor/autoload.php");
// Initialize EesyPHP application
App::init(
"$root_dir_path/config.yml",
array(
'overwrite_config_files' => array(
"$root_dir_path/config.local.yml",
),
'templates' => array(
'static_directories' => array(
"$root_dir_path/static"
),
),
),
$root_dir_path
);
$sentry_span = new SentrySpan('core.init', 'Core initialization');
// Put here your own initialization stuff
require 'views/index.php';
$sentry_span->finish();
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

View file

@ -0,0 +1,5 @@
<?php
// Put in this file/directory the code of your views handlers
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

4
skel/locales/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
headers.pot
js-*-messages.pot
php-messages.pot
templates-*-messages.pot

View file

@ -0,0 +1,8 @@
RewriteEngine On
#RewriteBase /app
# If the request is not for a valid file
RewriteCond %{REQUEST_FILENAME} !-f
# If the request is not for a valid link
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^ index.php [L]

View file

@ -0,0 +1,9 @@
<?php
include '../includes/core.php';
use EesyPHP\Url;
Url :: handle_request('');
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab

0
skel/templates/.gitignore vendored Normal file
View file

View file

@ -72,6 +72,8 @@ class App {
Email :: init(); Email :: init();
if (self :: get('i18n.enabled', true, 'bool')) if (self :: get('i18n.enabled', true, 'bool'))
I18n::init(); I18n::init();
if (self :: get('cli.enabled', true, 'bool'))
Cli::init();
$sentry_span->finish(); $sentry_span->finish();
} }

View file

@ -6,6 +6,24 @@ use Exception;
class Cli { class Cli {
/**
* Initialize
* @return void
*/
public static function init() {
if (php_sapi_name() != 'cli')
return;
self :: add_command(
'new_project',
array('\\EesyPHP\\Cli', 'cli_new_project'),
I18n :: ___("Create a new project using EesyPHP framework"),
null,
I18n :: ___(
"This command could be used to easily build the structure of a new project using the ".
"EesyPHP framework.")
);
}
/** /**
* Registered commands * Registered commands
* @var array<string,array> * @var array<string,array>
@ -180,4 +198,50 @@ class Cli {
} }
} }
/**
* Command to create new project based on EesyPHP framework
*
* @param array $command_args The command arguments
* @return void
*/
public static function cli_new_project($command_args) {
echo "This CLI tool permit to initialize a new project using the EesyPHP framework.\n";
readline('[Press enter to continue]');
echo "\n";
$root_path = null;
while (!$root_path) {
$root_path = getcwd();
$input = readline("Please enter the root directory of your new project [$root_path]: ");
if (empty($input))
break;
if (!is_dir($input))
echo "Invalid root directory specified: not found or is not a directory\n";
else if (!is_writeable($input))
echo "Invalid root directory specified: not writeable\n";
else
$root_path = $input;
}
$root_path = rtrim($root_path, "/");
$skel = __DIR__ . "/../skel";
foreach (
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($skel, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
) as $item
) {
$path = "$root_path/".$iterator->getSubPathname();
if ($item->isDir()) {
if (!mkdir($path))
die("Fail to create $path directory\n");
}
else {
if (!copy($item, $path))
die("Fail to copy file to $path\n");
}
}
echo "done. Start coding!\n";
}
} }

View file

@ -54,7 +54,7 @@ img { max-width: 100%; }
th a, th a:hover { th a, th a:hover {
color: #000; color: #000;
text-decoration: none; text-decoration: none;
} }
/* /*
* Forms * Forms

View file

@ -20,7 +20,7 @@
<!-- Font Awesome --> <!-- Font Awesome -->
<link href="{static_url path="lib/Fork-Awesome-1.1.7/css/fork-awesome.min.css"}" rel="stylesheet"/> <link href="{static_url path="lib/Fork-Awesome-1.1.7/css/fork-awesome.min.css"}" rel="stylesheet"/>
<link href="{static_url path="css/style.css"}" rel="stylesheet"/> <link href="{static_url path="css/eesyphp.css"}" rel="stylesheet"/>
{foreach $css as $path} {foreach $css as $path}
<link href="{$path|escape:"quotes"}" rel="stylesheet"/> <link href="{$path|escape:"quotes"}" rel="stylesheet"/>
{/foreach} {/foreach}