2. Working with Arrays

When simple variables are just not good enough, arrays come into play (or objects, but that’s another topic). The array section in the PHP manual, available at http://php.net/array, lists approximately 80 functions that are helpful. Therefore, this book could be filled with array-related phrases alone. However, not all of these functions are really used often. Therefore, this chapter presents the most important problems you’ll have to solve when working with arrays—and, of course, solutions for these problems.

There are two types of arrays. The names they are given differ sometimes, but usually arrays are distinguished between numeric arrays and associative arrays. The first type of array uses numeric keys, whereas the latter type can also use strings as keys.

Creating an array can be done in one of three ways:

• Using the array() statement

      $a = array('I', 'II', 'III', 'IV'),

• Successively adding values to an array using the variable name and square brackets

      $a[] = 'I';
      $a[] = 'II';
      $a[] = 'III';
      $a[] = 'IV';

• Using a new square brackets syntax introduced in PHP 5.4

      $a = ['I', 'II', 'III', 'IV'];

The latter method is probably the most intuitive one for Web developers because JavaScript features a very similar syntax. For the sake of backward compatibility, we will still use the first option throughout this chapter.

When using associative arrays, the same three methods can be used; however, this time keys and values must be provided:

$a1 = array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
$a2['one'] = 'I';
$a2['two'] = 'II';
$a2['three'] = 'III';
$a2['four'] = 'IV';

$a1 = ['one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'];

Arrays can also be nested, when an array element itself is an array:

$a = array(
  'Roman' =>
    array('one' => 'I', 'two' => 'II', 'three' =>
'III', 'four' => 'IV'),
  'Arabic' =>
    array('one' => '1', 'two' => '2', 'three' => '3', 'four' => '4')
);

Now, the Arabic representation of the number four can be accessed using $a['Arabic']['four'].

Of course, arrays are not only created within a script but can also come from other sources, including from HTML forms (see Chapter 5, “Interacting with Web Forms”) and from cookies and sessions (see Chapter 6, “Remembering Users (Cookies and Sessions)”). But if the array is there, what’s next? The following phrases give some pointers.

Accessing All Elements of Numeric Arrays

foreach ($a as $element)

<?php
  $a = array('I', 'II', 'III', 'IV'),
  foreach ($a as $element) {
    echo htmlspecialchars($element) . '<br />';
  }
?>

Looping through an Array with foreach (foreach-n.php)

Looping through numeric (or indexed) arrays can most easily be done using foreach because in each iteration of the loop, the current element in the array is automatically written in a variable, as shown in the preceding code.

Alternatively, a for loop can also be used. The first array element has the index 0; the number of array indices can be retrieved using the count() function:

<?php
  $a = array('I', 'II', 'III', 'IV'),
  for ($i = 0; $i < count($a); $i++) {
    echo htmlspecialchars($a[$i]) . '<br />';
  }
?>

Looping through an Array with for (for-n.php)

Both ways are equally good (or bad); though, usually, using foreach is the much more convenient way. However, there is a third possibility: The PHP function each() returns the current element in an array. The return value of each() is an array, in which you can access the value using the numeric index 1, or the string index 'value'. Using a while loop, the whole array can be traversed. The following code once again prints all elements in the array, this time using each():

<?php
  $a = array('I', 'II', 'III', 'IV'),
  while ($element = each($a)) {
    echo htmlspecialchars($element['value']) . '<br />'; //or: $element[1]
  }
?>

Looping through an Array with each (each-n.php)

The output of the three listings is always the same, of course.

Accessing All Elements of Associative Arrays

foreach ($a as $key => $value)

<?php
  $a = array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
  foreach ($a as $key => $value) {
    echo htmlspecialchars("$key: $value") . '<br />';
  }
?>

Looping through an Associative Array with foreach (foreach-a.php)

When using an associative array and wanting to access all data in it, the keys are also of relevance. For this, the foreach loop can also provide a variable name for the element’s key, not only for its value.

Using count() is possible: count() returns the number of values in the array, not the number of elements. Looping through all array elements with for is not feasible. However, the combination of each() and while can be used, as shown in the following code. The important point is that the key name can be retrieved either using the index 0 or the string index 'key':

<?php
  $a = array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
  while ($element = each($a)) {
    echo htmlspecialchars($element['key'] . ': ' . $element['value']) . '<br />';
    //or: $element[0] / $element[1]
  }
?>

Looping through an Associative Array with each (each-a.php)

Accessing All Array Elements in Nested Arrays

print_r($a);

<pre>
<?php
  $a = array(
    'Roman' =>
      array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
    'Arabic' =>
      array('one' => '1', 'two' => '2', 'three' => '3', 'four' => '4')
  );
  print_r($a);
?>
</pre>

Printing a Nested Array with print_r (print_r.php)

Nested arrays can be printed really easily by using print_r(). Take a look at the output of the listing in Figure 2.1.

Image

Figure 2.1. Printing array contents with print_r()


Tip

If you set the second parameter of print_r() to true, the associative array’s contents are not sent to the client, but are returned from the function, so that you can save this information in a variable and process it further.


However, the output of the preceding code (see Figure 2.1) is hardly usable for more than debugging purposes. Therefore, a clever way to access all data must be found. A recursive function is a reasonable way to achieve this. In this, all elements of an array are printed out; the whole output is indented using the HTML element <blockquote>. If the array element’s value is an array itself, however, the function calls itself recursively, which leads to an additional level of indention. Whether something is an array can be determined using the PHP function is_array(). Using this, the following code can be assembled; see Figure 2.2 for the result:

<?php
  function printNestedArray($a) {
    echo '<blockquote>';
    foreach ($a as $key => $value) {
      echo htmlspecialchars("$key: ");
      if (is_array($value)) {
        printNestedArray($value);
      } else {
        echo htmlspecialchars($value) . '<br />';
      }
    }
    echo '</blockquote>';
  }

  $arr = array(
    'Roman' =>
      array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
    'Arabic' =>
      array('one' => '1', 'two' => '2', 'three' => '3', 'four' => '4')
  );

  printNestedArray($arr);
?>

Printing a Nested Array Using a Recursive Function (printNestedArray.php)

Image

Figure 2.2. Printing array contents using a recursive function

Turning an Array into Variables

while (list($key, $value) = each($a))

<?php
  $a = array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
  while (list($key, $value) = each($a)) {
    echo htmlspecialchars("$key: $value") . '<br
/>';
  }
?>

Looping through an Array with list() and each() (each-list.php)

Whenever each() is used, the use of list() is a good idea. Within list(), you provide variable names for all values with numeric indices in the array that is returned by each(). This makes while/each() loops even easier to use, as the code shows. Within the parentheses, the variable names are provided.


Tip

If you are interested only in either the key or the value of the array, you can remove one of the two variables; just make sure that you keep the comma.


while (list(, $value) = each($a)) {
  echo htmlspecialchars("$value") . '<br />';
}

Converting Strings to Arrays

$a = explode(',', $csvdata);

<?php
  $csvdata = 'Pearson Education,800 East 96th Street,Indianapolis,Indiana,46240';
  $a = explode(',', $csvdata);
  $info = print_r($a, true);
  echo "<pre>$info</pre>";
?>

Turning a String into an Array (explode.php)

Sometimes, arrays are not used to store information; instead, a string is used. The single values are all within the string, but are separated by a special character. One example for this is the comma-separated values (CSV) format.

The PHP function explode() creates an array out of these values; you just have to provide the characters at which the string needs to be split. The browser then shows this output:

Array
(
    [0] => Pearson Education
    [1] => 800 East 96th Street
    [2] => Indianapolis
    [3] => Indiana
    [4] => 46240
)

Converting Arrays to Strings

$address = implode('<br />', $data);

<?php
  $data = array(
    'Pearson Education',
    '800 East 96th Street',
    'Indianapolis',
    'Indiana',
    '46240'
  );
  $address = implode('<br />', $data);
  echo $address;
?>

Turning an Array into a String (implode.php)

The way back (that is, making a string representation out of the elements in an array) can be done using implode(). Again, two parameters are required: the separation elements, then the array. The order is quite unusual, yet important.

So, PHP joins the elements of the array, using the <br /> HTML element. Therefore, in the browser, each array’s elements stay at its own line.

Sorting Arrays Alphabetically

sort($a, SORT_NUMERIC);
sort($a, SORT_STRING);

<pre>
<?php
  $a = array('4', 31, '222', 1345);
  sort($a, SORT_NUMERIC);
  print_r($a);
  sort($a, SORT_STRING);
  print_r($a);
?>
</pre>

Sorting an Array (sort.php)

Numeric arrays can be sorted rather easily by using sort(). However, a problem exists if the array contains both numeric and string values (for instance, “2” > “10” but 2 < 10). Therefore, the sorting can be tweaked so that a special data type is used for comparing elements when sorting:

SORT_NUMERIC sorts elements as numbers.

SORT_REGULAR sorts elements according to their data type (standard behavior).

SORT_STRING sorts elements as strings.

Here is the output of the preceding listing:

Array
(
    [0] => 4
    [1] => 31
    [2] => 222
    [3] => 1345
)
Array
(
    [0] => 1345
    [1] => 222
    [2] => 31
    [3] => 4
)


Note

If you want to sort the elements of the array in reverse order, use rsort() (r for reverse). The same optional second parameters are allowed that can be used with sort().


Sorting Associative Arrays Alphabetically

ksort($a);
asort($a);

<pre>
<?php
  $a = array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
  ksort($a);
  print_r($a);
  asort($a);
  print_r($a);
?>
</pre>

Sorting an Associative Array (sort_a.php)

Sorting associative arrays can be done in one of several ways:

• Sort by keys, leave key-value association intact: Use ksort().

• Sort by keys in reverse order, leave key-value association intact: Use krsort().

• Sort by values, leave key-value association intact: Use asort().

• Sort by values in reverse order, leave key-value association intact: Use arsort().

The preceding code shows these functions in action; Figure 2.3 shows the result.

Image

Figure 2.3. Sorting associative arrays


Note

Trying to use sort() or rsort() with associative arrays works, but the keys are then all lost.


Sorting Nested Arrays

function sortNestedArray(&$a) {
  sort($a);
  for ($i = 0; $i < count($a); $i++) {
    if (is_array($a[$i])) {
      sortNestedArray($a[$i]);
    }
  }
}

<pre>
<?php
  function sortNestedArray(&$a) {
    sort($a);
    for ($i = 0; $i < count($a); $i++) {
      if (is_array($a[$i])) {
        sortNestedArray($a[$i]);
      }
    }
  }

  $arr = array(
    'French',
    'Spanish',
    array('British English', 'American English'),
    'Portuguese',
    array('Schwitzerdütsch', 'Deutsch'),
    'Italian'
  );
  sortNestedArray($arr);
  print_r($arr);
?>
</pre>

Sorting a Nested Array Using a Recursive Function (sortNestedArray.php)

The standard sorting functions of PHP do not traverse nested arrays when performing their operations. However, if you use a recursive function, you can code this in just a few lines.

The goal is to sort an array that is nested but consists only of numeric subarrays so that only numeric (and, therefore, useless) keys are used.

The idea is the following: Calling sort() does sort the array, but leaves out all subarrays. Therefore, for all elements that are arrays, the sorting function is called again, recursively. The preceding code shows this concept; Figure 2.4 shows the result for a sample array.

Image

Figure 2.4. Sorting nested arrays


Note

The PHP function array_multisort() is an alternative way to sort arrays with more than one dimension.


Sorting Nested Associative Arrays

foreach ($a as $key => $value) {
  if (is_array($value)) {
    sortNestedArrayAssoc($value);
  }
}

<pre>
<?php
  function sortNestedArrayAssoc($a) {
    ksort($a);
    foreach ($a as $key => $value) {
      if (is_array($value)) {
        sortNestedArrayAssoc($value);
      }
    }
  }

  $arr = array(
    'Roman' =>
      array('one' => 'I', 'two' => 'II', 'three' => 'III', 'four' => 'IV'),
    'Arabic' =>
      array('one' => '1', 'two' => '2', 'three' => '3', 'four' => '4')
  );
  sortNestedArrayAssoc(&$arr);
  print_r($arr);
?>
</pre>

Sorting an Associative Nested Array Using a Recursive Function (sortNestedArrayAssoc.php)

If an associative nested array is to be sorted, two things have to be changed in comparison to the previous phrase that sorted a numeric (but nested) array. First, the array has to be sorted using ksort(), not sort(). Furthermore, the recursive sorting has to be applied to the right variable, the array element that itself is an array. Make sure that this is passed via reference so that the changes are applied back to the value:

foreach ($a as $key => &$value) {
  if (is_array($value)) {
    sortNestedArrayAssoc($value);
  }
}

Figure 2.5 shows the result of the code at the beginning of this phrase.

Image

Figure 2.5. Sorting nested, associative arrays

Sorting IP Addresses (as a Human Would)

natsort($a);

<?php
  $a = array('100.200.300.400', '100.50.60.70', '100.8.9.0'),
  natsort($a);
  echo implode(' < ', $a);
?>

Sorting IP Addresses Using a Natural String Order String Comparison (natsort.php)

Sorting IP addresses with sort() does not really work because if sorting as strings, '100.200.300.400' (which intentionally is an invalid IP) is less than '50.60.70.80'. In addition, there are more than just digits within the string, so a numeric sorting does not work.

What is needed in this case is a so-called natural sorting, something that has been implemented by Martin Pool’s Natural Order String Comparison project at http://sourcefrog.net/projects/natsort/. In PHP’s natcasesort() function, this algorithm is used. According to the description, it sorts “as a human would.” When case sensitivity is an issue, natsort() can be used. The preceding code shows the latter function.


Note

Internally, natsort() uses strnatcmp() (and natcasesort() uses strnatcasecmp()), which does a “natural” comparison of two strings. By calling this function a number of times, the array elements are brought into the correct order.


Sorting Anything

<?php
  function compare($a, $b) {
    return $a - $b;
  }

  $a = array(4, 1345, 31, 222);
  usort($a, 'compare'),
  echo implode(' < ', $a);
?>

If you do not want to limit yourself to the standard sorting functionality offered by PHP, you can write your own sorting algorithm. Internally, PHP uses the Quicksort algorithm to sort values in an array. For this to work, PHP has to know whether two values are equal; in the latter case, PHP needs to find out which value is greater. So, to implement a custom sort, all that is required is a function that takes two parameters and returns:

• A negative value if the first parameter is smaller than the second parameter

0 if both parameters are equal

• A positive value if the second parameter is smaller than the first parameter

The name of this function must be passed to usort()—as a string—or, if you are using at least PHP 5.3, you can also rely on an anonymous function similar to JavaScript. The rest of the work is done by PHP, as shown in the code. The comparison function used there is a very simple way to do a numeric sorting. By substracting the two values, the function returns the desired values: A positive number if the first parameter is larger than the second one, 0 if both parameters are equal, and a negative number otherwise.

Sorting with Foreign Languages

<?php
  function compare($a, $b) {
    if ($a == $b) {
      return 0;
    } else {
      for ($i = 0; $i < min(strlen($a), strlen($b)); $i++) {
        $cmp = compareChar(substr($a, $i, 1), substr($b, $i, 1));
        if ($cmp != 0) {
          return $cmp;
        }
      }
      return (strlen($a) > strlen($b)) ? 1 : 0;
    }
  }

  function compareChar($a, $b) {
    // ...
  }

  $a = array('Frédéric', 'Froni', 'Frans'),
  usort($a, 'compare'),
  echo implode(' < ', $a);
?>

Sorting an Array with Language-Specific Characters (languagesort.php; excerpt)

Sorting works well, as long as only the standard ASCII characters are involved. However, as soon as special language characters come into play, the sorting yields an undesirable effect. For instance, calling sort() on an array with the values 'Frans', 'Frédéric', and 'Froni' puts 'Frédéric' last because the é character has a much larger charcode than o.

For this special case, PHP offers no special sorting method; however, you can use strnatcmp()to emulate this behavior. The idea is to define a new order for some special characters; in the comparison function, you then use this to find out which character is “larger” and which is “smaller.”

You first need a function that can sort single characters:

function compareChar($a, $b) {
    $characters =
'AÀÁÄBCÇDEÈÉFGHIÌÍJKLMNOÒÓÖPQRSTUÙÚÜVWXYZ';
    $characters .=  'aàáäbcçdeèéfghiìíjklmnoòóöpqrstuùúüvwxyz';
    $pos_a = strpos($characters, $a);
    $pos_b = strpos($characters, $b);
    if ($pos_a === false) {
      if ($pos_b === false) {
        return 0;
      } else {
        return 1;
      }
    } elseif ($pos_b === false) {
      return -1;
    } else {
      return $pos_a - $pos_b;
    }
  }

Then, the main sorting function calls compareChar(), character for character, until a difference is found. If no difference is found, the longer string is considered to be the “greater” one. If both strings are identical, 0 is returned. The code at the beginning of this phrase shows the compare function. The result of this code is, as desired, Frans < Frédéric < Froni.

Starting with PHP 5.3, the ext/intl extension provides a mechanism for natural language sorting, as well. If the extension is installed (which may additionally require the ICU library set from http://site.icu-project.org/), you first need to create a so-called collator, which expects a locale (for instance, en_US, en_CA, en_GB, fr_FR, or de_AT). Then, you can call the sort() and asort() methods, which work analogously to their PHP counterparts but take the locale information into account:

$a = array('Frédéric', 'Froni', 'Frans'),
$coll = new Collator('fr_FR'),
$coll->sort($a);
echo implode(' < ', $a);

Sorting an Array with Locale Information (collator.php)

Applying an Effect to All Array Elements

$a = array_map('sanitize', $a);

<?php
  function sanitize($s) {
    return htmlspecialchars($s);
  }

  $a = array('harmless', '<bad>', '>>click here!<<'),
  $a = array_map('sanitize', $a);
  echo implode(' ', $a);
?>

Applying htmlspecialchars() to All Elements of an Array (array_map.php)

Sometimes, data in an array has to be preprocessed before it can be used. In Chapter 4, you will see how data coming from the user via HTML forms can be sanitized before it is used. To do so, every array element must be touched.

However, it is not required that you do a cumbersome for/foreach/while loop; PHP offers built-in functionality for this. The first possibility is to use array_map(). This takes an array (second parameter) and submits every element in that array to a callback function (first parameter, as a string, an array, or an anonymous function, as you can see below). At the end, the array is returned, with all of the elements replaced by the associated return values of the callback function.

In the preceding listing, all values in the array are converted into HTML using htmlspecialchars().


Note

Starting with PHP 5.3, you may use inline anonymous functions whenever a callback is expected by a PHP function. So, the previous code could be cleaned up a bit like this:


<?php
  $a = array('harmless', '<bad>', '>>click here!<<'),
  $a = array_map(
    function sanitize($s) {
      return htmlspecialchars($s);
    }.
    $a);
  echo implode(' ', $a);
?>

Applying htmlspecialchars() to All Elements of an Array, Using an Anonymous Function (array_map_anon.php)

If the array turns out to be a nested one, however, the tactic has to be changed a little. Then you can use a recursive function. If an array element is a string, it is HTML encoded. If it’s an array, the recursive function calls itself on that array. The following code implements this, and Figure 2.6 shows the result:

<?php
  function sanitize_recursive($s) {
    if (is_array($s)) {
      return(array_map('sanitize_recursive', $s));
    } else {
      return htmlspecialchars($s);
    }
  }

  $a = array(
    'harmless' =>
      array('no', 'problem'),
    'harmful' =>
      array('<bad>', '—> <worse> <<-')
  );

  $a = sanitize_recursive($a);
  echo '<pre>' . print_r($a, true) . '</pre>';
?>

Recursively Applying htmlspecialchars() to All Elements of a Nested Array (array_map_recursive.php)

Image

Figure 2.6. The nested arrays have been HTML-encoded.

Another function that behaves similarly is array_walk(). This one also applies a function to every element of an array; however, it also allows you to provide a parameter for this function call. In the following code, this is used to print out all elements of an array. A parameter is passed—a counter. Because it is passed by reference, increasing this counter by one within the function leads to a sequence of numbers:

<?php
  function printElement($s, &$i) {
    printf('%d: %s<br />', $i, htmlspecialchars($s));
    $i++;
  }

  $i = 1;
  $a = array('one', 'two', 'three', 'four'),
  $a = array_walk($a, 'printElement', $i);
?>

Printing Array Elements Using array_walk() and a Counter Passed by Reference (array_walk.php)

Running the preceding code shows the following:

0: one
1: two
2: three
3: four

Filtering Arrays

array_filter($values, 'checkMail')

<?php
  function checkMail($s) {
    // ...
  }

  $values = array(
    '[email protected]',
    'invalid@email',
    '[email protected]',
    '[email protected]'
  );
  echo implode(', ', array_filter($values, 'checkMail'));
?>

Filtering Valid Email Addresses (array_filter.php)

Imagine you get a bunch of values—from an HTML form, a file, or a database—and want to select which of these values are actually usable and which are not. You could again call for, foreach, or while and find out what is interesting, or you can let PHP do most of the work. In the latter case, get acquainted with the function array_filter(). This one takes two parameters: first, the array to be filtered; and second, a function name (as a string) that checks whether an array element is good. This validation function returns true upon success and false otherwise. The following is a very simple validation function for email addresses; see Chapter 1, “Manipulating Strings,” for a much better one:

function checkMail($s) {
  $ampersand = strpos($s, '@'),
  $lastDot = strrpos($s, '.'),
  return ($ampersand !== false &&
          $lastDot !== false &&
          $lastDot - $ampersand >= 3);
}

Now, the code at the beginning of this phrase calls array_filter() so that only (syntactically) valid email addresses are left.

As you would expect, the code just prints out the two valid email addresses.

Getting Random Elements Out of Arrays

array_rand($numbers, 6)

<?php
  for ($i = 1; $i <= 49; $i++) {
    $numbers[] = $i;
  }
  // we could use range() instead, too

  echo implode(' ', array_rand($numbers, 6));
?>

Picking Random Elements Out of an Array (array_rand.php)

With array_rand(), one or more random elements out of an array are determined by random. This can, for instance, be used to draw some lucky numbers. For instance, the German lottery draws 6 numbers out of 49. The preceding code implements this drawing using PHP and array_rand(); see Figure 2.7 for its output. The first parameter for this function is the array; the second (optional) one is the number of elements to be returned.

Image

Figure 2.7. Lucky numbers with PHP


Note

If you do not want to pick random elements but want to randomize the order of elements in the array (for example, when shuffling a deck of cards), use the shuffle() function.


Making Objects Behave Like Arrays

class MyArray implements ArrayAccess, Countable

<?php
  class MyArray implements ArrayAccess, Countable {
      private $_data = array();

      /* ArrayAccess interface */
      public function offsetSet($offset, $value) {
        $this->_data[$offset] = $value;
      }

      public function offsetExists($offset) {
        return isset($this->_data[$offset]);
      }

      public function offsetUnset($offset) {
        unset($this->_data[$offset]);
      }

      public function offsetGet($offset) {
        //return (isset($this->_data[$offset])) ? $this->_data[$offset] : null;
        return ($this->offsetExists($offset)) ? $this->_data[$offset] : null;
      }

      /* Countable Interface */
      public function count() {
        return count($this->_data);
      }

  }

  $a = new MyArray();
  $a[0] = 'I';
  $a[1] = 'II';
  $a[2] = 'III';
  $a[3] = 'IV';
  for ($i = 0; $i < count($a); $i++) {
    printf('<p>%s</p>', htmlspecialchars($a[$i]));
  }
?>

Using an Object Like an Array with SPL (splArray.php)

SPL, the Standard PHP Library, is one of the most underrated features of PHP. It basically offers a huge set of interfaces. If you create a class using one of those interfaces, you might use standard PHP functions with your class. The documentation at http://php.net/spl is not always as detailed as you would expect, but it does give you some great pointers as to what is possible.

As a simple example, we will create a class (you can find more about various aspects of object-oriented programming [OOP] in Chapter 4) that implements two interfaces:

ArrayAccess—Allows accessing individual elements within the object’s collection (in our code, a simple array)

Countable—Allows calling count() on an object instance

If you want full array support, you need to implement additional interfaces, including Iterable (for iterator support; for example, via foreach) and some more. The ArrayObject interface aggregates several of the required interfaces, which in turn could even allow advanced features like user-defined sorting.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.4.181