PHP - язык для написания динамических веб-страниц
Лекции

Оглавление

ЧАСТЬ I
   Лекция 1. Введение
   Лекция 2. Основы языка PHP
   Лекция 3. Операторы языка PHP
   Лекция 4. Массивы
   Лекция 5. Функции, определяемые пользователем
   Лекция 6. Ссылки
   Лекция 7. Файлы
   Лекция 8. Регулярные выражения
   Лекция 9. Базы данных
ЧАСТЬ II
   Лекция 10. Графика
   Лекция 11. Сессии
   Лекция 12. CMS
   

ЧАСТЬ I

Лекция 1. Введение

В данном курсе лекций рассматриваюся способы создания динамических страниц. Понятия веб-страница, сайт, веб-приложение, веб-сервер определены здесь. Рассмотрим подробнее требования, предъявляемые к веб-серверу

Конфигурация веб-сервера зависит от нагрузки, которую он испытывает. Введём обозначения:

Коэффициент загрузки ρ определяется как отношение интенсивность входного потока запросов λ к интенсивности их обслуживания μ:

 ρ =  λ  .               
      μ

Средняя длина очереди запросов, ожидающих обслуживания на веб-сервере, определяется по формуле

 L =   ρ2   ;    lim L = ∞                
      1 - ρ       ρ → 1   

Длина очереди при ρ → 1 растет неограниченно:

     lim L = ∞                
     ρ → 1     

Вместе с длиной очереди неограниченно растёт и время получения ответа на запрос пользователя, т.е. при большой загрузке сервер перестаёт отвечать на запросы. Именно это и происходит при хакерских атаках. На практике для приемлемого времени ожидания необходимо выполнение условия:

        
     ρ ≤ 0.6 .

Эти сведения необходимо учитывать при разработке сайта и выборе веб-сервера для установки сайта.

Сделанные выводы о зависимости времени обслуживания и длины очереди от коэффициента загрузки следуют из теории массового обслуживания. Подробнее на элементарном уровне проблема выбора производительности системы рассматривается в книге "C.П. Митрофанов и др. Организационно-технологическое проектирование ГПС – Ленинград «Машиностроение» ленинградское отделение, 1986. 292 с." в главе 5.

Если веб-сервер работает в локальной сети и обслуживает несколько десятков человек (например, веб-сервер кафедры), то его можно установить на ЭВМ, выполняющую роль сетевого сервера. По мере возрастания нагрузки требуется всё более мощное оборудование

  • многопроцессорный сервер с веб-сервером, настроенным на многопоточное обслуживание;
  • вычислительный комплекс, состоящий из объединённых в единую систему компьютеров

    В последнем случае в системе работает множество экземпляров программных веб-серверов. Заявки на выдачу статических страниц обслуживаются в десятки раз быстрее заявок на генерацию динамических страниц. Веб-сервер, предназначенный для обслуживания любых заявок, очень сложный и относительно медленный, поэтому разработаны "лёгкие" веб-серверы, умеющие обслуживать только заявки на передачу статических страниц. Лёгкие" веб-серверы работают гораздо быстрее "тяжёлых". Примеры "тяжёлых" веб-серверов - Apache, IIS (Internet Information Server). К лёгким относятся:

    На рис.1 показан веб-кластер, состоящий из одного лёгкого и N тяжёлых веб-серверов. Лёгкий веб-сервер принимает запросы пользователей и, если запрошена статическая страница, отправляет её пользователю. Если запрошена динамическая страница, то запрос передается одному из тяжёлых веб-серверов. Выбор тяжёлого веб-сервера делается по круговой (циклической) дисциплине. Сначала выбирается первый веб-сервер, для выполнения следующего запроса выбирается второй веб-сервер и так далее. После назначения N-ого веб-сервера, снова выбирается первый.

    Рис. 1. Использование лёгкого веб-сервера

    Для формирования динамических страниц требуется целый комплекс, состоящий из веб-сервера и управляемых им транслятов с языков типа PHP, Perl, Pithon, баз данных и CУБД.

    Веб-сервер Apache

    Apache -наиболее распространённый среди веб-серверов, кроссплатформенный, состоит из ядра и более, чем 500 модулей. Создан в 1995 году.

    Настройка конфигурации Apache производится с помощью текстовых конфигаруционных файлов, которые состоят из директив настройки. Рассматриваются три уровня конфигаруции:

    Директива имеет синтаксис

      имя_директивы значение
    

    В файле директив символ # означает комментарий. Рассмотрим несколько примеров директив.

     #Каталог с файлами сервера
     ServerRoot "C:/server/Apache/Apache2" # Для Windows
     ServerRoot "etc/apache2               # Для Ubuntu
     #Включение модуля PHP5
     LoadModule php5_module "/usr/local/php5/php5apache2_2.dll"
     # Привязывание к порту
     Listen 127.0.0.1:80
     #имена страниц, запускаемых по умолчанию в качестве стартовых,
     #если пользователь указывает только имя сайта
     
        DirectoryIndex index.php index.htm index.html index.shtm index.shtml
    </IfModule>
    

    Для того чтобы задать для какого-либо сайта или страницы директивы, отличные от заданных для всего веб-сервера, т.е. задать конфигурация уровня каталога, служит файл со странным именем, начинающимся с точки:

      .htaccess
    

    Этот файл нужно поместить в каталог сайта, для которого предназначены директивы. Например, для сайта pricom.loc в каталоге Z:\home\pricom.loc\www помещён следующий файл с именем .htaccess :

    AddDefaultCharset utf-8
    AddLanguage ru-RU .html .htm .css .js .php
    DefaultLanguage ru-RU
    php_value include_path ".;/home/pricom.loc/www/inc;/usr/local/lib/php"
    AddType text/csv csv
    
    #CSV - значения, разделённые запятыми
    php_flag session.use_trans_sid Off
    php_flag session.use_only_cookies On
    php_flag session.use_cookies On
    

    В директиве <I>php_value include_path указаны пути, по которым скрипт, написанный на языке PHP, должен искать файлы по умолчанию.

    Виртуальные хосты и ДЕНВЕР

    Механизм виртуальных хостов позволяет обслуживать на одном IP-адресе, т.е. на одной ЭВМ, множество сайтов с разными доменными именами. Протокол TCP/IP позволяет использовать одну ЭВМ одновременно и как веб-сервер и как клиента с браузером, образуя петлю между веб-сервером и браузером. Эти два обстоятельства используются для отладки и сопровождения множества сайтов на ЭВМ, не подключённой к Интернет. Для образования петли используется локальный IP-адрес 127.0.0.1.

    Сайт должен быть описан в одном из файлов конфигурации веб-сервера. Ниже приведён фрагмент файла конфигурации с описанием сайтов c доменными именами localhost и KafI5.loc.

    # Host /home/localhost/www (2): 
    Listen 127.0.0.1:443
    NameVirtualHost 127.0.0.1:443
    <VirtualHost 127.0.0.1:443>
      SSLEngine on
      DocumentRoot "Z:/home/localhost/www"  
      ServerName "localhost"
      ServerAlias "localhost" "www.localhost" 
      ScriptAlias /cgi/ "/home/localhost/cgi/"
      ScriptAlias /cgi-bin/ "/home/localhost/cgi-bin/"
    </VirtualHost>
    # Host /home/KafI5.loc/www : 
    #Listen 127.0.0.1:80
    #NameVirtualHost 127.0.0.1:80
    <VirtualHost 127.0.0.1:80>
      DocumentRoot "Z:/home/KafI5.loc/www"  
      ServerName "KafI5.loc"
      ServerAlias "KafI5.loc" "www.KafI5.loc" 
      ScriptAlias /cgi/ "/home/KafI5.loc/cgi/"
      ScriptAlias /cgi-bin/ "/home/KafI5.loc/cgi-bin/"
    </VirtualHost> 
    

    Директивы <VirtualHost ...> и используются, чтобы выделить группу директив, которые применятся только к данному виртуальному хосту. Приведёный фрагмент взят из файла vhosts.conf, сформированного автоматически системой ДЕНВЕР.

    Система ДЕНВЕР (Джентльментский набор веб-разработчика) разработана Дмитрием Котеровым, для того чтобы избавить пользователя от необходимости описывать вручную разрабатываемый сайт в файле конфигурации. ДЕНВЕР работает под управлением операционных систем семейства WINDOWS. В состав ДЕНВЕР входят несколько переделанный веб-сервер Apache, СУБД MySQL и встроенный интерпретатор языка PHP. Дополнительно можно подсоединить транслятор Perl.

    Для создания сайта в ДЕНВЕР необходимо в каталоге ДЕНВЕРа C:\WebServers\home создать каталог с именем, совпадающим с именем сайта, например, kam.loc.

    ДЕНВЕР мажет быть установлен на любом локальном диске. При запуске ДЕНВЕР создаётся виртуальный диск Z:, соответствующий каталогу <I>имя_локального_диска:\WebServers (например, C:\WebServers).

    В созданный каталог нужно поместить каталог www. В каталоге www будут храниться все файлы с HTML-документами и со скриптами на PHP. После создания каталогов сайта нужно запустить (или перезапустить) ДЕНВЕР. При запуске ДЕНВЕР создаёт файл vhosts.conf, читает каталог home и записывает в vhosts.conf данные о существующих сайтах. Например, обнаружив в каталоге home подкаталог anketaWin.loc, ДЕНВЕР добавит в файл vhosts.conf следующий фрагмент:

    # Host /home/anketaWin.loc/www (3): 
    #Listen 127.0.0.1:80
    #NameVirtualHost 127.0.0.1:80
    <VirtualHost 127.0.0.1:80>
      DocumentRoot "Z:/home/anketaWin.loc/www"  
      ServerName "anketaWin.loc"
      ServerAlias "anketaWin.loc" "www.anketaWin.loc" 
      ScriptAlias /cgi/ "/home/anketaWin.loc/cgi/"
      ScriptAlias /cgi-bin/ "/home/anketaWin.loc/cgi-bin/"
    </VirtualHost>
    

    После занесения данных о сайте в vhosts.conf можно запрашивать этот сайт через браузер, задавая доменное имя anketaWin .loc.

    Лекция 2.

    2.1. Назначение и история языка PHP

    Язык PHP предназначен для написания динамических HTML-страниц. Программа на PHP встраивается в текст (код) HTML-страницы в виде одного или нескольких блоков, начинающихся строкой <?php и заканчивающихся строкой ?>. Схематично структуру HTML-документа со скриптом на PHP можно изобразить так:

    	<HTML>
    	. . .
    	. . .
    	
    	<?php
    	  //Начало скрипта на PHP
    	  оператор на PHP
    	. . .
    	  оператор на PHP
    	?>
    	
    	. . .
    	. . .
    	
    	<?php
    	  //Продолжение скрипта на PHP
    	  оператор на PHP
    	. . .
    	  оператор на PHP
    	?>
    	
    	. . .
    	. . .
    	
    	<?php
    	  //Конец скрипта на PHP
    	  оператор на PHP
    	. . .
    	  оператор на PHP
    	?>
    	
    	. . .
    	. . .
    	</html>
    

    PHP относится к императивным языкам и удобен для обработки текстовой информации. Для этого в нём есть следующие средства:

    Распространено мнение, что PHP основывается на языке C. Это не совсем так. PHP разрабатывался как альтернатива языку Perl, который также предназначен для обработки текстовой информации. Оба языка относятся к языкам с контекстно зависимым типом данных, в отличие от СИ с его статическим, принудительным назначением типов. Разработчик языка Perl Ларри Уолл постарался использовать всё лучшее, что было накоплено в языках программирования на момент появления в 1987 году этого языка, в том числе и многие черты языка C.

    К сожалению, многое в PHP не только не лучше, чем в Perl, но и хуже. Вот пример оператора foreach, который служит для перебора всех элементов массива:

       Perl                               PHP
     foreach $element (@Arr)           foreach ($Arr as $element )
     {                     }           {                         }
    

    При выполнении обоих операторов выполняются одни и те же действия: перебираются все элементы массива и над каждым элементом производятся операции, указанные в блоке. Оператор, написанный на Perl, читается естественно: "для каждого элемента $element массива @Arr ...". А вот как прочитать тот же оператор на PHP? Не понятно, зачем было переделывать удобный оператор. К сожалению ещё хуже обстоит дело с регулярными выражениями в PHP по сравнению с Perl.

    PHP начал разрабатываться в 1994 году. Первая версия (релиз) появилась в 1995 году. Вторая - в 1997. Это были очень слабые версии. Тем не менее, PHP быстро завоевал популярность среди разработчиков, благодаря тому что его транслятор был встроен в самый распространённый веб-сервер Apache. Широко распространился набор LAMP (Linux, Apache, MySQL, PHP). Следующие версии PHP вышли:

    Шестая версия разрабатывалась с 2006 года, но в марте 2010 была признана бесперсперктивной. В настоящее время PHP имеет достаточно качественные трансляторы и большое количество модулей, содержащих функции для разработки веб-приложений.

    2.2. Основные элементы

    Алфавит языка PHP состоит из латинских букв, арабских цифр и специальных знаков.

    Константы в PHP объявляются следующим образом:

      define("имя_константы", значение);
    

    Например

       define("pi", 3.1415926);
       $R = 5; //раддиус
       $L = 2*pi*$R; // длина окружности
    

    Переменные могут быть трёх типов:

    Имя переменной состоит из префикса "$" и идентификатора. Идентификатор может состоять из букв, цифр и знака подчёркивания. Идентификатор не может начинаться с цифры. Пример:

         имя переменной $alfa
         $ -    префикс
         alfa - идентификатор
    

    Скаляр в зависимости от контекста может принимать один из внутренних типов

    Внутренний тип логично было бы назвать подтипом, но, к сожалению, такой термин не используется.

    Примеры целых скаляров.

        $a = 1234; //десятичное число
        $x = 020;  //восьмеричное = 1610
        $y = 0x20;  // шестнадцатеричное = 3210
    

    Числа с плавающей запятой имеют 13-14 значащих разрядов. Одна и та же скалярная переменная меняет внутренний тип в зависимости от контекста.

    Примеры контекстной зависимости внутреннего типа

        $a = 3; //целае скалярная переменная 
        $a *= 4;//целае $a=12
        $a /=9; //с плавающей запятой $a=1.33 333 333 333
        $a = $a.' метра'; //строковае $a="1.33333333333 метра"
        //В следующем операторе $a в качестве условия 
        //принимает логическое значение "истина" ($a = 1), 
        //а при печати $a="1.33333333333 метра"
        if($a)print "a=$a<BR>";
    

    Несколько неожиданно, что явно объявить переменную заданного типа нельзя, а привести к заданному типу можно. Допускаются следующие приведения типов:

    Примеры приведения типов

       $x = 2.0;     //float
        //Есть специальная функция определения встроенного типа gettype($x)
        echo "\$x==$x, тип: " . gettype($x) . "<br>\n";  
      //напечатается "$x==2, тип: double"
        $y = (int)$x; //integer $y=2
        $y = '3.14 - это пи'; //строковое
        $z = (float)$y; //$z=3.14 
       //если строка начинается не с цифры, то преобразование в число даёт нуль
       $x='red';
       print 'int '.(int)$x.', float '.(float)$x.'<BR>';//int 0, float 0
    

    Примеры округления

       $a = 2.3;  $b = 6.7;
       $e = floor($a);  //Отброшена дробная часть $e=2
       $f = floor($b);  //$f = 6
       $e = round($a);  //арифметическое округление $e=2
       $f = round($b);  //$f=7
       $e = ceil($a);  //округление до ближайшего большего или равного целого
       $f = ceil($b);  // $e=3, $f=7
    

    Пример форматирования переменной для печати или вставки в формируемую строку

      $a=2/3; 
      print "a=$a<BR>";//a=0.666666666667
      print strlen($a)."<BR>"; //14 знаков
      $a=sprintf("%4.2f",$a);
      print "a=$a<BR>";//a=0.67
    

    Примеры присвоения переменным логических значений

    $a=true; $b = false;//true соответсевует 1, false - 0 или "пусто"
    print "a=$a b=$b <BR>";//a=1 b=    (пусто)
    

    2.3. Операции над скалярами

    Арифметические операции

          +    – сложение,
          -    – вычитание,
          *    – умножение,
          /    – деление,
          %  – деление по модулю.

    Возведение в степень делается с помощь функции

       pow(основание, показатель_степени)
       print '2<sup>10<sup>='.pow(2,10);
       //Напечатается 210=1024
    

    Строковая операция - конкатенация (слияние двух строк) обозначается точкой.

    Пример конкатенации

       $a = 'Петер';
       $b = 'бург';
       $gor = $a.$b; //$gor='Петербург'
    

    Одиночные и двойные кавычки. Интерполяция

    В PHP, так же как и в Perl, большое внимание уделено способам формирования строк. Очень часто длинная строка составляется из коротких строк, представленых либо как значения переменных, либо в виде констант. Ниже приведён пример составления предложения из заданных слов двумя способами:

    Пример

      //Даны переменные
       $Fam = 'Петров';
       $Imja = 'Сергей';
       $Otch = 'Юрьевич';
       $den = '08';
       $mes = '11';
       $god = '1993';
       //Cоставим из них с помощью операции конкатенации  предложение
       //"Сергей Юрьевич Петров родился 08-11-1993"
       $R = $Imja.' '.$Otch.' '.$Fam.' родился '.$den.'-'.$mes.'-'.$god.'.';
       //При формировании строки $R использовалось 11 операций конкатенации
       //и 12 кавычек. При таком способе очень легко допустить ошибку.
       //Применим интерполяцию
       $R = "$Imja $Otch $Fam родился $den-$mes-$god.";
       print "$R<BR>";
       //Напечатается тот же результат
       // Сергей Юрьевич Петров родился 08-11-1993
    

    Операция интерполяции применяется только к строке, заключённой в двойные кавычки, и состоит в замене имён переменных их значениями. В строке, заключённой в одиночные кавычки, замена имён переменных их значениями не производится:

       $R = '$Imja $Otch $Fam родился $den-$mes-$god.';
       print "$R<BR>";
       //Напечатается
       //$Imja $Otch $Fam родился $den-$mes-$god.
    

    Операции сравнения

    Обозна-
    чение
    НазваниеПримерРезультат
        ==Равно$a == $bTRUE если $a равно $b.
        ===Тождественно равно$a === $b TRUE если $a равно $b и имеет тот же тип.
    (Добавлено в PHP 4)
        != Не равно$a != $bTRUE если $a не равно $b.
        <>Не равно$a <> $bTRUE если $a не равно $b.
        !== Тождественно не равно$a !== $b TRUE если $a не равно $b или в случае,
    если они разных типов (Добавлено в PHP 4)
        <Меньше$a < $bTRUE если $a строго меньше $b.
        > Больше$a > $bTRUE если $a строго больше $b.
        <=Меньше или равно$a <= $bTRUE если $a is меньше или равно $b.
        >=Больше или равно$a >= $bTRUE если $a больше или равно $b.

    Операции === и !== свидетельствуют о том, что в PHP не до конца решена задача определения типа переменной из контекcта.

    Идея контекстной зависимости состоит в том, что в любом месте программы автоматически выбирается исходя из контекста тип скалярной переменной. Эта проблема решена в языке Perl, появившемся в конце 1987 года (PHP - 1995). Здесь можно посмотреть, как определяется тип скаляров в контексте операций сравнения в языке Perl.

    Примеры операций сравнения.

        $x=2.0;
        $y=2;
        if($x==$y) print "true<BR>";//true
        if($x===$y)print "true<BR>";
        else       print "false<BR>";    //false
        $y='2 дня';
        if($x==$y) print "true<BR>";//true
        if($x===$y)print "true<BR>";
        else       print "false<BR>";    //false
    

    Логические операции

    Обозна-
    чение
    Название Пример Результат
    and Логическое 'и' $a and $b TRUE если и $a, и $b TRUE.
    or Логическое 'или' $a or $b TRUE если или $a, или $b TRUE.
    xor Исключающее 'или'$a xor $b TRUE если $a, или $b TRUE, но не оба.
    ! Отрицание ! $a TRUE если $a не TRUE.
    && Логическое 'и' $a && $b TRUE если и $a, и $b TRUE.
    || Логическое 'или' $a || $b TRUE если или $a, или $b TRUE.

    Операции and, or и xor имеют приоритеты ниже, чем && и ||.

    Примеры логических операций

    $a =5; $b = 2;
    $L = $a && $b; //$L=true
    $L1 = $a and $b; //$L1=5
    

    Инкремент и декремент

     $a = 3;
     Префиксный инкремент
     $b = ++$a; //$a=4; $b=4
     
     $a = 3;
     суффиксный инкремент
     $b = $a++; //$a=4; $b=3
     
      $a = 3;
     Префиксный декремент
     $b = --a; //$a=2; $b=2
     
     $a = 3;
     суффиксный декремент
     $b = $a--; //$a=2; $b=3
    

    Лекция 3. Операторы языка PHP

    Замечание о толковании терминов. В математике принято оператором называть значок операции. Например, в выражении

          $a + $b
    

    значок "+" обозначает операцию сложения. В программировании оператором называют всё выражение (лучше сказать, предложение) задающее операцию. Например, оператором if программисты назовут выражение

          if($a) $b = $a + 3;
    

    Более того, этот оператор назовут составным, так как он включает в себя оператор присваивания. В некоторых описаниях языков программирования операторы языка, чтобы отличить их от математических, называют инструкциями.

    Вместе с тем, программисты часто понимают оператор так же, как и математики. Например, говорят "оператор присваивания '=' ".

    Операторы присваивания

    В PHP так же, как и в языке C, есть базовый оператор присваивания "=" и комбинированные, только комбинированных операторов в PHP больше:

       +=
       -=
       *=
       /=
       %=
       .=   
    

    Пример присваивания с конкатенацией

       $a = 2;
       $a .= ' байта'; 
       print "a='$a'<BR>";   //Напечатается: a='2 байта'
    

    Оператор безусловного перехода

    Оператор безусловного перехода goto доступен в PHP начиная с версии 5.3. У этого оператора сложная история, связанная с тем, что его неумелое применение может привести к трудно выявляемым ощибкам. Поэтому его нет в некоторых языках, например, в Java и JavaScript. Гонения на goto начались после появления в 1968 году статьи Дейкст-ры (Edsger W. Dijkstra) "Go To Statement Considered Harmful" ("Обоснование пагубности оператора Go To"). В то время велись поиски структуры программы, устойчивой к появлению ошибок. Решения, использовать или не использовать тот или иной приём программирования, часто принимались не на основе результатов глубокого исследования, а следуя моде. Позже выяснилось, что являясь потенциально опасным, в некоторых случаях goto может сильно упростить программу

    После появления блочного оператора if() ... else у меня возникла необходимость использовать goto всего два раза. В обоих случаях решались задачи с очень сложным алгоритмом, и должен признаться, что я не нашёл способа обойтись без goto, хотя теоретически это всегда возможно.

    Оператор безусловного перехода служит для перехода в точку программы с указанной в нём меткой, и имеет синтаксис

            goto метка;
            . . .
            . . .
      метка:
            . . .
    

    Пример правильного применения goto

          . . .
          lf($c>2) goto M1
          $d = 7;
          $a = $d + 2;
          goto M2;
      M1: $a = 3; 
      M2: print "a=$a"; //если $c>2, то напечатается: a=3
          . . .
    

    Пример неправильного применения goto

     . . .
          lf($c>2) goto M1
          $d = 7;
          goto M2;
          $a = $d + 2; //Этот оператор никогда не выполнится     
      M1: $a = 3; 
      M2: print "a=$a"; 
          . . .
    

    Очевидно, что нельзя с помощью оператора goto входить внутрь цикла или в функции.

    Операторы условия

    Оператор вопросительный знак „?“ имеет вид

    условие ? значение1 : значение2
    

    Примеры

    a) Вариант с присвоением результата переменной
      $a = 3;
      $b = 4; 
      $min = ($а < $b) ? $a : $b;
      print "min=$min<BR>";// min=3
    б) Вариант без присвоения результата переменной 
      $vozrast = 28;
      $vozrast < 18 ? print 'несовершеннолетний' : print 'взрослый';
      //Напечатается "взрослый"
    

    Назовём блоком последовательность операторов, заключённую в фигурные скобки. Блок, состоящий из одного оператора в скобки можно не заключать.

    Оператор if имеет вид

          if(условие)
            блок операторов   
    

    Примеры

       if($vozrast < 18) print 'несовершеннолетний';
       $a = 3;
       $b = 4; 
       if($a < $b) 
       { $min = $a;
         $a = $b;
       }
    

    Оператор if ... else имеет вид

         if(условие)
            блок операторов
         else
            блок операторов
    

    Пример

       if($vozrast < 18) 
          print 'несовершеннолетний';
       else
       { $z = 40000;
         print "взрослый, зарплата $z рублей" ;
       }
    

    Оператор if ... elseif ... else имеет вид

         if(условие)
            блок операторов
         elseif
            блок операторов
         ....
         elseif
            блок операторов
        else
            блок операторов
    

    Пример

       /* Пусть y=f(x)
          если x < 0,      то у = -x;
          если 0 ≤ x < 2, то у = x<sup>2 
          если 2 ≤ x < 5, то у = x + 2 
          если      x ≥ 5, то у = 7  
        Запишем эти условия на PHP
        */
        if($x < 0) 
          $у = -$x;
        elseif($x < 2)
          $у = pow($x2,2);
        elseif($x < 5)
          $у = $x + 2;
        else
          $у = 7;
    

    Распространено заблуждение, что в языке C есть аналогичный оператор if ... else if ... else. На самом деле в C есть только оператор if ... else. Перепишем на C предыдущий пример.

        if(x < 0) 
          у = -x;
        else if(x < 2)
          y = pow(x,2);
        else if(x < 5)
          y = x + 2;
        else
          y = 7;
     //Перепишем его ещё раз, расставляя фигурные скобки.
       if(x < 0) 
          у = -x;
       else 
       {  if(x < 2)
           y = pow(x,2);
          else 
          {  if(x < 5)
               y = x + 2;
             else
               y = 7;
          }
       }
     // во внешние фигурные скобки заключён ОДИН составной оператор
     
          if(x < 2)
           y = pow(x2,2);
          else 
          {  if(x < 5)
               y = x + 2;
             else
               y = 7;
          }
    // но один, даже очень большой, оператор можно в скобки не заключать, тогда
    получится составной оператор, внешне похожий на if ... elseif ... else.
    

    Оператор switch (переключатель) какой-то странный. С одной стороны он заменяет оператор if ... elseif ... else, но только в простых случаях. и в if не используется оператор управления циклом break. С другой стороны switch даже относят к операторам цикла, но "цикл", записанный в виде оператора switch выглядит нелепо по сравнению с операторами for или while. Даже синтаксическую формулу для switch записать трудно, потому что последовательности операторов, по смыслу, являющиеся блоками, не заключены в фигурные скобки, т.е. формально блоками не являются. Поэтому синтаксис switch рассмотрим на примерах.

    Примеры применения switch

    //Пример 1. Похож на if ... elseif ... else
    $a=1; $b=3; $c=4;
    switch ($a+$b) // результат вычисления выражения в скобках 
                   // может иметь любой вид и встроенный тип
    {   case 7:    //выражение в case должно быть того же типа, 
                   //что и в switch
           $d=2*$b;
           print "Случай \$d=6<BR>";
           break;
        case $c: //значение $a+$b совпало с $c
           $d=$a+$c;
           print "Случай \$d=5<BR>";
           break;
        case $b:
           $d=$b;
           print "Случай \$d=3<BR>";
           break;
        default :
           print"переменная \$d не определена<BR>";
    }
    //результат: $d=5
    
    //Пример 2. Почти цикл. Подсчитывается сумма из четырёх единиц (6-$i при $i = 2) 
       $i=2; $S=0;
      switch ($i) 
    {   case 1:  
           $S++;
        case 2:
           $S++;
        case 3:
            $S++;
        case 4:
            $S++;
        case 5:
             $S++;
    }
    print "S=$S<BR>"; //S=4
    

    Первому из приведённых в примере оператору switch соответствует следующий оператор if.

        $a=1; $b=3; $c=4;
        $e = $a + $b;
        if($e == 7) 
        {  $d=2*$b;
           print "Случай \$d=6<BR>";
        }
        elseif ($e == $c)
        {   $d=$a+$c;
           print "Случай \$d=5<BR>";
        }
        elseif ($e == $b)
        {   $d=$b;
           print "Случай \$d=3<BR>";
        }
        else
           print"переменная \$d не определена<BR>";
    

    Второму switch соответствует следующий оператор for.

         $i=2; $S=0;
         for($k = $i; $k < 6; $k++) $S++
    

    Люди, в особенности программисты, по-разному понимают простоту и наглядность. Уважаемый читатель, если Вам нравится много раз писать слово break, то, пожалуйста, используйте switch вместо if. Если, как в примере 2, Вам хочется написать 11 строк вместо одной, то лучше switch вместо for.

    Операторы цикла

    Оператор цикла с предусловием

        while(условие)
          блок операторов
    

    Пример. Подсчитать сумму первых пяти членов натурального ряда ряда

         $S = 0; $i = 0;
         while($i $S< 5)
         { $i++;
           $S += $i;
         }
         print "S=$S<BR>"; //S=15
    

    Оператор цикла с постусловием

        do
          блок операторов
        while(условие);
    

    Пример. Подсчитать сумму первых пяти членов натурального

         $S = 0; $i = 0;
         do
         { $i++;
           $S += $i;
         }
         while($i < 5);
         print "S=$S <BR>"; //S=15
    

    В следующем примере двумя способами вычисляется квадратный корень из двух с помощью алгоритма Герона. Видна разница между операторами while и do ... while.

    $x=2;$y=1.5;
    $z = $x/$y;
    while (abs($z -$y) > 0.001) 
    {    $y =($z + $y)/2;
         $z = $x/$y;
    }
    print "y=$y<BR>"; //y=1.41421568627
    
    $x=2;$y=1.5;
    do
    {    $z = $x/$y;
         $y =($z + $y)/2;    
    }
    while (abs($z -$y) > 0.001);
    print "y=$y<BR>"; //y=1.41421568627
    

    Оператор цикла с заданным количеством повторений

        for(начальное_значение; условие_выхода; шаг
    

    Пример. Подсчитать сумму первых пяти членов натурального ряда

       $S=0;
       for($i = 1; $i < 6; $i++)$S += $i;
       print "S=$S<BR>"; //S=15
    
    

    Управление циклом

    Редко, но приходится нарушать ход выполнения цикла, например, при определённых условиях выйти из оператора цикла. Возникает необходимость в дополнительных, помимо предоставляемых операторами цикла. средствах управления циклом. Для этого в PHP служат операторы break и continue.

    Оператор break служит для перехода на первый оператор после цикла

    Пример. Как только элемент массива $A станет равным нулю, выйти из цикла.

         $K = 0;
         $A = Array(2,7,12, 1,11,1, 3,2,4, 9);
         for($i=0; $i < 10; $i++)
         { $A[$i]--;
           if(!$A[$i]) break;
           $K++;
         }
         print "K = $K<BR>";//$K=3
    

    Оператор continue служит для перехода на следующую итерацию цикла.

    Пример. Все элементы массива уменьшаются на единицу и подсчитывается количество ненулевых элементов в обновлённом массиве.

         $K = 0;
         $A = Array(2,7,12, 1,7,5, 1,2,1, 9);
         for($i=0; $i <10; $i++)
         { $A[$i]--;
           if(!$A[$i]) continue;
           $K++;
         }
         print "K = $K<BR>";//$K=7
    

    Оператор foreach будет рассмотрен вместе с массивами в следующей лекции.

    Лекция 4. Массивы

    Особенности массивов в PHP

    Своеобразие PHP в том, что в нём массивы обладают следующими свойствами.

    1. Все массивы - ассоциативные.
    2. Если ключи - целые числа, то массивом можно пользоваться как массивом с числовыми индексами.
    3. Элементы в массиве могут иметь разные типы.
    4. Количество элементов в разных строках двумерного массива может быть разным.

    Создание массива c числовыми индексами

    Создадим массив из одного элемента

      $m0=array('весна');
      print "m0[0]=$m0[0]<BR>"; // m0[0]=весна
    //Можно создать массив, присвоив значение его элементу
      $m1[0] = 3; //$m1 - новый массив
      print "m1[0]=$m1[0]<BR>"; //m1[0]=3
    

    Создадим массив из трёх элементов .

        $M1 = array(2,7,12);
    

    Добавим к нему ещё 2 элемента

        $M1[3] = 'Маша';
        $M1[4] = 2.71;
    

    Добавим к нему сразу 7-й элемент, пропустив 5-й и 6-й, и распечатаем весь массив, считая, что в нём не 8, а 9 элементов:

        $M1[7] = 100;
        for($i=0; $i<8; $i++)print "\$M1[$i] = $M1[$i]<BR>";
      /*напечатается
        $M1[0] = 2
        $M1[1] = 7
        $M1[2] = 12
        $M1[3] = Света
        $M1[4] = 2.83
        $M1[5] = 
        $M1[6] =
        $M1[7] = 100
        $M1[8] =
      В массиве 9 элементов, но 3 из них пустые.
      */
    

    Ассоциативные массивы и оператор foreach

    Создадим очевидный ассоциативный массив и распечатаем его.

        $AssM['сезон'] = 'весна';
        $AssM['месяц'] = 'май';
        $AssM['число'] = 1;
        foreach($AssM as $kluch => $znach) print "$kluch - $znach<BR>;
        /* Напечатается
           сезон - весна
           месяц - май
           число - 1
       */
    

    Часто нужно перебрать все элементы массив, длина которого неизвестна, например, при чтении в массив текстового файла. Для этой цели служит оператор foreach. Существую две разновидности foreach. Приведённый выше оператор служит для перебора массива, рассматриваемого, как ассоциативный. Распечатаем массив $AssM, как массив с числовыми индексами, используя другую разновидность foreach.

         foreach($AssM as $znach) print "$znach<BR>";
          /* Напечатается
            весна
            май
            1
         */
    

    Более нагляден следующий способ создания ассоциативного массива:

        $cveta=array(
                     "red"=>"Красный",
                     "yellow"=>"Жёлтый",
                     "green"=>"Зеленый",
                     "blue"=>"Синий",
                     "maroon"=>"Каштановый"
                    );   
    

    Распечатаем как ассоциативный массив $M1, который создавался как массив с числовыми индексами .

     foreach($M1 as $kluch => $znach) print "$kluch - $znach<BR>;
        /* Напечатается
          0 - 2
          1 - 7
          2 - 12
          3 - Света
          4 - 2.83
          7 - 100
    	*/
    

    Операции с массивами

    Примеры

    Объединение по ключам.

       $a = array(1,2);
       $b = array(3,4);
       //У этих массивов ключи имеют одинаковые значения 0 и 1
       $c = $a + $b;
       foreach($c as $x)print "$x ";
       // Результат 1 2
       // Элементы с повторяющимися ключами берутся только один раз
       //
       $d = array(2 => 'a', 3 => 'b', 8 => 'c')
       // Ключи в $a - 0,1; ключи в $b - 2, 3, 8. Совпадающих ключей нет
       $c = $a + $d; //$c=(1,2,'a','b','c')
    

    Сравнение массивов

       $b = $a; //Копирование массива. $b и $a одинаковые массивы
       $g = $a == $b; //$g = 1, т.е. "истина"
       $g = $a === $b; //$g = 1, т.е. "истина"
       $c = array(0 => 2, 1 => 1);
       $g = $a ==  $c; //$g = 1, т.е. "истина"
       $g = $a ===  $c; //$g = 0, т.е. "ложь", не идентично
    

    Функции, для работы с массивами

    В PHP огромное количество встроенных функций. В руководстве по PHP, выпущенном в 2005 году, описывается 5211 функций. Только на просмотр такого кличества нужно 5211/4/8 = 163 рабочих дня, если на каждую функцию тратить по 15 минут. И это многообразие всё время пополняется. Функций для работы с массивами - 80. Рассмотрим лишь некоторые из них.

    count(имя_массива) - функция, возвращающая количество элементов в массиве. Подсчитаем длину рассмотренного выше массива $M1:

        $L = count($M1); //$L=6
    

    Из примера следует, что функция count возвращает количество непустых элементов массива.

    Функции сортировки

    Примеры

    Cортировка по возрастанию элементов массива с числовыми индексами.

         sort($M1);
         for($i=0; $i<9; $i++)print "\$M1[$i] = $M1[$i]<BR>";
       /*Результат сортировки
         $M1[0] = Света
         $M1[1] = 2
         $M1[2] = 2.83
         $M1[3] = 7
         $M1[4] = 12
         $M1[5] = 100
         $M1[6] =
         $M1[7] =
         $M1[8] = 
      */
    

    Cортировка ассоциативного массива $cveta по возрастанию значений

       asort($cveta);
       foreach($cveta as $kluch=>$znach)
            print "$kluch - $znach<BR>";
      /*Результат сортировки 
        yellow - Жёлтый
        green -  Зеленый
        maroon - Каштановый
        red -    Красный
        blue -   Синий
      */
    

    Cортировка ассоциативного массива $cveta по возрастанию ключей

       ksort($cveta);
       foreach($cveta as $kluch=>$znach)
            print "$kluch - $znach<BR>";
      /*Результат сортировки   
         blue -   Синий
         green -  Зеленый
         maroon - Каштановый
         red -    Красный
         yellow - Жёлтый
      */
    

    Функции для добавления и удаления элементов массива

    Часто применяется функция array_shift(array), которая извлекает значение первого элемента массива array и возвращает это значение, сокращая размер array на один элемент. Все числовые ключи будут изменены таким образом, что нумерация массива начнётся с нуля, в то время как строковые ключи останутся прежними. Если array пуст (или не является массивом), будет возвращён NULL.

    Эту функцию удобно использовать для удаления из массива и запоминания в отдельной переменной шапки таблицы.

    Пример

      $Arr = Array('a'=> 'alfa','b'=> 'beta','c'=> 'gamma');
      $a1 = array_shift($Arr);
      print_r ($Arr); //print_r - функция для отладочной печати
      print "\$a1=$a1<BR>";
    //Результат
    // Array ( [b] => beta [c] => gamma ) 
    //  $a1=alfa  
    

    Двумерные массивы

    Создадим двумерный массив

    $M2= array(
              array(1,2,3),
              array(4,5,6,'семь'),
              array('a','b','c'),
             );
    

    Можно создавать двумерный массив поэлементно.

       $M3[0][0]='alfa';
       $M3[0][1]='beta';
    

    Подсчитаем количество строк в массиве $M2 и количество элементов в строке M2[1].

        $Lrow = count($M2); // $Lrow = 3
        $Lcol = count($M2[1]); // $Lcol = 4
    

    Распечатаем двумерный массив $M2.

       foreach($M2 as $row)
       {  foreach ($row as $el)
          { print "$el ";
          }
          print '<BR>';
       }
      /* Результат
         1 2 3
         4 5 6 семь
         a b c 
      */
    

    Создадим явный ассоциативный массив, добавив в $M3 строку

       $M3['odin'][0] = 'два';
       $M3['odin'][1] = 'три';
       print 'L='.count($M3)." \$M3['odin'][1] = ".$M3['odin'][1]."<BR>";
     /* Результат
        L=2 $M3['odin'][1] = три
     */
    

    Пример сортировки двумерного массива с помощью функции usort().
    Сортируется массив $M2 по второму столбцу (с индексом 1) в порядке возрастания

     usort($M2, 'compare1');
     foreach($M2 as $row)
     {  foreach ($row as $el)
        { print "$el ";
        }
        print '<BR>';
     }
    /* Результат
      a b c
      1 2 3
      4 5 6 семь 
    */
     function compare1($x,$y) //Сортировка по возрастанию
     {                        //$x и $y - две соседние строки
        if($x[1] == $y[1]) return 0; //порядок не определён
        elseif(($x[1] > $y[1]) return 0; //первым $y[1]
        else                   return 1; //первым $x[1]
      }
    

    Лекция 5

    Функции, определяемые пользователем

    Первоначально функции в программировании имели тот же, или почти тот же смысл, что и в математике. Существует даже целый класс языков функционального программирования (Lisp, Haskell,...), основная идея которых состоит в применении "чистых" функций, понимаемых точно так же как и в математике. Это объясняется тем, что ЭВМ на ранних стадиях развития (конец 40-х, начало 50-годов ХХ века) использовались в основном для решения научных задач с большим количеством математических расчётов, т.е. расчётов по формулам. Один из первых языков программирования так и назывался ForTran - транслятор формул.

    Пример записи формулы в математике и в программе на языке Fortran

      В математике
    y =   sin(ln(|x|) + cos(e2)   ;                    
                tg(2x) 
       
       на языке Fortran
      y = (sin(log(abs(x))) + cos(exp(x)))/tan(2*x)
    

    В обоих случаях все функции, используемые в формуле, вычисляются при заданных значениях аргументов, и полученные значения подставляются в формулу. Программисты говорят, что функция возвращает значение. В формуле указываются имя и аргументы (фактические параметры) функции. Сама функция - это обособленная часть программы. Чтобы функцию можно было вызывать с разными переменными или выражениями, в ней принимаемое значение аргумента присваивается переменной, видимой только в этой функции. Такие переменные называются локальными, или переменными с локальной областью видимости. Придумано огромное количество способов выделения областей видимости. Использование тех или иных видов областей видимости и различных структур данных в основном определяют различия языков программирования и усилия, затрачиваемые на их изучение.

    Кроме функций в Fortran-е используются подпрограммы, отличающиеся от функций, только тем, что подпрограмма не возвращает значения. Со временем стало ясным, что различия между функцией и подпрограммой несущественны и их в новых языках программирования перестали различать. В Perl-e используется термин подпрограмма, в C, JavaScript и PHP - функция.

    Создание пользовательской функции в PHP

    В C нужно объявить (указать транслятору) прототип функции, отличающийся от функции отсутствием тела. Описание функции - суть сама функция.

    В PHP указывать транслятору прототип функции не нужно. К сожалению, описание функции в PHP называется объявлением. Оно имеет следующий синтаксис:

       function имя_функции(список формальных параметров)
       { 
          тело функции
       }
    

    Имя функции должно состоять из латинских букв, цифр и знака подчёркивания и начинаться с буквы или знака подчёркивания.

    Список формальных параметров - это последовательность имён переменных, разделённых запятыми. Он может быть пустым.

    Тело функции - последовательность операторов.

    Пример. Вычислить n! двумя способами.

    $N1 = _fact(5);
    $k = 5;
    $N2 = factRec($k);
      //Вычисление факториала с помощью цикла 
    function _fact($n)
    {   $k=1;
        for($i = 2;$i <= $n; $i++) $k *= $i;
        return $k;
    }
    //Вычисление факториала с помощью рекурсивной функции
    function factRec($n)
    {   if($n < 2)return 1;
        else return $n*factRec($n-1);
    }
    

    Оператор "return выражение" cлужит для выхода из функции и возврата значения выражения. Оператор return возвращает значение, которое может иметь тип скаляр, массив или объект.

    Пример возврата нескольких скаляров в виде массива

    list ($Fam, $Imja, $vozrast) = spisok();
    print "$Fam $Imja, возраст - $vozrast года<BR>";
    //Напечатается: Петрова Маша, возраст - 4 года
    function spisok()
    {   $v = 4;
        return array ('Петрова', 'Маша',  $v );
    }
    

    Функция array(список) превращает список в массив, а языковая конструкция "list(список) = массив" разлагает массив на скалярные переменные.

    Вызов функции с переменным числом параметров

    Количество фактических параметров при вызове функции может быть меньше или равно количеству формальных параметров. Отбрасывать можно параметры только с конца списка фактических параметров. Если в списке фактических параметров параметр отброшен, то соответствующему формальному параметру должно быть присвоено начальное значение. Передаваемые значения фактических параметров имеют более высокий приоритет перед начальными значениями формальных параметров.

    Пример вызова функции с переменных количеством параметров

    <?php
    print SumABC(1).'<BR>';     // 6   1+2+3
    print SumABC(1,2).'<BR>';   // 53  1+2+50
    print sumAbc(1,2,3).'<BR>'; // 91  1+40+50
    
    function SumABC($x,$y=40,$z=50)
    {return $x+$y+$z;
    }
    ?>
    

    Передача массивов в функции

    Массивы и скалярные переменные могут передаваться в функцию в любом порядке, только список фактических и список формальных параметров должны строго соответствовать друг другу.

    Пример

    $x = 5;
    $A1 = array(1,2,3,4);
    $A2= array(
              array(8,9,10),
              array(4,5,6,'семь'),
              array('a','b','c'),
             );
    f_Mas($x,$A1,'y',$A2,15);
    function f_Mas($a,$Ar1,$b,$Ar2,$c)
    { print "a=$a, Ar1[1]=$Ar1[1], b=$b,  Ar2[1][1]=".$Ar2[1][1].", c=$c<BR>";
    }
    //Напечатается
    //a=5, Ar1[1]=2, b=y, Ar2[1][1]=5, c=15
    

    Примечание. Элементы двумерного массива не интерполируются, поэтому в примере использована операция конкатенации.

    Статические переменные

    Статические переменные видны только в теле функции и сохраняют своё значение при выходе из функции. Объявляются статические переменные с помощью оператора

      static имя_переменной = значение;
    

    Оператор static выполняется только при первом вызове функции.

    Пример

      for($i=1; $i<4; $i++) f_static();
      function f_static()
      { static $a = 0;
        $a++;
        print "$a<BR>";
      }
    /* Напечатается
      1
      2
      3
    */
    

    Локальные, глобальные и суперглобальные переменные

    Данный раздел демонстрирует, как резко усложняется язык программирования при организации взаимодействия разных областей видимости.

    Пример A

    1   $a=2;      //$a - глобальная переменная
    2   $b = f($a);
    3   print "a=$a, b=$b<BR>";//a=3, b=8
        // print "***** Суперглобальный массив ****<BR>";
        //foreach($GLOBALS as $kluch => $znach) print "$kluch - $znach<BR>";
    4   function f($c) //$c=2 - формальный параметр с локальной 
        {           //областью видимости
    5      $a = 5;  //локальная переменная
    6      $c++; 
    7      print "a=$a<BR>";//напечатается 5
    8      $d = $a + $c; //d = 5 + 3 =8
    9      global $a; // с этой точки в функции видна глобальная
                      // переменная $a, заслонившая локальную $a
    10     $a++; //$a=3
    11     return $d;
        }
    

    В примере главная программа состоит из строк 1-3. Это область видимости глобальных переменных $a и $b. Строки 4-11 - область видимости локальных переменных $c и $d. Локальная переменная $a видна в строках 5-8 до объявления global $a, после которого видна глобальная переменная с тем же именем "$a".

    Распространено мнение, что применение глобальных переменных в локальных областях видимости опасно. Из моей практики следует обратное. Использование в функциях с помощью объявления global переменных, которые по своему смыслу являются глобальными для всей программы, или большей её части, сильно упрощает программу по сравнению с вариантом передачи в функции всех необходимых переменных через механизм фактических и формальных параметров.

    Пример функции с 22-мя глобальными переменными

    #******** Вызов функции      *******
    stat_viv();
    . . . . . .
    . . . . . .
    #******** Печать статистики  *******
    function stat_viv()
    {global $Z,$n,$plan,$bezEx,$lgot,$vsFakt,$naObOsn,$naOb,$specNab,$gosObZak,
    	$nc,$ng,$prin_g,$net_g,$prin_c,$net_c,
    	$budg,$kontr,$bezExBK,$lgotBK,$vneOl,$vneOlBK;
        if(!$prin_c)$prin_c=0;
      . . . . . . 	
      . . . . . .  
    }  
    

    Синтаксис объявления global

       global список_переменных
    Например
       global $x, $y, $z;
    

    Список всех глобальных имён программы хранится в суперглобальном ассоциативном массиве $GLOBALS. Если выполнить два закомментированных оператора между строками 3 и 4 в примере A в начале этого раздела, то выведется содержимое массива $GLOBALS для этого примера:

    ***** Суперглобальный массив ****
    GLOBALS - Array
    _POST - Array
    _GET - Array
    _COOKIE - Array
    _FILES - Array
    a - 3
    b - 8
    znach - 8
    kluch - znach
    

    Суперглобальные переменные

    Суперглобальные переменные - это встроенные переменные (ассоциативные массивы), которые всегда доступны во всех областях видимости.

    Лекция 6. Ссылки

    В PHP есть два вида ссылок: жёсткие и символические. Жёсткая ссылка на переменную - это просто синоним.

    Пример жёсткой ссылки.

      $a = 5;
      $b = &$a; //$b - синоним (жёсткая ссылка) переменной $a
      print "\$b = $b <BR>"; //напечатается $b = 5
      $a = 10;
      print "\$a = $a, \$b = $b <BR>"; //напечатается $a=10, $b = 10
    

    Символические ссылки весьма необычны. Приведём сначала пример символической ссылки, а потом дадим разъяснения.

    Пример символической ссылки

      $a = 10;
      $z ='a'; 
      //$z - скалярная переменная, а $$z - символическая ссылка на $a
      print "\$z = $z, \$\$z =".$$z."<BR>";
      //напечатается "$z = a, $$z =10"
    

    Имя переменной состоит из префикса "$" и идентификатора. В примере значение переменной $z совпадает с идентификатором переменной $a. Даже если это совпадение произошло случайно, $z одновременно и просто переменная и символическая ссылка на переменную $a, т. е. $$z - это $a. Выполнение конструкции $$z можно считать состоящей из двух этапов:

      1. Конкатенация
         '$'.$z
       Получается строка "$a"
      2. Cтрока "$a" интерпретируется (интерполируется) как переменная $a, т.е.
      вместо неё подставляется значение (в примере $a=10).
    

    Передача значения по ссылке

    Если в функции нужно изменять при любом обращении одни и те же глобальные переменные, то достаточно воспользоваться объявлением global, как это сделано в приведённом выше примере. А как поступить, если в функции при разных обращениях нужно изменять разные глобальные переменные? Можно так:

    $a=5;$b=2; $c=1;
    list($a,$b,$c)=F1($a,$b,$c);
    print "a=$a,b=$b, c=$c<BR>";
    $d=50;$e=20; $f=10;
    list($d,$e,$f)=F1($d,$e,$f);
    print "d=$d,e=$e, f=$f<BR>";
    
    function F1($x,$y,$z)
    {     $x--;$y++;$z++;
        return array($x,$y,$z);   
    }
    /*Результат
      a=4, b=3, c=2
      d=49, e=21, f=11
    */
    

    Перепишем этот пример так:

    $a=5;$b=2; $c=1;
    F1($a,$b,$c);
    print "a=$a,b=$b,c=$c<BR>";
    $d=50;$e=20; $f=10;
    F1($d,$e,$f);
    print "d=$d,e=$e,f=$f<BR>";
    
    function F1(&$x,&$y,&$z)
    {     $x--;$y++;$z++;
    }
    /*Результат
      a=4,b=3, c=2
      d=49, e=21, f=11
    */
    

    В новом варианте примера &$x означает, что на время выполнения функции F1() локальная переменная $x служит синонимом глобальной переменной из списка фактических параметров. При первом обращении к F1() $x ≡ $a, а при втором $x ≡ $d. Таким образом, префикс & делает формальный параметр жёсткой ссылкой на соответствующий фактический.

    Символическая ссылка на функцию

    В PHP, если переменная содержит имя функции, то можно вызывать функцию, используя вместо имени функции имя переменной, например, если $y = 'sin', то вместо sin($x), можно написать $y($x).

    Пример

      $func ='sin';
      $y = 30;
      $x = $y/180*pi();
    //1. Выполнение функции, имя которой задано в переменной  
      $z = $func($x);
      print"$func($y<SUP>o</sup>)= $z<BR>"; //sin(30o) = 0.5
    
    //2. Передача имени функции в качестве параметра в другую функцию
      print ex_val($func,$x)."<BR>";  //1.5
      print ex_val('sin',$x)."<BR>";  //1.5
      function ex_val($f,$t)
      {  $x = $f($t)+1;
         return $x;
      }
    

    Функция eval() и символические ссылки

    Параметром функции eval() служит выражение, задаваемое в виде строки. Это выражение интерполируется и выполняется.

    Пример

       $f ='sin';
       $x=30;
       eval("\$z=$f($x/180*pi());");
       //Выражение в кавычках в результате интерполяции примет вид оператора
       //  $z=sin(30/180*pi();
       // Этот оператор выполнится и получится $z=0.5 
       print "$z<BR>"; //$z=0.5
    

    С помощью функции eval пользователь может вносить изменения в программу в процессе её выполнения. Этим свойством eval() могут воспользоваться и люди с недобрыми намерениями. Ниже приведён пример сайта, состоящего из двух страниц. Первая страница (рис. 2) служит для ввода пользователем фамилии и оклада

    Рис. 2. Форма для ввода

    На вторую страницу (рис. 3) выводятся результаты работы скрипта. Скрипт просто выводит полученные данные.

    Использование eval() для
    приёма параметров

    Фамилия - Петров
    Оклад 40000 рублей

    Рис. 3. Форма для вывода результатов

    Текст (код) первой HTML-страницы

    <HTML><HEAD>
    <meta http-equiv="Content-Type" content="text/html; charset=WINDOWS-1251">
    <TITLE>eval и симв. ссылки</title>
    </head>
    <BODY>
    <h2>Функция eval() и символические ссылки</h2>
    <FORM METHOD=GET action ='eval_.php'>
    ФИО . . . . <INPUT type=text name=FIO><BR>
    Оклад . . . <INPUT type=text name=x><BR>
    <INPUT type=submit value='выполнить'>
    </form>
    </body>
    </html>
    

    Текст (код) второй HTML-страницы с PHP-скриптом

    <html><body>
    <h2>Использование eval() для приёма параметров</h2>
    <?php
    //В цикле перебираются все поступившие с браузера параметры
    //В данномм примере всего два параметра FIO и x
    
    foreach ($_GET as $ke =>$val)
    { eval("\$$ke='$val';");}//Результат интерполяции выражения 
                             //"\$$ke='$val';" при $ke='FIO' $val='Петров'-
                             //$FIO='Петров'. Этот оператор выполнится
                             //функцией eval
    
      print "Фамилия=$FIO<BR>оклад=$x рублей";
    ?>
    </body></html>
    

    Строка адреса в браузере при отображении второй страницы будет иметь вид

    http://primery/PHPkucha/eval_.php?FIO=%CF%E5%F2%F0%EE%E2&x=45

    После знака вопроса перечисляюся пары "имя переменной= значение".Пары разделяются знаком амперсанта. (%CF - код буквы П в кодировке windows-1251, %E5 - е и %CF%E5%F2%F0%EE%E2 - 'Петров'). Попробуем похулиганить. Изменим значения параметров в адресной строке браузера следующим образом

    http://primery/PHPkucha/eval_.php?FIO='.sin(30/180*pi()).'&x=XA-XA-XA

    и перезагрузим страницу. Результат на рис. 4.

    Использование eval() для
    приёма параметров

    Фамилия - 0.5
    Оклад - XA-XA-XA

    Рис. 4. Результаты изменения значений параметров

    Это конечно шутка, мелкое хулиганство, но при определённых навыках таким приёмом можно нанести серьёзный вред. Можно заставить скрипт выполнить любую последовательность команд. Чтобы не дать злоумышленнику этой возможности, заменим eval() на символическую ссылку:

    foreach ($_GET as $ke =>$val)
    { //eval("\$$ke='$val';");
        $$ke=$val;
    }
    

    В окно браузера будет выведена испорченная информация

       Фамилия - '.sin(30/180*pi()).'
       Оклад - XA-XA-XA рублей  , 
    

    но её будет видеть только злоумышленник, а функция sin() в нашем примере выполнена не будет. В общем случае не будет выполнена введённая в адресную строку последовательность команд.

    Пример использования символических ссылок

    Задача. Вводится римскими цифрами век. Нужно напечатать, кто царствовал в этом веке. Например, ввели "XVI", нужно вывести
    "В XVI веке царствовал Иван Васильевич".

    Не будем показывать HTML-страницу с вводом века. Рассмотрим скрипт, решающий поставленную задачу

    <HTML>
    . . . . . 
    <?php
    $vek=$_GET['vek']
     $XVI="Иван Васильевич";
    $XVIII="Пётр Алексеевич";
    $XIX="Николай Павлович";
    //Если $vek="XIX", то $$vek - ccылка на переменную $XIX
    print "В $vek веке царствовал ". $$vek."<BR>";
    //В XIX веке царствовал Николай Павлович
    ?>
    </body>
    </html>
    

    Лекция 7. Файлы

    Переносимость сайта с веб-сервера на веб-сервер

    Обычно сайт разрабатывается и редактируется на веб-сервере, не подсоединённом к сети Internet, например, в системе ДЕНВЕР. При переносе на веб-сервер провайдера могут возникнуть несогласованности путей к файлам, прав на файлы и имён файлов. Может возникнуть несовместимость из-за разных веб-серверов у разработчика и провайдера. Для разрешения этих проблем необходимо уточнить понятие сайта.

    Возьму на себя смелость рассмотреть архитектуру сайта по аналогии c архитектурой базы данных. Рассмотрим три уровня архитектуры сайта:

    1. пользовательский или внешний;
    2. концептуальный;
    3. физический.

    На внешнем уровне сайт воспринимает пользователь через окно браузера. Пользователь, если он не программист, обычно не знает, что такое браузер, веб-сервер, HTML-документ, скрипт. Для него страница сайта - это документ, страница книги, фотография и т.д., т.е. интересующая его информация.

    Сайт на концептуальном уровне рассматривается веб-программистом. Если рассматривать HTML-документ как программу, то для программиста сайт - это система связанных между собой гиперссылками особого вида программ размещённых в системе каталогов, начиная с каталога, имя которого обычно совпадает с именем сайта.

    На физическом уровне рассматриваются конкретные устройства, на которых размещён сайт.

    Веб-страница, которую Вы сейчас читаете, имеет URL

    http://kafi5.loc/kam.loc/PHPzadan/LEKCII_PHP.html
    или

    http://i.voenmeh.ru/kafi5/Kam.loc/PHPzadan/LEKCII_PHP.html
    или адрес в файловой системе

    file:///Z:/home/KafI5.loc/www/Kam.loc/PHPzadan/LEKCII_PHP.html

    Последний адрес требует пояснений. Каталог Kam.loc состоит, за малым, не имеющим значения, исключением, из связанных между собой статических страниц. Стартовую страницу indexKam.html можно отобразить в браузере, задавая не URL, а имя файла

    file:///Z:/home/KafI5.loc/www/Kam.loc/indexKam.html

    Браузер прочитает этот файл прямо из файловой системы, не обращаясь к веб-серверу. Можно прочитать сайт Kam.loc на компьютере, не подключённом к интернет и не имеющем веб-сервера. Нужно иметь только каталог Kam.loc на любом носителе (винчестере, флешке, компакт-диске).

    В описанном примере используется расширенное толкование понятия сайт. Во-первых, сайтом назвали часть сайта, даже двух сайтов:

    http://kafi5.loc/

    и

    http://i.voenmeh.ru/kafi5/

    Во-вторых, сайтом назвали каталог, не имеющий даже URL

    file:///Z:/home/KafI5.loc/www/Kam.loc/

    Взгляд на часть сайта, в которой рассматривается самостоятельная тема, как на отдельный сайт, широко распространился. Поисковики yandex, google, rambler на запрос сайт Каминского дают ссылку

    http://i.voenmeh.ru/kafi5/Kam.loc/indexKam.html.

    Проблема несовместимости путей к файлам при переносе сайта с одного веб-сервера на другой решается путём использования относительных ссылок на HTML-страницы и относительных адресов файлов. Рассмотрим сайт Primery, путь к которому в windows следующий:

    C:\WebServers\home\Primery\www

    или на виртуальном диске, создаваемом системой ДЕНВЕР:

    Z:\home\Primery\www

    URL (доменный адрес или, просто, адрес) сайта Primery:

    http://Primery

    Рассмотрим четыре файла:

    Z:\home\Primery\Сортировка1.txt

    Z:\home\Primery\www\factorial.php

    Z:\home\Primery\www\Fio.htmi

    Z:\home\Primery\www\PHPkucha\ArrPrim.php

    Обратите внимание, что файл Сортировка1.txt находится за пределами сайта и его невозможно отобразить в окне браузера, так как у него нет URL.

    Несогласованности путей к файлам при переносе сайта легко преодолеть, используя только относительные ссылки на страницы и относительные адреса файлов.

    В скрипте factorial.php относительные ссылки на страницы Fio.html и ArrPrim.php выглядит так:

    <A href="Fio.html">ФИО

    <A href="PHPkucha/ArrPrim.php">Примеры массивов

    Файл Fio.html находится в том же каталоге, что и ссылающийся на него factorial.php, поэтому в ссылке достаточно указать только его имя. Файл ArrPrim.php находится в каталоге на ступеньку ниже, поэтому перед именем ArrPrim.php нужно указать весь относительный путь, состоящий в данном случае из одного каталога.

    Сортировка1.txt придётся прочитать как файл:

      $fil=fopen("../Сортировка1.txt",'r');//открыть файл на каталог выше
      while(!feof($fil)) //читать построчно и печатать
      {  $str=fgets($fil,999);
       print "$str<BR>";
      }
      /* Результат
      ФИО телефон Город Дом_ улица_кв
      Прилепский Евгений 139-23-77 Новгород Казанская ул.7,кв24
      Губарь Мария Фёдоровна 143-67-67 С-Петербург Московский пр, д 34,кв 57 
      . . . . . 
     */
      

    Cсылка на factorial.php в файле ArrPrim.php:

    <a href="../factorial.php">faktorial</a>;

    Файл Сортировка1.txt находится на два каталога вверх по отношению к скрипту ArrPrim.php:

    $fil=fopen("../../Сортировка1.txt",'r');//открыть файл на 2 каталога выше
      while(!feof($fil)) //читать построчно и печатать
      {  $str=fgets($fil,999);
       print "$str<BR>";
      }
    

    В суперглобальный массив $_SERVER — PHP-интерпретатор помещает переменные, полученные от сервера. Приведём значения некоторых элементов массива $_SERVER для скрипта factorial.php, рассматривавшегося выше:

    В $_SERVER['DOCUMENT_ROOT'] хранится корневой каталог для сайта Primery

    Z:/home/Primery/www

    В $_SERVER['SCRIPT_FILENAME'] хранится полное имя (спецификация) файла

    Z:/home/Primery/www/factorial.php

    В $_SERVER['SCRIPT_NAME'] хранятся путь от корневого каталога и имя скрипта. Элемент $_SERVER['SCRIPT_NAME'] для скрипта factorial.php примет значение

    /factorial.php

    Для скрипта ArrPrim.php в $_SERVER['SCRIPT_NAME'] хранится значение

    /PHPkucha/ArrPrim.php

    Открытие и чтение текстового файла построчно

    Ещё раз прочитаем из скрипта ArrPrim.php файл Сортировка1.txt, но при этом воспользуемся именем корневого каталога:

      $alfa=$_SERVER['DOCUMENT_ROOT']; //$alfa - корневой каталог сайта Primery
      //Символ @ подавляет системные сообщения об ошибках
      // "../" - открыть файл на  каталог выше
      // 'r' - для чтения
    @ $fil=fopen("$alfa/../Сортировка1.txt",'r'); 
      while(!feof($fil)) //читать построчно и печатать
                         //функция feof($fil) возвращает true 
                         //при достижении конца файла
      {  $str=fgets($fil,999); //чтение строки (записи) файла
                               //999 - размер буфера
         print "$str<BR>";
      }
    

    Чтение всего файла в массив

    Для чтения всего файла в массив служит функция file(имя файла), возвращающая массив. При этом открывать файл не надо. Перепишем предыдущий пример:

      $alfa=$_SERVER['DOCUMENT_ROOT'];
      $f="Сортировка1.txt";
      //Чтение файла в массив $M1
      //открыть файл на каталог выше
    @ $M1=file("$alfa/../$f");
      if(!$M1)
      {  print "Файл $f не открылся";
         exit;
      }
      foreach($M1 as $str) {  print "$str<BR>";}
    

    Использовать путь к корневому каталогу излишне:

      $f="Сортировка1.txt";
      //Чтение файла в массив $M1
      //открыть файл на два каталога выше
     @  $M1=file("../../$f"); 
      if(!$M1)
      {  print "Файл $f не открылся";
         exit;
      }
      foreach($M1 as $str) {  print "$str<BR>";}
    

    Запись в файл

    Открытие файла для записи.

    $fil_W = fopen('Text.txt','w');
    

    Открываемый файл по умолчанию считается текстовым. Если файл с таким именем уже существует, в нем происходит автоматическое удаление всех данных;

    Запись в файл . Функция fwrite().

    Синтаксис fwrite():

    fwrite(указатель_файла, строка[,длина строки]);
    [] - содержимое скобок не обязательно
    

    Запись в файл одной строки

    $a = 'Маша';
    fwrite($fil_W,$a);
    

    Запись в файл массива. Записываются стоки с номерами по порядку.

    $i = 1;
    foreach($Arr as $x)
      fwrite($fil_W,$i++."\t $x");
    

    Проверка существования файла

      if(file_exists($fil)
      {
      
      }
    

    Функции для перемещения внутри файла

    Функции ftell() и fseek() предназначены для работы только с двоичными файлами.

    Пример открытия двоичного файла Text.txt в режиме добавления в конец.

       $fil_bin = fopen('Text.txt','ab');
    // a - добавление в конец;
    // b - файл рассматривается как двоичный.
    

    Согласно принципу фон Неймана информация в памяти ЭВМ хранится в виде последовательности бит, то есть нулей и единиц. Как рассматривать содержимое файла, зависит от читающей программы (строго говоря, от программиста, написавшего программу). Если файл рассматривается как двоичный, то программа просто читает его последовательно бит за битом. Рассматривая файл как текстовый, программа разбивает его на байты и строки (записи). Более точно было бы говорить не "двоичный" или "текстовый" файл, а "файл, читаемый как двоичный или текстовый".

    BOM

    Часто приходится при чтении текстового файла определять его кодировку. Существует большое количество кодировок текста. Разработаны алгоритмы распознавания кодировки путём последовательного просмотра и анализа последовательностей байтов в файле. Эти алгоритмы распознают кодировку с некоторой вероятностью.

    В файлах с кодировками UNICODE используеся другой способ распознавания кодировки - в начало файла добавляется состоящий из нескольких байт признак кодировки. Этот признак получил название BOM (Byte Order Mark - маркер последовательности байтов).

    В UCS (Universal Character Set - универсальный набор символов ) и в UTF-8 (UTF –unicode transformation format) BOM имеет код

     UCS "U+FFFE" => UTF-8 "EF BB BF"
    

    Перевод BOM из UCS в UTF-8

    UCS
     FFFE16 => 1111 1111 1111 11102
    UTF-8 трёхбайтный код
            11101111 10111111 101111102 => EF BB BF16
    

    Этот символ можно даже вывестина экран: . Его Escape-последовательность - &#65534;

    Пример. Рассмотрим, как выглядит содержимое файла с BOM при чтении в двоичном и текстовом форматах.

    Для чтения файла в двоичном виде используется функция fread().

    Описание
    string fread ( resource $handle , int $length )
    
    fread() читает до length байт из источнмка с файловым указателем handle. 
    Чтение останавливается как только выполнится одно из следующих условий:
    
        было прочитано length байт;
        достигнут EOF (конец файла);
        стал доступен пакет или произошел таймаут сокета (для сетевых потоков);
        если читаемый поток является буферизованным и не представляет собой обычный 
           файл, то за один раз максимум читается количество байт, равное размеру 
           одной порции данных (обычно это 8192), однако, в зависимости от ранее 
           буферизованных данных размер возвращаемых данных может быть больше размера 
           одной порции данных.
    

    Создадим текстовый файл BOM.txt в кодировке UTF-8 c BOM. Файл состоит из одной строки:

      abc АБВ
    

    Здесь "a" и "c" латинские, а "А" и "В" - русские.

    Напишем программу для чтения файла BOM.txt как двоичного. Текст программы - в кодировке windows-1251. Все символы файла, в том числе и три байта служебнного символа BOM, бит за битом помещаются в переменную $contents. Прочитанный файл (переменная $contents) распечатывается как текстовый в кодировке windows-1251.

    Программа для чтения файла BOM.txt как двоичного

    <?php
    $filename = "BOM.txt";
    $handle = fopen($filename, "rb"); //rb - для чтения, двоичный   
    print filesize($filename). "<br>\n"; //длина файла в байтах
    $contents = fread($handle, filesize($filename));
    fclose($handle);
     echo "\$x=$contents, тип: " . gettype($contents) . "<br>\n"; 
     echo 'L='.strlen($contents)."<br>\n"; //Длина прочитанной из
                                        // файла информации в байтах
     for($i=0;$i<3;$i++)
     {  $x=substr($contents,$i,1);//$x - i-й символ строки
        $y=ord($x); //$y - код символа $x в windows-1251
        echo $y.'<sub>10</sub>='.dechex($y). "'<sub>16</sub>'<br>\n"; 
     }
    

    Результаты

    13
    $x=п»їabc АБВ, тип: string
    L=13
    23910=ef16
    18710=bb16
    19110=bf16
    

    Строка "п»їabc АБВ" - содержимое файла в кодировке windows-1251. ef bb bf - три первых байта файла, добавленные при его создании в кодировке utf-8 c BOM. Эти байты отобразились в кодировке windows-1251 в виде символов п»ї. Код ef16 отобразился в кодировке windows-1251 в виде буквы "п", bb16 - в виде кавычки », bf16 - в виде ї. Каждая русская буква отобразилась двумя символами.

    Напишем программу, текст которой - в кодировке utf-8. Эта программа читает файл BOM.txt как текcтовый и "BOM" пропускает.

    <?php
     $fil=fopen("BOM.txt",'r');
     $a=fgets($fil,999);
     print"a=$a<BR>";
     //Напечатается содержимое файла без BOM
     // abc АБВ
    ?>
    

    Передача файла в браузер. Функция readfile()

    Обычно скрипт служит для генерации динамической страницы, но иногда возникает необходимость передать в браузер готовый HTML-документ. Для этой цели и служит функция readfile(имя_файла). Нельзя перед выполнением функции readfile() ничего посылать на браузер.

    Пример использования функции readfile()

    Включение в скрипт содержимого другого файла. Функция require()

    Часто в нескольких скриптах встречаются одинаковые фрагменты. Можно записать такой фрагмент в отдельный файл и с помощью функций include(имя_файла) или require(имя_файла) включать его в разные скрипты.

    При использовании функции require в случае, если подключаемый файл не будет найден, выполнение скрипта будет остановлено, в то время как при использовании include() просто выводится предупреждение и продолжается выполнение скрипта.

    Пример использования функции require

      Скрипт
    <?php
    $a = 2; $b = 5;
    print "$a<sup>$b</sup> = $c <BR>";
    //Напечатается 2<sup>5</sup> =
    require('a_b_req.txt');
    print "$a<sup>$b</sup> = $c <BR>";
    //Напечатается 2<sup>5 = 32
    ?>
    ******
      Вставляемый файл a_b_req.txt
    <?php
    $c = pow($a,$b);
    ?>
    

    SSI

    SSI (Server Side Includes — включения на стороне сервера) — несложный язык для динамической «сборки» веб-страниц на сервере из отдельных файлов. SSI никак не связан с PHP и описывает вставку в HTML-документ.

    Пример вставки баннера. Исходный HTML-документ содержит директиву с именем файла, содержимое которого нужно вставить на место директивы перед отправкой документа в браузер.

    Исходный HTML-документ Vstav_SSI.html

    <html>
    <head>
    <title>Пример SSI</title>
    </head>
    <body>
    <!--#include file="bann.txt"-->
    <h2 align=center>Классический пример SSI</h2>
    </body>
    </html>
    

    Директива вставки <!--#include file="bann.txt"--> закомментирована. Если веб-сервер не поддерживает SSI, то эта директива отправится на браузер, иначе она будет заменена указанным в ней файлом. Иными словами, наличие этой директивы в документе, полученном браузером, является признаком отсутствия поддержки SSI веб-сервером.

    Вставляемый файл "bann.txt"

    <DIV style="position:relative;margin: 0 auto;
         padding: 0;text-align:center;
         background-image:url(/risunki/zag_centr.png);width:100%;">
        <IMG align=middle
          alt='Кафедра: "Информационных Систем и Компьютерных Технологий"' 
          src="/risunki/zag.png">
    </DIV>
    

    На рис. 7.1 показан вид страницы со вставленным баннером

    Рис. 7.1. Инфологическая модель расписания

    Для поддерки веб-сервером SSI, в его файлах конфигурации должны быть следующие директивы

    AddType text/html .shtml
    AddHandler server-parsed .shtml .html .htm
    

    Для надёжности эти директивы можно поместить в файл ".hraccess" в корневом каталоге сайта.

    Лекция 8. Регулярные выражения

    Содержание лекции

    Понятие "регулярное выражение"
    1. Поиск известного слова (подстроки).Функция preg_match()
    2. Поиск подстроки заданного формата, но неизвестного содержания
    3. Поиск в строке всех совпадений с шаблоном. Функция preg_match_all()
    4. Замена. Функция preg_replace()
    5. Расщепление строки по заданным разделителям. Функция preg_split()
    6. Справочные материалы

    Понятие "регулярное выражение"

    Рассмотрим встретившуюся на практике задачу. В базе данных хранились фамилия, имя и отчество, а напечатать нужно было фамилию и инициалы. Введём обозначения:

    [А-Я] - одна любая большая буква русского алфавита;

    [а-я]* - ноль или больше любых маленьких букв русского алфавита.

    Запишем выражение

       [А-Я][а-я]* [А-Я][а-я]* [А-Я][а-я]*
    

    Здесь [А-Я][а-я]* - слово, состоящее из русских букв, начинающееся с большой буквы. Этому условию удовлетворяют и фамилия и имя и отчество. Всё выражение обозначает в общем виде фамилию, имя и отчество, отделённые друг от друга пробелами. Этому выражению удовлетворяет не только ФИО, но и множество других словосочетаний, например, Российская Федеративная Республика. Но точно известно, что в базе хранятся только ФИО.

    Подобное выражение называется регулярным. Ещё его называют шаблоном или образцом (англ. pattern - образец, маска) для поиска.

    Термин регулярные выражения используется и в более широком смысле как грамматика искусственного языка, в котором одни строки допускаются, а другие - нет.

    Выделим круглыми скобками из составленного шаблона части, соответствующие фамилии и инициалам:

       ([А-Я][а-я]*) ([А-Я])[а-я]* ([А-Я])[а-я]*
    

    В PHP задача выделения инициалов с помощью регулярного выражения решается так.

       $str = 'Петрова Мария Ивановна';
    preg_match("/([А-Я][а-я]*) ([А-Я])[а-я]* ([А-Я])[а-я]*/",$str,$Ar);
    print "$Ar[1] $Ar[2]. $Ar[3].<BR>";
    /*Напечатается: Петрова М.И.
    
     Обозначения: 
      preg_match() - функция поиска по шаблону;
      $str - строка, в которой ищут;
      $Ar  - массив результатов;
      $Ar[0] = 'Петрова Мария Ивановна';
      $Ar[1] = 'Петрова';
      $Ar[2] = 'М';
      $Ar[3] = 'И'.
    */  
    

    Существуют два стиля записи регулярных выражений: POSIX и Perl. Примеры использования стиля POSIX приведены здесь.

    POSIX (англ. portable operating system interface for Unix — переносимый интерфейс операционных систем Unix) — набор стандартов, описывающих интерфейсы между операционной системой и прикладной программой (системный API).

    Стиль Perl более детально проработан, имеет больше возможностей и поэтому более распространён. Дальнейшее описание регулярных выражений будем излагать в стиле Perl.

    Пример использования регулярного выражения на языке Perl для удаления тегов

    $s='<beg>История<end>';
    $s=~m/.*?>(.*)*?</;
    print $1;
    #Результат - История
    

    Собственно регулярное выражение /.*?>(.*)*?</ на языках PHP и Perl одинаковое, но в Perl весь оператор с регулярным выражением проще, легче получить результаты поиска.

    Если в предыдущем примере можно было интуитивно уловить хоть какой-то смысл, то последнее регулярное выражение для непосвящённого выглядит как полная бессмыслица. Такие выражения и отпугивают начинающих программистов.

    Изложение регулярных выражений в общем виде вызывает у читателя непонимание и даже испуг. Именно поэтому многие программисты избегают применения регулярных выражений. Объективно, регулярные выражения сложны. Легко написать шаблон, сложность которого лежит на грани возможностей человеческого ума. Наверное, лучше избегать такой сложности, разбивать задачу на более простые части. Достоинство регулярных выражений в том, что в некоторых случаях их применение позволяет сократить программу в сотни раз.

    Изучим сначала регулярные выражения на примерах, а затем сделаем обобщения.

    1. Поиск известного слова (подстроки)

    Соответствие шаблону будем искать в строке

      $c="aaphpbbb PHP nnnn PhpmmmpHp"
    

    1.1. Найти в строке $c подстроку Php, различая большие и маленькие (прописные и строчные) буквы.

       if(preg_match("/Php/",$c)) print "<BR>Есть Php<BR>";
       else print "<BR>Нет Php<BR>";
       //Напечатается "Есть Php"
    

    Функция preg_match() возвращает логическое значение true или false. Англ. match - соответствие. Название preg_match - функция поиска соответствия в стиле Perl.
    В примере /Php/ - регулярное выражение, ограниченное символами "/". Вместо "/" можно использовать в качестве ограничителей другие символы, например, написать %Php%.

    1.2. Найти в строке $c подстроку PHP, не различая большие и маленькие буквы.

    Для различия больших и маленьких букв используется модификатор "i", который записывается сразу после регулярного выражения. Результат помещается в массив $rez, состоящий в данном случае из одного элемента.

       if(preg_match("/Php/i",$c,$rez)) print "$rez[0]<BR>";
       else print "<BR>Нет Php<BR>";
       //Напечатается "php"
    

    1.3. Поиск нескольких последовательных подстрок. Массив найденных подстрок.

    Для дальнейшего изложения нам понадобится понятие метасимвол.

    Метасимвол - это символ, подменяющий собой что-то, чаще всего какую-то последовательность символов. Метасимвол может быть приказом выполнить какое-то действие, например, искать с конца строки. Метасимволы применяются не только в регулярных выражениях. Например, для того чтобы, пользуясь Проводником, найти в файловой системе OS Windows все файлы с расширением ".php" нужно написать "*.php". Здесь "*" означает "любая последовательность допустимых в имени файла символов".

    Рассмотрим следующие метасимволы, используемые в регулярных выражениях.

    Метасимволы "?" и "*" называют множителями.

    1.3.1. Найдём в строке $c="aaphpbbb PHP nnnn PhpmmmpHp" подстроку, содержащую три слова PHP с любым сочетанием больших и маленьких (другими словами, прописных и строчных) букв, т.е, php, PHP, Php. Cлова PHP могут разделяться любым количеством любых символов.

    preg_match ("/(php).*?(php).*?(php)/i", $c, $regs);
    print "<BR>\$regs[0]=$regs[0] <BR>\$regs[1]=$regs[1] "
          ."<BR>\$regs[2]=$regs[2] <BR>\$regs[3]=$regs[3] <BR>";
    
    /*Напечатается:
    $regs[0]=phpbbb PHP nnnn Php
    $regs[1]=php
    $regs[2]=PHP
    $regs[3]=Php
    */
    

    Выражение .*?(php) означает "искать любую последовательность символов до ближайшего (php)".

    Элемент массива $regs[0] содержит подстроку, соответствующую всему шаблону.
    Элемент $regs[i] содержит подстроку, соответствующую i-му подшаблону (i = 1,2, . . , n; n - число подшаблонов, заключённых в круглые скобки) .

    1.3.2. Прожорливость. Регулярному выражению

         /.*(php))/i
    

    в строке

        $c="aaphpbbb PHP nnnn PhpmmmpHp"
    

    формально удовлетворяют следующие подстроки

       aaphp 
       aaphpbbb PHP
       aaphpbbb PHP nnnn Php
       aaphpbbb PHP nnnn PhpmmmpHp"
    

    Ищется самая длинная подстрока:

    preg_match ("/.*(php))/i", $c, $regs);
    print  <BR>\$regs[0]=$regs[0] <BR>\$regs[1]=$regs[1 <BR>";
    /*Напечатается
    $regs[0]=aaphpbbb PHP nnnn PhpmmmpHp
    $regs[1]=pHp
    */
    

    В процессе поиска по рассматриваемому шаблону проявляется, как бы, жадность, "пожирается" как можно большая часть строки. Отсюда название прожорливость или жадность.

    1.3.3. Подавление прожорливости. Если в предыдущем регулярном выражении вместо "*" поставить "*?", то будет найдена подстрока минимальной длины.

    preg_match ("/.*?(php))/i", $c, $regs);
    print " <BR>\$regs[0]=$regs[0]  <BR>\$regs[1]=$regs[1] <BR>";
    /*Напечатается
    $regs[0]=aaphp
    $regs[1]=pHp
    */
    

    1.3.4. Найти все подстроки, соответствующие шаблону и подшаблонам, и их смещения относительно начала строки. Результаты помещаются в двумерный массив.

    $c="aaphpbbb PHP nnnn PhpmmmpHp";
    preg_match ("/(php).*?(php).*?(php)/i", $c, $regs, PREG_OFFSET_CAPTURE);
    for($i=0;$i<4;$i++)
    print "\$regs[$i][0]=".$regs[$i][0]
          ." Смещение \$regs[$i][1]=".$regs[$i][1]."<BR>";
    /* Результат
    $regs[0][0]=phpbbb PHP nnnn Php Смещение $regs[0][1]=2
    $regs[1][0]=php Смещение $regs[1][1]=2
    $regs[2][0]=PHP Смещение $regs[2][1]=9
    $regs[3][0]=Php Смещение $regs[3][1]=18
    */
    

    Флаг PREG_OFFSET_CAPTURE определяет смещение найденных данных, соответствующих подшаблонам, заключённым в круглые скобки. Массив $regs становится двумерным. Первый элемент строки - найденная подстрока, второй - её смещение относительно начала исходной строки.

    В PHP принят весьма странный способ размещения результатов поиска. Они то помещаются в массив, состоящий из одного элемента, то в одномерный массив из нескольких элементов, то в двумерный массив. Вершиной этого многообразия (безобразия) является трёхмерный массив. Удивительно, ведь PHP появился позже языка Perl, в котором эта проблема решена просто и изящно.

    1.3.5. Начать поиск с заданной позиции. Номер позиции указывается в последнем параметре функции preg_match().

    $c="aaphpbbb PHP nnnn PhpmmmpHp";
    preg_match ("/(php).*?(php)/i",$c,$regs,PREG_OFFSET_CAPTURE,7);
    for($i=0;$i<4;$i++) 
      print "\$regs[$i][0]=".$regs[$i][0]"
          ." Смещение \$regs[$i][1]=".$regs[$i][1]."<BR>";
    /* Результат поиска с восьмого символа
    regs[0][0]=PHP nnnn Php Смещение $regs[0][1]=9
    $regs[1][0]=PHP Смещение $regs[1][1]=9
    $regs[2][0]=Php Смещение $regs[2][1]=18	  
    */
    

    В примере жирным курсивом в строке $c выделен восьмой от начала строки символ, с которого начинается поиск.

    1.3.6. Циклический поиск всех подстрок, соответствующих шаблону.

    $c="aaphpbbb PHP nnnn PhpmmmpHp";
    $offset=0;
    while(preg_match("/php/i",$c,$regs,PREG_OFFSET_CAPTURE,$offset))
    {  print $regs[0][0]." offset=$offset Смещение = ".$regs[0][1]." <BR>";
       $offset = $regs[0][1] + strlen($regs[0][0]);
    }
    /* Результат поиска
    php offset=0  Смещение = 2
    PHP offset=5  Смещение = 9
    Php offset=12 Смещение = 18 
    pHp offset=21 Смещение = 24
    */
    

    В примере offset - номер позиции, с которой начинается очередной поиск, а "Смещение" - это номер позиции, с которой начинается слово "php" (в любом сочетании прописных и строчных букв) .

    2. Поиск подстроки заданного формата, но неизвестного содержания

    Соответствие шаблону будем искать в строке

      $a="Итоги торгов 25.04.2007 на ММВБ";
    

    2.1. Найти в строке $a дату в формате ДД.ММ.ГГГГ.

    Escape-последовательность \d - любая цифра.

    if(preg_match("/(\d\d)\.(\d\d)\.(\d\d\d\d)/",$a,$rez))
    print "$rez[1]/$rez[2]/$rez[3]<BR>";
    /* Напечатается:
    25/04/2007
    */
    

    Регулярное выражение читается так: "две цифры точка две цифры точка четыре цифры". Так как точка в регулярном выражении - метасимвол, то "\." читается как просто "точка". Сравните: "t" просто буква, "\t" - escape-последовательность "табуляция".

    2.2. Количество повторений. В предыдущем примере для задания шаблона года пришлось четыре раза написать "\d". Для задания числа повторений используются следующие специальные выражения, называемые квантификаторами.

    В регулярных выражениях используется логическая операция ИЛИ, обозначаемая символом "|".

    Перепишем предыдущий пример, используя квантификаторы и "|".

    $a="Итоги торгов 25.04.2007 на ММВБ";
    if(preg_match("/(\d{2})\.(\d{2})\.(\d{4}|\d{2})/",$a,$rez))
    print "$rez[1]/$rez[2]/$rez[3]<BR>";
    /* Напечатается:
    25/04/2007
    */
    

    Операция "ИЛИ" выполняется до первого совпадения. Если написать (\d{2}|\d{4}), то будет найдено"20" вместо "2007".

    2.3. Классы символов. Шаблон [МСДК]аша служит для поиска в строке слов Mаша, Саша, Даша, Каша.. В квадратных скобках указаны четыре символа, с одним из которых должна совпадать первая буква искомого имени. Список символов в квадратных скобках называют классом. Для обозначения некоторых классов служат ESCAPE-последовательности. Например, класс всех десятичных цифр [0123456789] мы заменяли ESCAPE-последовательностью \d. Класс всех цифр можно записать короче: [0-9]. Вообще, можно указывать через дефис первый и последний символы закономерной последовательности, например, [6-9], [Б-E], все заглавные русские буквы - [А-ЯЁ]. Буквы Ё и ё выпадают из последовательности и их приходится указывать дополнительно.

    Символ ^ в классе означает отрицание, например [^246] - любая цифра, кроме 2, 4, 6.

    Рассмотрим пример с частичной проверкой искомой даты.

    $a="Итоги торгов 25.04.2007 на ММВБ";
    if(preg_match("/(0[1-9]|[12]\d|3[01])\.(0[1-9]|1[0-2])\.(\d{4}|\d{2})/",$a,$rez))
    print "*** $rez[0]<BR>";
    //напечатается "25.04.2007"
    

    На практике такая проверка может оказаться ... вредной, так как скроет от пользователя напечатанную с ошибкой дату. Лучше напечатать неверную дату, например, 35.04.2007, и предоставить пользователю возможность исправить её.

    2.4. Метасимволы ^, $, ?, *, +. Метасимволы ? и * уже рассматривались.

    2.4.1. Шаблону должна соответствовать вся строка. В примере рассматривается три варианта строки для поиска. Первые две будут найдены, а третья - нет.

    Пример

    //вариант 1
    $a="http://kafi5.loc";
    /*вариант 2
    $a="http://kafi5.loc/Kam.loc";
    вариант 3
    $a="http://kafi5.loc/Gushin";
    */
    if(preg_match("%^http:.*loc$%",$a,$rez))
    print "$rez[0]<BR>";
    /*Напечатается для варианта 1 "http://kafi5.loc";
                   для варианта 2 "http://kafi5.loc/Kam.loc";
    */
    

    В примере в качестве ограничителей шаблона использовалась пара символов % %, так как в символ / есть в строке для поиска

    2.4.2. Поиск одного символа или его отсутствия на заданном месте.

    $a="Навиков Новиков Новичков";
    if(preg_match("/Нович?ков/",$a,$rez))
    print "$rez[0]<BR>";
    //Напечатается "Новиков", так как поиск идет от начала строки
    if(preg_match("/Нович?ков$/",$a,$rez))
    print "$rez[0]<BR>";
    //Напечатается "Новичков", так как поиск идет в конце строки
    

    2.4.3. Хранение шаблона в переменной

    $a="Итоги торгов 25.04.2007 на ММВБ";
    $c="(\d\d)";
    if(preg_match("/$c\.$c/",$a,$rez))
    print "$rez[0]<BR>";
    //Напечатается "25.04"
    

    3. Поиск в строке всех совпадений с шаблоном

    В строке "Навиков Новиков Новичков" шаблону "/Н[ао]вич?ков/" удовлетворяют все три фамилии. Функция preg_match_all() служит для нахождения сразу всех совпадений. В зависимости от шаблона возможно несколько видов хранения результатов поиска. Во всех случаях результаты хранятся либо в двумерном либо в трёхмерном массиве. Начнём с самого простого шаблона

    3.1. В шаблоне нет подшаблонов. Результат поиска хранится в двумерном массиве, состоящем из одной строки.

    $a="Навиков Новиков Новичков";
    if(preg_match_all("/Н[ао]вич?ков/",$a,$rez))
    $i=0;
    foreach($rez as $x)
    {    print $i++.".";
         foreach($x as $y) print "$y ";
         print "<BR>";
    }
    //Напечатается "0. Навиков Новиков Новичков"
    

    3.2. В шаблоне есть несколько подшаблонов.

    $f="Начало весны 01.03, середина 15.04, конец 31.05 лето";
    $i=0;
    if(preg_match_all("/(\d\d)\.(\d\d)/",$f,$rez))
    foreach($rez as $x)
    {    print $i++.". ";
         foreach($x as $y) print "$y ";
         print "<BR>";
    }
    /*Напечатается
    0. 01.03 15.04 31.05
    1. 01 15 31
    2. 03 04 05 
    */
    

    Результаты поиска требуют пояснений. Поиск по шаблону производится три раза. В первой строке выводятся результаты совпадений со всем шаблоном во всех трёх поисках, т.е. все даты. Во второй строке выводятся результаты совпадений с первым подшаблоном , т.е. все дни, в третьей - со вторым подшаблоном , т.е. все месяцы.

    3.3. Прожорливость при многократном поиске.

    $f="Начало весны 01.03, середина 15.04, конец 31.05 лето";
    $i=0;
    if(preg_match_all("/.*(\d\d)\.(\d\d)/",$f,$rez))
    foreach($rez as $x)
    {    print $i++.". ";
         foreach($x as $y) print "$y ";
         print "<BR>";
    }
    /*Напечатается
    0. Начало весны 01.03, середина 15.04, конец 31.05
    1. 31
    2. 05 
    

    В первой строке выводится самая длинная подстрока, соответствующая всему шаблону.

    Подавим прожорливость.

    $i=0;
    if(preg_match_all("/.*?(\d\d)\.(\d\d)/",$f,$rez))
    foreach($rez as $x)
    {    print $i++.". ";
         foreach($x as $y) print "$y ";
         print "<BR>";
    }
    /*Напечатается
    0. Начало весны 01.03 , середина 15.04 , конец 31.05
    1. 01 15 31
    2. 03 04 05 
    */
    

    3.4. Изменение порядка вывода результатов поиска. Флаг PREG_SET_ORDER. В одной строке массива выводятся подстроки, соответствующие всему шаблону и подшаблонам, т.е. результаты i-го поиска (в примере i=0, 1, 2).

    $i=0;
    if(preg_match_all("/.*?(\d\d)\.(\d\d)/",$f,$rez,PREG_SET_ORDER))
    foreach($rez as $x)
    {    print $i++.". ";
         foreach($x as $y) print "$y *** ";
         print "<BR>";
    }
    /*Напечатается
    
    0. Начало весны 01.03 *** 01 *** 03 ***
    1. , середина 15.04 *** 15 *** 04 ***
    2. , конец 31.05 *** 31 *** 05 *** 
    */
    

    3.5. Вывод результатов многократного поиска со смещениями. Результаты выводятся в трёхмерный массив.

    preg_match_all("/(\d\d)\.(\d\d)/",$f,$rez,PREG_OFFSET_CAPTURE);
    $i=0;
    foreach($rez as $x)
    {    print "<<BR>";
         $j=0;
         foreach($x as $y)
         {   print '<BR>'; $k=0;
             foreach($y as $z)
            {  print "\$rez[$i][$j][$k]=$z "; 
               $k++;
            }
            $j++;
         }
         $i++;
    }
    /*Напечатается три группы строк
    
        1. Соответствие всему шаблону
    $rez[0][0][0]=Начало весны 01.03 $rez[0][0][1]=0
    $rez[0][1][0]=, середина 15.04 $rez[0][1][1]=29
    $rez[0][2][0]=, конец 31.05 $rez[0][2][1]=53
    
        2. Соответствие первому подшаблону
    $rez[1][0][0]=01 $rez[1][0][1]=24
    $rez[1][1][0]=15 $rez[1][1][1]=48
    $rez[1][2][0]=31 $rez[1][2][1]=66
        
         3.Соответствие второму подшаблону
    $rez[2][0][0]=03 $rez[2][0][1]=27
    $rez[2][1][0]=04 $rez[2][1][1]=51
    $rez[2][2][0]=05 $rez[2][2][1]=69 
    

    4. Замена

    Для замены части строки служит функция preg_replace(), имеющая следующий синтаксис

    новая строка=preg_replace("шаблон",заменяющая подстрока,
                  исходная строка,
                  [ограничение на количество замен,
                  [количество произведённых замен]])
    

    Параметры "ограничение на количество замен" и "количество произведённых замен" не обязательны. Если "ограничение на количество замен" не указано, то производятся все возможные замены.

    Пример 1. Замена делается только один раз.

    $a="Город Ленинград основан в 1703 году. Ленинград - город-герой";
    $b=preg_replace("/Ленинград/","Санкт-Петербург",$a,1);
    print "$b<BR>";       
    /*Напечатается
    Город Санкт-Петербург основан в 1703 году. Ленинград - город-герой
    */
    

    Пример 2. В массиве заменить теги <DIV> и <\div> на <H2> и <\h2> соответственно.

    $Ar[0]='<DIV style="position:relative;text-align:center;width:97.8%;
                font-size: 24px;">Грибы</div>';
    $Ar[1]='<DIV style="position:relative;text-align:center;width:70%;
                color:red;">Ягоды</div>';
    for($i=0;$i<2;$i++) 
    {	$Ar[$i]=preg_replace("/<DIV.*?>(.*)<\/div>/i","<H2>$1</h2>",$Ar[$i]);
    	print "$Ar[$i]\n";
    }
    // ****Р е з у л ь т а т ********
    <H2>Грибы</h2>
    <H2>Ягоды</h2>
    

    5. Расщепление строки по заданным разделителям. Функция preg_split()

    Рассмотрим пример строки, разделителями в которой служат точка с запятой и пробел.

    $a="Размеры 10;20;8";
    $razm=preg_split("/[; ]/",$a);
    foreach($razm as $x) print"$x<BR>";
    
    /*Напечатается
    Размеры
    10
    20
    8
    */
    

    6. Справочные материалы

    В PHP сами регулярные выражения (pattern - образец, шаблон) ничем не отличаются от регулярных выражений в Perl. Разный вид имеют функции, в которых используются регулярные выражения. В качестве примера рассмотрим поиск одного из слов: сан, сон, сын.

      Perl
      $a=~m/с[аоы]н/;
      
      PHP
      preg_match("/с[аоы]н/",$a);
    

    Так как грамматика регулярных выражений в Perl и PHP одинаковая, то cправочные материалы можно посмотреть в описании языка Perl.

    В PHP вместо модификатора "g" для многократного поиска используется функция preg_match_all().

    Лекция 9. Базы данных

    Принципы разработки реляционных баз данных

    База данных никогда не разрабатывается сама по себе. Она всегда является частью программного комплекса, например, информационной системы, построенной в виде сайта.

    Подробно базы данных описываются в находящемся на этом сайте курсе лекций Базы данных.

    Разработка информационной системы начинается с анализа предметной области, в ходе которого определяется состав информации, которую нужно будет хранить в базе данных, уточняется постановка задачи. Например, при разработке расписания вуза, расположенного в нескольких корпусах, нужно учесть время на перемещение из одного корпуса в другой.

    Следующий этап - построение модели сущность-связь, относящейся к инфологическим моделям. В модели сущность-связь описываются все данные, необходимые для функционирования информационной системы и семантические связи между ними. К сожалению, некоторые авторы включают в состав атрибутов сущности формальный идентификатор (номер экземпляра или счётчик), который саму сущность не характеризует, а нужен для удобства построения базы данных. В базе данных такой искусственный атрибут таблицы (отношения) называется суррогатным ключём.

    Пример упрощённой модели сущность-связь для расписания вуза представлен на рисунке 5. Атрибуты сущностей описаны в в табл. 1.
    Табл. 1. Описание сущностей для модели расписания
    СущностьАтрибуты
    ПреподавательФИО преподавателя, степень, должность
    ГруппаНаименование группы, староста, количество студентов
    АудиторияНомер аудитории, вместимость, корпус
    ДисциплинаНаименование дисциплины, количество часов
    День неделиДень недели
    Часы занятийНачало, конец

    Любопытно, что собственно расписание отображается в модели точкой соединения связей сущностей.

    Рис. 5. Инфологическая модель расписания

    Модель сущность-связь формальными методами легко преобразуется в реляционную, (рис. 6) являющуюся одним из видов даталогических моделей баз данных.
    Рис. 6. Даталогическая модель расписания

    Реляционная модель содержит в себе всю информацию, необходимую для создания и ведения базы данных. В ней описаны все таблицы, их наименования и связи между ними. В каждой таблице описаны столбцы (атрибуты), их типы, ограничения, значения по умолчанию. Связи между таблицами задаются первичными и внешними ключами.

    Создание базы данных под управлением СУБД MySQL

    СУБД MySQL входит в состав широко распространённых средств веб-программирования LAMP (Linux, Apache, MySQL, PHP) и ДЕНВЕР. Создать базу данных в MySQL можно двумя способами. Первый способ заключается в написании на языке SQL и выполнении операторов CREATE DATABASE и CREATE TABLE. Этот способ применяется при переносе информационной системы, в состав которой входит база данных, с одной ЭВМ на другую.

    Синтаксис оператора CREATE DATABASE в разных СУБД может отличаться, так как этот оператор не входит в стандарт SQL92.

    Синтаксис оператора CREATE DATABASE в СУБД MySQL:

    CREATE DATABASE [IF NOT EXISTS] db_name [CHARACTER SET charset] [COLLATE collation];
    

    Вторым способом база данных строится средствами оболочки СУБД, например phpMyAdmin для СУБД MySQL.

    phpMyAdmin - сайт, служащий веб-интерфейсом для администрирования базы данных.

    Как создать и заполнить таблицу программным путём можно посмотреть в примерах 14.1 и 14.2.

    После создания базы данных и таблиц приступают к вводу в таблицы данных Первоначально нужно заполнять те таблицы, которые соответствуют сильным сущностям. Например, сначала заполняются данные о факультетах, затем о кафедрах, затем о группах. Для заполнения таблиц нужно создать экранный формы и скрипты. Наиболее трудные задачи - составление с помощью скрипта оператора языка SQL, соответствующего запросу пользователя. Обычно приходится генерировать оператор SELECT для выборки из нескольких таблиц. Составим запрос на выдачу расписания группы, воспользовавшись базой данных, даталогическая модель которой изображена на рис. 6.

    На практике удобно составлять запрос в следующем порядке.

    1. Выпишем предложения оператора SELECT и названия таблиц с псевдонимами

    SELECT
    FROM prepod a, gruppa b, discip c, aud d, den_ned e, para f, raspisan g
    WHERE
    ORDER BY
    

    2. Перечислим выводимые поля, сформулируем условия отбора строк и порядок их выдачи

    SELECT e.den,f.nach,f.konec,c.naim,a.prepod
    FROM prepod a, gruppa b, discip c, aud d, den_ned e, para f, raspisan g
    WHERE a.kod_prep = g.kod_prep 
      AND b.kod_gr = g.kod_gr
      AND c.kod_disc = g.kod_disc
      AND d.kod_aud = g.kod_aud
      AND e.kod_d_n = g.kod_d_n
      AND f.kod_par = g.kod_par
         AND b.Ngr = 'И966'
    ORDER BY e.den,f.nach
    

    Наиболее сложно составить предложение WHERE, в котором задаются условия выборки. В примере заданы шесть условий связи между таблицами. В общем случае количество связей на единицу меньше количества таблиц. Ещё одно условие задаёт отбор строк расписания, относящихся к группе И966. В скрипте обычно номер группы задаётся пользователем и хранится в переменной.

    Порядок работы с СУБД MySQL на языке PHP

    В PHP есть около пятидесяти функций для работы с СУБД MYSOL. На практике чаще всего используются следующие функции.

    Опишем синтаксис этих функций.

    Открыть соединение с сервером MySQL

    указатель1 = mysql_connect ( server, username, password)
    

    указатель1 - имя соединения с СУБД MySQL.
    Пример

    $link = mysql_connect("localhost", "mysql_user", "mysql_password")
            or die("Could not connect: " . mysql_error());
    print ("Connected successfully");
    mysql_close($link);
    

    Выбрать базу данных MySQL

    bool mysql_select_db (database_name [, указатель1] )
    

    Пример

    $lnk = mysql_connect('localhost', 'mysql_user', 'mysql_password')
           or die ('Not connected : ' . mysql_error());
    
    // сделать alfa текущей базой данных
    mysql_select_db('alfa', $lnk) or die ('Can\'t use alfa : ' . mysql_error());
    
    /* string mysql_error ( [resource link_identifier] )
    
    Возвращает строку, содержащую текст ошибки выполнения последней 
    функции MySQL, или '' (пустая строка) если операция выполнена успешно.
    */
    

    Послать SQL-запрос

    [указатель2 =] mysql_query(SQL-запрос)
    

    Только для запросов SELECT, SHOW, EXPLAIN, DESCRIBE функция mysql_query() возвращает указатель2 на результат запроса, или FALSE если запрос не был выполнен. В остальных случаях (INSERT, UPDATE, DELETE, DROP, и т.п.) mysql_query() возвращает TRUE в случае успешного запроса и FALSE в случае ошибки. Значение не равное FALSE говорит о том, что запрос был выполнен успешно. Он не говорит о количестве затронутых или возвращённых строк. Вполне возможна ситуация, когда успешный запрос не затронет ни одной строки.

    Пример

    $query="SELECT ids,passport FROM students WHERE fio='$fio'";
    if($pas){$query.=" AND passport='$pas'";}
    $result=mysql_query($query) 
    $A=mysql_fetch_row($result)
    

    Получить строку результата запроса в виде неассоциативного массива

    array mysql_fetch_row ( resource result )
     mysql_fetch_row() обрабатывает одну строку результата, на который 
     ссылается переданный указатель. Строка возвращается в массиве. 
     Элементами массива являются поля списка вывода запроса. Массив 
     начинается с индекса 0.
    
    Последующие вызовы функции mysql_fetch_row() вернут следующие строки
    или FALSE, если строк не осталось. 
    

    Получить количество строк результата запроса

    int mysql_num_rows ( resource result )
    
    mysql_num_rows() возвращает количество строк результата запроса. Эта 
    команда работает только с запросами SELECT. Чтобы получить количество 
    строк, обработанных функцями INSERT, UPDATE, DELETE, используйте 
    функцию mysql_affected_rows().
    

    Cообщение об ошибки в последней операции с MySQL

     string mysql_error ( [resource link_identifier] )
    
    Возвращает строку, содержащую текст ошибки выполнения последней функции 
    MySQL, или '' (пустая строка) если операция выполнена успешно.
    

    Последовательность применения описанных функций.

    Перечисленная последовательность применена в примере 14.3.

    Кодировки текстовой информации

    Самый простой способ избавиться от появления в окне браузера "кряказябр" вместо русских букв - указать во всех настройках базы данных и скрипта одну и ту же кодировку. В MySQL для настройки кодировок применяются специальные операторы и переменные. Конкретная настройка показана в примере 11.

    Транзакции

    Транзакцией называется состоящая из нескольких операций процедура, результаты которой фиксируются в базе данных, если все операции были выполнены правильно, или отменяются, если произошла хотя бы одна ошибка.

    Пример транзакции в СУБД Transact SQL Server.

    В MySQL транзакции поддерживаются только таблицами innoDB. Таблицы MyISAM транзакции не поддерживают. В innoDB по умолчанию включен AUTOCOMMIT, это значит, что по умолчанию каждый запрос эквивалентен одной транзакции.

    В PHP транзакция начинается со специального запроса «START TRANSACTION», либо «BEGIN». Чтобы закончить транзакцию, нужно либо зафиксировать изменения (запрос COMMIT), либо произвести откат (запрос ROLLBACK), восстановив состояние, бывшее до выполнения транзакции.

    Пример транзакции в СУБД MySQL

     . . . . . 
    //УСТАНОВКА РЕЖИМА ТРАНЗАКЦИЙ
      $query='SET AUTOCOMMIT=0';
      $result=mysql_query($query); 
      if(!$result)
      { print'Ошибка AUTOCOMMIT: ' . mysql_error($link);
    	exit;
      }	
    //********* Начало транзакции ***********
      @mysql_query("BEGIN");	
      $query="DELETE FROM gosObZak WHERE idZavod=$id";
      . . . . . 
      //Операторы Insert, UPDATE, DELETE
     . . . . . 
    //*** КОНЕЦ ТРАНЗАКЦИИ ****
      if($osh)
      { print "Ошибка. Результаты транзакции отменены. Данные НЕ добавлены<BR> $osh";
        @mysql_query("ROLLBACK");
      }
      else
      {	print "<P align=center><FONT size=+2>Название предприятия удалено</b></font></p>";
    	@mysql_query("COMMIT");
      }
    

    InnoDB — одна из выбираемых подсистем низкого уровня в СУБД MySQL, входит во все стандартные сборки для различных операционных систем. Основным отличием InnoDB от других подсистем низкого уровня MySQL является наличие механизма транзакций и внешних ключей.

    Импорт данных

    Данные в базу импортируются либо из другой базы данных в формате SQL, либо из текстового файла в формате CSV.

    Импорт из другой базы производится в два этапа.

    1. Данные экспортируются в формате SQL из исходной базы данных (в phpMyAdmin пункт меню Экспорт, формат SQL ). Результаты экспорта выводятся в текстовый файл в виде последовательности операторов CREATE TABLE и INSERT ( пример таких операторов рассмотрен выше.
    2. Подготовленный файл импортируется в новую базу данных (в phpMyAdmin пункт меню Импорт, формат SQL).

    Для импорта из текстового файла импортируемые данные подготавливаются в формате CSV.

    CSV (Comma-Separated Values — значения, разделённые запятыми) — текстовый формат, предназначенный для представления табличных данных.

    Каждая строка файла — это одна строка таблицы.
    Разделителем (англ. delimiter) значений колонок является символ запятой (,). Однако на практике часто используются другие разделители, то есть, формат CSV путают с DSVruen и TSV
    Значения, содержащие зарезервированные символы (двойная кавычка, запятая, точка с запятой, новая строка) обрамляются двойными кавычками ("). Если в значении встречаются кавычки — они представляются в файле в виде двух кавычек подряд.

    Большинство программ (в том числе, phpMyAdmin) понимают под CSV более общий формат DSVruen ( delimiter-separated values — значения разделённые разделителем), допускающий использование иных символов в качестве разделителя. В частности, в русской и других локалях запятая по умолчанию зарезервирована под десятичный разделитель. Поэтому как разделитель используется точка с запятой или табуляция (формат TSV).

    Несмотря на наличие RFC, на сегодняшний день под CSV, как правило, понимают набор значений, разделенных какими угодно разделителями, в какой угодно кодировке с какими угодно окончаниями строк и множеством различных способов экранирования спецсимволов. Это значительно затрудняет перенос данных из одних программ в другие, несмотря на всю простоту реализации поддержки CSV.

    Подготовленный файл импортируется в новую базу данных (в phpMyAdmin пункт меню Импорт, формат CSV).

    Пример файла CSV.