Learning PHP

This is just a playground with examples and snarky comments. It is a concise rendering of different things tried from the real tutorial is at W3Schools.


intro: echo (Oh!)

echo "base"; //haha star wars
$x = 1100 /* + 15 */ + 38;
echo $x; // see what I did there :)  Think THX
$txt = "my wife";
echo "<p>I love $txt!</p>";
echo "<p>I love <b>" . $txt . "</b>!</p>";
echo "echo"; // look a meta-echo ;P
base1138

I love my wife!

I love my wife!

echo

adding variables

$x = 8670000;
$y = 5309;
echo $x + $y;

What if Jenny was to write that code?

8675309

global and local scopes

Scope comes from the Greek word "skopos" (σκοπος) meaning "to look intently."

$x = 7; // global scope

function myTest() {
    // using x inside this function will generate an error
    echo "<p>Variable x inside function is: $x</p>";
} 
myTest();

echo "<p>Variable x outside function is: $x</p>";

Variable x inside function is:

Variable x outside function is: 7

global keyword

$x = 5250000;
$y = 1977;

function myTest2() {
    global $x, $y;
    $y = $x + $y;
}

myTest2();
echo $y; // outputs 5251977 - date of a new hope ;) 

A new hope?

5251977

$GLOBALS array

$x = 5250000;
$y = 1977;

function myTest3() {
    $GLOBALS['y'] = $GLOBALS['x'] + $GLOBALS['y'];
} 

myTest3();
echo $y; // outputs 5251977 - date of a new hope ;) 

Wait, what episode is this??

5251977

static keyword to keep variable from deletion

function myTest4() {
    static $x = 10;
    echo "<p>$x</p>";
    $x--;
}

myTest4();
myTest4();
myTest4();

"We're heading for Venus and still we stand tall ..." ~ Europe

10

9

8

Hmmm... It's like a "final countdown."

print

$txt1 = "Learn PHP";
$txt2 = "W3Schools.com";
$txt3 = "or"; 
$x = 37;
$y = 53;

print "<h3>" . $txt1 . "</h3>";
print "Study PHP at " . $txt2 . "<br />";
print $txt3 . " " . $x . "" . $y . "<br />"; // get it?
print $x + $y;

Learn PHP

Study PHP at W3Schools.com
or 3753
90

var_dump to tell type of variable

$x = 1138;
var_dump($x);
$y = "THX";
var_dump($y);

int(1138) string(3) "THX"

object

class Autobot {
    function Autobot() {
        $this->model = "VW";
    }
}

// create an object
$bumblebee = new Autobot();

// show object properties
echo $bumblebee->model;
VW

null

$x = "Some dude who refuses to apply himself";
$x = null;
var_dump($x);

NULL

string length

echo "There are ".strlen("12 disciples")." characters in '12 disciples' and it is not from 12 being in the string (remember space counts as one)."; // outputs 12

There are 12 characters in '12 disciples' and it is not from 12 being in the string (remember space counts as one).

count words

echo "There are ".str_word_count("Peter Andrew James John Phillip Thomas Matthew James Thaddeus Simon Judas Bartholomew")." disciples in 'Peter Andrew James John Phillip Thomas Matthew James Thaddeus Simon Judas Bartholomew'."; // outputs 12
There are 12 disciples in 'Peter Andrew James John Phillip Thomas Matthew James Thaddeus Simon Judas Bartholomew'.

put it in reverse

echo strrev("What are you looking for the devil for when you ought to be looking for the Lord?"); // outputs the backmasking in Petra's "Judas' Kiss" song; i.e., the string backwards
?droL eht rof gnikool eb ot thguo uoy nehw rof lived eht rof gnikool uoy era tahW
~ Petra in "Judas' Kiss"

find in position in string

echo "Where in the world? The word 'world' is at position ".strpos("Hello world!", "world")." (think arrays) in 'Hello world!'"; // outputs 6
Where in the world? The word 'world' is at position 6 (think arrays) in 'Hello world!'

constant GREETING using the define keyword (note: constants are global)

define("GREETING", "Bah-weep-Graaaaagnah wheep ni ni bong!");
Bah-weep-Graaaaagnah wheep ni ni bong!

constant case-insensitive greeting

define("GREETING", "Bah Weep Graaagnah Wheep Ni Ni Bong!", true);
echo greeting;

Bah Weep Graaagnah Wheep Ni Ni Bong!

operators

Most operators are the same, but here are some unique ones (but while they work smoothly, I don't think any of them are a Smooth Operator):

** is exponent as in $x**$y (but it requires PHP 5.6).

=== is identical to as in they are equal AND of the same type.

!== is not identical.

xor is Xor: $x xor $y is True if either $x or $y is true, but not both.

While . is concatenation, .= appends as in $x.=$y appends $y to $x

conditional statements / control structures / case (switch) statements

Just know that elseif is how you do that one in php ;) and switch statements are handled just as they are in JS.

loops and arrays

While and for loops are the same as in JS. Foreach is useful for arrays like this (note use as instead of in):

$robins = array("Dick Grayson","Jason Todd","Tim Drake","Stephanie Brown","Damian Wayne"); 

foreach ($robins as $value) {
    echo "$value <br />";
}
Dick Grayson
Jason Todd
Tim Drake
Stephanie Brown
Damian Wayne

Or you can do this:

$batgirls = array("Betty/Bette Kane","Barbara Gordon","Cassandra Cain","Stephanie Brown","Helena Bertinelli","Charlotte \"Charlie\" Gage-Radcliffe");
$arrlength = count($batgirls);

for($x = 0; $x < $arrlength; $x++) {
    echo $batgirls[$x];
    echo "<br />";
}
Betty/Bette Kane
Barbara Gordon
Cassandra Cain
Stephanie Brown
Helena Bertinelli
Charlotte "Charlie" Gage-Radcliffe

Associative Arrays

$type = array("Bumblebee"=>"Autobot", "Soundwave"=>"Decepticon", "Doubledealer"=>"Double-Agent");
echo "Bumblebee is an " . $type['Bumblebee'] . ".";
Bumblebee is an Autobot.

Now loop through it:

$type = array("Bumblebee"=>"Autobot", "Soundwave"=>"Decepticon", "Doubledealer"=>"Double-Agent");

foreach($type as $x => $x_value) {
    echo "Key=" . $x . ", Value=" . $x_value;
    echo "<br />";
}

Key=Bumblebee, Value=Autobot
Key=Soundwave, Value=Decepticon
Key=Doubledealer, Value=Double-Agent

And you can sort:

For multi-dimensional arrays, remember $cars[$row][$col] for an array like this:

$cars = array
  (
  array("Volvo",22,18),
  array("BMW",15,13),
  array("Saab",5,2),
  array("Land Rover",17,15)
  );

More details about multidimensional arrays.

functions

Like JS, but there is a default value option as in function setHeight($minheight = 50) and if one calls setHeight(), the value is 50.

Superglobals

Not Kal-El!

$x = 75; 
$y = 25;
 
function addition() { 
    $GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y']; 
}
 
addition(); 
echo $z; 

100

Server Variables


echo $_SERVER['PHP_SELF'];
echo "<br />";
echo $_SERVER['SERVER_NAME'];
echo "<br />";
echo $_SERVER['HTTP_HOST'];
echo "<br />";
echo $_SERVER['HTTP_REFERER'];
echo "<br />";
echo $_SERVER['HTTP_USER_AGENT'];
echo "<br />";
echo $_SERVER['SCRIPT_NAME'];

/php/index.php
tech.beacondeacon.com
tech.beacondeacon.com

CCBot/2.0 (https://commoncrawl.org/faq/)
/php/index.php

forms

Forms on W3Schools

Set form action like this to prevent XSS:

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

Remember this to process input name="name" and input name="email":

Welcome <?php echo $_POST["name"]; ?><br />
Your email address is: <?php echo $_POST["email"]; ?>

And here's a validation script for the self-referring form page:

$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  $name = test_input($_POST["name"]);
  $email = test_input($_POST["email"]);
  $website = test_input($_POST["website"]);
  $comment = test_input($_POST["comment"]);
  $gender = test_input($_POST["gender"]);
}

function test_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

Make things required:

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  if (empty($_POST["name"])) {
    $nameErr = "Name is required";
  } else {
    $name = test_input($_POST["name"]);
  }
  ...
}

While having this in the form HTML:

Name: <input type="text" name="name">
<span class="error">* <?php echo $nameErr;?></span>

Resource: RegEx to validate email, etc.

Time (and date) ... why do you punish me?

echo "Today is " . date("Y/m/d") . "<br />";
echo "Today is " . date("Y.m.d") . "<br />";
echo "Today is " . date("Y-m-d") . "<br />";
echo "Today is " . date("l") . "<br />";
echo "The time is " . date("h:i:sa") . " per the server <br />";
date_default_timezone_set("America/New_York");
echo "The time is " . date("h:i:sa") . " in America/New_York<br />";
$d=mktime(11, 14, 54, 8, 12, 2014);
echo "Created date is " . date("Y-m-d h:i:sa", $d) . "<br />";
$d=strtotime("10:30pm April 15 2014");
echo "Created date is " . date("Y-m-d h:i:sa", $d) . "<br />";
$d=strtotime("tomorrow");
echo date("Y-m-d h:i:sa", $d) . " is tomorrow<br />";

$d=strtotime("next Saturday");
echo date("Y-m-d h:i:sa", $d) . " is next Saturday<br />";

$d=strtotime("+3 Months");
echo date("Y-m-d h:i:sa", $d) . " is in 3 months<br />";

echo "<p>Saturdays the next 6 weeks: </p>";
$startdate = strtotime("Saturday");
$enddate = strtotime("+6 weeks", $startdate);
while ($startdate < $enddate) {
  echo date("M d", $startdate) . "<br />";
  $startdate = strtotime("+1 week", $startdate);
}

$d1=strtotime("December 25");
$d2=ceil(($d1-time())/60/60/24);
echo "<br /><br />There are " . $d2 ." days until Christmas.";
Today is 2019/08/20
Today is 2019.08.20
Today is 2019-08-20
Today is Tuesday
The time is 08:22:38am per the server
The time is 04:22:38am in America/New_York
Created date is 2014-08-12 11:14:54am
Created date is 2014-04-15 10:30:00pm
2019-08-21 12:00:00am is tomorrow
2019-08-24 12:00:00am is next Saturday
2019-11-20 04:22:38am is in 3 months

Saturdays the next 6 weeks:

Aug 24
Aug 31
Sep 07
Sep 14
Sep 21
Sep 28


There are 127 days until Christmas.

readfile

echo readfile("../batman.txt");
{"data": [{"alias": "Mr. Freeze","name": "Dr. Victor Fries","gender": "Male","appeared": "1959"},{"alias": "Poison Ivy","name": "Pamela Lillian Isley","gender": "Female","appeared": "1966"},{"alias": "The Penguin","name": "Oswald Chesterfield Cobblepot","gender": "Male","appeared": "1941"},{"alias": "The Joker","name": "Jack Napier (according to some)","gender": "Male","appeared": "1940"},{"alias": "Harley Quinn","name": "Dr. Harleen Frances Quinzel","gender": "Female","appeared": "1992"},{"alias": "Mad Hatter","name": "Jervis Tetch","gender": "Male","appeared": "1948"},{"alias": "The Riddler","name": "Edward Nygma/Nigma or Nashton","gender": "Male","appeared": "1948"},{"alias": "Catwoman","name": "Selina Kyle","gender": "Female","appeared": "1940"},{"alias": "Two-Face","name": "Harvey Dent","gender": "Male","appeared": "1942"}]}841

file handling

$myfile = fopen("../batman.txt", "r") or die("Unable to open file!");
// Output one character until end-of-file
while(!feof($myfile)) {
  echo fgetc($myfile);
}
fclose($myfile);
{"data": [{"alias": "Mr. Freeze","name": "Dr. Victor Fries","gender": "Male","appeared": "1959"},{"alias": "Poison Ivy","name": "Pamela Lillian Isley","gender": "Female","appeared": "1966"},{"alias": "The Penguin","name": "Oswald Chesterfield Cobblepot","gender": "Male","appeared": "1941"},{"alias": "The Joker","name": "Jack Napier (according to some)","gender": "Male","appeared": "1940"},{"alias": "Harley Quinn","name": "Dr. Harleen Frances Quinzel","gender": "Female","appeared": "1992"},{"alias": "Mad Hatter","name": "Jervis Tetch","gender": "Male","appeared": "1948"},{"alias": "The Riddler","name": "Edward Nygma/Nigma or Nashton","gender": "Male","appeared": "1948"},{"alias": "Catwoman","name": "Selina Kyle","gender": "Female","appeared": "1940"},{"alias": "Two-Face","name": "Harvey Dent","gender": "Male","appeared": "1942"}]}

From https://www.w3schools.com/php/php_file_open.asp:

Modes Description
r Open a file for read only. File pointer starts at the beginning of the file
w Open a file for write only. Erases the contents of the file or creates a new file if it doesn't exist. File pointer starts at the beginning of the file
a Open a file for write only. The existing data in file is preserved. File pointer starts at the end of the file. Creates a new file if the file doesn't exist
x Creates a new file for write only. Returns FALSE and an error if file already exists
r+ Open a file for read/write. File pointer starts at the beginning of the file
w+ Open a file for read/write. Erases the contents of the file or creates a new file if it doesn't exist. File pointer starts at the beginning of the file
a+ Open a file for read/write. The existing data in file is preserved. File pointer starts at the end of the file. Creates a new file if the file doesn't exist
x+ Creates a new file for read/write. Returns FALSE and an error if file already exists

write to file

$myfile = fopen("transformers.txt", "w") or die("Unable to open file!");
$txt = "Orion Pax\n";
fwrite($myfile, $txt);
$txt = "Megatronus\n";
fwrite($myfile, $txt);
fclose($myfile);

Overwriting

NOTE: With "transformers.txt" containing some data, when it is opened for writing, all existing data is erased.

Upload file

Refer to https://www.w3schools.com/php/php_file_upload.asp.

cookies

This is before the opening <html> tag

$cookie_name = "yummy";
$cookie_value = "MarbleBar";
setcookie($cookie_name, $cookie_value, time() + (86400 * 30), "/"); // 86400 = 1 day and expires after 30 days

This is in the body:

if(!isset($_COOKIE[$cookie_name])) {
    echo "Cookie named '" . $cookie_name . "' is not set! (Refresh if you see this to see it get set)";
} else {
    echo "Cookie '" . $cookie_name . "' is set!
"; echo "Value is: " . $_COOKIE[$cookie_name]; }
Cookie named 'yummy' is not set! (Refresh if you see this to see it get set)

To modify, just do the above setcookie() method with different data.

To delete the cookie:

setcookie("yummy", "", time() - 3600); // set the expiration date to one hour ago

Of course it's good to check if cookies are enabled:

Cookies are disabled.
if(count($_COOKIE) > 0) {
    echo "Cookies are enabled.";
} else {
    echo "Cookies are disabled.";
}

session

This goes before the opening html tag:

//start the session
session_start();

And this is on all pages utilizing the session.

Set session variables by putting this in the body:

// Set session variables
$_SESSION["favnum"] = "2520";
$_SESSION["favgijoe"] = "Snake-Eyes";
echo "Session variables are set.";
Session variables are set.

And then typically used on another page one can get session variables:

// Echo session variables that were set on previous page
echo "Favorite number is " . $_SESSION["favnum"] . " as it is evenly divisible by all integers 1 through 10.<br />";
echo "Favorite G.I. Joe is " . $_SESSION["favgijoe"] . "; he is a commando.";
Favorite number is 2520 as it is evenly divisible by all integers 1 through 10.
Favorite G.I. Joe is Snake-Eyes; he is a commando.

To show all session variables:

print_r($_SESSION);
Array ( [favnum] => 2520 [favgijoe] => Snake-Eyes )

Modify a session variable:

// to change a session variable, just overwrite it 
$_SESSION["favgijoe"] = "classified";
print_r($_SESSION);
Array ( [favnum] => 2520 [favgijoe] => classified )

End session:

// remove all session variables
session_unset(); 

// destroy the session 
session_destroy(); 

Now one cannot output any session variables (They're gone!). Look what happens when I try this:

print_r($_SESSION);
Array ( )

An empty array because the session has been destroyed.

filters

<table>
  <tr>
    <td>Filter Name</td>
    <td>Filter ID</td>
  </tr>
  <?php
  foreach (filter_list() as $id =>$filter) {
    echo '<tr><td>' . $filter . '</td><td>' . filter_id($filter) . '</td></tr>';
  }
  ?>
</table>
Filter Name Filter ID
int257
boolean258
float259
validate_regexp272
validate_url273
validate_email274
validate_ip275
string513
stripped513
encoded514
special_chars515
unsafe_raw516
email517
url518
number_int519
number_float520
magic_quotes521
callback1024

Sanitize a string:

$str = "<h3>&#161;Hola Mundo</h3>";
$newstr = filter_var($str, FILTER_SANITIZE_STRING);
echo $newstr;
¡Hola Mundo!

However, what if there are special characters in the string that pass? We can stop that by removing characters with ASCII value greater than 127:

$str = "<h1>'&#161;¡Hola Mundo!' is 'Hello World!' in SpanishÆØÅ.</h1>";

$newstr = filter_var($str, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH);
echo $newstr;
'¡Hola Mundo!' is 'Hello World!' in Spanish.

However, using the entity for an inverted exclamation point (&#161; for ¡) is allowed. If I directly paste it in the code as I did above, it is stripped out. That is why you don't see two inverted exclamation points as well as the other special characters.

Validate an integer (and remember 0):

$int = 0;

if (filter_var($int, FILTER_VALIDATE_INT) === 0 || !filter_var($int, FILTER_VALIDATE_INT) === false) {
    echo("Integer is valid");
} else {
    echo("Integer is not valid");
}
Integer is valid

Validate IP address:

$ip = "867.5.30.9";

if (!filter_var($ip, FILTER_VALIDATE_IP) === false) { // use if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) for IPv6
    echo("$ip is a valid IP address");
} else {
    echo("$ip is not a valid IP address");
}
867.5.30.9 is not a valid IP address

Silly Jenny! That's a phone number, not an IP address!

Sanitize email address:

$email = "yawn@an@example.com";

// Remove all illegal characters from email
$email = filter_var($email, FILTER_SANITIZE_EMAIL);

// Validate e-mail
if (!filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
    echo("$email is a valid email address");
} else {
    echo("$email is not a valid email address");
}
yawn@an@example.com is not a valid email address

Sanitize URL:

$url = "https://tech.beacondeacon.com";

// Remove all illegal characters from a url
$url = filter_var($url, FILTER_SANITIZE_URL);

// Validate url
if (!filter_var($url, FILTER_VALIDATE_URL) === false) { // use if (!filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED) to check to see if URL has a querystring
    echo("<a href=\"$url\" target=\"_blank\">$url</a> is a valid URL"); // and I set it to make a link if valid
} else {
    echo("$url is not a valid URL");
}
http://tech.beacondeacon.com is a valid URL

error handling

Please see https://www.w3schools.com/php/php_error.asp though this is a good practical example:

//error handler function
function customError($errno, $errstr) {
  echo "Error: [$errno] $errstr
"; echo "Ending Script"; die(); } //set error handler set_error_handler("customError",E_USER_WARNING); //trigger error $test=2; if ($test>=1) { trigger_error("Value must be 1 or below",E_USER_WARNING); }

However, I don't implement this as it doesn't allow subsequent examples to execute ;)

"Don't kill him! If you kill him ... he won't learn nothing." ~ The Riddler, Batman Forever (1995)

exception - as in try, throw and catch

Here's my favorite example at https://www.w3schools.com/php/php_exception.asp:

class customException extends Exception {
  public function errorMessage() {
    //error message
    $errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
    .': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
    return $errorMsg;
  }
}

$email = "bruce.wayne@example.com";

try {
  //check if
  if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
    //throw exception if email is not valid
    throw new customException($email);
  }
  //check for "example" in mail address
  if(strpos($email, "example") !== FALSE) {
    throw new Exception("$email is an example e-mail");
  }
}

catch (customException $e) {
  echo $e->errorMessage();
}

catch(Exception $e) {
  echo $e->getMessage();
}
bruce.wayne@example.com is an example e-mail

XML Parsing

SimpleXML: read from file

For this and other XML examples, I will be using batman.xml.

$xml=simplexml_load_file("../batman.xml") or die("Error: Cannot create object");
print_r($xml);
SimpleXMLElement Object ( [villain] => Array ( [0] => SimpleXMLElement Object ( [alias] => Mr. Freeze [name] => Dr. Victor Fries [gender] => Male [appeared] => 1959 ) [1] => SimpleXMLElement Object ( [alias] => Poison Ivy [name] => Pamela Lillian Isley [gender] => Female [appeared] => 1966 ) [2] => SimpleXMLElement Object ( [alias] => The Penguin [name] => Oswald Chesterfield Cobblepot [gender] => Male [appeared] => 1941 ) [3] => SimpleXMLElement Object ( [alias] => The Joker [name] => Jack Napier (according to some) [gender] => Male [appeared] => 1940 ) [4] => SimpleXMLElement Object ( [alias] => Harley Quinn [name] => Dr. Harleen Frances Quinzel [gender] => Female [appeared] => 1992 ) [5] => SimpleXMLElement Object ( [alias] => Mad Hatter [name] => Jervis Tetch [gender] => Male [appeared] => 1948 ) [6] => SimpleXMLElement Object ( [alias] => The Riddler [name] => Edward Nygma/Nigma or Nashton [gender] => Male [appeared] => 1948 ) [7] => SimpleXMLElement Object ( [alias] => Catwoman [name] => Selina Kyle [gender] => Female [appeared] => 1940 ) [8] => SimpleXMLElement Object ( [alias] => Two-Face [name] => Harvey Dent [gender] => Male [appeared] => 1942 ) ) )

SimpleXML: get node values

$xml=simplexml_load_file("../batman.xml") or die("Error: Cannot create object");
$pronoun = '';
$i=0;
// need array reference to reference grandchildren nodes like gender; if gender were a child and not a grandchild, it would be $xml->gender
$g = $xml->villain[0]->gender;
if($g == "Male"){
	$pronoun = "he";
} else {
	$pronoun = "she";
}
echo "The true identity of " . $xml->villain[$i]->alias . " is " . 
	$xml->villain[$i]->name . " and " . $pronoun . " first appeared in " . 
	$xml->villain[$i]->appeared . ".<br />"; 
The true identity of Mr. Freeze is Dr. Victor Fries and he first appeared in 1959.

Let's up the ante and loop through the nodes of batman.xml.

$xml=simplexml_load_file("../batman.xml") or die("Error: Cannot create object");
foreach($xml->children() as $villains) { 
    echo "The true identity of ". $villains->alias . " is "; 
    echo $villains->name . " and "; 
    if($villains->gender=="Male"){
		echo "he"; 
	} else { 
		echo "she";
	}
    echo " first appeared in " . $villains->appeared . ".<br />"; 
} 
The true identity of Mr. Freeze is Dr. Victor Fries and he first appeared in 1959.
The true identity of Poison Ivy is Pamela Lillian Isley and she first appeared in 1966.
The true identity of The Penguin is Oswald Chesterfield Cobblepot and he first appeared in 1941.
The true identity of The Joker is Jack Napier (according to some) and he first appeared in 1940.
The true identity of Harley Quinn is Dr. Harleen Frances Quinzel and she first appeared in 1992.
The true identity of Mad Hatter is Jervis Tetch and he first appeared in 1948.
The true identity of The Riddler is Edward Nygma/Nigma or Nashton and he first appeared in 1948.
The true identity of Catwoman is Selina Kyle and she first appeared in 1940.
The true identity of Two-Face is Harvey Dent and he first appeared in 1942.

If each villain had an attribute such as <villian residence="Arkham">, then the first villain's residence attribute would be referenced like this:

echo $xml->villain[0]['residence'];

In this case, there are no attributes for the nodes, but if there were, e.g., <appeared city="Gotham">1959</appeared>, and this was the first villain, then I would reference that attribute like this:

echo $xml->villain[0]->appeared['city'];

XML Expat Parser

// Initialize the XML parser
$parser=xml_parser_create();
echo "<style>.nodelabel{width:60px;display:inline-block;background:#cc9;}</style>";
// Function to use at the start of an element
function start($parser,$element_name,$element_attrs) {
  switch($element_name) { 
    case "VILLAIN": // Use ALL-CAPS to reference the nodes 
	echo "<b style=\"background:#cc9;\">---------------------------- Villain ----------------------------</b><br />";
	break;
	case "ALIAS":
    echo "<b class=\"nodelabel\">Alias:</b>";
    break;
    case "NAME":
    echo "<b class=\"nodelabel\">Name:</b>";
    break;
    case "GENDER":
    echo "<b class=\"nodelabel\">Sex:</b>";
    break;
    case "APPEARED":
    echo "<b class=\"nodelabel\">Debut:</b>";
  }
}

// Function to use at the end of an element
function stop($parser,$element_name) {
  echo "<br />";
}

// Function to use when finding character data
function char($parser,$data) {
  echo $data;
}

// Specify element handler
xml_set_element_handler($parser,"start","stop");

// Specify data handler
xml_set_character_data_handler($parser,"char");

// Open XML file
$fp=fopen("../batman.xml","r");

// Read data
while ($data=fread($fp,4096)) {
  xml_parse($parser,$data,feof($fp)) or 
  die (sprintf("XML Error: %s at line %d", 
  xml_error_string(xml_get_error_code($parser)),
  xml_get_current_line_number($parser)));
}

// Free the XML parser
xml_parser_free($parser);

---------------------------- Villain ----------------------------
Alias:Mr. Freeze
Name:Dr. Victor Fries
Sex:Male
Debut:1959

---------------------------- Villain ----------------------------
Alias:Poison Ivy
Name:Pamela Lillian Isley
Sex:Female
Debut:1966

---------------------------- Villain ----------------------------
Alias:The Penguin
Name:Oswald Chesterfield Cobblepot
Sex:Male
Debut:1941

---------------------------- Villain ----------------------------
Alias:The Joker
Name:Jack Napier (according to some)
Sex:Male
Debut:1940

---------------------------- Villain ----------------------------
Alias:Harley Quinn
Name:Dr. Harleen Frances Quinzel
Sex:Female
Debut:1992

---------------------------- Villain ----------------------------
Alias:Mad Hatter
Name:Jervis Tetch
Sex:Male
Debut:1948

---------------------------- Villain ----------------------------
Alias:The Riddler
Name:Edward Nygma/Nigma or Nashton
Sex:Male
Debut:1948

---------------------------- Villain ----------------------------
Alias:Catwoman
Name:Selina Kyle
Sex:Female
Debut:1940

---------------------------- Villain ----------------------------
Alias:Two-Face
Name:Harvey Dent
Sex:Male
Debut:1942


XML DOM Parser

$xmlDoc = new DOMDocument();
$xmlDoc->load("../batman.xml");

$x = $xmlDoc->documentElement;
foreach ($x->childNodes AS $item) {
  print $item->nodeName . " = " . $item->nodeValue . "<br />";
}
#text =
villain = Mr. Freeze Dr. Victor Fries Male 1959
#text =
villain = Poison Ivy Pamela Lillian Isley Female 1966
#text =
villain = The Penguin Oswald Chesterfield Cobblepot Male 1941
#text =
villain = The Joker Jack Napier (according to some) Male 1940
#text =
villain = Harley Quinn Dr. Harleen Frances Quinzel Female 1992
#text =
villain = Mad Hatter Jervis Tetch Male 1948
#text =
villain = The Riddler Edward Nygma/Nigma or Nashton Male 1948
#text =
villain = Catwoman Selina Kyle Female 1940
#text =
villain = Two-Face Harvey Dent Male 1942
#text =

The end?