PHP Course support - Mario LEZOCHE

bemutefrogtownΑσφάλεια

18 Νοε 2013 (πριν από 3 χρόνια και 7 μήνες)

172 εμφανίσεις

1
ESIAL 2A – Mario Lezoche
2.3

PHP
ESIAL 2A – Mario Lezoche
About this course on PHP


Welcome to this course in PHP.


I will assume that you have had at least some exposure to
programming (i.e., know what a variable is, have an
understanding about what an if statement should do etc.)


A bit of knowledge of HTML might help, but certainly
isn't required. You'll pick it up as we go.
Conception et Exploitation des Systèmes d’Information

2
ESIAL 2A – Mario Lezoche
Table of Contents


What is PHP?


How does PHP work?


PHP syntax – control structures, functions, etc.


Making Websites with PHP Using Oracle with PHP


PHP Security


If we have time – other stuff (cool extensions, etc.)?
Conception et Exploitation des Systèmes d’Information

3
ESIAL 2A – Mario Lezoche
What is PHP?


PHP is a general-purpose programming language although
almost always used in practice on the web


The first PHP acronym meant Personal Home Page. After
a period of great diffusion it became:


PHP: Hypertext Preprocessor (yes, a recursive acronym...)


Interpreted language, not compiled


Good at interacting with HTML


Can add extensions to PHP for added functionality
Conception et Exploitation des Systèmes d’Information

4
2
ESIAL 2A – Mario Lezoche
What is PHP? – Dynamic Aspect


PHP runs on a web-server ('server side' script), not on the
client (i.e., your own) computer


Dynamically generates the websites you see in your
internet browser.


For regular, non-PHP websites: The HTML for some webpage is
just sent straight to your browser with no modification.


With PHP however, websites files are first passed through PHP,
and then sent to your browser (see next slide)
Conception et Exploitation des Systèmes d’Information

5
ESIAL 2A – Mario Lezoche
Static web site: HTML
Conception et Exploitation des Systèmes d’Information

6
GET /index.html HTTP/1.1
HTML
ESIAL 2A – Mario Lezoche
Dynamic web site: PHP
Conception et Exploitation des Systèmes d’Information

7
GET /index.php
HTTP/1.1
HTML
PHP
PHP
Interpreter
ESIAL 2A – Mario Lezoche
How PHP works on the web


When a webserver receives a request for a PHP page, it
first passes it through the PHP interpreter.


The PHP interpreter runs your PHP code, which outputs
HTML.


You can produce PHP code to create the HTML, access a
database, etc.


This HTML is sent back to the client who made the
request, and displayed in the web browser.
Conception et Exploitation des Systèmes d’Information

8
3
ESIAL 2A – Mario Lezoche
Start working with PHP


PHP files look like HTML files, except with special PHP
tags to mark sections of code:


<?php
and
?>


The PHP parser evaluates the code within this block, and
replaces it with the resulting output


Anything
not
in the PHP tag is ignored by the PHP parser
and just outputted as HTML
Conception et Exploitation des Systèmes d’Information

9
ESIAL 2A – Mario Lezoche
Example (example1.php)
<html>

<head><title>I love PHP!</title> </head>

<body>


<?php




print


<p>Hello, World!</p>
”;




print


<p> This is some PHP code. Note that it is
embedded in a regular HTML file!</p>
”;



?>


</body>
</html>
Conception et Exploitation des Systèmes d’Information

10
ESIAL 2A – Mario Lezoche
Example (example1.php) output
<html>

<head><title>I love PHP!</title> </head>

<body>


<p>Hello, World!</p> <p> This is some PHP code. Note that it


is embedded in a regular HTML file!</p>

</body>
</html>
Conception et Exploitation des Systèmes d’Information

11
ESIAL 2A – Mario Lezoche
How PHP works


Note that PHP outputs HTML formatted data.


The client computer will only see the resultant HTML
output. It won't see the original PHP source code


The client has no idea that the page is being generated
dynamically on the server – all it does is display the
HTML!
Conception et Exploitation des Systèmes d’Information

12
4
ESIAL 2A – Mario Lezoche
PHP Configuration


PHP has a number of options that it can be configured with


Default settings are in a file called php.ini (location
dependent upon installation)


Additionally, you can put a php.ini file in the folder of
your webpage that applies just to that folder


Can use php.ini to tell PHP to load certain extensions for
your code (i.e., MySQLi, which will be discussed later)
Conception et Exploitation des Systèmes d’Information

13
ESIAL 2A – Mario Lezoche
The function called: php_info()


To see all of PHP's current settings you can use: php_info()
function. It causes PHP to print a nice, pretty webpage that
lists MANY settings


Useful for debugging, but don't leave php_info() on your
website. Hackers could try and find potential vulnerabilties
to exploit


Some example settings include: max_execution_time,
memory_limit, etc.
Conception et Exploitation des Systèmes d’Information

14
ESIAL 2A – Mario Lezoche
php_info() example
<?php

phpinfo();
?>

Conception et Exploitation des Systèmes d’Information

15
ESIAL 2A – Mario Lezoche
PHP Script Configuration


Other way to set PHP options: from the script itself


The ini_set() and ini_get() functions allow you to get and
set configuration options


Ex: ini_set('display_errors','On');
Conception et Exploitation des Systèmes d’Information

16
5
ESIAL 2A – Mario Lezoche
Syntax - Overview


PHP syntax is similar to C/C++/Java, but it is loose with
types like Python


PHP always lies between
<?php
and
?>
tags. Anything not
between these tags will just be outputted directly from the
parser and not evaluated


End lines with a ; just like Java, C, and C++


PHP is case sensitive (like most other languages)
Conception et Exploitation des Systèmes d’Information

17
ESIAL 2A – Mario Lezoche
Syntax - Variables


Variables hold values.


Unlike most other programming languages, all PHP
variables must begin with a dollar-sign ($)


Ex. $username, $color, etc.


Can hold many types of values: integers, decimals, strings
of text, classes
Conception et Exploitation des Systèmes d’Information

18
ESIAL 2A – Mario Lezoche
Syntax – Variables typing


Don't need to declare variables before you use them


Variables can hold values of different types at different
times


I.e., $var = 4;

Later on:

$var = 'This is text';


Although being loose with types like this can be useful, it
also opens the door for some common coding errors
Conception et Exploitation des Systèmes d’Information

19
ESIAL 2A – Mario Lezoche
Syntax - Assignment
// String
$
yummy
= “
potato
”;

// Number variables
$
c
=
3
;

print “<p>” . $c/2 . “<p>”;
// You don’t have to worry about




// i n t e g e r s/d e c i m a l s i n P H P

C o n c e p t i o n e t E x p l o i t a t i o n d e s S y s t è m e s d ’ I n f o r m a t i o n

2 0
6
ESIAL 2A – Mario Lezoche
Syntax - Comments
PHP supports C-style and C++-style comments.
//This is a comment on one line. It isn’t interpreted by
// the PHP compiler.
/* This is

also a comment. Just like C. */
Conception et Exploitation des Systèmes d’Information

21
ESIAL 2A – Mario Lezoche
Syntax – Expression
// Expression example
$c = 3.141592653;
$c +1;


// Evaluates to 4.141592653
2*($c +1);

// Evaluates to 8.283185306
// Assignments also evaluate to things
$c = 5;


// Evaluates to 5
Conception et Exploitation des Systèmes d’Information

22
ESIAL 2A – Mario Lezoche
Syntax – Operators:
Arithmetic and Assignment
Arithmetic Operators
Symbol
Name
Semantics
$a
+
$
b

Addition
Sum of $a and $
b

$a
-
$
b

Subtraction
Difference between $a and $
b

$a
*
$
b

Multiplication
$a multiplied by $
b

$a
/
$
b

Division
$a divided by $
b

$a
%
$
b

Modulus
Reminder of $a divided by $
b

Conception et Exploitation des Systèmes d’Information

23
Assignment Operators
Symbol
Name
Semantics
$a
=
$
b

Equals
Sets $a to equal $
b

$a
=&
$
b

Reference
Sets $a to reference $
b

ESIAL 2A – Mario Lezoche
Syntax – Operators: Bitwise
Bitwise Operators
Symbol
Name
Semantics
$a
&
$
b

And
Bits set in $a and $
b
are set
$a
|
$
b

Or
Bits set in $a or $
b
are set
$a
^
$
b

Xor

Bits set in $a or $
b
, but not both, are set
~
$a
Not
Bits set in $a are not set and vice versa
$a
<<
$
b

Shift left
Shift the bits of $a to the left by $
b
steps.
This is equivalent, but faster, to
multiplication. Each step counts as
"multiply by two"
$a >> $
b

Shift right
Shift the bits of $a to the right by $
b
steps.
Conception et Exploitation des Systèmes d’Information

24
Bitwise operators aren't used very often. They manipulate the binary digits of
numbers, which is generally a lot more control than many programmers need.
7
ESIAL 2A – Mario Lezoche
Syntax – Operators: Comparison
Comparison Operators
Symbol
Name
Semantics
$a
==
$
b

Equals
True if $a is equal to $
b

$a
===
$
b

Identical
True if $a is equal to $
b
, and of the same type
$a
!=
$
b

Not equal
True if $a is not equal to $
b

$a
<>
$
b

Not equal
True if $a is not equal to $
b

$a
!==
$
b

Not identical
True if $a is not equal to $
b
or if they are not
of the same type
$a < $
b

Less then
True if $a is less than $
b

$a > $
b

Greater then
True if $a is greater than $
b

$a <= $
b

Less then or equals
True if $a is less than or equal to $
b

$a >= $
b

Greater then or equals
True if $a is less than or equals to $
b

Conception et Exploitation des Systèmes d’Information

25
ESIAL 2A – Mario Lezoche
Syntax – Operators:
Incrementing and Decrementing
Incrementing and decrementing Operators
Symbol
Name
Semantics
++
$a
Pre-increment
Increments $a by one, then returns $a
$a++
Post-increment
Returns $a, then increments $a by one
--
$a
Pre-decrement
Decrements $a by one, then returns $a
$a
--
Post-decrement
Returns $a, then decrements $a by one
$a
%
$
b

Modulus
Reminder of $a divided by $
b

Conception et Exploitation des Systèmes d’Information

26
ESIAL 2A – Mario Lezoche
Syntax – Operators: Logical
Comparison Operators
Symbol
Name
Semantics
$a
and
$
b

And
True if both $a and $
b
are true
$a
&&
$
b

And
True if both $a and $
b
are true
$a
or
$
b

Or
True if either $a and $
b
are true
$a
||
$
b

Or
True if either $a and $
b
are true
$a
xor
$
b

Xor

True if both $a and $
b
are true, but not both
!
$a
Not
True if $a is not true
Conception et Exploitation des Systèmes d’Information

27
ESIAL 2A – Mario Lezoche
Syntax – Others operators
Conception et Exploitation des Systèmes d’Information

28
Presentation Operators
Symbol
Name
Semantics
$a . $
b

String concatenation
Concatenation of string $a to string $
b

$a -> $
b

Member field / function
Access to class $a member $
b

8
ESIAL 2A – Mario Lezoche
Syntax – String Quoting


PHP has two ways to quote strings – double quotes (”) and
single quotes (').


When writing a string, you
must
put quotes at the
beginning and at the end of the string.


Single quotes
are interpreted literally – exactly what is
inside the quotes


Double quotes
can have values embedded in them – not
interpreted literally
Conception et Exploitation des Systèmes d’Information

29
ESIAL 2A – Mario Lezoche
Syntax – String Quoting Example
// An example demonstrating the difference between
// single and double quoting.
$food = “potatoes”;
$double_quoted = “I like to eat $food.”;
$single_quoted = ‘I like to eat $food.’;
print “<p> $double_quoted </p>”;
print “<p> $single_quoted </p>”;
Conception et Exploitation des Systèmes d’Information

30
I like to eat potatoes.
I like to eat $food.
ESIAL 2A – Mario Lezoche
Syntax - Array


Arrays are collections of values


PHP arrays sort of like '
dictionaries
' in other languages
such as Python


Accessed via an
key
. Each key is associated with exactly
one value


A key may be an integer ('index'), a string, or any other
type
Conception et Exploitation des Systèmes d’Information

31
ESIAL 2A – Mario Lezoche
Array – Indexing


Example that uses integers as keys:
// Arrays
$languages = array (‘PHP’, ‘Java’, ‘C++’, ‘Python’);
print “<p> The first language is ” . $languages[0] . “!</p>”;
print “<p> The second language is ” . $languages[1] . “!</p>”;
Conception et Exploitation des Systèmes d’Information

32
Prints:

The first language is PHP!

The second language is Java
9
ESIAL 2A – Mario Lezoche
Arrays – Adding new values
// Adding to an array example
$languages[] = “Perl”;


Arrays, unlike languages such as Java, C, and C++, can be
made arbitrarily long using this technique. However, doing
this a lot is
slow
.
Conception et Exploitation des Systèmes d’Information

33
ESIAL 2A – Mario Lezoche
Syntax – if


If statements are arguably one of the most important
concepts in computer science!
// If statement example
if ($favorite_color == “blue”) {


// This code will execute if $favorite_color is blue …
} else if ($favorite_color == “red”) {


// This code will execute if $favorite_color is red …
} else {
// None of the above!

}
Conception et Exploitation des Systèmes d’Information

34
ESIAL 2A – Mario Lezoche
Syntax – if (true and false)


Note – the whatever you supply as the condition of the if
statement will be converted by PHP to a boolean (either
'true' or 'false')


Things that get converted to '
false
’:


The boolean value 'false' (trivial case)


The number 0


Zero-length strings


Null


Zero-length errors


Other things converted to
true
Conception et Exploitation des Systèmes d’Information

35
ESIAL 2A – Mario Lezoche
Syntax – For loop


The for loop:
$languages = array (‘PHP’, ‘Java’, ‘C++’, ‘Python’);
// For-loop example with an array
for ($c = 0; $c < count ($languages); $c++) {


$lang = $languages[$c];


print “<p> The language at index $c is $lang.</p>”;


Paramters:
Initialization, Looping condition, Increment
condition


But, there's actually a better way to loop through arrays...
Conception et Exploitation des Systèmes d’Information

36
10
ESIAL 2A – Mario Lezoche
Syntax – foreach loop


The foreach loop:
// Example of using non-integer keys
$languages = array (‘best’ => ‘PHP’, ‘tastiest’ => ‘Java’,
‘funkiest’ => ‘Scheme’);
A for-each loop.
foreach ($languages as $adjective => $lang) {


print “<p> The $adjective language is $lang. </p>”;
}
Conception et Exploitation des Systèmes d’Information

37
ESIAL 2A – Mario Lezoche
Syntax – alternate foreach loop


If you don’t care about the keys:
foreach ($languages as $lang) {

print “<p> $lang is a programming language.</p>”;
}
Conception et Exploitation des Systèmes d’Information

38
ESIAL 2A – Mario Lezoche
Syntax – functions


Functions are very important! They help group your code
together, save you time, and make your code better.
Functions can return values, or not.
$num = count($arr);

Count is a function that returns the size of array $arr. This returned
value is stored in the variable $num.


To declare a function use the
function
keyword and to
return a value, use the
return

statement.
function hereIsAFunction($x) {


return $x * $x;
}
Conception et Exploitation des Systèmes d’Information

39
ESIAL 2A – Mario Lezoche
Syntax – functions calling


Functions can optionally accept argument, or values that
allow a function to do what you want it to do.


Functions can call other functions or can call themselves
('recursion').


Another example of function:
function countDown ($start) {


while ($start >= 0) {


p r i n t “ < p > $ s t a r t … </p > ”;


$ s t a r t - -;


}
}



C o n c e p t i o n e t E x p l o i t a t i o n d e s S y s t è m e s d ’ I n f o r m a t i o n

4 0
11
ESIAL 2A – Mario Lezoche
Some useful functions


isset($var) – Returns true if $var has been set.
isset($_GET['name']) will return true if $_GET['name'] has a value
(namely, the browser has sent a 'name' parameter in the URL)


explode($delimiter, $string) – breaks up $string into an
array of substrings, separaring using $delimiter


count()


print_r($array) – prints an array. Useful for debugging


header($data) – If at the beginning, outputs HTTP header
data.
Conception et Exploitation des Systèmes d’Information

41
ESIAL 2A – Mario Lezoche
Syntax – Variable Scope


Variables used within a function are local only to that
function. They can't be accessed outside of the function –
they basically disappear after the function is called. If
you've declared a variable outside of a function and you
want to use it inside of the function, use the
global
keyword:
$numeric = 4;
function
foo() {


global
$numeric; print $numeric;
}
Conception et Exploitation des Systèmes d’Information

42
ESIAL 2A – Mario Lezoche
Syntax – Classes


PHP classes start with the
class
keyword


::
Scoping resolution operator is used to access a super-
class and call it's functions


->
operator use to access functions/fields of classes


The '
new
' keyword is for generating instances of classes.
Conception et Exploitation des Systèmes d’Information

43
ESIAL 2A – Mario Lezoche
Syntax – Class example
class
user {


public
$ID;


public
$first_name;


public
$last_name;


function
__construt ($first, $last, $id) {



$this->ID = $id;



$this->first_name = $first;



$this->last_name = $last;


}


function
getname () {



return
$this->first_name . “ ” . $this->last_name;


}


function
toString () {



return
$this->getname() . “ (” . $this->ID . “)”;


}
}
Conception et Exploitation des Systèmes d’Information

44
12
ESIAL 2A – Mario Lezoche
Syntax – Extended Classes
class
facebook_user
extends
user {


// Since this class extends user, it has everything that user has. However, override the toString


// function to do something else


public
$network;


// Provide a new constructor that uses the old one



function
__construt ($first, $last, $id, $net) {



parent::
__construct($first, $last, $id, $net) ;



$this->network = $net;


}


// Override user::toString()



function
toString () {



return
$this->getname() . “ (” . $this->network . “)”;


}
}
Conception et Exploitation des Systèmes d’Information

45
ESIAL 2A – Mario Lezoche
Syntax – Classes example use
$some_user =
new
user (“Steve”, “Levine”, “123”);
print
“<p>” . $some_user
->
toString() . “</p>” ;
$another_user =
new
facebook_user (“Steve”, “Levine”,
“123”, “MIT”);
print
“<p>” . $another_user
->
toString() . “</p>” ;
Conception et Exploitation des Systèmes d’Information

46
ESIAL 2A – Mario Lezoche
PHP with Websites


Although PHP is technically a general-purpose language, it
is almost always used to output HTML, or at least web-
related XML


From the previous slides on syntax, you already actually
know most of what you need to know to make websites!


Just a few more modifications needed to effectively make
a website!
Conception et Exploitation des Systèmes d’Information

47
ESIAL 2A – Mario Lezoche
PHP Magic Variables


There are several useful PHP '
super global
' arrays that are
automatically created by PHP when you view a website


$_GET,


$_POST,


$_REQUEST,


$_SERVER,


$_COOKIE,


$_SESSION.


All of these are arrays that contain useful information that
you can use in making your website
Conception et Exploitation des Systèmes d’Information

48
13
ESIAL 2A – Mario Lezoche
$_GET


$_GET contains parameters passed to your script from the
web browser


Unlike POST parameters, GET parameters are visible in
your URL bar


http://example.com/index.php?key1=value1&key2=value2&...


Within index.php


$_GET['key1'] == 'value1’


$_GET['key2'] == 'value2’…
Conception et Exploitation des Systèmes d’Information

49
ESIAL 2A – Mario Lezoche
$_POST


$_POST works just like $_GET, except that parameters
aren't passed in the URL.


This is the recommended way to pass form data – so that
way it won't be easily visible (and changeable!) by the user


Accessing the $_POST variable works just like accessing
the $_GET variable.
Conception et Exploitation des Systèmes d’Information

50
ESIAL 2A – Mario Lezoche
HTML POST form example
<HTML>
<HEAD>
<TITLE>HTML form tutorial example</TITLE>
</HEAD>
<BODY>
<H1>HTML form tutorial example</H1>
<FORM ACTION="cgi-bin/formmail.pl" METHOD="POST”>
Name: <INPUT TYPE="TEXT" NAME="Name" VALUE="" SIZE="25" MAXLENGTH="50">
<BR>
Email: <INPUT TYPE="TEXT" NAME="Email" VALUE="" SIZE="25"
MAXLENGTH="50"><BR>
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Sign Me Up!”>
</FORM>
</BODY>
</HTML>

Conception et Exploitation des Systèmes d’Information

51
ESIAL 2A – Mario Lezoche
Cookies


Cookies are little data files that websites can store on your
computer.


Cookies are used to store information about the user, or the
website state, on the computer


Ex., you could store a shopping cart on the user's computer, or a
username, or authentication information.


Commonly use form:


bool setcookie($name, $value, $expire)
Conception et Exploitation des Systèmes d’Information

52
14
ESIAL 2A – Mario Lezoche
Cookies setting


Cookie information exchanges happens at the beginning of
the HTTP protocol


Happens before any real data is sent


As a result, the
setcookie()
method
MUST
be called at the
very beginning of your PHP file, before anything else.


No <html>, no whitespace, etc.
Conception et Exploitation des Systèmes d’Information

53
ESIAL 2A – Mario Lezoche
Cookies example
<?php

// Cookies happen at the beginning of the HTTP. See if we’ve stored a cookie yet.
if
(
isset
($_COOKIE[‘username’])) {

// The cookie is set. This means that there is a non expired cookie, so the user was already here.

$username = $_COOKIE[‘username’];

print
“<h1>Welcome back!</h1><BR><p>I see you’ve been here before, $username !</p>”;
}
else
{

// The cookie is not set, so assume that the user hasn’t been here yet. Check and see if the user just submitted.

if
(
isset
($_POST[‘username’])) {

// The user just submitted. Store a cookie


$username = $_POST[‘username’];


setcookie
(‘username’, $username, time() + 120);


print
“<h1>Nice to meet you!</h1><BR><p>It is nice to meet you, $username !</p>”;

}
else
{
// The user didn’t submit, and we don’t have a cookie. Display a form.




print
“<h1>Hello, stranger!</h1><BR><p>What is your name?</p>”;


print
“<form method=\”POST\” action=\”cookie.php\” /> Username: <input type=\”text\” name=\”username\” />”;


print
“<input type=\”submit\” value=\”Hello!\” /> </form>”;

}
}
?>
Conception et Exploitation des Systèmes d’Information

54
ESIAL 2A – Mario Lezoche
$_REQUEST


The $_REQUEST variable is the union of


$_GET,


$_SET,


$_COOKIE.


It exists merely as a convenience, for situations in which
you don't really care where the input comes from
Conception et Exploitation des Systèmes d’Information

55
ESIAL 2A – Mario Lezoche
Sessions


Sessions are another way to store information about your
websites' users, kind of cookies objects.


Unlike cookies however, sessions are
stored on the server
,
not on the client's computer.


This means that
you can trust
that sessions data you store
about a user hasn't been tampered or altered, since you have
control of it on the server.


Data that you store in sessions can be accessed across different
pages in your website, making them very useful; PHP does
most of the hard work
Conception et Exploitation des Systèmes d’Information

56
15
ESIAL 2A – Mario Lezoche
How Sessions work


There's a magic, super-global array called $_SESSION.


It acts like $_GET, $_POST, and $_REQUEST


To save information in a session, you set a string- valued key
of $_SESSION to the data you want:


$_SESSION['name'] = 'William B. Rogers';


After you set a session variable, it will be visible to all other
pages in your website.


You can use sessions to implement a login system, shopping
carts… basically anything where you need to remember
something about the user.
Conception et Exploitation des Systèmes d’Information

57
ESIAL 2A – Mario Lezoche
Session example
<?php

// Sessions must also happen at the beginning of the HTTP.
if
(
isset
($_SESSION[‘username’])) {

// The session is set. This means that there is a non expired sesison, so the user was already here.

$username = $_SESSION[‘username’];

print
“<h1>Welcome back!</h1><BR><p>I see you’ve been here before, $username !</p>”;
}
else
{

// The session is not set, so assume that the user hasn’t been here yet. Check and see if the user just submitted.

if
(
isset
($_POST[‘username’])) {

// The user just submitted. Set a session variable!


$username = $_POST[‘username’];


$_SESSION[‘username’] = $username;


print
“<h1>Nice to meet you!</h1><BR><p>It is nice to meet you, $username !</p>”;

}
else
{
// The user didn’t submit, and we don’t have a session variable set. Display a form.




print
“<h1>Hello, stranger!</h1><BR><p>What is your name?</p>”;


print
“<form method=\”POST\” action=\”session.php\” /> Username: <input type=\”text\” name=\”username\” />”;


print
“<input type=\”submit\” value=\”Hello!\” /> </form>”;

}
}
?>
Conception et Exploitation des Systèmes d’Information

58
ESIAL 2A – Mario Lezoche
PHP and Oracle


As more and more PHP users are using Oracle as their
database of choice, it is becoming more important to
address issues surrounding PHP's Oracle Interfaces.


The journey begins with a quick look into the
fundamentals of Oracle and more specifically in this case,
Oracle8 and Oracle8i using PHPs OCI8 Function Library.


http://fr2.php.net/manual/en/ref.oci8.php

Conception et Exploitation des Systèmes d’Information

59
ESIAL 2A – Mario Lezoche
Oracle8 Call-Interface (OCI8)


These functions allow you to access Oracle8 and Oracle7
databases.


It uses the Oracle8 Call-Interface (OCI8). You will need the
Oracle8 client libraries to use this extension.
This extension is more flexible than the standard Oracle
extension. It supports:


binding
of global and local PHP variables to Oracle placeholders,


has full
LOB
,
FILE
and
ROWID
(record physical location on disk)
support and


allows you to use user-supplied define variables.
Conception et Exploitation des Systèmes d’Information

60
16
ESIAL 2A – Mario Lezoche
Large Object


A
LOB
is a Large Object.


LOBs replace the older and less functional LONG and
LONG RAW data types.


The Oracle database implements the following LOB types:


CLOB
, character large objects;



NCLOB
, national character large objects; and


BLOB
, binary large objects.
Conception et Exploitation des Systèmes d’Information

61
ESIAL 2A – Mario Lezoche
Character Large Object


A
CLOB
(Character Large Object) is an Oracle data type
that can hold up to 4 GB of data. CLOB's are handy for
storing text.


Examples


CREATE TABLE t1(id NUMBER, doc CLOB);


INSERT INTO t1 VALUES (1, 'some CLOB data');
Conception et Exploitation des Systèmes d’Information

62
ESIAL 2A – Mario Lezoche
National Character Large Object


NCLOB
(National Character Large Object) is an Oracle data
type that can hold up to 4 GB of character data. It's similar to a
CLOB, but characters are stored in a NLS or multi byte national
character set.


Examples


CREATE TABLE t1(c1 NCLOB);


INSERT INTO t1 VALUES( N'any nchar literal' );


NLS
(National Language Support) is used to define national
date, number, currency and language settings. For example, it
can be used to change the currency symbol from $ to € (Euro);
the language from English to Dutch, etc.
Conception et Exploitation des Systèmes d’Information

63
ESIAL 2A – Mario Lezoche
Binary Large Object


A
BLOB
(Binary Large Object) is an Oracle data type that
can hold up to 4 GB of data. BLOB's are handy for storing
digitized information (e.g.,
images
, audio, video).


Examples:
Create a table containing a BLOB column.


CREATE TABLE lob_table (id NUMBER, doc BLOB);


INSERT INTO lob_table VALUES (1, EMPTY_BLOB());
Conception et Exploitation des Systèmes d’Information

64
17
ESIAL 2A – Mario Lezoche
Using Oracle


General strategy for accessing data from an already-
existing database:


1. Connect to the Oracle database


2. Prepare your 'query'


3. Actually execute your query


4. Process the results


(Repeat 2-4 as necessary)


5. Close your connection to the database
Conception et Exploitation des Systèmes d’Information

65
ESIAL 2A – Mario Lezoche
OCI8: Starting the work…
<?php
/*




We'll start by setting up the groundwork for the display of results using a limit/offset scheme.

if $offset is set below zero (invalid) or empty, set to zero
*/


if (empty($offset) || $offset < 0) { $offset=0; }

/*



Now set the limit, or the number of results you would like displayed 'per page' of the total result set.
*/

$limit = 3;

/*
Conception et Exploitation des Systèmes d’Information

66
ESIAL 2A – Mario Lezoche
OCI8: Connect to Oracle DB
/*

Connecting to Oracle.

First, you can set your Oracle environment variable
ORACLE_SID
.

- In Windows this can be done using regedit,

- on UNIX you can use the
putenv()
function: putenv("ORACLE_SID=ORASID"); Or, if you would
rather, you can reference your ORACLE_SID directly when connecting to the database.

Let's reference
ORACLE_SID directly
in this example.

If you are using PHP as an Apache Module you can use persistent connections. Using PHP as a CGI,
use non-persistant connections. Let's
use

non-persistent connections
in this example.

*/

$conn = OCILogon("user_name", "password", "ORASID");

Conception et Exploitation des Systèmes d’Information

67
Alias of
oci_connect,
connect to an Oracle database

ESIAL 2A – Mario Lezoche
OCI8: Oracle Connection and
SQL statement creation
/*

Error checking. If no database connection was made display an error and exit the script.
*/

if (!$conn) { echo "<h1>ERROR - Could not connect to Oracle</h1>"; exit; }
/*

If the connection was successful,
$conn
will now
contain the connection identifier
.

Otherwise, the script will end and output "Could not connect to Oracle.”

Now create and parse your SQL statement to drop old table.

*/

$sql = ”drop table tab1";

Conception et Exploitation des Systèmes d’Information

68
18
ESIAL 2A – Mario Lezoche
OCI8: SQL statement
Parsing and Executing

$stmt = OCIParse($conn, $sql);

if (!$stmt) {


echo "<h1>ERROR - Could not parse SQL statement.</h1>";


exit;

}
/*

If you entered an invalid SQL statement, or another error occurred, you will see the "Could not parse
SQL" error, otherwise $stmt now contains the Statement Identifier.

Now execute your parsed statement.
*/

OCIExecute($stmt);


Conception et Exploitation des Systèmes d’Information

69
Alias of
oci_parse,
Prepares an Oracle
statement for execution

Alias of
oci_execute,
Executes a statement

ESIAL 2A – Mario Lezoche
OCI8: Create Table
/*

The statement has now been executed, but as you can see, there is
no result identifier returned
from
OCIExecute().

The Statement Identifer returned by
OCIParse() contains all the information Oracle will need
.

Now create new table.
*/

$sql = "create table tab1 (col1 number, col2 varchar2(30))";



$stmt = OCIParse($conn, $sql);

if (!$stmt) {


echo "<h1>ERROR - Could not parse SQL statement.</h1>";


exit;

}



OCIExecute($stmt);


Conception et Exploitation des Systèmes d’Information

70
Alias of
oci_parse,
Prepares an Oracle
statement for execution

ESIAL 2A – Mario Lezoche
OCI8: Insert Data into Table
/*

The statement has now been executed, but as you can see, there is
no result identifier returned
from
OCIExecute().

The Statement Identifer returned by
OCIParse() contains all the information Oracle will need
.

Now create new table.
*/

$sql = "insert into tab1 values (1, ‘Frank’)";



$stmt = OCIParse($conn, $sql);

if (!$stmt) {


echo "<h1>ERROR - Could not parse SQL statement.</h1>";


exit;

}



OCIExecute($stmt);


Conception et Exploitation des Systèmes d’Information

71
Alias of
oci_parse,
Prepares an Oracle
statement for execution

ESIAL 2A – Mario Lezoche
OCI8: Insert Data
using Bind variables

$var1 = 2;

$var2 = “Scott”;

$sql = "insert into tab1 values (:bind1, :bind2)”;



$stmt = OCIParse($conn, $sql);

if (!$stmt) {


echo "<h1>ERROR - Could not parse SQL statement.</h1>";


exit;

}



oci_bind_by_name($stmt, “bind1”, “var1”);

oci_bind_by_name($stmt, “bind2”, “var2”);


OCIExecute($stmt);


Conception et Exploitation des Systèmes d’Information

72
Binds a PHP variable to an Oracle placeholder

19
ESIAL 2A – Mario Lezoche
OCI8: display results
/*

Select data
*/

$sql = ”select * from tab1”;



$stmt = OCIParse($conn, $sql);

if (!$stmt) {


echo "<h1>ERROR - Could not parse SQL statement.</h1>";


exit;

}

oci_execute ($stmt);

while (oci_fetch($stmt)) {


echo ”<p>COL1 = “ . oci_result($stmt, “COL1”) . “, COL2 = ” . oci_result($stmt, “COL2”) . “ <br>\n";

}
Conception et Exploitation des Systèmes d’Information

73
Fetches the next row from a query into internal buffers

Returns field's value from the fetched row

ESIAL 2A – Mario Lezoche
OCI8: Commit and
Closing connection

//
Commit to save changes

oci_commit($conn);
/*

We're all done with Oracle, so free the last statement identifier and logoff
*/

OCIFreeStatement($stmt);

OCILogoff($conn);
?>
Conception et Exploitation des Systèmes d’Information

74
Alias of
oci_close,
Closes an Oracle
connection

Alias of
oci_free_statement,
Frees all resources associated with
statement or cursor

Commits the outstanding database
transaction

ESIAL 2A – Mario Lezoche
Oracle / OCI8 references


http://php.net/manual/en/book.oci8.php
(in English)

http://php.net/manual/fr/book.oci8.php
(in French)



Oracle Wiki:
http://www.orafaq.com/wiki


http://wiki.oracle.com/page/PHP+Oracle+FAQ


http://www.orafaq.com/wiki/PHP_FAQ



http://www.jainsachin.com/php_manual_tutorial/ref.oci8.html
Conception et Exploitation des Systèmes d’Information

75
ESIAL 2A – Mario Lezoche
BLOB – PL/SQL language –
Load an external file into a table
DECLARE

src_lob BFILE := BFILENAME('MY_DIR', '/tmp/me.gif');

dest_lob BLOB;
BEGIN

INSERT INTO lob_table VALUES(2, EMPTY_BLOB()) RETURNING doc INTO dest_lob;

DBMS_LOB.OPEN(src_lob, DBMS_LOB.LOB_READONLY);

DBMS_LOB.LoadFromFile ( DEST_LOB => dest_lob,



S R C _ L O B = > s r c _ l o b,



A M O U N T = > D B M S _ L O B.G E T L E N G T H ( s r c _ l o b ) );

D B M S _ L O B.C L O S E ( s r c _ l o b );

C O M M I T;
END;
Conception et Exploitation des Systèmes d’Information

76
20
ESIAL 2A – Mario Lezoche
OCI8: Upload Images (BLOB)
<?php
/*
Sample form to upload and insert an image into an ORACLE BLOB column using PHP5's OCI8 API. Before
running this script, execute these statements in SQL:

drop table btab;

create table btab (blobid number, blobdata blob);
This example uploads an JPG file and inserts it into a BLOB column. The image is retrieved back from the
column and displayed.
Make sure there is no whitespace before "<?php" else the wrong HTTP header will be sent and the image won't
display properly. Based on a sample originally found in

http://www.php.net/manual/en/function.oci-new-descriptor.php
*/
$myblobid = 1; //
should really be a unique id e.g. a sequence number
if (!isset ($_FILES['lob_upload'])) {
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST” enctype="multipart/form-data”>
Image filename: <input type="file" name="lob_upload"> <input type="submit" value="Upload”>
</form>
Conception et Exploitation des Systèmes d’Information

77
ESIAL 2A – Mario Lezoche
OCI8: Upload Images (BLOB) 2
<?php
} else {


$conn = oci_connect("hr", "hrpwd", "localhost/XE");

//
Delete any existing BLOB so the query at the bottom displays the new data

$query = 'DELETE FROM BTAB WHERE BLOBID = :MYBLOBID';

$stid = oci_parse ($conn, $query);

oci_bind_by_name($stid, ':MYBLOBID', $myblobid);

$e = oci_execute($stid, OCI_COMMIT_ON_SUCCESS);

if (!$e) {die;}

oci_free_statement($stid);
//
Insert the BLOB from PHP's tempory upload area

$lob = oci_new_descriptor($conn, OCI_D_LOB);

$stid = oci_parse($conn, 'INSERT INTO BTAB (BLOBID, BLOBDATA) ' .'VALUES(:MYBLOBID,


E M P T Y _ B L O B ( ) ) R E T U R N I N G B L O B D A T A I N T O :B L O B D A T A');

o c i _ b i n d _ b y _ n a m e ( $ s t i d, ':M Y B L O B I D', $ m y b l o b i d );

o c i _ b i n d _ b y _ n a m e ( $ s t i d, ':B L O B D A T A', $ l o b, - 1, O C I _ B _ B L O B );

o c i _ e x e c u t e ( $ s t i d, O C I _ D E F A U L T );

C o n c e p t i o n e t E x p l o i t a t i o n d e s S y s t è m e s d ’ I n f o r m a t i o n

7 8
E S I A L 2 A – M a r i o L e z o c h e
O C I 8: U p l o a d I m a g e s ( B L O B ) 3
/*

T h e f u n c t i o n $ l o b - > s a v e f i l e (...) r e a d s f r o m t h e u p l o a d e d f i l e. I f t h e d a t a w a s a l r e a d y i n a P H P v a r i a b l e $ m y v, t h e
$ l o b - > s a v e ( $ m y v ) f u n c t i o n c o u l d b e u s e d i n s t e a d.
*/
i f ( $ l o b - > s a v e f i l e ( $ _ F I L E S ['l o b _ u p l o a d'] ['t m p _ n a m e'] ) ) {

o c i _ c o m m i t ( $ c o n n );
} e l s e {

e c h o "C o u l d n't u p l o a d B l o b\n";
}
$ l o b - > f r e e ( );
o c i _ f r e e _ s t a t e m e n t ( $ s t i d );

//
Now query the uploaded BLOB and display it

$query = 'SELECT BLOBDATA FROM BTAB WHERE BLOBID = :MYBLOBID';
$stid = oci_parse ($conn, $query);
oci_bind_by_name($stid, ':MYBLOBID', $myblobid);
oci_execute($stid, OCI_DEFAULT);
$arr = oci_fetch_assoc($stid);
$result = $arr['BLOBDATA']->load();

Conception et Exploitation des Systèmes d’Information

79
ESIAL 2A – Mario Lezoche
OCI8: Upload Images (BLOB) 4
/*

If any text (or whitespace!) is printed before this header is sent, the text won't be displayed and the image won't
display properly. Comment out this line to see the text and debug such a problem.
*/

header("Content-type: image/JPEG");

echo $result;

oci_free_statement($stid);

oci_close($conn); //
log off

}
?>

Conception et Exploitation des Systèmes d’Information

80
21
ESIAL 2A – Mario Lezoche
PHP various stuff


PHP can send emails, using the email() function


The
cURL
extension lets you download other
webpages
, or
talk to other websites.


Nifty for getting price quotes from other websites, or getting
directions from
GoogleMaps
in your PHP


The GD extension allows PHP to generate images. Useful
for creating CAPTHA systems


The
mcrypt
extension useful for encryption


And now let’s talk about security…
Conception et Exploitation des Systèmes d’Information

81
ESIAL 2A – Mario Lezoche
Security


Now is a good time to talk about PHP security, starting with
Database security.


Database can be prone to '
injection attacks
'


There are other ways to do queries in OCI:


$result = $conn->query ("SELECT * FROM people WHERE name=
\"$name\"");


This way of making a query opens up the door for injection
attacks and probably shouldn't be used
Conception et Exploitation des Systèmes d’Information

82
ESIAL 2A – Mario Lezoche
Injection Attacks


$result = $conn->query("SELECT * FROM people
WHERE name=\"$name\"");


Assume that $name is retrieved directly from form data
(i.e., $name = $_POST['name']; )


This will work just fine if $name is something sane like
'Bob' or Jane'.


But, consider the case of the malicious user who, instead of
typing 'Bob', types the following (quotes included):


”; DROP people; SELECT * FROM foo WHERE name=”
Conception et Exploitation des Systèmes d’Information

83
ESIAL 2A – Mario Lezoche
How do Injection Attacks work?


Then, in this case, the query string will read:


SELECT * FROM people WHERE name=””;
DROP people;
SELECT * FROM foo WHERE name=””;


Deleted our table!


Solution:


Escape (i.e., encode quotes properly) any text used in a query.
Conception et Exploitation des Systèmes d’Information

84
22
ESIAL 2A – Mario Lezoche
XSS Attacks


XSS stands for 'Cross-Site Scripting’


On many websites, data entered and entered by one user is
stored in a database, and then later displayed to another
user (ex. Forums, blogs, wiki's, etc.)


This is usually okay, for regular data. But what if a
malicious user attempts to enter HTML or JavaScript into
the database (into a forum post)?
Conception et Exploitation des Systèmes d’Information

85
ESIAL 2A – Mario Lezoche
XSS Attacks – example
Conception et Exploitation des Systèmes d’Information

86
Name
Course
Gender
Prog
. Language
Steve
6
Male
PHP
Alice
2
Female
Java
Bob
8
Male
C++
Name
Course
Gender
Prog
. Language
Steve
6
Male
PHP
Alice
2
Female
Java
Bob
8
Male
C++
Mary
19
Female
</td></
tr
></table>
<script>
aler
(“XSS
!”); </script>
ESIAL 2A – Mario Lezoche
XSS attacks results


If PHP outputs this data as is, it will be interpreted by the
web browser as HTML (just like anything else outputted
from PHP)


The hacker's entry will close off the HTML table we made,
and start executing arbitrary JavaScript code


In this example, the code doesn't do much – just shows a
message box.
Conception et Exploitation des Systèmes d’Information

87
ESIAL 2A – Mario Lezoche
XSS attacks results


However, a hacker could potentially read and send- off
your cookies


PHP Sessions are implemented by storing a session- ID
cookie on the computer.


If this is stolen, someone else can impersonate you on
those websites!


Called 'Session hijacking!'
Conception et Exploitation des Systèmes d’Information

88
23
ESIAL 2A – Mario Lezoche
Way to prevent XSS


One way to make things a lot safer: use the PHP function
htmlspecialchars()


Takes as input a string, and encodes it in a way such the content remains
the same, but it will not be interpreted as HTML


For example,


htmlspecialchars('<script>alert("XSS!");</script>', ENT_QUOTES)


will output:


&lt;script&gt;alert(&quot;XSS!&quot;);&lt;/script&gt;


Note that this is 'HTML-ified'; the browser will interpret this as
text, not as HTML
Conception et Exploitation des Systèmes d’Information

89
ESIAL 2A – Mario Lezoche
Common mistakes


If, when you run code, you just see a blank page, you made
a syntax mistake in your code


Forgetting a semicolon on the end of lines


() {} mismatches


To see these (and other errors) instead of an annoying
blank page, enable debugging and warning messages:


error_reporting(E_ALL
);


ini_set('display_errors','On
');
Conception et Exploitation des Systèmes d’Information

90
ESIAL 2A – Mario Lezoche
Other resources


php.net – PHP's official website. Extensive, useful
documentation



Tutorial: http://it.php.net/tut.php


http://www.w3schools.com/PHP/DEfaULT.asP



Wicked Cool PHP, by William Steinmetz and Brian Ward.
Very useful book
Conception et Exploitation des Systèmes d’Information

91
ESIAL 2A – Mario Lezoche
OCI8
Another way to parse query results
Conception et Exploitation des Systèmes d’Information

92
24
ESIAL 2A – Mario Lezoche
OCI8 ex2: Oracle Connection
and SQL statement creation
/*

Error checking. If no database connection was made display an error and exit the script.
*/

if (!$conn) { echo "<h1>ERROR - Could not connect to Oracle</h1>"; exit; }
/*

If the connection was successful,
$conn
will now
contain the connection identifier
.

Otherwise, the script will end and output "Could not connect to Oracle.”

Now create and parse your SQL statement to count the number of rows to be returned by a non
LIMIT-ed query.

The format is "Select count(*) from table_name" as count() in this case is a SQL function and will
execute in the database, rather than relying on PHP to count the number of rows returned in a result
set.

*/

$sql = "Select count(*) from table_name";

Conception et Exploitation des Systèmes d’Information

93
ESIAL 2A – Mario Lezoche
OCI8 ex2: results fetching
/*

The statement has now been executed, but as you can see, there is
no result identifier returned
from
OCIExecute().

The Statement Identifer returned by
OCIParse() contains all the information Oracle will need
.

Now extract the result of this query. $total_rows[0] will contain the magic number.

If there are no rows returned, display an error and exit the script.
*/

OCIFetchInto ($stmt, &$total_rows);

if ( !$total_rows[0] ) {


echo "<h1>Error - no rows returned!</h1>";


exit;

}
Conception et Exploitation des Systèmes d’Information

94
Fetches the next row into an array

ESIAL 2A – Mario Lezoche
OCI8 ex2: display notice
/*

Optional, but nice, this section of code will display a notice similar to "There are 15 results. Now
showing results 4 to 7".
*/

$begin = ($offset + 1);

$end = ($begin + ($limit-1));

if ($end > $total_rows[0]) {


$end = $total_rows[0];

}

echo "There are <
b
>$total_rows[0]</
b
> results.<
br
>\
n
";

echo "Now showing results <
b
>$begin</
b
> to <
b
>$end</
b
>.<
br
><
br
>\
n
";
Conception et Exploitation des Systèmes d’Information

95
ESIAL 2A – Mario Lezoche
OCI8 ex2: continuing…
/*

Free up the previous statement identifier then create the main SQL statement, parse it and execute it.

Coding note:



Unlike MySQL, Oracle
does not support the LIMIT

argument in a SQL statement
.


As such, there are a few ways to select specific rows of a result set for output. The best way is

to select the results into a
temporary table
and "
cache
" the entire result set. We will take the

simpler approach explained just a bit further down.
*/

OCIFreeStatement($stmt);

$sql = "Select * from table_name";

$stmt = OCIParse ($conn, $sql);

if (!$stmt) {


echo "<h1>ERROR - Could not parse SQL statement.</h1>";


exit;

}

OCIExecute($stmt);
Conception et Exploitation des Systèmes d’Information

96
Alias of
oci_free_statement,
Frees all
resources associated with statement
or cursor

25
ESIAL 2A – Mario Lezoche
OCI8 ex2: Display results
/*

Now display the results. The easiest way to do this is to loop through the result set. HTML can be
worked into the final display, but this example is very simple, so we won't use any.

Coding note: As stated above, there is not LIMIT in Oracle, so what
we have to do is cycle through
the entire result set pulling out only the results we want

and stopping once we have them all
.
*/
$i=0; $j=0;

while ( OCIFetchInto($stmt, &$result_array) ) {


if ($i>=$offset) {



if ($j <$limit) {



f o r ( $ k = 0; $ k < = c o u n t ( $ r e s u l t _ a r r a y ); $ k + + ) {



e c h o $ r e s u l t _ a r r a y [ $ k ]." ";



}



e c h o " "; $ j + +;



}


}


$ i + +;

} e c h o " ";
C o n c e p t i o n e t E x p l o i t a t i o n d e s S y s t è m e s d ’ I n f o r m a t i o n

9 7
E S I A L 2 A – M a r i o L e z o c h e
O C I 8 e x 2: P a g i n g
/*

T h e r e s u l t s h a v e b e e n d i s p l a y e d f o r t h e c u r r e n t p a g e. T i m e t o g i v e t h e v i s i t o r a w a y t o h i t t h e o t h e r
p a g e s! O n w i t h t h e N E X T/P R E V l i n k s!
*/ //
C a l c u l a t e t o t a l n u m b e r o f p a g e s i n r e s u l t

$ p a g e s = i n t v a l ( $ t o t a l _ r o w s [ 0 ] / $ l i m i t );

//
$pages now contains total number of pages needed unless there is a remainder from division

if ($total_rows[0] % $limit) {


//
has remainder so add one page


$pages++;

}


//
Don't display PREV link if on first page

if ($offset!=0) {


$prevoffset = $offset - $limit;


echo "<a href=\"$PHP_SELF?offset=$prevoffset\"> << PREV </a> &nbsp; \n";

}
Conception et Exploitation des Systèmes d’Information

98
ESIAL 2A – Mario Lezoche
OCI8 ex2:
Paging with numbered links
//
Now loop through the pages to create numbered links ex. 1 2 3 4 5 NEXT >>
for ($i=1; $i <= $pages; $i++) {

//
Check if on current page


if ( ($offset / $limit) == ($i-1) ) {


//
$i is equal to current page, so don't display a link


echo "$i &nbsp; ";

} else {


//
$i is NOT the current page, so display a link to page


$i $newoffset=$limit*($i-1);


echo "<a href=\"$PHP_SELF?offset=$newoffset\"> $i </a> &nbsp; \n";

}
}
Conception et Exploitation des Systèmes d’Information

99
ESIAL 2A – Mario Lezoche
OCI8 ex2: Closing connection

//
Check to see if current page is last page

if (!( (($offset / $limit) +1) == $pages) && $pages != 1) {


//
Not on the last page yet, so display a NEXT Link


$newoffset = $offset+$limit;


echo "<a href=\"$PHP_SELF?offset=$newoffset\"> NEXT >> </a><p>\n";

}
/*

We're all done with Oracle, so free the last statement identifier and logoff
*/

OCIFreeStatement($stmt);

OCILogoff($conn);
?>
Conception et Exploitation des Systèmes d’Information

100
Alias of
oci_close,
Closes an Oracle
connection

26
ESIAL 2A – Mario Lezoche
The end…
Thanks for coming, everyone!
Conception et Exploitation des Systèmes d’Information

101