facut pana la video 7
This commit is contained in:
@@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en-US">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<title>My test page</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Titlul paginii</h1>
|
|
||||||
<p>de Andrei Cerbu</p>
|
|
||||||
<h2>Capitolul 0</h2>
|
|
||||||
<p>
|
|
||||||
Acesta este un paragraf <strong>f interesant</strong> despre aceasta pagina foarte interesanta
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>Drive to the end of the road</li>
|
|
||||||
<li>Turn right</li>
|
|
||||||
<li>Go straight across the first two roundabouts</li>
|
|
||||||
<li>Turn left at the third roundabout</li>
|
|
||||||
<li>The school is on your right, 300 meters up the road</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
</br>
|
|
||||||
</br>
|
|
||||||
</br>
|
|
||||||
I'm creating a link to
|
|
||||||
<a href="https://www.mozilla.org/en-US/" title="exemplu jmecher">the Mozilla homepage</a>.
|
|
||||||
</p>
|
|
||||||
<hr></hr>
|
|
||||||
<address>Chris Mills, Manchester, The Grim North, UK</address>
|
|
||||||
<pre>Acesta este
|
|
||||||
un
|
|
||||||
test
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
DB_DSN = mysql:host=localhost;port=3306;dbname=Web
|
||||||
|
DB_USER = root
|
||||||
|
DB_PASSWORD = root
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
vendor
|
||||||
|
.idea
|
||||||
|
dbconfig
|
||||||
|
!.gitignore
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ErrorDocument 403 /404.php
|
||||||
|
ErrorDocument 404 /404.php
|
||||||
|
DirectoryINdex index.php
|
||||||
|
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine on
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^(.*)$ index.php?page=$1 [L,QSA]
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_URI} !^/$
|
||||||
|
RewriteCond %{REQUEST_URI} !^/admin
|
||||||
|
RewriteRule .*/ [L,R=301]
|
||||||
|
</IfModule>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "andrei/knowyourfood",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Cerbu Andrei Mihnea",
|
||||||
|
"email": "andreimihneacerbu@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload" : {
|
||||||
|
"psr-4" : {
|
||||||
|
"app\\": "./"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"vlucas/phpdotenv": "^5.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+488
@@ -0,0 +1,488 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "c85ce2bf1fa0099ca65ec02d2a26abb1",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "graham-campbell/result-type",
|
||||||
|
"version": "v1.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||||
|
"reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831",
|
||||||
|
"reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\ResultType\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An Implementation Of The Result Type",
|
||||||
|
"keywords": [
|
||||||
|
"Graham Campbell",
|
||||||
|
"GrahamCampbell",
|
||||||
|
"Result Type",
|
||||||
|
"Result-Type",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||||
|
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-02-25T20:23:15+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoption/phpoption",
|
||||||
|
"version": "1.9.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/schmittjoh/php-option.git",
|
||||||
|
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e",
|
||||||
|
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": true
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\": "src/PhpOption/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Johannes M. Schmitt",
|
||||||
|
"email": "schmittjoh@gmail.com",
|
||||||
|
"homepage": "https://github.com/schmittjoh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Option Type for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"language",
|
||||||
|
"option",
|
||||||
|
"php",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||||
|
"source": "https://github.com/schmittjoh/php-option/tree/1.9.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-02-25T19:38:58+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-ctype",
|
||||||
|
"version": "v1.27.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||||
|
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-ctype": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ctype": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.27-dev"
|
||||||
|
},
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Ctype\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Gert de Pagter",
|
||||||
|
"email": "BackEndTea@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for ctype functions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"ctype",
|
||||||
|
"polyfill",
|
||||||
|
"portable"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-mbstring",
|
||||||
|
"version": "v1.27.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
|
||||||
|
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.27-dev"
|
||||||
|
},
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for the Mbstring extension",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"mbstring",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-php80",
|
||||||
|
"version": "v1.27.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
|
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||||
|
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.27-dev"
|
||||||
|
},
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Php80\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ion Bazan",
|
||||||
|
"email": "ion.bazan@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vlucas/phpdotenv",
|
||||||
|
"version": "v5.5.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||||
|
"reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
|
||||||
|
"reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"graham-campbell/result-type": "^1.0.2",
|
||||||
|
"php": "^7.1.3 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.8",
|
||||||
|
"symfony/polyfill-ctype": "^1.23",
|
||||||
|
"symfony/polyfill-mbstring": "^1.23.1",
|
||||||
|
"symfony/polyfill-php80": "^1.23.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.4.1",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-filter": "Required to use the boolean validator."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": true
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.5-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dotenv\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vance Lucas",
|
||||||
|
"email": "vance@vancelucas.com",
|
||||||
|
"homepage": "https://github.com/vlucas"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||||
|
"keywords": [
|
||||||
|
"dotenv",
|
||||||
|
"env",
|
||||||
|
"environment"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||||
|
"source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-10-16T01:01:54+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.3.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controllers;
|
||||||
|
use app\core\middlewares\AuthMiddleware;
|
||||||
|
use app\core\Application;
|
||||||
|
use app\core\Controller;
|
||||||
|
use app\core\Request;
|
||||||
|
use app\core\Response;
|
||||||
|
use app\models\LoginForm;
|
||||||
|
use app\models\User;
|
||||||
|
|
||||||
|
class AuthController extends Controller{
|
||||||
|
public function __construct(){
|
||||||
|
$this->registerMiddleware(new AuthMiddleware(['profile']));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(Request $request, Response $response){
|
||||||
|
$loginForm = new LoginForm();
|
||||||
|
|
||||||
|
if($request->isPost()){
|
||||||
|
$loginForm->loadData($request->getBody());
|
||||||
|
if($loginForm->validate() && $loginForm->login()){
|
||||||
|
$response->redirect('home');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLayout('auth');
|
||||||
|
return $this->render('login', [
|
||||||
|
'model' => $loginForm
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLayout('auth');
|
||||||
|
return $this->render('login', [
|
||||||
|
'model' => $loginForm
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register(Request $request, $response){
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
if($request->isPost()){
|
||||||
|
$user->loadData($request->getBody());
|
||||||
|
|
||||||
|
if($user->validate() && $user->save()){
|
||||||
|
Application::$app->session->setFlash('success', 'Thanks for registering');
|
||||||
|
$response->redirect('home');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLayout('auth');
|
||||||
|
return $this->render('register', [
|
||||||
|
'model'=>$user
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setLayout('auth');
|
||||||
|
return $this->render('register', [
|
||||||
|
'model'=>$user
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout($request, $response){
|
||||||
|
Application::$app->logout();
|
||||||
|
$response->redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function profile(){
|
||||||
|
return $this->render('profile');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controllers;
|
||||||
|
use app\core\Controller;
|
||||||
|
use app\core\Request;
|
||||||
|
|
||||||
|
class SiteController extends Controller{
|
||||||
|
public function handleContact(Request $request){
|
||||||
|
$body = $request->getBody();
|
||||||
|
return 'handling requested data';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contact(){
|
||||||
|
return $this->render('contact');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function home(){
|
||||||
|
$params = [
|
||||||
|
'name' => "TheCodeholic"
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->render('home', $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function profile(){
|
||||||
|
return $this->render('profile');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _404(){
|
||||||
|
return $this->render('_404');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
use app\core\exceptions\ForbiddenException;
|
||||||
|
use app\core\exceptions\NotFoundException;
|
||||||
|
|
||||||
|
class Application
|
||||||
|
{
|
||||||
|
public static string $ROOT_DIR;
|
||||||
|
public string $userClass;
|
||||||
|
public string $layout = 'main';
|
||||||
|
|
||||||
|
public Router $router;
|
||||||
|
public Request $request;
|
||||||
|
public Response $response;
|
||||||
|
public static Application $app;
|
||||||
|
public ?Controller $controller = null;
|
||||||
|
public Database $db;
|
||||||
|
public Session $session;
|
||||||
|
public ?DbModel $user;
|
||||||
|
|
||||||
|
public function __construct($rootPath, array $config)
|
||||||
|
{
|
||||||
|
self::$ROOT_DIR = $rootPath;
|
||||||
|
$this->userClass = $config['userClass'];
|
||||||
|
self::$app = $this;
|
||||||
|
|
||||||
|
$this->request = new Request();
|
||||||
|
$this->response = new Response();
|
||||||
|
$this->router = new Router($this->request, $this->response);
|
||||||
|
|
||||||
|
$this->db = new Database($config['db']);
|
||||||
|
$this->session = new Session();
|
||||||
|
|
||||||
|
$primaryValue = $this->session->get('user');
|
||||||
|
if ($primaryValue) {
|
||||||
|
$userObj = new $this->userClass();
|
||||||
|
$primaryKey = $userObj->primaryKey();
|
||||||
|
$this->user = $userObj->findOne([$primaryKey => $primaryValue]);
|
||||||
|
} else {
|
||||||
|
$this->user = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setController($controller)
|
||||||
|
{
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getController()
|
||||||
|
{
|
||||||
|
return $this->controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(DbModel $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$primaryKey = $user->primaryKey();
|
||||||
|
$primaryValue = $user->{$primaryKey};
|
||||||
|
$this->session->set('user', $primaryValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout()
|
||||||
|
{
|
||||||
|
$this->user = null;
|
||||||
|
$this->session->remove('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isLoggedIn()
|
||||||
|
{
|
||||||
|
return !self::$app->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
echo $this->router->resolve();
|
||||||
|
}catch(ForbiddenException $e){
|
||||||
|
$this->response->setStatusCode(403);
|
||||||
|
echo $this->router->renderView('_error', [
|
||||||
|
'exception' => $e
|
||||||
|
]);
|
||||||
|
}catch(NotFoundException $e){
|
||||||
|
$this->response->setStatusCode(404);
|
||||||
|
echo $this->router->renderView('_error', [
|
||||||
|
'exception' => $e
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
use app\core\middlewares\BaseMiddleware;
|
||||||
|
|
||||||
|
class Controller{
|
||||||
|
public string $layout = 'main';
|
||||||
|
public string $action = '';
|
||||||
|
protected array $middlewares = [];
|
||||||
|
|
||||||
|
public function render($view, $params = []){
|
||||||
|
return Application::$app->router->renderView($view, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLayout($layout){
|
||||||
|
$this->layout = $layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerMiddleware(BaseMiddleware $middleware){
|
||||||
|
$this->middlewares[] = $middleware;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMiddlewares(){
|
||||||
|
return $this->middlewares;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
|
||||||
|
class Database{
|
||||||
|
public \PDO $pdo;
|
||||||
|
|
||||||
|
public function __construct(array $config){
|
||||||
|
$dsn = $config['dsn'] ?? '';
|
||||||
|
$user = $config['user'] ?? '';
|
||||||
|
$password = $config['password'] ?? '';
|
||||||
|
$this->pdo = new \PDO($dsn, $user, $password);
|
||||||
|
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyMigrations(){
|
||||||
|
$this->createMigrationsTable();
|
||||||
|
$appliedMigrations = $this->getAppliedMigrations();
|
||||||
|
|
||||||
|
$newMigrations = [];
|
||||||
|
$files = scandir(Application::$ROOT_DIR.'/migrations');
|
||||||
|
$toApplyMigrations = array_diff($files, $appliedMigrations);
|
||||||
|
|
||||||
|
foreach($toApplyMigrations as $migration){
|
||||||
|
if($migration === '.' || $migration === '..'){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once Application::$ROOT_DIR.'/migrations/'.$migration;
|
||||||
|
$className = pathinfo($migration, PATHINFO_FILENAME);
|
||||||
|
|
||||||
|
$instance = new $className();
|
||||||
|
$this->log("Applying migration $migration" . PHP_EOL);
|
||||||
|
$instance->up();
|
||||||
|
$this->log("Applied migration $migration" . PHP_EOL);
|
||||||
|
|
||||||
|
$newMigrations[] = $migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($newMigrations)){
|
||||||
|
$this->saveMigrations($newMigrations);
|
||||||
|
}else{
|
||||||
|
$this->log('All migrations are applied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createMigrationsTable(){
|
||||||
|
$this->pdo->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
id INT AUTO_INCREMENT,
|
||||||
|
migration varchar(255),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
ENGINE=INNODB;");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAppliedMigrations(){
|
||||||
|
$statement = $this->pdo->prepare("SELECT migration FROM migrations");
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
return $statement->fetchAll(\PDO::FETCH_COLUMN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveMigrations(array $migrations){
|
||||||
|
$str = implode(",", array_map(fn($m) => "('$m')", $migrations));
|
||||||
|
$statement = $this->pdo->prepare("INSERT INTO migrations (migration) VALUES $str");
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepare($sql){
|
||||||
|
return $this->pdo->prepare($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function log($message){
|
||||||
|
echo '['.date('Y-m-d H:i:s').'] - '.$message.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
|
||||||
|
abstract class DbModel extends Model{
|
||||||
|
abstract public function tableName(): string;
|
||||||
|
|
||||||
|
abstract public function attributes(): array;
|
||||||
|
|
||||||
|
abstract public function primaryKey(): string;
|
||||||
|
|
||||||
|
public function save(){
|
||||||
|
$tableName = $this->tableName();
|
||||||
|
$attributes = $this->attributes();
|
||||||
|
$params = array_map(fn($attr) => ":$attr", $attributes);
|
||||||
|
|
||||||
|
$statement = self::prepare("INSERT INTO $tableName ("
|
||||||
|
.implode(',', $attributes)
|
||||||
|
.") VALUES ("
|
||||||
|
.implode(',', $params)
|
||||||
|
.")"
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($attributes as $attribute){
|
||||||
|
$statement->bindValue(":$attribute", $this->{$attribute});
|
||||||
|
}
|
||||||
|
|
||||||
|
$statement->execute();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function prepare($sql){
|
||||||
|
return Application::$app->db->pdo->prepare($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOne($where){
|
||||||
|
$tableName = static::tableName();
|
||||||
|
$attributes = array_keys($where);
|
||||||
|
|
||||||
|
$sql = implode("AND ", array_map(fn($attr) => "$attr = :$attr", $attributes));
|
||||||
|
$statement = self::prepare("SELECT * from $tableName WHERE $sql");
|
||||||
|
|
||||||
|
foreach($where as $key => $item){
|
||||||
|
$statement->bindValue(":$key", $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
return $statement->fetchObject(static::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
|
||||||
|
abstract class Model{
|
||||||
|
public const RULE_REQUIRED = 'required';
|
||||||
|
public const RULE_EMAIL = 'email';
|
||||||
|
public const RULE_MIN = 'min';
|
||||||
|
public const RULE_MAX = 'max';
|
||||||
|
public const RULE_UNIQUE = 'unique';
|
||||||
|
|
||||||
|
public function loadData($data){
|
||||||
|
foreach ($data as $key => $value){
|
||||||
|
if(property_exists($this, $key)){
|
||||||
|
$this->{$key} = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function rules():array;
|
||||||
|
|
||||||
|
public function labels(): array{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public array $errors = [];
|
||||||
|
|
||||||
|
private function addErrorForRule(string $attribute, string $rule, $params =[]){
|
||||||
|
$message = $this->errorMessages()[$rule] ?? '';
|
||||||
|
foreach($params as $key => $value){
|
||||||
|
$message = str_replace("{{$key}}", $value, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->errors[$attribute][] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addError(string $attribute, string $message){
|
||||||
|
$this->errors[$attribute][] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function errorMessages(){
|
||||||
|
return [
|
||||||
|
self::RULE_REQUIRED => 'This field is required',
|
||||||
|
self::RULE_EMAIL => 'This field must be valid email address',
|
||||||
|
self::RULE_MIN => 'Min length of this field must be {min}',
|
||||||
|
self::RULE_MAX => 'Max length of this field must be {max}',
|
||||||
|
self::RULE_UNIQUE => 'Email already exists'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(){
|
||||||
|
foreach($this->rules() as $attribute => $rules){
|
||||||
|
$value = $this->{$attribute};
|
||||||
|
foreach($rules as $rule){
|
||||||
|
$ruleName = $rule;
|
||||||
|
|
||||||
|
if(!is_string($ruleName)){
|
||||||
|
$ruleName = $rule[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ruleName === self::RULE_REQUIRED && !$value){
|
||||||
|
$this->addErrorForRule($attribute, self::RULE_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ruleName === self::RULE_EMAIL && !filter_var($value, FILTER_VALIDATE_EMAIL)){
|
||||||
|
$this->addErrorForRule($attribute, self::RULE_EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ruleName === self::RULE_MIN && strlen($value) < $rule['min']){
|
||||||
|
$this->addErrorForRule($attribute, self::RULE_MIN, $rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ruleName === self::RULE_MAX && strlen($value) > $rule['max']){
|
||||||
|
$this->addErrorForRule($attribute, self::RULE_MAX, $rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ruleName === self::RULE_UNIQUE){
|
||||||
|
$className = $rule['class'];
|
||||||
|
$uniqueAttr = $rule['attirubute'] ?? $attribute;
|
||||||
|
$tableName = $className::tableName();
|
||||||
|
|
||||||
|
$statement = Application::$app->db->prepare(("SELECT * FROM $tableName WHERE $uniqueAttr = :attr"));
|
||||||
|
$statement->bindValue(":attr", $value);
|
||||||
|
$statement->execute();
|
||||||
|
$record = $statement->fetchObject();
|
||||||
|
|
||||||
|
if($record){
|
||||||
|
$this->addErrorForRule($attribute, SELF::RULE_UNIQUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return empty($this->errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasError($attribute){
|
||||||
|
return $this->errors[$attribute] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFirstError($attribute){
|
||||||
|
return $this->errors[$attribute][0] ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
|
||||||
|
class Request{
|
||||||
|
private int $lengthOfRoot;
|
||||||
|
|
||||||
|
public function __construct(){
|
||||||
|
$this->lengthOfRoot = strlen('knowyourfood') + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPath(){//identificam din URL pe pagina trebuie accesata
|
||||||
|
$path = $_SERVER['REQUEST_URI'];
|
||||||
|
$path = substr($path, $this->lengthOfRoot);
|
||||||
|
|
||||||
|
$position = strpos($path, '?');
|
||||||
|
|
||||||
|
if(!$position){
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return substr($path, 0, $position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function method(){
|
||||||
|
return strtolower($_SERVER['REQUEST_METHOD']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isGet(){
|
||||||
|
return $this->method() === 'get';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPost(){
|
||||||
|
return $this->method() === 'post';
|
||||||
|
}
|
||||||
|
|
||||||
|
//bazat pe tipul de request, accesam tuplele key/value pentru a identifica body ul requestului
|
||||||
|
public function getBody(){
|
||||||
|
$body = [];
|
||||||
|
if($this->method() === 'get'){
|
||||||
|
foreach($_GET as $key => $value){
|
||||||
|
$body[$key] = filter_input(INPUT_GET, $key, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->method() === 'post'){
|
||||||
|
foreach($_POST as $key => $value){
|
||||||
|
$body[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
|
||||||
|
class Response{
|
||||||
|
public function setStatusCode(int $code){
|
||||||
|
http_response_code($code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redirect(string $url){
|
||||||
|
header("Location: ".$url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
use app\core\exceptions\NotFoundException;
|
||||||
|
|
||||||
|
class Router
|
||||||
|
{
|
||||||
|
public Request $request;
|
||||||
|
public Response $response;
|
||||||
|
protected array $routes = [];
|
||||||
|
|
||||||
|
public function __construct(Request $request, Response $response)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($path, $callback)
|
||||||
|
{
|
||||||
|
$this->routes['get'][$path] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post($path, $callback)
|
||||||
|
{
|
||||||
|
$this->routes['post'][$path] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
in aceasta functie se identifica path ul catre fisier, metoda (GET/POST) si se identifica un "routes" callback ul (in general controller ul) pe care trebuie sa l returneze
|
||||||
|
astfel, in call_user_function, se ofera clasa de tip constructor si request ul de la user ca dupa controller ul sa se ocupe de restul
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function resolve()
|
||||||
|
{
|
||||||
|
$path = $this->request->getPath();
|
||||||
|
$method = $this->request->method();
|
||||||
|
$callback = $this->routes[$method][$path] ?? false;
|
||||||
|
|
||||||
|
if (!$callback) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($callback)) {
|
||||||
|
return $this->renderView($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($callback)) {
|
||||||
|
$controller = new $callback[0]();
|
||||||
|
Application::$app->controller = $controller;
|
||||||
|
$controller->action = $callback[1];
|
||||||
|
$callback[0] = $controller;
|
||||||
|
|
||||||
|
foreach($controller->getMiddlewares() as $middleware){
|
||||||
|
$middleware->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return call_user_func($callback, $this->request, $this->response);
|
||||||
|
}
|
||||||
|
|
||||||
|
//layout ne va ajuta sa identificam ce fel de layout trebuie sa aiba pagina pe care o trimitem (elemente extra in afara de continut: navbar, elemente din baza de date etc)
|
||||||
|
protected function layoutContent()
|
||||||
|
{
|
||||||
|
$layout = Application::$app->layout;
|
||||||
|
if (Application::$app->controller) {
|
||||||
|
$layout = Application::$app->controller->layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
include_once Application::$ROOT_DIR . "/views/layouts/$layout.php";
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function renderView($view, $params = [])
|
||||||
|
{
|
||||||
|
$layoutContent = $this->layoutContent();
|
||||||
|
$viewContent = $this->renderOnlyView($view, $params);
|
||||||
|
|
||||||
|
return str_replace('{{content}}', $viewContent, $layoutContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderContent($viewContent)
|
||||||
|
{
|
||||||
|
$layoutContent = $this->layoutContent();
|
||||||
|
return str_replace('{{content}}', $viewContent, $layoutContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderOnlyView($view, $params)
|
||||||
|
{
|
||||||
|
foreach ($params as $key => $value) {
|
||||||
|
$$key = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
include_once Application::$ROOT_DIR . "/views/$view.php";
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core;
|
||||||
|
|
||||||
|
class Session{
|
||||||
|
protected const FLASH_KEY = 'flash_messages';
|
||||||
|
public function __construct(){
|
||||||
|
session_start();
|
||||||
|
$flashMessages = $_SESSION[self::FLASH_KEY] ?? [];
|
||||||
|
foreach($flashMessages as $key => &$flashMessage){
|
||||||
|
$flashMessage['remove'] = 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION[self::FLASH_KEY] = $flashMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFlash($key, $message){
|
||||||
|
$_SESSION[self::FLASH_KEY][$key] = [
|
||||||
|
'value' => $message,
|
||||||
|
'remove' => 'false'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFlash($key){
|
||||||
|
return $_SESSION[self::FLASH_KEY][$key]['value'] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($key, $value){
|
||||||
|
$_SESSION[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key){
|
||||||
|
return $_SESSION[$key] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove($key){
|
||||||
|
unset($_SESSION[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct(){
|
||||||
|
$flashMessages = $_SESSION[self::FLASH_KEY] ?? [];
|
||||||
|
foreach($flashMessages as $key => &$flashMessage){
|
||||||
|
if($flashMessage['remove']){
|
||||||
|
unset($flashMessages[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION[self::FLASH_KEY] = $flashMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\exceptions;
|
||||||
|
|
||||||
|
class ForbiddenException extends \Exception{
|
||||||
|
protected $code = 403;
|
||||||
|
protected $message = 'You do not have permission to access this page';
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\exceptions;
|
||||||
|
|
||||||
|
class NotFoundException extends \Exception{
|
||||||
|
protected $code = 404;
|
||||||
|
protected $message = 'Page not found';
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\form;
|
||||||
|
|
||||||
|
class Button{
|
||||||
|
public function __toString(){
|
||||||
|
return sprintf('
|
||||||
|
<button type="submit" class="button">Submit</button>
|
||||||
|
',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\form;
|
||||||
|
use app\core\Model;
|
||||||
|
|
||||||
|
class Field{
|
||||||
|
public const TYPE_TEXT = 'text';
|
||||||
|
public const TYPE_PASSWORD = 'password';
|
||||||
|
|
||||||
|
public string $type;
|
||||||
|
public Model $model;
|
||||||
|
public string $attribute;
|
||||||
|
|
||||||
|
public function __construct(Model $model, string $attribute){
|
||||||
|
$this->type = self::TYPE_TEXT;
|
||||||
|
$this->model = $model;
|
||||||
|
$this->attribute = $attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(){
|
||||||
|
return sprintf('
|
||||||
|
<div class="mb-3">
|
||||||
|
<label>%s</label>
|
||||||
|
<input type="%s" name="%s" value="%s" class="form-control%s">
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
',
|
||||||
|
$this->model->labels()[$this->attribute] ?? $this->attribute,
|
||||||
|
$this->type,
|
||||||
|
$this->attribute,
|
||||||
|
$this->model->{$this->attribute},
|
||||||
|
$this->model->hasError($this->attribute) ? ' is invalid' : '',
|
||||||
|
$this->model->getFirstError($this->attribute)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passwordField(){
|
||||||
|
$this->type = self::TYPE_PASSWORD;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\form;
|
||||||
|
use app\core\Model;
|
||||||
|
|
||||||
|
class Form{
|
||||||
|
public static function begin($action, $method){
|
||||||
|
echo sprintf('<form action ="%s" method ="%s">', $action, $method);
|
||||||
|
return new Form();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function end(){
|
||||||
|
echo '</form>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function field(Model $model, $attribute){
|
||||||
|
return new Field($model, $attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function button(){
|
||||||
|
return new Button();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\middlewares;
|
||||||
|
use app\core\Application;
|
||||||
|
use app\core\exceptions\ForbiddenException;
|
||||||
|
|
||||||
|
class AuthMiddleware extends BaseMiddleware{
|
||||||
|
public array $actions = [];
|
||||||
|
|
||||||
|
public function __construct(array $actions = []){
|
||||||
|
$this->actions = $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(){
|
||||||
|
if(Application::isLoggedIn()){
|
||||||
|
if(empty($this->actions) || in_array(Application::$app->controller->action, $this->actions)){
|
||||||
|
throw new ForbiddenException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\core\middlewares;
|
||||||
|
|
||||||
|
abstract class BaseMiddleware{
|
||||||
|
abstract public function execute();
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$config = [
|
||||||
|
'userClass' => \app\models\User::class,
|
||||||
|
'db' => [
|
||||||
|
'dsn' => 'mysql:host=localhost;port=3306;dbname=Web',
|
||||||
|
'user' => 'root',
|
||||||
|
'password' => '',
|
||||||
|
]
|
||||||
|
];
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use app\controllers\AuthController;
|
||||||
|
use app\core\Application;
|
||||||
|
use app\controllers\SiteController;
|
||||||
|
|
||||||
|
require_once __DIR__.'/vendor/autoload.php';
|
||||||
|
require_once __DIR__.'/dbconfig.php';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Clasa Application se va ocupa de stocarea asocierilor dintre o pagina si controller-ul corespunzator. Deoarece prin .htaccess am fortat
|
||||||
|
ca user ul sa ajunga mereu in acest 'index.php' la orice URL specificat, putem identifica din URL ce pagina vrea sa acceseze pentru a apela
|
||||||
|
un GET cu controller-ul, view ul si model ul corespunzator, ori printr-un submit sa se ajunga la metoda corespunzatoare de POST care sa intoarca
|
||||||
|
un raspuns valid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$app = new Application('.', $config);
|
||||||
|
|
||||||
|
$app->router->get('/', [SiteController::class, '/']);
|
||||||
|
|
||||||
|
$app->router->get('/home', [SiteController::class, 'home']);
|
||||||
|
|
||||||
|
$app->router->get('/_404', [SiteController::class, '_404']);
|
||||||
|
|
||||||
|
$app->router->get('/contact', [SiteController::class, 'contact']);
|
||||||
|
$app->router->post('/contact', [SiteController::class, 'handleContact']);
|
||||||
|
|
||||||
|
$app->router->get('/login', [AuthController::class, 'login']);
|
||||||
|
$app->router->post('/login', [AuthController::class, 'login']);
|
||||||
|
|
||||||
|
$app->router->get('/register', [AuthController::class, 'register']);
|
||||||
|
$app->router->post('/register', [AuthController::class, 'register']);
|
||||||
|
|
||||||
|
$app->router->get('/profile', [AuthController::class, 'profile']);
|
||||||
|
$app->router->post('/profile', [AuthController::class, 'profile']);
|
||||||
|
|
||||||
|
$app->router->get('/logout', [AuthController::class, 'logout']);
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use app\core\Application;
|
||||||
|
|
||||||
|
require_once __DIR__.'/vendor/autoload.php';
|
||||||
|
require_once __DIR__.'/dbconfig.php';
|
||||||
|
|
||||||
|
$app = new Application('.', $config);
|
||||||
|
|
||||||
|
$app->db->applyMigrations();
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
use app\core\Application;
|
||||||
|
|
||||||
|
class m01_initial{
|
||||||
|
public function up(){
|
||||||
|
$db = Application::$app->db;
|
||||||
|
$SQL = "CREATE TABLE users(
|
||||||
|
id INT AUTO_INCREMENT,
|
||||||
|
email VARCHAR(255) NOT NULL,
|
||||||
|
firstname VARCHAR(255) NOT NULL,
|
||||||
|
lastname VARCHAR(255) NOT NULL,
|
||||||
|
status TINYINT NOT NULL,
|
||||||
|
create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
) Engine=INNODB;
|
||||||
|
";
|
||||||
|
$db->pdo->exec($SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(){
|
||||||
|
$db = Application::$app->db;
|
||||||
|
$SQL = "DROP TABLE users";
|
||||||
|
$db->pdo->exec($SQL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use app\core\Application;
|
||||||
|
|
||||||
|
class m02_add_password_column{
|
||||||
|
public function up(){
|
||||||
|
$db = Application::$app->db;
|
||||||
|
$SQL = "ALTER TABLE users ADD COLUMN password VARCHAR(512) NOT NULL";
|
||||||
|
$db->pdo->exec($SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(){
|
||||||
|
$db = Application::$app->db;
|
||||||
|
$SQL = "ALTER TABLE users DROP COLUMN password";
|
||||||
|
$db->pdo->exec($SQL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\models;
|
||||||
|
use app\core\Application;
|
||||||
|
use app\core\Model;
|
||||||
|
|
||||||
|
class LoginForm extends Model{
|
||||||
|
public string $email = '';
|
||||||
|
public string $password = '';
|
||||||
|
|
||||||
|
public function rules(): array{
|
||||||
|
return [
|
||||||
|
'email' => [self::RULE_REQUIRED, self::RULE_EMAIL],
|
||||||
|
'password' => [self::RULE_REQUIRED]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function labels():array {
|
||||||
|
return [
|
||||||
|
'email' => 'Email',
|
||||||
|
'password' => 'Password'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(){
|
||||||
|
$user = new User();
|
||||||
|
$user = $user->findOne(['email' => $this->email]);
|
||||||
|
if(!$user){
|
||||||
|
$this->addError('email', 'User does not exist with this email');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!password_verify($this->password, $user->password)){
|
||||||
|
$this->addError('password', 'Incorrect password');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Application::$app->login($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\models;
|
||||||
|
use app\core\DbModel;
|
||||||
|
|
||||||
|
|
||||||
|
class User extends DbModel{
|
||||||
|
const STATUS_INACTIVE = 0;
|
||||||
|
const STATUS_ACTIVE = 1;
|
||||||
|
CONST STATUS_DELETED = 2;
|
||||||
|
|
||||||
|
public string $firstname = '';
|
||||||
|
public string $lastname = '';
|
||||||
|
public int $status = self::STATUS_INACTIVE;
|
||||||
|
public string $email = '';
|
||||||
|
public string $password = '';
|
||||||
|
|
||||||
|
public function save(){
|
||||||
|
$this->status = self::STATUS_INACTIVE;
|
||||||
|
$this->password = password_hash($this->password, PASSWORD_DEFAULT);
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tableName(): string{
|
||||||
|
return 'users';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array{
|
||||||
|
return [
|
||||||
|
'firstname' => [self::RULE_REQUIRED],
|
||||||
|
'lastname' => [self::RULE_REQUIRED],
|
||||||
|
'email' => [self::RULE_REQUIRED, self::RULE_EMAIL, [self::RULE_UNIQUE, 'class' => self::class]],
|
||||||
|
'password' => [self::RULE_REQUIRED, [self::RULE_MIN, 'min' => 8], [self::RULE_MAX, 'max' => 15]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function primaryKey(): string{
|
||||||
|
return 'id';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function labels():array {
|
||||||
|
return [
|
||||||
|
'firstname' => 'First name',
|
||||||
|
'lastname' => 'Last name',
|
||||||
|
'email' => 'Email',
|
||||||
|
'password' => 'Password'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attributes(): array{
|
||||||
|
return ['firstname', 'lastname', 'email', 'password', 'status'];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
!.gitignore
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h1><?php echo $exception->getCode() ?> - <?php echo $exception->getMessage();?> </h1>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<h1>Contact</h1>
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label>Subject</label>
|
||||||
|
<input type="text" name="subject" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label>Email</label>
|
||||||
|
<input type="text" name="email" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label>Body</label>
|
||||||
|
<textarea name="body" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<h1>Home</h1>
|
||||||
|
<h2> Welcome <?php echo $name;?> to our site!</h2>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php use app\core\Application ?>
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Bootstrap demo</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<?php if(Application::$app->session->getFlash('success')): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<?php
|
||||||
|
echo Application::$app->session->getFlash('success');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
{{content}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Bootstrap demo</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">Navbar</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" aria-current="page" href="/knowyourfood/home">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/knowyourfood/contact">contact</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" aria-current="page" href="/knowyourfood/login">Login</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/knowyourfood/register">Register</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/knowyourfood/profile">Profile</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{{content}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<h1>Login</h1>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use app\core\form\Form;
|
||||||
|
/* @var $model \app\models\User */
|
||||||
|
|
||||||
|
$form = Form::begin('', 'post');
|
||||||
|
|
||||||
|
echo $form->field($model, 'email');
|
||||||
|
echo $form->field($model, 'password')->passwordField();
|
||||||
|
|
||||||
|
echo $form->button();
|
||||||
|
|
||||||
|
Form::end()
|
||||||
|
?>
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h1>Profile</h1>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<h1>Create An Account</h1>
|
||||||
|
<?php
|
||||||
|
use app\core\form\Form;
|
||||||
|
/* @var $model \app\models\User */
|
||||||
|
|
||||||
|
$form = Form::begin('', 'post');
|
||||||
|
|
||||||
|
echo $form->field($model, 'firstname');
|
||||||
|
echo $form->field($model, 'lastname');
|
||||||
|
echo $form->field($model, 'email');
|
||||||
|
echo $form->field($model, 'password')->passwordField();
|
||||||
|
|
||||||
|
echo $form->button();
|
||||||
|
|
||||||
|
Form::end()
|
||||||
|
?>
|
||||||
|
|
||||||
Reference in New Issue
Block a user