강력한 보안 계층으로 둘러싸인 많은 기능을 캡슐화하는 작고 간단한 PHP MVC 프레임 워크 골격.
MINIPHP는 소규모 프로젝트에 유용한 매우 간단한 응용 프로그램으로, PHP MVC 골격을 이해하고, 데이터를 인증하고 승인하고, 데이터를 암호화하고, 보안 개념을 적용하고, 소독 및 검증을 적용하고, AJAX 호출을하는 방법을 알고 있습니다.
전체 프레임 워크도 아니지만 매우 기본적인 프레임 워크는 아니지만 복잡하지 않습니다. 모든 프로젝트에서 쉽게 설치, 이해 및 사용할 수 있습니다.
프레임 워크의 복잡성을 제거하는 것은 들여 쓰기되었습니다. 라우팅, 인증, 권한 부여, 사용자 세션 및 쿠키 및 쿠키 등과 같은 것은 스크래치에서 발명 한 것이 아니지만 다른 프레임 워크에서 이미 구현 된 개념의 집계이지만 훨씬 간단한 방식으로 구축되므로 이해하고 더 나아갈 수 있습니다.
더 큰 응용 프로그램을 구축하고 프레임 워크에서 사용 가능한 대부분의 기능을 활용 해야하는 경우 CakePhp, Laravel, Symphony를 볼 수 있습니다.
어느 쪽이든, PHP MVC 골격을 이해하고 인증 및 승인, 보안 문제에 대해 배우고, 어떻게 패배 할 수 있는지, 프레임 워크를 사용하여 자신의 응용 프로그램을 구축하는 방법을 알고있는 것이 중요합니다.
GitHub 자동 페이지 생성기가 작성한 전체 문서도 여기에서 찾을 수 있습니다.
라이브 데모는 여기에서 제공됩니다. 라이브 데모는이 섹션 의이 프레임 워크 위에 구축 된 데모 애플리케이션을위한 것입니다. @EverterStraat에 감사드립니다.
일부 기능은 데모에서 작동하지 않습니다.
작곡가를 통해 설치하십시오
composer install
응용 프로그램에 요청할 때마다 공개 폴더 내부의 Index.php 로 지시됩니다. 따라서 요청을하는 경우 http://localhost/miniPHP/User/update/412 . 이것은 나뉘어지고 번역됩니다
실제로, htaccess는 모든 것이 http://localhost/miniPHP 이후에 나오고 querystring 인수로 URL에 추가합니다. 따라서이 요청은 http://localhost/miniPHP?url='User/update/412' 로 변환됩니다.
그런 다음 splitUrl() 내부의 App 클래스는 쿼리 문자열 $_GET['url'] 컨트롤러, 액션 메소드 및 전달 된 인수로 분할됩니다.
App Class에서 Inside run() 에서는 컨트롤러 클래스에서 객체를 인스턴스화하고 Call to Action 메소드를 작성하여 존재하는 경우 인수를 전달합니다.
App 클래스가 컨트롤러 개체를 무관하게하면 $this->controller->startupProcess() 메소드를 호출하여 3 연속 이벤트/메소드를 트리거합니다.
initialize() : 컴포넌트를로드하는 데 사용하십시오beforeAction() : 컨트롤러의 조치 방법을 호출하기 전에 로직 조치 수행triggerComponents() :로드 된 구성 요소의 trigger startup () 메소드 Controller 클래스의 생성자를 재정의 하지 않아야하며 대신 확장 클래스에서 initialize() & beforeAction() 메소드를 무시할 수 있습니다.
구축 자의 시작 프로세스가 작업을 마치면 요청 된 조치 방법이 호출되고 인수가 통과됩니다 (있는 경우).
구성 요소는 중간 전쟁입니다. 컨트롤러의 일부로 사용될 재사용 가능한 논리를 제공합니다. 인증, 승인, 양식 변조 및 검증 CSRF 토큰은 구성 요소 내부에서 구현됩니다.
이러한 논리를 컨트롤러 클래스에서 가져오고 이러한 구성 요소 내부에 다양한 작업과 검증을 유지하는 것이 좋습니다.
모든 구성 요소는 Component 라는 기본/슈퍼 클래스에서 상속됩니다. 각각 정의 된 작업이 있습니다. 인증 및 승인을 위해 Auth를 호출하는 두 가지 구성 요소가 있고 다른 하나는 다른 보안 문제에 대해 보안을 불렀습니다.
그것들은 다루기가 매우 간단하며 내부 컨트롤러 생성자라고 불립니다.
사용자는 올바른 자격 증명이 있습니까?
인증자는 사용자 세션을 관리합니다.
X 액션에 액세스하거나 수행 할 권리가 있습니까?. 인증 구성 요소는 각 컨트롤러의 승인을 처리합니다. 따라서 각 컨트롤러는 isAuthorized() 메소드를 구현해야합니다. 당신이해야 할 일은 boolean 가치를 반환하는 것입니다.
예를 들어, 현재 사용자가 관리자인지 아닌지 확인하려면 다음과 같은 작업을 수행 할 수 있습니다.
// AdminController
public function isAuthorized (){
$ role = Session:: getUserRole ();
if ( isset ( $ role ) && $ role === " admin " ){
return true ;
}
return false ;
} 더 자세히 취하고 허가 규칙을 적용하려면 권한 규칙을 정의 할 수있는 Permission 이라는 강력한 클래스가 있습니다. 이 클래스를 사용하면 "현재 컨트롤러에서 특정 작업 방법을 수행 할 수있는 사람"을 정의 할 수 있습니다.
예를 들어, 관리자는 메모에 대한 조치를 수행 할 수 있도록하는 반면, 일반 사용자는 메모 만 편집 할 수 있습니다.
// NotesController
public function isAuthorized (){
$ action = $ this -> request -> param ( ' action ' );
$ role = Session:: getUserRole ();
$ resource = " notes " ;
// only for admins
// they are allowed to perform all actions on $resource
Permission:: allow ( ' admin ' , $ resource , [ ' * ' ]);
// for normal users, they can edit only if the current user is the owner
Permission:: allow ( ' user ' , $ resource , [ ' edit ' ], ' owner ' );
$ noteId = $ this -> request -> data ( " note_id " );
$ config = [
" user_id " => Session:: getUserId (),
" table " => " notes " ,
" id " => $ noteId
];
// providing the current user's role, $resource, action method, and some configuration data
// Permission class will check based on rules defined above and return boolean value
return Permission:: check ( $ role , $ resource , $ action , $ config );
}이제 사용자의 역할, 리소스 및 각 조치 방법에 따라 승인을 확인할 수 있습니다.
SecurityComponent는 다양한 보안 작업 및 검증을 처리합니다.
요청 방법을 제한하는 것이 중요합니다. 예를 들어, 양식 값을 수락하는 조치 방법이 있으면 사후 요청 만 허용됩니다. Ajax, Get, ..etc에 대한 동일한 아이디어. beforeAction() 메소드 내부 에서이 작업을 수행 할 수 있습니다.
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ actions = [ ' create ' , ' delete ' ];
$ this -> Security -> requireAjax ( $ actions );
$ this -> Security -> requirePost ( $ actions );
}또한 모든 요청이 보안 연결을 통해 필요한 경우 전체 컨트롤러 또는 HTTP 대신 HTTPS로 모든 요청을 리디렉션하기 위해 특정 작업을 구성 할 수 있습니다.
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ actions = [ ' create ' , ' delete ' ]; // specific action methods
$ actions = [ ' * ' ]; // all action methods
$ this -> Security -> requireSecure ( $ actions );
}요청이 동일한 도메인에서 오는지 확인하고 확인합니다. 그것들은 가짜 일 수 있지만 보안 계층의 일부로 유지하는 것이 좋습니다.
사후 요청에서 제출 된 양식을 확인하십시오. 이 방법의 함정은 예상 양식 필드 또는 사후 요청으로 전송 될 데이터를 정의해야한다는 것입니다.
기본적으로 프레임 워크는 포스트 요청이 이루어질 때 양식 변조에 대한 유효성을 유지하며 CSRF 토큰이 양식 필드와 함께 전달되는지 확인합니다. 이 상황에서 CSRF 토큰을 통과하지 못한 경우 보안 스레드로 간주됩니다.
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ action = $ this -> request -> param ( ' action ' );
$ actions = [ ' create ' , ' delete ' ];
$ this -> Security -> requireAjax ( $ actions );
$ this -> Security -> requirePost ( $ actions );
switch ( $ action ){
case " create " :
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' note_text ' ]]);
break ;
case " delete " :
// If you want to disable validation for form tampering
// $this->Security->config("validateForm", false);
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' note_id ' ]]);
break ;
}
}CSRF 토큰은 제출 된 양식을 검증하고 위조되지 않도록하는 데 중요합니다. 해커는 사용자에게 웹 사이트에 요청하거나 링크를 클릭하도록 할 수 있습니다.
특정 기간 (> = 1 일)에 유효한 다음, 사용자 세션에 재생 및 저장됩니다.
CSRF 유효성 검사는 기본적으로 비활성화됩니다. CSRF 토큰을 검증하려면 아래 예제와 같이 validateCsrfToken true 에 할당하십시오. 요청이 게시되고 양식 변조가 활성화되면 CSRF 유효성 검사가 강요됩니다.
이제 모든 요청에서 CSRF 토큰을 수동으로 확인할 필요가 없습니다. 보안 구성 요소는 요청에서 토큰을 확인합니다.
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ action = $ this -> request -> param ( ' action ' );
$ actions = [ ' index ' ];
$ this -> Security -> requireGet ( $ actions );
switch ( $ action ){
case " index " :
$ this -> Security -> config ( " validateCsrfToken " , true );
break ;
}
}CSRF 토큰은 세션 당 생성됩니다. 숨겨진 양식 필드를 추가하거나 URL에서 쿼리 매개 변수로 추가 할 수 있습니다.
형태
<input type="hidden" name="csrf_token" value="<?= Session::generateCsrfToken(); ?>" />
URL
<a href="<?= PUBLIC_ROOT . "?csrf_token=" . urlencode(Session::generateCsrfToken()); ?>">Link</a>
자바 스크립트
CSRF 토큰을 JavaScript 변수에 할당 할 수도 있습니다.
<script>config = <?= json_encode(Session::generateCsrfToken()); ?>;</script>
index.php 로 리디렉션됩니다. 때로는 인증이나 인증없이 컨트롤러를 갖고 싶거나 보안 구성 요소가 활성화 될 때와 같은 이러한 구성 요소를 제어해야합니다. 이는 컨트롤러 클래스 내부의 initialize() 메소드를 사용하여 수행 할 수 있으며 필요한 구성 요소 만로드 할 수 있습니다.
예 1 : 구성 요소, 인증 또는 권한 부여 또는 보안 유효성 검사를로드하지 마십시오.
public function initialize (){
$ this -> loadComponents ([]);
}예제 2 : 보안을로드하고 구성 요소를 부드링하지만 액션 메소드 내부의 인증 구성 요소를 사용하려는 경우를 대비하여 인증 및 승인하지 마십시오. LoginController는 로그인 사용자가 필요하지 않고 페이지에 액세스하는 방법 에 대한 예입니다.
public function initialize (){
$ this -> loadComponents ([
' Auth ' ,
' Security '
]);
}예 3 : 보안을로드하고 구성 요소를 인증하고 사용자 인증 및 현재 컨트롤러에 대한 승인. 이것은 코어/컨트롤러 클래스의 기본 동작입니다
public function initialize (){
$ this -> loadComponents ([
' Auth ' => [
' authenticate ' => [ ' User ' ],
' authorize ' => [ ' Controller ' ]
],
' Security '
]);
}액션 메소드 내에서 모델을 호출하여 일부 데이터를 가져 오거나 views 폴더 내부에서 페이지를 렌더링 할 수 있습니다.
// NotesController
public function index (){
// render full page with layout(header and footer)
$ this -> view -> renderWithLayouts (Config:: get ( ' VIEWS_PATH ' ) . " layout/default/ " , Config:: get ( ' VIEWS_PATH ' ) . ' notes/index.php ' );
// render page without layout
$ this -> view -> render (Config:: get ( ' VIEWS_PATH ' ) . ' notes/note.php ' );
// get the rendered page
$ html = $ this -> view -> render (Config:: get ( ' VIEWS_PATH ' ) . ' notes/note.php ' );
// render a json view
$ this -> view -> renderJson ( array ( " data " => $ html ));
}MVC에서 모델은 정보 (데이터) 및 비즈니스 규칙을 나타냅니다. 보기에는 텍스트, 양식 입력과 같은 사용자 인터페이스의 요소가 포함되어 있습니다. 컨트롤러는 모델과보기 간의 통신을 관리합니다. 원천
생성, 삭제, 업데이트 및 유효성 검사와 같은 모든 작업은 모델 클래스에서 구현됩니다.
// NotesController
public function create (){
// get content of note submitted to a form
// then pass the content along with the current user to Note class
$ content = $ this -> request -> data ( " note_text " );
$ note = $ this -> note -> create (Session:: getUserId (), $ content );
if (! $ note ){
$ this -> view -> renderErrors ( $ this -> note -> errors ());
} else {
return $ this -> redirector -> root ( " Notes " );
}
}메모 모델에서
// Notes Model
public function create ( $ userId , $ content ){
// using validation class(see below)
$ validation = new Validation ();
if (! $ validation -> validate ([ ' Content ' => [ $ content , " required|minLen(4)|maxLen(300) " ]])) {
$ this -> errors = $ validation -> errors ();
return false ;
}
// using database class to insert new note
$ database = Database:: openConnection ();
$ query = " INSERT INTO notes (user_id, content) VALUES (:user_id, :content) " ;
$ database -> prepare ( $ query );
$ database -> bindValue ( ' :user_id ' , $ userId );
$ database -> bindValue ( ' :content ' , $ content );
$ database -> execute ();
if ( $ database -> countRows () !== 1 ){
throw new Exception ( " Couldn't create note " );
}
return true ;
}프레임 워크를 사용하면 로그인, 등록 및 로그 아웃이있을 수 있습니다. 이러한 작업은 App/Models/Login & App/Controllers/LoginController 에서 구현됩니다. 대부분의 상황에서는 로그인 작업과 관련된 것을 수정할 필요가 없으며 프레임 워크의 동작을 이해합니다.
참고 SSL이없는 경우 클라이언트 측에서 수동으로 데이터를 암호화하는 것이 좋습니다. 그렇다면이 글을 읽으십시오.
사용자가 등록 할 때마다 암호화 된 사용자 ID와 연결된 토큰으로 이메일이 전송됩니다. 이 토큰은 24 시간 후에 만료됩니다. 이 토큰을 만료하고 등록 된 이메일이 만료되면 재사용하는 것이 훨씬 낫습니다.
PHP v5.5의 최신 알고리즘을 사용하여 암호는 해시됩니다.
$ hashedPassword = password_hash ( $ password , PASSWORD_DEFAULT , array ( ' cost ' => Config:: get ( ' HASH_COST_FACTOR ' )));사용자가 자신의 비밀번호를 잊어 버리면 복원 할 수 있습니다. 만료 된 토큰에 대한 같은 아이디어가 여기에 간다.
또한 특정 기간 (> = 10 분) 동안 잊혀진 암호 수를 초과 한 경우 특정 지속 시간 (> = 10 분) 동안 사용자를 차단하십시오.
무차별 적 공격은 해커가 올바른 암호를 찾을 때까지 가능한 모든 입력 조합을 시도 할 때입니다.
해결책:
보안 문자는 특히 자동 로그인을 방지하는 데 효과적입니다. Captcha를 사용하여 멋진 PHP 보안 문자 라이브러리를 사용합니다.
IP 주소 차단은 마지막으로 생각하는 솔루션입니다. 동일한 IP가 다른 자격 증명 (> = 10)을 사용하여 여러 번 로그인하지 않은 경우 IP 주소가 차단됩니다.
PHP (PHP Data Objects)는 데이터베이스 쿼리를 준비하고 실행하는 데 사용됩니다. Database 클래스 내부에는 복잡성을 숨기고 데이터베이스 개체를 인스턴스화하고 몇 줄로 준비하고 바인딩하고 실행하는 다양한 방법이 있습니다.
SELECT, INSERT, UPDATE, DELETE 사용자에게 충분합니다Admin 클래스에 언급되어 있습니다.utf8mb4 사용해야합니다.utf8 Charset은 1 ~ 3 바이트로 구성된 UTF-8 인코딩 된 기호를 저장합니다. 그러나 4 바이트가있는 기호는 할 수 없습니다.utf8 입니다. 그러나 utf8mb4 로 업그레이드하려면 다음을 따르십시오.utf8mb4 로 변경하는 것을 잊지 마십시오 Encryption 클래스는 데이터의 암호화 및 암호 해독을 담당합니다. 암호화는 쿠키, 사용자 ID, Post ID, ..etc와 같은 것에 적용됩니다. 암호화 된 문자열은 인증되어 있으며 암호화 할 때마다 다릅니다.
유효성 검사는 사용자 입력을 검증하기위한 작은 라이브러리입니다. 모든 유효성 검사 규칙은 내부 Validation 클래스입니다.
$ validation = new Validation ();
// there are default error messages for each rule
// but, you still can define your custom error message
$ validation -> addRuleMessage ( " emailUnique " , " The email you entered is already exists " );
if (! $ validation -> validate ([
" User Name " => [ $ name , " required|alphaNumWithSpaces|minLen(4)|maxLen(30) " ],
" Email " => [ $ email , " required|email|emailUnique|maxLen(50) " ],
' Password ' => [ $ password , " required|equals( " . $ confirmPassword . " )|minLen(6)|password " ],
' Password Confirmation ' => [ $ confirmPassword , ' required ' ]])) {
var_dump ( $ validation -> errors ());
} Handler 클래스는 모든 예외와 오류를 처리 할 책임이 있습니다. 로거를 사용하여 로그 오류를 사용합니다. 모든 오류가 App/Logs/Log.txt 에 로그인하고 저장되므로 오류보고는 기본적으로 꺼집니다.
오류가 발생하거나 예외가 발생하면 응용 프로그램에 시스템 내부 오류가 표시됩니다 (500).
모든 것을 기록하고 app/log/log.txt 에 저장할 수있는 장소. 실패, 오류, 예외 또는 기타 악의적 인 행동 또는 공격을 작성할 수 있습니다.
Logger:: log ( " COOKIE " , self :: $ userId . " is trying to login using invalid cookie " , __FILE__ , __LINE__ ); 이메일은 이메일을 보내기위한 다른 라이브러리 인 SMTP를 통해 phpmailer를 사용하여 전자 메일을 전송합니다. PHP의 mail() 함수를 사용해서는 안됩니다.
app/config 에는 메인 애플리케이션 구성을 위해 config.php 라는 두 개의 파일이 있고 다른 하나는 javaScript.php 라는 JavaScript의 파일이 있습니다. 그런 다음 JavaScript 구성은 Fumer.php 의 JavaScript 변수에 할당됩니다.
요청을 보내고 응답을 받으려면 Ajax 전화에 따라 달라질 수 있습니다. 이 프레임 워크는 조치를 수행하기위한 AJAX 요청에 크게 의존하지만, 약간의 조정만으로 정상적인 요청에 대해 동일한 작업을 수행 할 수 있습니다.
config 객체는 족지에서 key-value 쌍에 할당됩니다. 이 키 값 쌍은 Config::setJsConfig('key', "value"); , 그 후 객체를 구성 하도록 할당됩니다.
AJAX AJAX 요청을 보내기위한 두 가지 주요 기능이있는 네임 스페이스입니다. 하나는 일반적인 Ajax 호출이고 다른 하나는 파일 업로드를위한 것입니다.
도우미 다양한 기능이있는 네임 스페이스에 오류가 표시되고 직렬화, 리디렉션, Encodehtml 등이 표시됩니다.
앱 현재 페이지의 전체 JavaScript 이벤트를 비현실화하는 데 사용되는 네임 스페이스
이벤트 이벤트 사용자가 링크를 클릭하여 생성, 삭제 또는 업데이트를 클릭 할 때와 같이 발생할 수있는 모든 이벤트를 선언하는 데 사용되는 네임 스페이스.
실제 상황에서 프레임 워크를 사용하는 방법을 보여주기 위해 프레임 워크에는 사용자 프로필 관리, 대시 보드, 뉴스 피드, 업로드 및 다운로드 파일, 게시물 및 댓글, 페이지 매김, 관리자 패널, 시스템 백업 관리, 보고서 버그 등과 같은 기능에 대한 구현이 제공됩니다.
단계 :
자격 증명으로 앱/config/config.php 에서 구성 파일을 편집하십시오
_ 설치 디렉토리에서 SQL 쿼리를 순서대로 실행하십시오
로그인
이메일 설정
app/config/config.php 에서 SMTP 계정 데이터를 구성해야합니다. 그러나 SMTP 계정이없는 경우 Logger를 사용하여 App/Logs/Log.txt 에 이메일을 저장합니다.
이렇게하려면 Core/이메일에서 주석 $mail->Send() & Uncomment Logger::log("EMAIL", $mail->Body);
모든 사용자는 이름, 이메일, 비밀번호를 변경할 수 있습니다. 또한 프로필 사진을 업로드하십시오 (즉, 처음에는 Default.png에 할당 됨).
사용자가 자신의 이메일을 변경하도록 요청할 때마다 알림이 사용자의 이전 이메일과 새 이메일로 전송됩니다.
이전 이메일로 전송 된 알림은 사용자에게 이메일 변경을 취소 할 수있는 기회를 제공하는 반면 새 이메일로 전송 된 알림은 확인을 요청합니다. 사용자는 변경 사항을 확인할 때까지 이전 이메일로 로그인 할 수 있습니다.
이것은 UserController , Methods updateProfileInfo() , revokeEmail() 및 updateEmail() 에서 수행됩니다. 대부분의 상황에서는 이러한 방법의 동작을 수정할 필요가 없습니다.
파일을 업로드하고 다운로드 할 수 있습니다.
file_uploads true로 설정하십시오upload_max_filesize, max_file_uploads, post_max_size 설정하십시오뉴스 피드를 트위터에서 트윗으로 생각하고 Github에서 문제를 열 때와 같은 게시물로 생각하십시오.
이 프레임 워크의 상단에 구현됩니다.
관리자는 일반 사용자가 할 수없는 행동을 수행 할 수 있습니다. 그들은 뉴스 피드, 게시 또는 댓글을 삭제, 편집, 생성 할 수 있습니다. 또한 모든 사용자 프로필을 제어하고 백업을 작성 및 복원합니다.
관리자만이 등록 된 모든 사용자를 볼 수 있습니다. 정보를 삭제하고 편집 할 수 있습니다.
대부분의 상황에서는 시스템의 백업을 생성하고 원할 때마다 복원해야합니다.
이것은 mysqldump를 사용하여 백업을 생성하고 복원하여 수행됩니다. 모든 백업은 앱/백업 에 저장됩니다.
Facebook에서 빨간색 알림을 보았습니까? 같은 생각이 여기에 있습니다. 그러나 대신 트리거를 사용하여 구현됩니다. 트리거는 _ installation/triggers.sql 에 정의됩니다.
따라서 사용자가 새로운 뉴스 피드, 게시 또는 파일을 업로드 할 때마다 다른 모든 사용자의 카운트가 증가하고 탐색 표시 줄에 빨간색 알림이 표시됩니다.
사용자는 버그, 기능 및 향상을보고 할 수 있습니다. 양식을 제출하면 앱/config/config.php 에 정의 된 ADMIN_EMAIL 로 이메일이 전송됩니다.
간단한 TODO 응용 프로그램을 구축하고 싶다고 가정 해 봅시다. 여기에서는 Ajax 호출이없는 프레임 워크를 사용하여 Todo 앱을 만드는 방법에 대해 단계별로 진행할 것입니다.
(1) 위의 설치 설정 단계를 따르는 경우 초기 사용자 계정을 만드는 데 아무런 문제가 없어야합니다.
(2) int, content varchar, user_id로 ID가있는 테이블 만들기 users 테이블 테이블 이외의 테이블을 만듭니다.
CREATE TABLE ` todo ` (
` id ` int ( 11 ) NOT NULL AUTO_INCREMENT,
` user_id ` int ( 11 ) NOT NULL ,
` content ` varchar ( 512 ) NOT NULL ,
PRIMARY KEY ( ` id ` ),
FOREIGN KEY ( ` user_id ` ) REFERENCES ` users ` ( ` id ` ) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_general_ci;(3) 토지 컨트롤러를 만듭니다
앱/컨트롤러 내부에서 TodoController.php 라는 파일을 만듭니다
class TodoController extends Controller{
// override this method to perform any logic before calling action method as explained above
public function beforeAction (){
parent :: beforeAction ();
// define the actions in this Controller
$ action = $ this -> request -> param ( ' action ' );
// restrict the request to action methods
// $this->Security->requireAjax(['create', 'delete']);
$ this -> Security -> requirePost ([ ' create ' , ' delete ' ]);
// define the expected form fields for every action if exist
switch ( $ action ){
case " create " :
// you can exclude form fields if you don't care if they were sent with form fields or not
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' content ' ]]);
break ;
case " delete " :
// If you want to disable validation for form tampering
// $this->Security->config("validateForm", false);
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' todo_id ' ]]);
break ;
}
}
public function index (){
$ this -> view -> renderWithLayouts (Config:: get ( ' VIEWS_PATH ' ) . " layout/todo/ " , Config:: get ( ' VIEWS_PATH ' ) . ' todo/index.php ' );
}
public function create (){
$ content = $ this -> request -> data ( " content " );
$ todo = $ this -> todo -> create (Session:: getUserId (), $ content );
if (! $ todo ){
// in case of normal post request
Session:: set ( ' errors ' , $ this -> todo -> errors ());
return $ this -> redirector -> root ( " Todo " );
// in case of ajax
// $this->view->renderErrors($this->todo->errors());
} else {
// in case of normal post request
Session:: set ( ' success ' , " Todo has been created " );
return $ this -> redirector -> root ( " Todo " );
// in case of ajax
// $this->view->renderJson(array("success" => "Todo has been created"));
}
}
public function delete (){
$ todoId = Encryption:: decryptIdWithDash ( $ this -> request -> data ( " todo_id " ));
$ this -> todo -> delete ( $ todoId );
// in case of normal post request
Session:: set ( ' success ' , " Todo has been deleted " );
return $ this -> redirector -> root ( " Todo " );
// in case of ajax
// $this->view->renderJson(array("success" => "Todo has been deleted"));
}
public function isAuthorized (){
$ action = $ this -> request -> param ( ' action ' );
$ role = Session:: getUserRole ();
$ resource = " todo " ;
// only for admins
Permission:: allow ( ' admin ' , $ resource , [ ' * ' ]);
// only for normal users
Permission:: allow ( ' user ' , $ resource , [ ' delete ' ], ' owner ' );
$ todoId = $ this -> request -> data ( " todo_id " );
if (! empty ( $ todoId )){
$ todoId = Encryption:: decryptIdWithDash ( $ todoId );
}
$ config = [
" user_id " => Session:: getUserId (),
" table " => " todo " ,
" id " => $ todoId ];
return Permission:: check ( $ role , $ resource , $ action , $ config );
}
} (4) 앱/모델 에서 Todo.php 라는 노트 모델 클래스를 만듭니다
class Todo extends Model{
public function getAll (){
$ database = Database:: openConnection ();
$ query = " SELECT todo.id AS id, users.id AS user_id, users.name AS user_name, todo.content " ;
$ query .= " FROM users, todo " ;
$ query .= " WHERE users.id = todo.user_id " ;
$ database -> prepare ( $ query );
$ database -> execute ();
$ todo = $ database -> fetchAllAssociative ();
return $ todo ;
}
public function create ( $ userId , $ content ){
// using validation class
$ validation = new Validation ();
if (! $ validation -> validate ([ ' Content ' => [ $ content , " required|minLen(4)|maxLen(300) " ]])) {
$ this -> errors = $ validation -> errors ();
return false ;
}
// using database class to insert new todo
$ database = Database:: openConnection ();
$ query = " INSERT INTO todo (user_id, content) VALUES (:user_id, :content) " ;
$ database -> prepare ( $ query );
$ database -> bindValue ( ' :user_id ' , $ userId );
$ database -> bindValue ( ' :content ' , $ content );
$ database -> execute ();
if ( $ database -> countRows () !== 1 ){
throw new Exception ( " Couldn't create todo " );
}
return true ;
}
public function delete ( $ id ){
$ database = Database:: openConnection ();
$ database -> deleteById ( " todo " , $ id );
if ( $ database -> countRows () !== 1 ){
throw new Exception ( " Couldn't delete todo " );
}
}
}(5) 내부 관점/
(a) header.php & footer.php 내부 보기/레이아웃/Todo를 만듭니다
<! DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf- 8 ">
<meta http-equiv="X- UA -Compatible" content=" IE =edge">
<meta name="viewport" content="width=device-width, initial-scale= 1 ">
<meta name="description" content="mini PHP ">
<meta name="author" content="mini PHP ">
<title>mini PHP </title>
<!-- Stylesheets -->
<link rel="stylesheet" href=" <?= PUBLIC_ROOT ; ?> css/bootstrap.min.css">
<link rel="stylesheet" href=" <?= PUBLIC_ROOT ; ?> css/sb-admin-2.css">
<link rel="stylesheet" href=" <?= PUBLIC_ROOT ; ?> css/font-awesome.min.css" rel="stylesheet" type="text/css">
<!-- Styles for ToDo Application -->
<style>
.todo_container{
width:80%;
margin: 0 auto;
margin-top: 5%
}
#todo-list li{
list-style-type: none;
border: 1px solid #e7e7e7;
padding: 3px;
margin: 3px;
}
#todo-list li:hover{
background-color: #eee;
}
form button{
float:right;
margin: 3px;
}
form:after{
content: '';
display: block;
clear: both;
}
</style>
</head>
<body> <!-- footer -->
<script src="https: //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!--<script src=" <?= PUBLIC_ROOT ; ?> js/jquery.min.js"></script>-->
<script src=" <?= PUBLIC_ROOT ; ?> js/bootstrap.min.js"></script>
<script src=" <?= PUBLIC_ROOT ; ?> js/sb-admin-2.js"></script>
<script src=" <?= PUBLIC_ROOT ; ?> js/main.js"></script>
<!-- Assign CSRF Token to JS variable -->
<?php Config:: setJsConfig ( ' csrfToken ' , Session:: generateCsrfToken ()); ?>
<!-- Assign all configration variables -->
<script>config = <?= json_encode (Config:: getJsConfig ()); ?> ;</script>
<!-- Run the application -->
<script>$(document).ready(app.init());</script>
<?php Database:: closeConnection (); ?>
</body>
</html> (b) index.php 가있는 내부 뷰/ 생성 TODO 폴더는 TODO 목록이 포함됩니다.
<div class="todo_container">
<h2> TODO Application</h2>
<!-- in case of normal post request -->
<form action= " <?= PUBLIC_ROOT . " Todo/create " ?> " method="post">
<label>Content <span class="text-danger " >*</span></label>
<textarea name= " content" class ="form-control " required placeholder= " What are you thinking? " ></textarea>
<input type='hidden' name = "csrf_token" value = " <?= Session:: generateCsrfToken (); ?> ">
<button type="submit" name="submit" value="submit" class="btn btn-success">Create</button>
</form>
<!-- in case of ajax request
<form action= "#" id="form-create-todo" method="post">
<label>Content <span class="text-danger">*</span></label>
<textarea name="content" class="form-control" required placeholder="What are you thinking?"></textarea>
<button type="submit" name="submit" value="submit" class="btn btn-success">Create</button>
</form>
-->
<br>
<?php
// display success or error messages in session
if (! empty (Session:: get ( ' success ' ))){
echo $ this -> renderSuccess (Session:: getAndDestroy ( ' success ' ));
} else if (! empty (Session:: get ( ' errors ' ))){
echo $ this -> renderErrors (Session:: getAndDestroy ( ' errors ' ));
}
?>
<br><hr><br>
<ul id="todo-list">
<?php
$ todoData = $ this -> controller -> todo -> getAll ();
foreach ( $ todoData as $ todo ){
?>
<li>
<p> <?= $ this -> autoLinks ( $ this -> encodeHTMLWithBR ( $ todo [ " content " ])); ?> </p>
<!-- in case of normal post request -->
<form action= " <?= PUBLIC_ROOT . " Todo/delete " ?> " method="post">
<input type='hidden' name= "todo_id" value=" <?= " todo- " . Encryption:: encryptId ( $ todo [ " id " ]); ?> ">
<input type='hidden' name = "csrf_token" value = " <?= Session:: generateCsrfToken (); ?> ">
<button type="submit" name="submit" value="submit" class="btn btn-xs btn-danger">Delete</button>
</form>
<!-- in case of ajax request
<form class="form-delete-todo" action= "#" method="post">
<input type='hidden' name= "todo_id" value=" <?= " todo- " . Encryption:: encryptId ( $ todo [ " id " ]); ?> ">
<button type="submit" name="submit" value="submit" class="btn btn-xs btn-danger">Delete</button>
</form>
-->
</li>
<?php } ?>
</ul>
</div>(6) JavaScript 코드는 Ajax 전화를 보내고 ranning respond
// first, we need to initialize the todo events whenever the application initalized
// the app.init() is called in footer.php, see views/layout/todo/footer.php
var app = {
init : function ( ) {
events . todo . init ( ) ;
}
} ;
// inside var events = {....} make a new key called "todo"
var events = {
// ....
todo : {
init : function ( ) {
events . todo . create ( ) ;
events . todo . delete ( ) ;
} ,
create : function ( ) {
$ ( "#form-create-todo" ) . submit ( function ( e ) {
e . preventDefault ( ) ;
ajax . send ( "Todo/create" , helpers . serialize ( this ) , createTodoCallBack , "#form-create-todo" ) ;
} ) ;
function createTodoCallBack ( PHPData ) {
if ( helpers . validateData ( PHPData , "#form-create-todo" , "after" , "default" , "success" ) ) {
alert ( PHPData . success + " refresh the page to see the results" ) ;
}
}
} ,
delete : function ( ) {
$ ( "#todo-list form.form-delete-todo" ) . submit ( function ( e ) {
e . preventDefault ( ) ;
if ( ! confirm ( "Are you sure?" ) ) { return ; }
var cur_todo = $ ( this ) . parent ( ) ;
ajax . send ( "Todo/delete" , helpers . serialize ( this ) , deleteTodoCallBack , cur_todo ) ;
function deleteTodoCallBack ( PHPData ) {
if ( helpers . validateData ( PHPData , cur_todo , "after" , "default" , "success" ) ) {
$ ( cur_todo ) . remove ( ) ;
alert ( PHPData . success ) ;
}
}
} ) ;
}
}
}나는 공부 중에 여가 시간 에이 대본을 썼습니다. 이것은 무료, 무급입니다. 나는 많은 개발자들이 어떤 소프트웨어에 대해 매우 무례한 행동을하는 것을 보았 기 때문에 나는 이것을 말하고 있으며, 그들의 행동은 정말 실망 스럽다. 왜 그런지 모르겠어요?! 모든 사람은 불평하고 가혹한 말을하는 경향이 있습니다. 나는 피드백을 받아들이지 만, 훌륭하고 정중 한 방식으로.
구매를 위해 온라인으로 다른 많은 스크립트가 있으며 (그 이상은 아니더라도) 저자는 그로부터 좋은 돈을 벌고 있지만, 나는 그것을 공개적으로 유지하기로 선택하고 모든 사람이 이용할 수 있습니다.
당신이 무언가를 배웠거나 내가 당신의 시간을 절약했다면, 단어를 전파하여 프로젝트를 지원하십시오.
새로운 문제를 만들거나 GitHub에서 풀 요청을 보내거나 다음과 같은 이메일을 보낼 수 있습니다. [email protected]
MIT 라이센스에 따라 구축되었습니다.