Webprogramozás
Horváth Győző
Egyetemi docens
1117 Budapest, Pázmány Péter sétány 1/c., 2.408-as szoba
Tel: (1) 372-2500/8469
horvath.gyozo@inf.elte.hu
include
kerulet.php
<?php
// debug
var_dump($_POST);
// dependencies
include('jsonio.php');
include('jsonstorage.php');
include('userrepository.php');
// function declarations
function validate($input, &$data, &$errors, $userRepository) {
if (!isset($input['username']) || trim($input['username']) === '') {
$errors['username'] = "Username is required";
} else {
$data['username'] = trim($input['username']);
}
if (!isset($input['password']) || trim($input['password']) === '') {
$errors['password'] = "Password is required";
} else {
$data['password'] = trim($input['password']);
}
if (count($errors) === 0) {
if (user_exists($userRepository, $input['username'])) {
$errors['global'] = "User already exists";
}
}
return count($errors) === 0;
}
function user_exists($userRepository, $username) {
$users = $userRepository->filter(function ($user) use ($username) {
return $user['username'] === $username;
});
return count($users) >= 1;
}
function add_user($userRepository, $user) {
$user['password'] = password_hash($user['password'], PASSWORD_DEFAULT);
return $userRepository->insert($user);
}
// main
$userRepository = new UserRepository();
$data = [];
$errors = [];
if ($_POST) {
if (validate($_POST, $data, $errors, $userRepository)) {
add_user($userRepository, $data);
header('Location: login.php');
exit();
}
}
?>
<!-- template -->
<style>
.error {
color: red;
}
</style>
<?php if (isset($errors['global'])) : ?>
<p><span class="error"><?= $errors['global'] ?></span></p>
<?php endif; ?>
<form action="" method="post">
Username:
<input type="text" name="username">
<?php if (isset($errors['username'])) : ?>
<span class="error"><?= $errors['username'] ?></span>
<?php endif; ?>
<br>
Password:
<input type="password" name="password">
<?php if (isset($errors['password'])) : ?>
<span class="error"><?= $errors['password'] ?></span>
<?php endif; ?>
<br>
<button>Register</button>
</form>
<?php
// debug
var_dump($_POST);
// dependencies
include('jsonio.php');
include('jsonstorage.php');
include('userrepository.php');
// function declarations
function validate($input, &$data, &$errors, $userRepository) { /* ... */}
function user_exists($userRepository, $username) { /* ... */}
function add_user($userRepository, $user) { /* ... */}
// main
$userRepository = new UserRepository();
$data = [];
$errors = [];
if ($_POST) {
if (validate($_POST, $data, $errors, $userRepository)) {
add_user($userRepository, $data);
header('Location: login.php');
exit();
}
}
?>
<!-- template -->
<?php if (isset($errors['global'])) : ?>
<p><span class="error"><?= $errors['global'] ?></span></p>
<?php endif; ?>
<form action="" method="post">
<!-- ... -->
</form>
<?php
// ...
// dependencies
include('jsonio.php');
include('jsonstorage.php');
include('userrepository.php');
// ...
⇩
// ...
// auto-loading classes
spl_autoload_register(function ($class) {
include './classes/' . strtolower($class) . '.php';
});
// ...
// function declarations
function validate($input, &$data, &$errors, $userRepository) { /* ... */}
function user_exists($userRepository, $username) { /* ... */}
function add_user($userRepository, $user) { /* ... */}
// main
$userRepository = new UserRepository();
if ($_POST) {
if (validate($_POST, $data, $errors, $userRepository)) {
add_user($userRepository, $data);
/* ... */
}
}
// function declarations
function validate($input, &$data, &$errors, $auth) {
// ...
if ($auth->user_exists($input['username'])) { /* ... */ }
// ...
}
// main
$auth = new Auth();
if ($_POST) {
if (validate($_POST, $data, $errors, $auth)) {
$auth->register($data);
/* ... */
}
}
class Auth {
private $userRepository;
public function __construct() {
$this->userRepository = new UserRepository();
}
public function register($user) { /* ... */ }
public function user_exists($username) { /* ... */ }
}
// function declarations
function validate($input, &$data, &$errors, $auth) {
// ...
if ($auth->user_exists($input['username'])) { /* ... */ }
// ...
}
// main
$auth = new Auth();
$data = [];
$errors = [];
if ($_POST) {
if (validate($_POST, $data, $errors, $auth)) {
$auth->register($data);
/* ... */
}
}
class Registration_Controller {
private $auth;
public $errors = [];
public $data = [];
public function __construct() {
$this->auth = new Auth();
}
public function validate($input) { /* ... */ }
public function run() {
if ($_POST) {
if ($this->validate($_POST)) {
$this->auth->register($this->data);
header('Location: login.php');
exit();
}
}
}
}
<?php
// ...
$controller = new Registration_Controller();
$controller->run();
$errors = $controller->errors;
?>
<!-- template -->
<?php
// ...
$controller = new Registration_Controller();
$controller->run();
$errors = $controller->errors;
?>
<!-- template -->
<?php if (isset($errors['global'])) : ?>
<p><span class="error"><?= $errors['global'] ?></span></p>
<?php endif; ?>
<form action="" method="post">
Username:
<input type="text" name="username">
<?php if (isset($errors['username'])) : ?>
<span class="error"><?= $errors['username'] ?></span>
<?php endif; ?>
<br>
Password:
<input type="password" name="password">
<?php if (isset($errors['password'])) : ?>
<span class="error"><?= $errors['password'] ?></span>
<?php endif; ?>
<br>
<button>Register</button>
</form>
class Registration_Controller {
// ...
public function run() {
if ($_POST) { /* ... */ }
$this->display('reg.php', [
'errors' => $this->errors,
]);
}
public function display(string $template, array $data) {
extract($data);
include(__DIR__ . '/../templates/' . $template);
}
}
// ...
$controller = new Registration_Controller();
$controller->run();
abstract class Base_Controller {
protected $errors = [];
protected $data = [];
abstract public function run();
public function __construct() {}
public function display(string $template, array $data) {
extract($data);
include(__DIR__ . '/../templates/' . $template);
}
}
class Registration_Controller extends Base_Controller {
private $auth;
// ...
public function run() {
if ($_POST) { /* ... */ }
$this->display('reg.php', [
'errors' => $this->errors,
]);
}
}
// ...
$controller = new Registration_Controller();
$controller->process_form(); // instead of: run
class Registration_Controller extends Base_Controller {
private $auth;
// ...
public function process_form() { // instead of: run
if ($_POST) { /* ... */ }
$this->display('reg.php', [
'errors' => $this->errors,
]);
}
}
abstract class Base_Controller {
protected $errors = [];
protected $data = [];
// no run method in base class
public function __construct() {}
public function display(string $template, array $data) { /* ... */ }
}
// ...
$controller = new Registration_Controller();
$controller->process_form();
⇩
// ...
$controller = new Registration_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->show_form();
} else {
$controller->process_form();
}
class Registration_Controller extends Base_Controller {
// ...
public function process_form() {
if ($this->validate($_POST)) {
$this->auth->register($this->data);
header('Location: login.php');
exit();
}
$this->show_form();
}
public function show_form() {
$this->display('reg.php', [
'errors' => $this->errors,
]);
}
}
// debug
var_dump($_POST);
// auto-loading classes
spl_autoload_register(function ($class) {
include(__DIR__ . '/classes/' . strtolower($class) . '.php');
});
// main
$controller = new Registration_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->show_form();
} else {
$controller->process_form();
}
// debug
var_dump($_POST);
// auto-loading classes
spl_autoload_register(function ($class) {
include(__DIR__ . '/classes/' . strtolower($class) . '.php');
});
// main
$controller = new A_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->method1();
} else {
$controller->method2();
}
// debug
var_dump($_POST);
// auto-loading classes
spl_autoload_register(function ($class) {
include(__DIR__ . '/classes/' . strtolower($class) . '.php');
});
// main
$controller = new B_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->method3();
} else {
$controller->method4();
}
// debug
var_dump($_POST);
// auto-loading classes
spl_autoload_register(function ($class) {
include(__DIR__ . '/classes/' . strtolower($class) . '.php');
});
// main
$controller = new C_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->method5();
} else {
$controller->method6();
}
index.php
// debug
var_dump($_POST);
// auto-loading classes
spl_autoload_register(function ($class) {
include(__DIR__ . '/classes/' . strtolower($class) . '.php');
});
// routing
// ???
// |----> A_Controller::method1
// |----> A_Controller::method2
// |----> B_Controller::method3
// |----> B_Controller::method4
// ...
$controller = new A_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->method1();
} else {
$controller->method2();
}
$controller = new B_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->method3();
} else {
$controller->method4();
}
$controller = new C_Controller();
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$controller->method5();
} else {
$controller->method6();
}
http://example.com/index.php?path=registration
✓http://example.com/index.php?registration
http://example.com/index.php/registration
http://example.com/registration
// routes.config.php
$router->get('registration', 'Registration_Controller', 'show_form');
$router->post('registration', 'Registration_Controller', 'process_form');
index.php
// index.php
// start session
session_start();
// auto-loading classes
spl_autoload_register(function ($class) {
include(__DIR__ . '/classes/' . strtolower($class) . '.php');
});
// start routing
$router = new Router();
include(__DIR__ . '/routes.config.php');
$router->start();
// routes.config.php
$router->get('registration', 'Registration_Controller', 'show_form');
$router->post('registration', 'Registration_Controller', 'process_form');
class Router
{
private $routes = [
'GET' => [],
'POST' => [],
];
public function get(string $path, $controller, $method) {
$this->routes['GET'][$path] = [$controller, $method];
}
public function post(string $path, $controller, $method) {
$this->routes['POST'][$path] = [$controller, $method];
}
public function start() {
$request_method = $_SERVER['REQUEST_METHOD'];
$path = $_GET['path'] ?? '';
if ($path) {
unset($_GET[$path]);
}
list($controller, $method) = $this->routes[$request_method][$path] ?? NULL;
if ($controller && $method) {
$obj = new $controller();
if (method_exists($obj, $method)) {
$obj->$method();
} else {
die('404: Path not found');
}
} else {
die('404: Path not found');
}
}
}
index.php
: elővezérlő, adottroutes.config.php
: irányításclasses/
vezérlő osztályok
$_POST
, $_GET
)classes/
repository osztályok
data/
: tárolt adatoktemplates/
: sablonok// routes.config.php
$router->get('registration', 'Registration_Controller', 'show_form');
$router->post('registration', 'Registration_Controller', 'process_form');
// classes/registration_controller.php
class Registration_Controller extends Base_Controller {
private $auth;
public function __construct() {
parent::__construct();
$this->auth = new Auth();
}
private function validate($input) {
if (!isset($input['username']) || trim($input['username']) === '') {
$this->errors['username'] = "Username is required";
} else {
$this->data['username'] = trim($input['username']);
}
if (!isset($input['password']) || trim($input['password']) === '') {
$this->errors['password'] = "Password is required";
} else {
$this->data['password'] = trim($input['password']);
}
if (count($this->errors) === 0) {
if ($this->auth->user_exists($input['username'])) {
$this->errors['global'] = "User already exists";
}
}
return count($this->errors) === 0;
}
public function process_form() {
if ($this->validate($_POST)) {
$this->auth->register($this->data);
header('Location: login.php');
exit();
}
$this->show_form();
}
public function show_form() {
$this->display('reg.php', [
'errors' => $this->errors,
]);
}
}
// classes/userrepository.php
class UserRepository extends JsonStorage {
public function __construct() {
parent::__construct('users.json');
}
}
// classes/auth.php
class Auth {
private $userRepository;
public function __construct() {
$this->userRepository = new UserRepository();
}
public function register($user) {
$user['password'] = password_hash($user['password'], PASSWORD_DEFAULT);
return $this->userRepository->insert($user);
}
public function user_exists($username) {
$users = $this->userRepository->filter(function ($user) use ($username) {
return $user['username'] === $username;
});
return count($users) >= 1;
}
public function login($user) {
$_SESSION["user"] = $user;
}
public function check_credentials($username, $password) {
$users = $this->userRepository->filter(function ($user) use ($username) {
return $user['username'] === $username;
});
if (count($users) === 1) {
$user = array_values($users)[0];
return password_verify($password, $user["password"])
? $user
: false;
}
return false;
}
public function is_authenticated() {
return isset($_SESSION["user"]);
}
public function logout() {
unset($_SESSION["user"]);
}
}
// index.php
define('TOKEN', 'A secret token');
// ...
// classes/registration_controller.php
<?php if ( ! defined('TOKEN')) die('Directly not accessible!');
.htaccess
állomány
classes
templates
data
# .htaccess
deny from all
|+ (D) classes
| |--+ .htaccess
|+ (D) data
| |--+ .htaccess
|+ (D) templates
| |--+ .htaccess
|+ index.php
Könyvtárszerkezet
- (D) www
|--+ index.php
- (D) app
|--+ (D) classes
|--+ (D) data
|--+ (D) templates
Szerveroldali kiegészítés
function getPoster() {
const title = input.value
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', responseHandler)
xhr.open('GET', `http://www.omdbapi.com/?t=${title}&apikey=<key>`)
xhr.responseType = 'json'
xhr.send(null)
}
function responseHandler() {
img.src = this.response.Poster
}
async function getPoster() {
const title = document.querySelector('input').value
const response = await fetch(`http://www.omdbapi.com/?t=${title}&apikey=2dd0dbee`)
const json = await response.json()
document.querySelector('img').src = json.Poster
}
Mozart;Beethoven;Dvorak
$composers = [
'Mozart',
'Beethoven',
'Dvorak',
];
echo implode(';', $composers);
Statikus
[
"Mozart",
"Beethoven",
"Dvorak"
]
Dinamikus
$composers = [
'Mozart',
'Beethoven',
'Dvorak',
];
echo json_encode($composers);
{
"success": true
}
<?php $success = true; ?>
{
"success": <?= (string)$success ?>
}
<ul>
<li>Mozart</li>
<li>Beethoven</li>
<li>Dvorak</li>
</ul>
<?php $composers = ['Mozart', 'Beethoven', 'Dvorak',]; ?>
<ul>
<?php foreach($composers as $name) : ?>
<li>Beethoven</li>
<?php endforeach ?>
</ul>
<form>
<input type="text" name="event" value="Advent">
<input type="text" name="year" value="2019">
</form>
const form = document.querySelector('form')
// creating FormData from a form
const formData = new FormData(form);
// append new item
formData.append('place', 'St Gerardus Church')
// output key-value pairs
for (const pair of formData.entries()) {
console.log(pair[0], pair[1])
}
// event Advent
// year 2019
// place St Gerardus Church
// creating an empty FormData object
const formData2 = new FormData()
// adding items manually
formData2.append('event', 'Advent')
<form>
<input type="text" name="event" value="Advent">
<input type="text" name="year" value="2019">
</form>
const form = document.querySelector('form')
const formData = new FormData(form);
formData.append('place', 'St Gerardus Church')
// Option 1.
const params = new URLSearchParams(formData);
const queryString = params.toString();
// "event=Advent&year=2019&place=St+Gerardus+Church"
// Option 2.
const url = new URL("http://example.com/foo.php")
Array.from(formData).forEach(([key, value]) => url.searchParams.append(key, value))
const queryString = url.searchParams.toString()
url.toString()
// http://example.com/foo.php?event=Advent&year=2019&place=St+Gerardus+Church
const form = document.querySelector('form')
const formData = new FormData(form)
const url = new URL("http://example.com/foo.php")
Array.from(formData).forEach(([key, value]) => url.searchParams.append(key, value))
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.send(null)
async function get(url) {
const response = await fetch(url)
}
const form = document.querySelector('form')
const formData = new FormData(form)
const url = new URL("http://example.com/foo.php")
const xhr = new XMLHttpRequest()
xhr.open('POST', url)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(formData)
async function post(url) {
const response = await fetch(url, {
method: 'POST',
body: formData
})
}
XMLHttpRequest
setRequestHeader()
fetch
Request
Headers
const headers = new Headers()
headers.append('Content-Type', 'text/plain')
const options = {
method: 'POST',
headers: headers,
}
const request = new Request(url, options)
const response = await fetch(request)
XMLHttpRequest
const xhr = new XMLHttpRequest()
xhr.open('GET', `foo.php`)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.send(null)
fetch
async function get() {
const response = await fetch(`foo.php`, {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
// ...
}
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) {
//request is ajax
}
De nem lehetünk benne biztosak!!!
const data = {
type: 'drama',
favourites: [
'Macbeth',
'Hamlet',
]
}
const xhr = new XMLHttpRequest()
xhr.open('GET', `foo.php`)
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(JSON.stringify(data))
async function postJSON(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
}
<form>
<input type="file" name="photo">
</form>
const form = document.querySelector('form')
const formData = new FormData(form);
const xhr = new XMLHttpRequest()
xhr.open('POST', url)
xhr.send(formData)
async function post(url) {
const response = await fetch(url, {
method: 'POST',
body: formData
})
}
XMLHttpRequest
responseType
text
json
document
blob
arraybuffer
fetch
Response
objektum
.text()
.json()
.formData()
.blob()
.arrayBuffer()
alma,körte,szilva,barack,eper,málna,szeder
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', function () {
const fruits = this.response.split(',');
})
xhr.open('GET', `fruits.txt`)
xhr.responseType = 'text'
xhr.send(null)
async function text() {
const response = await fetch(`fruits.txt`)
const text = await response.text()
const fruits = text.split(',');
}
Ritkán használatos, bármi lehet, egyedi feldolgozás
[
"alma",
"körte",
"szilva",
"barack",
"eper",
"málna",
"szeder"
]
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', function () {
const fruits = this.response;
})
xhr.open('GET', `fruits.json`)
xhr.responseType = 'json'
xhr.send(null)
async function json() {
const response = await fetch(`fruits.json`)
const fruits = await response.json()
}
Legelterjedtebb, egyszerű
<ul>
<li>alma</li>
<li>körte</li>
<li>szilva</li>
<li>barack</li>
<li>eper</li>
<li>málna</li>
<li>szeder</li>
</ul>
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', function () {
const fruitsHTML = this.response; // text or document
// innerHTML
})
xhr.open('GET', `fruits.html`)
xhr.responseType = 'text' // or 'document'
xhr.send(null)
async function html() {
const response = await fetch(`fruits.html`)
const fruitsHTML = await response.text()
// innerHTML
}
Mikroformátum (AHAH), egyszerű feldolgozás
<?xml version="1.0" encoding="UTF-8"?>
<fruits>
<fruit>alma</fruit>
<fruit>körte</fruit>
<fruit>szilva</fruit>
<fruit>barack</fruit>
<fruit>eper</fruit>
<fruit>málna</fruit>
<fruit>szeder</fruit>
</fruits>
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', function () {
const fruitsDOM = this.response;
const fruitsElems = fruitsDOM.getElementsByTagName('fruit');
const fruits = [];
for (let i = 0; i < fruitsElems.length; i++) {
fruits.push(fruitsElems[i].firstChild.nodeValue);
};
})
xhr.open('GET', `fruits.xml`)
xhr.responseType = 'document'
xhr.send(null)
Szabványos adatleírási formátum, ritka
function getFruits() {
return [
"alma",
"körte",
"szilva",
"barack",
"eper",
"málna",
"szeder"
];
}
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', function () {
eval(this.response);
const fruits = getFruits();
})
xhr.open('GET', `fruits.js`)
xhr.responseType = 'text'
xhr.send(null)
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', function () {
const script = doc.createElement('script')
const src = URL.createObjectURL(this.response)
script.src = src;
document.body.appendChild(script);
})
xhr.open('GET', `fruits.js`)
xhr.responseType = 'blob'
xhr.send(null)
Lazy loading
http://www.omdbapi.com/?s=hobbit&callback=processResponse
processResponse({"Search":[{"Title":"The Hobbit: An Unexpected Journey","Year":"2012","imdbID":"tt0903624","Type":"movie"},{"Title":"The Hobbit: The Desolation of Smaug","Year":"2013","imdbID":"tt1170358","Type":"movie"},{"Title":"The Hobbit","Year":"1977","imdbID":"tt0077687","Type":"movie"}]});
function omdbCall(search) {
const script = document.createElement('script');
script.src = 'http://www.omdbapi.com/?s='+search+'&callback=processResponse';
document.body.appendChild(script);
}
function processResponse(json) {
console.log(json);
}
sz = JSON.stringify(adat)
<form id="formMent" action="" method="post">
<input type="hidden" name="adat" id="adat">
<input type="submit" value="Mentés">
</form>
//Eseménykezelők hozzárendelése
function init () {
$('formMent').addEventListener('submit', mentes, false);
}
//Elküldéskor
function mentes (e) {
var adat = ...;
$('adat').value = JSON.stringify(adat);
}
//Eseménykezelők hozzárendelése
function init () {
$('gombMent').addEventListener('click', mentes, false);
}
//Elküldéskor
function mentes (e) {
var adat = ...;
var s = JSON.stringify(adat);
ajax({
url: 'mentes.php',
mod: 'post',
postadat: 'adat=' + s,
siker: function () {
//...
}
});
}
$s = $_POST['adat'];
//s feldolgozása, elmentése, pl.
$tomb[] = $s;
// vagy
$adat = json_decode($s, true);
Szerver
$s = json_encode($adat);
echo $s;
Kliens
function leker(e) {
ajax({
url: 'leker.php',
mod: 'get',
getadat: 'azon=12',
siker: function (xhr, text) {
var json = JSON.parse(text);
console.log(json);
//JSON feldolgozása
}
});
}
Verzió 1.
<?php
$s = json_encode($adat);
?>
<script>
var adat = <?php echo $s; ?>
</script>
Verzió 2.
<?php
$id = $_GET['id'];
?>
<!doctype html>
<html>
<head>
<!-- ... -->
<script type="text/javascript" src="adat.php?id=<?php echo $id; ?>"></script>
<!-- ... -->
</head>
index.php