All about PHP

The very basics. How PHP works.
Sessions. A detailed description of the work and explanation of the mechanism.
I want to learn PHP and Mysql. Where to begin?
\ "Quotes \". Creating queries, slashes, SQL Injection
Regular expressions.
Documentation on PHP. Manual on PHP, books.
Nothing works! What to do??? Search for errors and debugging.
The solution to the problem "Can not add header information - headers already sent"
Variables are not passed! The Undefined variable problem
The difference between absolute and relative paths. In the file system and on the site.
How to break output from mysql page by page
Identifying the IP address
Problems with the encoding in MySQL versions 4.1+
Example code running with MySQL
How to write music in PHP
Example of a site management system
Mail injection. Working with e-mail means PHP.
PHP Script Security
Basic concepts of MySQL and differences from text files.
Drawing a calendar
Error handling, Part 1. General principles.
Error handling, Part 2. Example parsing. Exceptions.
What is PHP?
How to make UPLOAD files on a server
How to make a small copy of the picture?
Templates


The very basics. How PHP works.

The difference between a web application and a regular program
How does PHP work, where is it running?
How to pass a variable from PHP to JavaScript and vice versa?
Ways of communicating the browser with the server.
View HTTP headers exchange
VERY IMPORTANT NOTE
Comments

The difference between a web application and a regular program
Starting to write programs for the web, many novice programmers encounter such a mistake. They view the browser-server system as a normal application. Interactive. I pressed the button - the system reacted. He waved his mouse - reacted. All the information that is available to the client is available to the program, the program is always in memory.
So, in web programming this is not so! .
At the moment when the user sees a page in front of him and begins to perform some actions with it, PHP has already completed the work ! And the user interacts not with the PHP script, but with his HTML page, which he received in the browser. The result of running the script in PHP in most cases is plain text. Text of the HTML page. Which is given to the browser and shown to them as normal HTML. You can see for yourself by writing in the script
<? echo "Привет, <b>Вася!</b>" ; ?> ;
And then viewing in the browser the source text of the received page. There are no PHP tags there! Only
Привет, <b>Вася!</b>
Because PHP is running on the server!


The server and the browser communicate by sending each other requests for a specific protocol - HTTP . The connection can only be initiated by the browser. It sends a request to the server to show such a file. The server sends the file to the client.
Only this way it happens. The client requested - the server gave. And I forgot about the client right away. Hence it becomes clear the answer to the question whether it is possible to know exactly how many users are on the site. You can not. Because "on the site" there is not one. They connect, request a page, and disconnect. Do not have a permanent connection to the server, as, for example, players in Kwaku. You can only learn about it by recording the time of each connection and selecting records for a certain period of time.

Also, from here it becomes clear that the server can learn about the client very little. Only what the client sends in the HTTP request. Screen resolutions are not there ;-)
All that the server can know about the client, you can see the command phpinfo()

An example of communication between the browser and the server:
The user clicks on the link, the browser sends a request to the server and waits for a response:
Browser -> PHP

PHP executes the script, returns the result to the browser and exits:
PHP -> browser

The browser displays the page, "browsing" it for links that need to be requested from the server (tags <img src>, <script src> and so on) and sends relevant requests. They can be seen by looking through the exchange of headings, which will be discussed below:
Browser -> Server, Browser -> Server, Browser -> Server ...

The user fills the form and clicks on the button:
Browser -> PHP

PHP processes the form, writes data to the database and sends the header to the browser
Location:
PHP -> browser

The browser, having received this title, requests the specified page
Browser -> PHP

PHP executes it ... and so on.

How does PHP work, where is it running?
PHP is running on the server. The browser sends a request to the server for the page with the php code. The server renders this page for execution to the PHP interpreter, the interpreter generates the HTML code, gives it to the server, and sends the server to the client. No PHP code gets to the browser (this is important! This means that you can not see the source code of the PHP script!). The only way to send something to the script is to click on the link or click on the button in the form. So that PHP handles some user actions in the browser - it's impossible. PHP remained on the server, waiting for new requests with data to process. PHP, but not a script! The script that was executed, giving the user a page, completed the work. All the data that was in it - disappeared. That's why, if some variable is needed for subsequent calls to the script, it must be transferred to this script again.

A very high-quality and detailed article on the basics of web programming is on the site PHPWIKI.RU. Make sure to read it.

How to pass a variable from PHP to JavaScript and vice versa?
To begin with, of course, no variable can be transmitted, of course, impossible. Because the variable is part of the program. And from one to another to pass it on. You can pass only the value of a variable. Those. text. That is, the difference between "passing a variable to javascript" and the formation of an html table is NO!
Hence the conclusion - "Transfer variable" in Javascript is very easy. Especially, I repeat, if we take into account that there is no "transfer". PHP simply generates JavaScript in the same way as the rest of the page, along with all the variables.
Just like you display the string "Hello World, this is Vasya Pupkin!" In the browser, any JavaScript is output, with all its variables.
The only condition is that you have to imagine the javascript you want to receive.
For example, in PHP there is a variable $name = "Вася" $name = "Вася" , The value of which must be passed to javascript in order to get
<script>name="Вася";</script>
We just write
<?
$name
= "Вася" ;
?>
<script>name=" <? echo $name ; ?> "</script>

That is, in fact, we just formed our PHP script some text that looks like the code we need on javascript. Or, on the other hand, we wrote our javascript, inserting the output of variables from PHP in the right places.
In order not to go crazy with various quotes, it is strongly recommended that you do not print all echo with echo, as it is written here - by closing the PHP tag and opening them only where you need to display the variable.

How to pass a variable from JavaScript to PHP?
Just like any other data - sending a request to the server.
But we must clearly understand that during the execution of the php script to get something from javascript, of course, it is impossible. You can only send at the next request. And it will be processed by another PHP script.
If you need to update the onClick event to a database, you should remember that it is on the server. That is, it is necessary to request a server that will launch a PHP script that will access the database, receive an answer from it and pass it to the browser.

All of the above does not contradict, of course, the fashionable technology of asynchronous communication between the browser and the server. All methods are the same, just the browser does not communicate with the server, but the program on javascript.
More details can be read on the resources dedicated to the Javascript. From a PHP point of view, AJAX technology requests are no different from the usual ones.

Ways of communicating the browser with the server.
The methods provided by the HTTP protocol are few. This is important information. There are no other ways. In practice, two are used:
GET is when data is transferred in the address bar, for example, when the user presses a link.
POST - when he presses a button on the form.
Formed a page with a link or a form using the GET method - the request will come from the GET. Formed with the form in which the POST method is specified - POST will come.
Determine which way to apply is very simple. If the form serves for requesting some information, for example - when searching, then it should be sent using the GET method. To be able to update the page, you could put a bookmark and or send a link to a friend.
If, as a result of submitting the form, the data is written or changed on the server, then they should be sent using the POST method, and it is necessary to redirect the browser with the GET method after processing the form. Also, POST may be needed if you need to transfer a large amount of data to the server (it has a very limited amount of GET), as well as if you do not "shine" the transmitted data in the address bar (when entering a login and password, for example). But in any case, after processing POST, you should always redirect the browser to some page, even the same one, but without the data of the form, so that when you update the page, they are not re-recorded. For example:
header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'REQUEST_URI' ]);
exit;


The most important thing to remember is that the server can not turn to the client on its own initiative. We can only issue a request to the browser after a request - either a page or a command to request a different resource.

Useful information can be contained in various HTTP headers.
Cookie - if the server put the cookie, and it is not obsolete, then the browser sends it along with each request.
HTTP authentication - if the server requested HTTP authorization, then the browser sends the login and password entered every time it is accessed.

PHP can send HTTP headers with two commands - header() and setcookie() .

View HTTP headers exchange
I highly recommend practicing with HTTP headers, see how they are exchanged between the server and the client.
There are many different ways for this. If you have a popular download manager FlashGet, then you can use it. The same headlines are shown by the popular Proxomitron program, you can download any special utilities.
For IE, you can offer a plugin http://blunck.se/iehttpheaders/iehttpheaders.html
For Mozilla browser there is a convenient plugin http://livehttpheaders.mozdev.org/
Also, there are many other utilities easily found on the network by the HTTP sniffer request.
Be sure to use any way to see the HTTP headers exchanged between the browser and the server. This is a very good practice, as well as checking - that sends your script. It is convenient when debugging the installation of cookies or problems with sessions.
A rough idea of ​​the incoming headers can also be obtained by using the getallheaders() function. But keep in mind that it only works if PHP is built as a module.

VERY IMPORTANT NOTE
From the fact that PHP runs on the server, and sends the result of its work to the browser, one simple but very important conclusion follows. That PHP in principle can not display anything in the browser that it would not be possible to do with the means of html.
BEFORE writing something on PHP - try to do it clean HTML.
"Pressing Enter" does not translate the string? And in html you did not try to translate this line in this way? Did not work out? What a shame. Read how to make a translation of a line in html and come again.

PHP as a result of its work does not form a picture with texts, as you see it on the screen of the monitor! PHP forms HTML code! And this code differs significantly from the image that you see on the screen. If you do not get something, you should always look at the source code of the page, and not how the browser draws it. In Internet Explorer, you can view the source code by selecting View - View HTML from the View menu.
If your JavaScript does not work, generated by a PHP script, or html does not show what you want, then it's very easy to fix this problem.
1. First, write the desired javascript or html hands. If you have this problem - contact the appropriate forum - by javascript or html. PHP has nothing to do with it.
2. Compare with what is derived from PHP
3. Make corrections in the PHP script so that the text given to them does not differ from that written by the hands.

The browser does not know how to display files, which are html images simultaneously. The browser knows how to display only the types of data that are known to it. In particular, this is an OR html OR picture. But not together. If the picture is ONE. A few images in a row the browser can not show. The browser is able to display HTML, in which there are LINKS to several pictures.
Please, before learning PHP - learn at least the basics of HTML! Before you need anything from PHP, try this on html.



To the top of the section


Sessions. A detailed description of the work and explanation of the mechanism.

Introduction
How are the sessions arranged and how are they working?
Application area.
Possible problems and their elimination.
Security
Additional Information:
Example of authorization using sessions
OPS! Very Useful Links:
Comments

Introduction
Sessions - it's actually very simple.
You just need to understand what they are for and how they are arranged.
First we answer the first question.
As shown in the corresponding section of this FAQ , the web server does not support permanent connection with the client, and each request is treated as new, without any connection with the previous ones.
That is, you can not track requests from the same visitor, nor save variables for it between views of individual pages. To solve these two problems, sessions were invented.
Actually, the session, in a nutshell - is a mechanism that allows you to uniquely identify the browser and create a file for this browser on the server in which session variables are stored.

I will not detail the need for such a mechanism. This is such a textbook case, like shopping cart in the e-store, authorization, as well as not entirely trivial problems, such as protecting the interactive parts of the site from spam.

In principle, it's fairly easy to make your own analog of sessions, not as functional as the built-in PHP, but similar in nature. On the cookies and the database.
When requesting the script, see if the cook came with a specific name. If there is no cookie, then put it and write to the database a new line with the user's data. If there is a cook, then we read the data from the database. Another request removes old records from the database and we have a mechanism for sessions. Quite simply. But there are some nuances that make it preferable to use the built-in session mechanism.

How are the sessions arranged and how are they working?
First you need to somehow identify the browser. To do this, you must give him a unique identifier and ask him to pass it on with every request. It's a shame to admit, but when I first learned about the sessions, I thought it was some kind of special mechanism, some new way of communicating the browser with the server - "sessions". That the session identifier is passed in some special way. Disappointment was cruel.
Sessions use standard, well-known methods of data transmission. Actually, others simply do not.
An identifier is an ordinary variable. By default, its name is PHPSESSID.
The task of PHP is to send it to the browser to return it with the next request. From the already mentioned FAQ section it is clear that the variable can be transferred only in two ways: in cookies or POST / GET request.
PHP uses both.
For this, there are two settings in php.ini:
session.use_cookies - if it is 1, PHP passes the identifier in the cookie, if 0 is not.
session.use_trans_sid if it is 1, then PHP passes it by adding to the URL and form, if 0 is not.
You can change these and other session settings the same way as other PHP settings - in the php.ini file, as well as with the ini_set() command or in the web server configuration files

If only the first is enabled, then at the start of the session (each time session_start () session_start () ) The client is set to a cookie. The browser regularly returns this cookie at each next request and PHP has a session ID. Problems start if the browser does not return a cookie. In this case, without getting the cookie with the identifier, PHP will always start a new session, and the mechanism will not work.

If only the second is on, the cookie is not set. And what happens for what, basically, actually, and it's worth using the built-in mechanism of sessions. After the script does its job and the page is fully formed, PHP looks at it and appends the session ID to each link and to each form. It looks something like this:
<a href="/index.php">Index</a> turns into
<a href="/index.php?PHPSESSID=9ebca8bd62c830d3e79272b4f585ff8f">Index</a>
And a latent field is added to the forms
<input type="hidden" name="PHPSESSID" value="00196c1c1a02e4c37ac04f921f4a5eec" />
And the browser when clicking on any link, or when you click on the button in the form, will send the requested variable to the request - the session ID!
For obvious reasons, the identifier is added only to relative links.

Theoretically, in our self-made sessions on the cookies and the database, you can manually assign all the links to all the links with the id - and then our own sessions will work regardless of the cook. But, you will agree - it is more pleasant when someone else does this work? ;-)

By default, the latest versions of PHP include both options. How does PHP come in this case? Cook always shows. A link is auto-complete only if PHP does not detect the cookie with the session ID. When a user visits the site several times during this session, he is given a cookie and links are added. On the next request, if the cookie is supported, PHP sees the cookie and stops adding links. If the cookie does not work, PHP continues to regularly add the id to the links, and the session is not lost.
Users who work with cookies will see a long link with id once.

Fuh. With the transfer of the identifier finished.
Now it remains to bind to it a file with data on the server side.
PHP will do it for us. It is enough simply to write
session_start ();
$_SESSION [ 'test' ]= 'Hello world!' ;

And PHP will write the variable test into the file associated with this session.
Here is a very important point.
The $_SESSION array is special.
In it, in fact, there are variables that we go to make available in various scripts.
To put a variable in a session, just assign it to the $ _SESSION array element.
To get its value - just go to the same element. The example will be a little lower.

Collection of garbage - deleting obsolete files PHP is also engaged in itself. As well as coding data and a bunch of other necessary things. As a result of this care, working with sessions is very simple.
So, we, actually, approached the example of the work of the sessions.
The example is very small:
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз. " ;
echo
"<br><a href=" . $_SERVER [ 'PHP_SELF' ]. ">обновить" ;
?>

We check if we have a counter variable in the session, if not, then create it with a value of 0, and then output its value and increase it by one. The increased value will be written to the session, and the next time the script is called, the variable will have a value of 1, and so on.
Everything is very simple.

In order to access the session variables on any pages of the site, you must write ONLY (!) A line at the very beginning of EACH file, in which we need the session:
session_start ();
And then refer to the elements of the array $ _SESSION. For example, the authorization check will look something like this:
session_start ();
if (
$_SESSION [ 'authorized' ]<> 1 ) {
header ( "Location: /auth.php" );
exit;
}


Удаление переменных из сессии.
Если у вас register_globals=off , то достаточно написать
unset( $_SESSION [ 'var' ]);
Если же нет, то тогда рядом с ней надо написать
session_unregister ( 'var' );

Область применения.
Очень важно понимать, для чего сессии стоит использовать, а для чего - нет.

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

Secondly. Важно четко себе представлять тот факт, что сессия - это сеанс работы с сайтом, так как его понимает человек. Пришел, поработал, закрыл браузер - сессия завершилась. Как сеанс в кино. Хочешь посмотреть еще один – покупай новый билет. Стартуй новый сеанс. Этому есть и техническое объяснение. Гарантированно механизм сессий работает только именно до закрытия браузера. Ведь у клиента могут не работать куки, а в этом случае, естественно, все дополненные идентификатором ссылки пропадут с его закрытием.
Правда, сессия может пропасть и без закрытия браузера. В силу ограничений, рассмотренных в самом главном разделе этого FAQ, механизм сессий не может определить тот момент, когда пользователь закрыл браузер. Для этого используется таймаут – заранее определенное время, по истечении которого мы считаем, что пользователь ушел с сайта. По умолчанию этот параметр равен 24 минутам.
Если вы хотите сохранять пользовательскую информацию на более длительный срок, то используйте куки и, если надо - базу данных на сервере. В частности, именно так работают все популярные системы авторизации:
- по факту идентификации пользователя стартует сессия и признак авторизованности передается в ней.
- Если надо "запомнить" пользователя, то ему ставится кука, его идентифицирующая.
- При следующем заходе пользователя на сайт, для того, чтобы авторизоваться, он должен либо ввести пароль, либо система сама его опознает по поставленной ранее куке, и стартует сессию. Новую сессию, а не продолжая старую.

В-третьих, не стоит стартовать сессии без разбору, каждому входящему на сайт. Это создаст совершенно лишнюю нагрузку. Не используйте сессии по пустякам – к примеру, в счетчиках. То, что спайлог называет сессиями, считается, конечно же, на основе статистики заходов, а не с помощью механизма сессий, аналогичного пхп-шному.
К тому же, возьмем поисковик, который индексирует ваш сайт. Если поисковый робот не поддерживает куки, то пхп по умолчанию будет поставлять к ссылкам PHPSESSID, что - согласистесь - может не сильно понравится поисковику, который, по слухам, и так-то динамические ссылки не жалует, а тут вообще при каждом заходе - новый адрес!
Если сессии используются для ограничения доступа к закрытому разделу сайта, то все просто поисковик и не должен его индексировать.
Если же приходится показывать одну и ту же страницу как авторизованным, так и не авторизованным пользователям, то тут поможет такой трюк – стартовать сессию только тем, кто ввел пароль, или тем, у кого уже стартовала сессия.
Для этого в начало каждой страницы вместо просто session_start () пишем
if (isset( $_REQUEST [ session_name ()])) session_start ();
таким образом, Мы стартуем сессию только тем, кто прислал идентификатор.
Соответственно, надо еще в первый раз отправить его пользователю – в момент авторизации.
Если имя и проль верные – пишем session_start () !!

Возможные проблемы и их устранение.

Самыми распространенными ошибками, которые выдает РНР при попытке работать с сессиями, являются такие:
Две из них,
Warning: Cannot send session cookie - headers already sent
Warning: Cannot send session cache limiter - headers already sent

вызваны одной и той же причиной, решение описано в этом факе здесь
Третья,
Warning: open(/tmp\sess_SID, O_RDWR) failed: No such file or directory (2) in full_script_path on line number (ранее она выглядела, как Warning: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/tmp) ),
если перевести ее с английского, подробно объясняет проблему: недоступен указанный в php.ini путь к каталогу, в который пишутся файлы сессий. Эту ошибку исправить проще всего. Просто прописать каталог, который существует, и доступен на запись, например,
session.save_path = c:\windows\temp
И не забыть перезагрузить апач после этого.

Как выясняется, сообразительность людская не имеет пределов, и поэтому я вынужден пояснить:
сообщение о третьей ошибке (невозможно найти каталог) НЕИЗБЕЖНО приведет к появлению первых двух, поскольку сообщение об ошибке - это вывод в браузер и после него заголовками пользоваться нельзя. Поэтому не спешите искать преждевременный вывод, а сначала пропишите правильный путь!


Следующей по распространенности проблемой при работе с сессиями является тяжелое наследие register_globals. НЕ давайте переменным скрипта имена, совпадающие с индексами массива $_SESSION!
При register_globals=on значения будут перезаписывать друг друга, и вы запутаетесь.
А при register_globals=off появится другая ошибка: "Your script possibly relies on a session side-effect which existed until PHP 4.2.3.", в случае, если в скрипте есть переменная сессии не имеющая значения, и глобальная переменная с тем же именем. Чтобы от неё избавиться, надо всегда инициализировать переменные перед использованием (или хотя бы проверять на существование) и не давать глобальным переменным имена, совпадающие с индексами массива $_SESSION.

Если не работает, но и никаких сообщений не выводится, то добавьте в самое начало скрипта две строчки, отвечающие за вывод ВСЕХ ошибок на экран - вполне возможно, что ошибки есть, но вы их просто не видите.
ini_set ( 'display_errors' , 1 );
error_reporting ( E_ALL );

или смотрите ошибки в error_log. Вообще, тема отображения сообщений об ошибках выходит за рамки данной статьи, поэтому просто убедитесь хотя бы, что вы можете их видеть. Чуть продробнее о поиске ошибок можно прочитать в <?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз.<br>
<a href="
. $_SERVER [ 'PHP_SELF' ]. '?' . session_name (). '=' . session_id (). ">обновить</a>" ;
?>

При этом следует убедиться, что не включена директива session.use_only_cookies , которая запрещает PHP принимать идентификатор сессии, если он был передан через URL

Если этот пример не заработает, то проблема либо в банальных опечатках (половина "проблем" с сессиями происходит от неправильно написанного имени переменной), либо в слишком старой версии PHP: поддержка сессий появилась в версии 4.0, а массив $_SESSION - в 4.1 (До этого использовался $HTTP_SESSION_VARS ).
Если же заработает - то проблема в куках. Отслеживайте - что за куку ставит сервер браузеру, возвращает ли браузер ее. Искать очень полезно, просматривая просматривая обмен HTTP-заголовками между браузером и сервером .
Объяснение принципа работы кук выходит за рамки этого и так уж слишком большого текста, но хотя бы убедитесь, что сервер куку с идентификатором посылает, а браузер - возвращает. И при этом идентификаторы совпадают друг с другом =)
Установка куки должна выглядеть, как
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;
или как
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; path=/
(если вы запрашиваете скрипт не из корневого каталога)
Ответ сервера должен выглядеть, как
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6
or
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b
если браузер возвращает другие куки, кроме идентификатора сессии.

Если браузер куки не возвращает - проверьте, работают ли куки вообще.
Убедитесь, что домен, к которому вы обращаетесь, имеет нормальное имя (в котором есть хотя бы одна точка и не содержится запрещенных символов, например подчеркивания) и почистите кэш браузера - это две основные причины, по которм куки могут не работать.

Если пример отсюда работает, а ваш собственный код - нет, то проблема, очевидно, не в сессиях, а в алгоритме. Ищите, где потеряли переменную, по шагам переносите пример отсюда, <a href=> , но не делает этого для header-ов, яваскрипта, мета-тегов.
Поэтому надо добавлять идентификатор руками, например, так:
header ( "Location: /script.php?" . session_name (). '=' . session_id ());

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

Так же, весьма редкая, и совершенно непонятно, откуда появляющаяся, проблема бывает в том, что настройка session.save_handler имеет значение, отличное от files. Если это не так - исправляйте.

Security
Безопасность сессий - тема обширная. Поэтому остановлюсь на нескольких основных моментах.
Самый хрестоматийный - не передавать идентификатор через адресную строку. Об этом написано даже в php.ini, но это ограничивает функциональность сессий. Если вы решите последовать этому совету, то кроме session.use_trans_sid = 0 не забудьте session.use_only_cookies = 1
Желательно привязывать сессию к IP адресу: таким образом, если идентификатор будет украден, то злодей все равно не сможет им воспользоваться в большинстве случаев.
Рекомендуется пользоваться директивой session.save_path, с помощью которой задать собственный каталог для сохранения файлов сессий. Это более безопасно, чем когда они хранятся в общем временном каталоге сервера по умолчанию.

Дополнительная информация:
  • Кроме кук, механизм сессий посылает еще и заголовки, запрещающие кэширование страниц (тот самый cache limiter). Для html это правильно и необходимо. Но вот когда вы пытаетесь скриптом, проверяющим авторизацию, отдать файл, то интернет эксплорер отказывается его скачивать. Именно из-за этого заголовка. Call
    session_cache_limiter ( "private" );
    перед стартом сессии должен решить проблему.
  • Как это ни кажется странным, но в массиве $_SESSION нельзя использовать числовые индексы - $_SESSION [ 1 ], $_SESSION [ '10' ] - cессии работать не будут.
  • Где-то между версиями 4.2 и 5.0 невозможно было установить session.use_trans_sid с помощью ini_set () . Начиная с 5.0 уже можно снова.
  • До версии 4.3.3 куку PHP отправлял куку только если при старте сессии в запросе отсутстввал идентификатор. Теперь же кука посылается при каждом вызове session_start ()

    Пример авторизации с помощью сессий
    Проиллюстрируем все вышенаписанное небольшим примером:
    создадим файл auth.php:
    <?
    if (isset( $_POST [ 'auth_name' ])) {
    $name = mysql_real_escape_string ( $_POST [ 'auth_name' ]);
    $pass = mysql_real_escape_string ( $_POST [ 'auth_pass' ]);
    $query = "SELECT * FROM users WHERE name='$name' AND pass='$pass'" ;
    $res = mysql_query ( $query ) or trigger_error ( mysql_error (). $query );
    if (
    $row = mysql_fetch_assoc ( $res )) {
    session_start ();
    $_SESSION [ 'user_id' ] = $row [ 'id' ];
    $_SESSION [ 'ip' ] = $_SERVER [ 'REMOTE_ADDR' ];
    }
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'REQUEST_URI' ]);
    exit;
    }
    if (isset(
    $_GET [ 'action' ]) AND $_GET [ 'action' ]== "logout" ) {
    session_start ();
    session_destroy ();
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. "/" );
    exit;
    }
    if (isset(
    $_REQUEST [ session_name ()])) session_start ();
    if (isset(
    $_SESSION [ 'user_id' ]) AND $_SESSION [ 'ip' ] == $_SERVER [ 'REMOTE_ADDR' ]) return;
    else {
    ?>
    <form method="POST">
    <input type="text" name="auth_name"><br>
    <input type="password" name="auth_pass"><br>
    <input type="submit"><br>
    </form>
    <?
    }
    exit;
    ?>


    теперь достаточно написать во всех защищаемых скриптах строчку
    require "auth.php";

    ОПС! Очень Полезные Ссылки:
    http://www.php.net/manual/ru/ref.session.php - самая последняя и свежая информация о поддержке сессий в PHP в официальной документации, плюс многочисленные комментарии пользователей. Настоятельно рекомендуется к прочтению.
    http://phpclub.ru/manrus/f/ref.session.html - ВЕСЬМА устаревший перевод этой главы на русский, из документации в переводе Александра Пирамидина.
    http://phpclub.ru/detail/article/sessions
    Статья с пафосным названием "Правда о сессиях". Двойственное впечатление оставляет. Вначале автор ОЧЕНЬ доступно рассказывает о механизме сессий, но методы, которые он предлагает к концу статьи - совершенно мутные.

    Хрестоматийная статья Дмитрия Бородина с сайта
    http://php.spb.ru/ настоятельно НЕ рекомендуется.
    Ребята, она страшно устарела. Мало того, что в ней есть фактические неточности, так с сессиями в PHP уже давно просто не работают.
    Огромное Диме спасибо за нее, это была первая статья по сессиям на русском языке, я сам по ней учился, но сейчас надо ее отправить на заслуженный отдых.
    Так же, устарели к сожалению, и многие другие статьи, лежащие в интернете и не обновлявшиеся годами.


    В начало раздела Наверх


    I want to learn PHP and Mysql. Where to begin?

    Информация для начинающих изучать PHP и MySQL
    Программное обеспечение.
    Форумы.
    Comments

    Информация для начинающих изучать PHP и MySQL
    Для тех, кто хочет изучать PHP, можно посоветовать великолепный " Самоучитель PHP " с сайта PHP5.RU
    Курс находится в процессе написания, но уже сейчас ссылки на отдельные уроки из него стоят в различных разделах этого FAQ. И, поверьте - оно стоит того.
    Не могу не порекомендовать замечательный материал Вадима Ткаченко АКА Bizon-а "Вступление в PHP и MySQL" . Он даже издавался отдельной книгой, а сейчас - исправленный и дополненный - размещается на сайте
    " PHP в деталях ". Этот ресурс стоит особняком. В отличие от предыдущих, рекомендовать прочесть его целиком может только садист - там слишком много информации. но в этом и его прелесть. Это неисчерпаемый ресурс информации по PHP. Единственное замечание - обращайте внимание на дату написания статьи. Не стоит особо доверять тем, что написаны до 2003 года.
    Ну, и, конечно же - этот сайт, http://phpfaq.ru
    Если вы еще не прочли его целиком - обязательно сделайте это. Здесь перечислены проблемы, с которыми рано или поздно столкнется КАЖДЫЙ, кто пишет на PHP.
    Неплохой курс изучения MySQL:
    http://www.intuit.ru/department/database/mysql/

    Программное обеспечение.
    Для работы с РНР под Windows, надо установить следующие программы:
    - web-сервер Apache (5Mb)
    - сам PHP (10Mb)
    - по желанию - MySQL (23Mb).
    The setting is very simple. Апач устанавливается программой установки. Там, где он запрашивает имя вашего сервера и емейл администратора, надо 2 раза написать localhost и свой e-mail.
    PHP распаковывается из зипа в любой каталог по желанию (стандартно - C:\PHP) и настраивается обязательно как модуль Апача. Для этого надо выполнить три действия:
    - переписать файл php5ts.dll в каталог WINDOWS
    - в файл httpd.conf (C:\Program Files\Apache Group\Apache\conf\httpd.conf), в самом низу, добавить две строчки
    LoadModule php5_module c:/php/php5apache2_2.dll
    AddType application/x-httpd-php .php .php3 .phtml

    - перезапустить Апач (ярлыком Restart в группе Apache HTTP Server/Configure Apache Server)
    Выполнив все эти действия, можно положить тестовый php скрипт (допустим, он называется test.php и состоит из строчки
    <?php phpinfo (); ?>
    ) в каталог, который является корневым для веб-сервера (по умолчанию это C:\Program Files\Apache Group\Apache\htdocs\) и обратиться к нему, написав в браузере адрес
    http://127.0.0.1/test.php

    MySQL после скачивания следует распаковать из зипа, запустить setup.exe и установить. Во избежание проблем, ЛУЧШЕ установить в папку по умолчанию - c:\mysql. Если вы этого не сделали, то внимательно читайте документацию.
    После установки в командной строке (Пуск - Выполнить - cmd.exe) выполните следующие команды:
    C:\mysql\bin\mysqld --install
    net start mysql

    All! MySQL установлена! Для проверки наберите
    C:\mysql\bin\mysql -uroot
    если консоль запустилась - все работает. Наберите exit для выхода и приступайте к конфигурированию поддержки mysql в PHP.
    Для этого, если вы не сделали этого раньше, возьмите файл c:\php\php.ini-recommended и скопируйте под именем php.ini в каталог windows. Затем отредактируйте его, убрав точку с запятой в начале строки
    ;extension=php_mysql.dll
    а затем перепишите файлы c:\php\libmysql.dll и c:\php\ext\php_mysql.dll в каталог WINDOWS и не забудьте после этого перезапустить Апач, как это было описано выше.
    Теперь вы можете использовать mysql в своих php-скриптах.

    Те, для кого эта инструкция слишком сложна, могут попробовать установить готовый комплект Денвер-2 .
    В него входит сразу все, что нужно, и еще много ненужного. А главное - работает все само.
    Еще одно достоинство Денвера в том, что объем базового комплекта в 10 раз меньше полных версий - всего 4 мегабайта. А так же то, что его автор пишет интересные книжки по PHP.

    Так же, всем любознательным рекомендуется ВЕСЬМА толковая статья Установка и настройка Apache+PHP
    с сайта PHP5.RU. И, конечно же - разделы официальной документации, посвященные установке соответствующих программ.

    Форумы.
    При изучении любого дела обязательно появятся вопросы.
    Вопросы удобно задавать на форумах.
    http://phpclub.ru/talk/forumdisplay.php?s=&forumid=12
    Форум PHPклуба. Самый посещаемый и известный. К сожалению, известность служит ему дурную службу. Очень часто на вопрос новичка отвечает еще более зеленый новичок, давая совершенно неправильный ответ. Однако профессионалов там тоже предостаточно, готовых объяснить ошибки и первому и второму.

    PHP представлен и в русскоязычном сегменте Livejournal
    В сообществах ru_php и [Info] ru_mysql всегда найдутся профессионалы, кототорые помогут с любой проблемой. Только не забудьте сначала прочитать правила сообщества!

    Задавая вопрос на форуме, помните:
    Что, скорее всего, с ним уже сталкивалась тыща человек. И подробные ответы можно найти в поиске. Если же, все-таки, вопрос приходится задавать - то описывайте как можно подробнее (только своими словами, а не кодом!), что вы делали, что хотели получить и что получилось в результате, а так же точно копируйте сообщения об ошибках.

    Сайты для начинающих.
    Ранее здесь были размещены ссылки на различные сайты от начинающих для начинающих.
    К сожалению, и так-то не блиставшие качеством материала, они давно заброшены своими авторами и окончательно потеряли актуальность.
    Все, что есть лучшего по теме PHP, перечислено вверху страницы.
    Если вы знаете хороший сайт - напишите о нем в разделе "Обратная связь".

    В начало раздела Наверх


    \ "Quotes \". Cоставление запросов, слеши, SQL Injection

    Быстрые рекомендации.
    Правила составления запросов MySQL
    Динамическое составление запросов
    Правильная работа со спецсимволами при составлении запросов
    Подготовленные выражения
    SQL Injection
    Особенности работы с оператором LIKE
    О слешах. Как от них избавиться
    Замечания
    ОПС: очень полезные ссылки:
    Примечание: формы
    Comments

    Быстрые рекомендации.
    Для предотвращения SQL инъекций следует соблюдать два простых правила:
    1. Не помещать в БД данные без обработки.
    Это можно сделать либо с помощью подготовленных выражений, либо обрабатывая параметры вручную.
    Если запрос оставляется вручную, то
    - все числовые параметры должны быть приведены к нужному типу
    - все остальные параметры должны быть обработаны функцией mysql_real_escape_string() и заключены в кавычки.

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

    Почитать об этом подробнее - почему надо делать именно так и как это все работает, можно ниже. И настоятельно рекомендуется .

    Правила составления запросов MySQL
    Для начала - немного о том, почему вообще нужны эти слеши.
    Если мы подставляем в запрос какие-либо данные, то, чтобы отличить эти данные от команд SQL, их надо брать в кавычки.
    К примеру, если написать
    SELECT * FROM table WHERE name = Bill
    то база решит, что Bill - это имя другого поля, не найдёт его, и выдаст ошибку. Поэтому подставляемые данные (в данном случае имя Bill) надо заключать в кавычки - тогда база сочтет его строкой, значение которой надо присвоить полю name:
    SELECT * FROM table WHERE name = 'Bill'
    Однако, и в самих данных могут встречаться кавычки тоже. К примеру,
    SELECT * FROM table WHERE name = 'Д'Артаньян'
    Здесь база данных решит, что 'Д' - это данные, а Артаньян - команда, которую она не знает, и тоже выдаст ошибку. Поэтому и надо прослешивать все данные, чтобы объяснить базе, что встречающиеся в них кавычки (и некоторые другие спецсимволы) относятся к данным.
    В результате мы получим правильный запрос, который ошибок не вызовет:
    SELECT * FROM table WHERE name = 'Д\'Артаньян'

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

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

    На самом деле, всё вышесказанное относится к данным строкового типа и датам. Числа можно вставлять не прослешивая и не окружaя кавычками. Если вы так делаете, то ОБЯЗАТЕЛЬНО! насильно приводите данные к нужному типу перед вставкой в запрос, например:
    $id = intval ( $id );
    Однако для простоты (и надёжности) можно и с числами работать, как со строками (проскольку mysql всё равно преобразует их к нужному типу). Соответственно, мы будем любые данные, вставляемые в запрос, прослешивать и заключать в кавычки.

    Так же, есть ещё одно правило - необязательное, но его следует придерживаться во избежание появления ошибок:
    Имена полей и таблиц следует заключать в обратные одинарные кавычки - "`" (клавиша с этим символом находится на стандартной клавиатуре слева от клавиши "1") Ведь имя поля может совпадать с ключевыми словами mysql, но если мы используем обратную кавычку, то MySQL поймёт всё правильно:
    SELECT * FROM `table` WHERE `date` = '2006-04-04'
    Следует различать эти кавычки и не путать одни с другими. Следует также помнить, что обратные кавычки слешами не экранируются.

    Динамическое составление запросов
    Если SQL запрос в скрипте написан целиком, и никак не меняется, например
    SELECT * FROM `table`
    то никаких проблем с ним и не будет.
    Но вся сила наших скриптов именно в динамическом составлении запросов!
    Вместо того, чтобы писать готовые запросы на все случаи жизни, мы составляем их на основании поступающих в скрипт данных.
    И вот здесь нас подстерегает опасность.
    Допустим, мы составляем запрос с использованием переменной:
    SELECT * FROM table WHERE name = '$name'
    Вроде бы - всё нормально?
    А если $name у нас будет Д'Артаньян? Запрос выдаст ошибку!
    То есть, переменную перед подстановкой в запрос надо прослешить.
    Это можно сделать несколькими путями.
    Самый простой (и неправильный) - положиться на волшебные кавычки. Как вы уже догадались, именно для этого случая они и были придуманы. Ради того, чтобы уберечь SQL запросы забывчивых программистов от ошибок, ВСЕ поступающие в скрипт данные прослешиваются без разбору.
    Если вы используете чужой код, то лучше воспользоваться волшебными кавычками . Это может создавать некоторые неудобства и не гарантирует вас от ошибок или взлома (поскольку прослешиванием правила составления запросов не исчерпываются) но хотя бы снижает риск. Поэтому, при использовании чужого кода, обязательно убедитесь, что волшебные кавычки включены.
    Если же вы пишете весь код самостоятельно, то следует научиться правильному составлению запросов.

    Правильная работа со спецсимволами при составлении запросов
    So. Как мы уже узнали выше, чтобы правильно составить запрос, надо заключать данные в кавычки и прослешивать их.
    С первым всё понятно. При составлении динамических запросов мы никогда не забываем все данные заключить в кавычки:
    $query = "INSERT INTO `table` VALUES(NULL,'$name','$date','$price')" ;
    Если переменная $price должна быть типа int и мы приведём её к этому типу, то можно её не заключать в кавычки. Однако, если заключим, то беды особой не будет, но зато можно будет сделать работу с данными единообразной.

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

    Сначала отключим волшебные кавычки. Так, как это описано в самом начале.
    Почему это следует сделать?
    For many reasons. Самая очевидная - логическая. "Волшебные кавычки" добавляют слеши не там, где они нужны - при составлении запроса , а еще до попадания в скрипт! Но ведь данные совсем не обязательно после этого будут вставляться в запрос. Может быть, их придётся выводить пользователю, и слеши будут только мешать. Плюс к тому, добавленные слеши помешают, к примеру, правильно проверить длину введённой строки. К тому же, прослешивать нам надо не только пришедшие от пользователя данные, а вообще любые, вставляемые в запрос - многим этот очевидный факт даже не приходил в голову! Список можно продолжать, но вывод один: добавлять слеши надо не автоматом, без разбору, до начала выполнения скрипта, а только там, где действительно надо – при составлении запроса.
    Есть и ещё одна причина: при использовании кодировки Unicode, которая приобретает всё большую популярность, а со временем займёт доминирующее положение в веб, волшебные кавычки могут попросту испортить текст, приняв часть мультибайтной строки за спецсимвол.

    Теперь займёмся добавлением слешей самостоятельно.
    Во-первых, для прослешивания мы воспользуемся функцией mysql_real_escape_string()
    Следует помнить, что применять её можно только после установления соединения с базой.
    Эта функция делает гораздо больше, чем устаревшие addslashes и mysql_escape_string. Во-первых, она облегчает ведение и чтение логов mysql, заменяя, например, символ перевода строки на "\n" и некоторые другие символы на escape-последовательности. Во-вторых, и самое главное - она корректно работает с многобайтными кодировками, принимая во внимание текущую кодировку MySQL и не портит, таким образом, тексты в кодировке Unicode.
    Во-вторых, не забываем, что прослешить надо все те данные, которые мы заключили в запросе в кавычки:

    $name
    = mysql_real_escape_string ( $name );
    $age = mysql_real_escape_string ( $age );
    $query = "INSERT INTO table (name,age,class) VALUES ('$name','$age',11)" ;

    or:
    $query = "SELECT * FROM table WHERE name LIKE '" . mysql_real_escape_string ( $_GET [ 'name' ]). "%'" ;

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

    Подготовленные выражения
    Есть еще один способ отправлять запросы в БД, называемый "подготовленными выражениями" (prepared statements).
    Суть его заключается в том, что подготавливается шаблон запроса, со специальными маркерами, на место которых будут подставлены динамические компоненты. Пример такого шаблона:
    SELECT * FROM table WHERE name=?
    Знак вопроса здесь - это тот самый маркер. По-другому он называетсй плейсхолдером (placeholder). Весь секрет в том, что данные на его место подставляет специальная функция, которая "привязывает" переменную к запросу.
    Вот как выглядит код в таком случае:
    $stmt = $mysqli -> prepare ( "SELECT District FROM City WHERE Name=?" );
    $stmt -> bind_param ( "s" , $city );
    $stmt -> execute ();

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

    Понятно, что возникает много вопросов. Но ни объём, ни тематика данной статьи не позволяют остановиться на них более подробно. Рекомендую обратиться к документации по библиотекам mysqli и PDO , реализующим данный принцип.
    Так же, можно использовать библиотеку DbSimple Дмитрия Котерова или PEAR::DB . Основное отличие этих двух состоит в том, что они реализуют механизм подготовленных выражений только внешне. А внутри работают по-старинке - составляя запрос и отправляя его в базу, беря на себя работу по обработке переменных. А PDO и mysqli работают, как было описано выше - то есть, шаблон запроса и данные уходят в базу по отдельности.

    SQL Injection

    Итак, мы научились правильно подставлять в запрос данные.
    BUT! Динамическое составление запросов не исчерпывается подстановкой данных. Часто нам приходится подставлять в запрос команды SQL и имена полей. И здесь мы уже переходим к теме безопасности:

    SQL Injection - это способ хакерской атаки, когда передаваемые скрипту данные модифицируются таким образом, что запрос, формируемый в этом скрипте, начинает выполнять совсем не то, для чего он предназначался.
    Правила защиты от таких атак можно разделить на два пункта:
    1. Работа с данными.
    2. Работа с управляющими элементами запроса.

    Первый пункт мы подробно рассматривали выше. Он, можно сказать, и не является, собственно, защитой. Соблюдение правил добавления занных в запрос продиктовано, в первую очередь, требованиями СИНТАКСИСА SQL. А как побочный эффект мы имеем и защиту от взлома.

    Второй пункт гораздо сложнее, поскольку не существует такого же единого универсального правила, как для данных - обратная кавычка никак не защитит имя поля от модификации хакером. Невозможно кавычками защитить имя таблицы, операторы SQL, параметры команды LIMIT, и другие операторы.
    Поэтому основное правило при подстановке управляющих элементов в запрос такое:
    Если требуется динамически подставлять в запрос операторы SQL или имена полей, баз данных, таблиц, то ни под каким видом не вставлять их в запрос напрямую.
    Все варианты таких добавлений должны быть ЗАРАНЕЕ прописаны в вашем скрипте и выбираться на основании того, что ввёл пользователь.
    К примеру, если надо передать имя поля в оператор order by, то ни в коем случае нельзя подставлять его напрямую. Надо сначала проверить его. К примеру, сделать массив допустимых значений, и подставлять в запрос только если переданный параметр в этом массиве присутствует:
    $orders =array( "name" , "price" , "qty" );
    $key = array_search ( $_GET [ 'sort' ], $orders ));
    $orderby = $orders [ $key ];
    $query = "SELECT * FROM `table` ORDER BY $orderby" ;

    Мы ищем в массиве заранее описанных вариантов введённое пользователем слово, и, если находим, то выбираем соответствующий элемент массива. Если совпадения не будет найдено, то будет выбран первый элемент массива.
    Таким образом, в запрос подставляется не то, что ввёл пользователь, а то, что было прописано у нас в скрипте.
    Точно так же надо поступать и во всех остальных случаях
    К примеру, если динамически формируется оператор WHERE:
    if (!empty( $_GET [ 'price' ])) $where .= "price='" . mysql_real_escape_string ( $_GET [ 'price' ]). "'" ;
    $query = "SELECT * FROM `table` WHERE $where" ;

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

    Особенности работы с оператором LIKE
    Совершенно отдельный случай - оператор LIKE.
    Во-первых, помимо обычного прослешивания, в переменных, которые подставляются в LIKE, надо удваивать слеши. То есть, если в переменной содержится символ \, то его надо удвоить, а после этого выполнить обычное прослешивание, через mysql_real_escape_string.
    К примеру, если мы ищем строку
    символ \ называется "backslash"
    и нам нужно точное совпадение, то мы просто применяем mysql_real_escape_string и запрос получается стандартный:
    SELECT * FROM test WHERE field = 'символ \\ называется \"backslash\"'
    Если же мы хотим подставить эту строку в LIKE, то сначала надо заменить каждый слеш на два, а потом применить mysql_real_escape_string. В результате получится
    SELECT * FROM table WHERE field LIKE '%символ \\\\ называется \"backslash\"%'

    Во-вторых, следует обратить внимание на то, что ни одна из функций, добавляющих слеши, не добавляет их к метасимволам поиска "%" и "_", используемым в операторе LIKE. Поэтому, если вы используете этот оператор, и не хотите, чтобы символы _ и % использовались, как маски, то добавляйте слеши вручную. Это можно сделать командой
    $data = addCslashes ( $data , '%_' );
    Внимание - это не addslashes! В имени этой функции есть дополнительная буква "c".

    Таким образом получается, что переменные, используемые в операторе LIKE мы должны обрабатывать отдельно.
    сначала заменять один слеш на два, с помощью такого, к примеру, кода:
    $var = str_replace ( '\\' , '\\\\' , $var );
    затем (можно наравне со всеми другими данными, идущими в запрос) прослешиваем:
    $var = mysql_real_escape_string ( $var );
    а затем, если хотим, чтобы _ и % соответствовали точно самим себе, делаем
    $var = addCslashes ( $var , '_%' );

    В результате, если мы будем искать, к примеру, такую строку
    символ \ называется "backslash", а символ _ называется "underscore"
    то после обработки, в запросе она должна выглядеть так:
    '%символ \\\\ называется \"backslash\", а символ \_ называется \"underscore\"
    То есть, слеш, который был в строке изначально - учетверился. Остальные символы прослешились, как обычно. Плюс - прослешился символ подчёркивания.

    О слешах. Как от них избавиться
    Слеш, или бэкслеш, от английского back slash - обратная косая черта ("\"), которая непонятным образом вдруг сама собой появляется в ваших переменных. Добавляется он к некоторым спецсимволам, но в основном его замечают из-за кавычек.
    Происходит это из-за специальных настроек PHP, обычно включённых на хостинге по умолчанию. Теоретически, эти настройки могут повысить безопасность скриптов, работаюющих с БД. Практически же, от автоматического добавления слешей часто получается путаница и неудобство, как при работе с БД, так и при её отсутствии.
    Ниже мы подробно разберём оба этих случая.

    За автоматическое добавление слешей отвечают директивы php.ini, которые носят общее название "волшебные кавычки":
    magic_quotes_gpc и magic_quotes_runtime
    Если включена первая, то PHP автоматически добавляет слеши к данным, пришедшим от пользователя - из POST, GET запросов и кук (а так же - к логину и паролю, полученным через HTTP Authorisation).
    Если вторая, то слеши добавляются к данным, полученым во время исполнения скрипта - например, из файла или базы данных.

    Если вы работаете без базы данных, или же работаете с БД правильно (о чём будет написано ниже), лишние слеши вам только мешают, и от них надо избавляться. Проще и правильнее всего отключить автоматическое добавление, в настройках PHP.
    Это можно сделать либо поправив соответствующие директивы в php.ini, если у вас есть к нему доступ, либо создав в коневом каталоге сайта файл .htaccess , и добавив в него строчки
    php_flag magic_quotes_gpc 0
    php_flag magic_quotes_runtime 0


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

    С данными, получаемыми во время работы, разобраться проще всего: достаточно в начале скрипта написать
    set_magic_quotes_runtime(0);
    Для данных, полученных от пользователя, всё гораздо сложнее. Для этого кода нам потребуется две функции:
  • проверить, добавил ли PHP, можно с помощью функции get_magic_quotes_gpc .
  • удаляет слеши функция stripslashes .
    Соответственно, с помощью первой надо проверить, и, если PHP добавил, то перебрать все входящие переменные и очистить с помощью второй.
    Если вы работаете правильно, при register_globals = off , то достаточно применить stripslashes ко всем массивам, содержащим данные, приходящие из браузера.
    к примеру, можно включить во все скрипты сайта вот такой код:
    function strips (& $el ) {
    if (
    is_array ( $el ))
    foreach(
    $el as $k => $v )
    strips ( $el [ $k ]);
    else
    $el = stripslashes ( $el );
    }
    if (
    get_magic_quotes_gpc ()) {
    strips ( $_GET );
    strips ( $_POST );
    strips ( $_COOKIE );
    strips ( $_REQUEST );
    if (isset(
    $_SERVER [ 'PHP_AUTH_USER' ])) strips ( $_SERVER [ 'PHP_AUTH_USER' ]);
    if (isset(
    $_SERVER [ 'PHP_AUTH_PW' ])) strips ( $_SERVER [ 'PHP_AUTH_PW' ]);
    }

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

    Замечания
    • Среди причин, по которым не стоит полагаться на "волшебные кавычки", есть ещё одна. Весьма маловероятная, но всё же. К "волшебным кавычкам" относится на самом деле не две директивы, а три. Третья - magic_quotes_sybase . Мало того, что она вместо слеша добавляет кавычку - так она ещё и отменяет действие magic_quotes_gpc. Если каким-то чудом обе эти директивы имеют статус 'on', то последняя не сработает! То есть, полагаясь на "волшебные кавычки", мы в этом случае получим все прелести неправильно составленных запросов. Вообще, чисто теоретически, надо учитывать наличие этой директивы, поскольку она преподносит ещё и такой сюрприз, как... изменение поведения функций addslashes и stripslashes! Если magic_quotes_sybase = on , то эти функции начинают вместо слеша добавлять и удалять одинарную кавычку соответственно.
    • Все приведенные примеры касаются только БД Mysql. Конкретные правила составления запросов могут отличаться для других СУБД, но общий принцип остается прежним:
      • если API для работы с БД или сторонняя библиотека предоставляет специальные функции для составления запросов , и есть возможность их использования, то пользоваться в первую очередь надо ими.

      • если таких функций нет, то следует искать в документации функции экранирования спецсимволов для этой СУБД.


    ОПС: очень полезные ссылки:
    Про волшебные кавычки на сайте PHP.NET
    Про SQL-инъекции на сайте PHP.NET

    Несколько материалов по SQL Injection:
    http://www.securitylab.ru/45438.html
    http://www.securitylab.ru/49424.html
    http://www.nextgenss.com/papers/advanced_sql_injection.pdf

    Примечание: формы
    При выводе value в тегах input форм, слеши не помогают.
    Чтобы текст в таком поле выводился целиком, value надо заключать в кавычки , а к выводимым данным применять функцию htmlspecialchars()
    Example:
    <input type="text" name="name" value=" <? echo htmlspecialchars ( $name , ENT_QUOTES ) ?> ">
    <textarea> <? echo htmlspecialchars ( $text , ENT_QUOTES ) ?> </textarea>

    Необходимо так же отметить (хоть это уже совсем не имеет отношения к кавычкам и слешам), что функцию htmlspecialchars следует применять при выводе в браузер вообще ко всем данным, которые получены от непроверенного пользователя. Почему это следует делать, можно почитать в гугле по запросу что такое XSS уязвимость

    В начало раздела Наверх


    Regular expressions.


    Что такое регулярные выражения?
    Это замечательный инструмент для работы с текстом.
    Такой же революционный, по сравнению с обычными строковыми функциями, как БД - по сравнению с текстовыми файлами. Это специальный язык для работы с текстом. Причём подчас одна строчка с использованием регулярных выражений может заменить страницу другую обычного кода!

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

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

    При практическом же использовании не обойтись без справочных материалов официальной документации.
    В PHP поддерживается два стандарта регулярных выражений - POSIX и PCRE.
    Первый считается более устаревшим и медленным, вы не найдёте по нему хороших примеров и даже документация не переведена на русский.
    По PCRE (функции preg_*) документация очень солидная.
    Во-первых, это подробнейшее изложение синтаксиса и пояснение значения всех спецсимволов - Синтаксис регулярных выражений
    Во-вторых, - Общее описание, список функций и полезные комментарии
    В-третьих, пояснение довольно важного элемента - Модификаторов шаблонов
    Ну, и документация на все функции, разумеется. Основными из которых являются preg_match , preg_match_all и preg_replace с незаменимыми комментариями пользователей.
    Всё (кроме комментариев) - на русском языке.

    Нельзя, так же, обойти вниманием фундаментальный труд столпа российского PHP Дмитрия Бородина,
    Регулярные выражения в PHP (сравнение Perl и PHP)

    Так же можно скачать знаменитую книгу Дж.Фридла "Регулярные выражения" в формате DjVu.
    Книга написана с примерами на Perl, но разобраться там несложно.

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

    чтобы данный текст не выглядел сухой теорией, приведем пример решения наиболее распространённой задачи - "подсветка ссылок" или превращение http://phpfaq.ru в ссылку

    $text = preg_replace ( "#(https?|ftp)://\S+[^\s.,>)\];'\"!?]#" , '<a href="\\0">\\0</a>' , $text );

    Им можно пользоваться, не понимая ни слова. А узнать, что означают эти закорючки, можно из ссылок выше Smile happy

    В начало раздела Наверх


    Documentation on PHP. Manual on PHP, books.

    Документация на русском языке:
    Документация на английском языке.
    Книги, журналы
    Документация и книги по MySQL
    Comments

    Документация на русском языке:
    Перевод официальной документации - http://www.php.net/manual/ru/
    Далеко не все еще переведено, но, тем не менее, эту ссылку можно рекомендовать, как замену официального англоязычного мана. Что переведено - будет по-русски. Не переведенное же будет точной копией английского.
    Скачать полную версию документации в различных форматах (в том числе - в очень удобном .chm) можно здесь: http://www.php.net/download-docs.php
    Перевод делается руками добровольцев, которых, как всегда, не хватает. Если вы хотите помочь этому благородному делу и чувствуете себя в силах, то пишите (in English please), в лист рассылки по адресу [email protected] , а так же по этому адресу с благодарностью будут приняты сообщения о неточностях в переводе.

    Удобно: Быстрый доступ к описанию функции из онлайн документации можно получить введя в браузере адрес www.php.net/имя_функции

    Существует полный русский перевод документации от версии 4.2 от некоего Александра Пирамидина.
    Перевод машинный, но тем не менее - вполне читабельный. ЧУДОВИЩНО устарел.
    пользоваться этим переводом можно только в самом КРАЙНЕМ случае!
    Только если нужный вам участок официальной документации не переведен, а в английском, даже с переводчиком - ни в зуб ногой.
    Онлайн, на PHPClub-e, без баннеров: http://phpclub.ru/manrus/
    chm-версия: http://web.php.net.ua/download?what=php4

    Документация на английском языке.
    Самая актуальная и правильная:
    Онлайн, с комментариями пользователей: http://www.php.net/manual/en/
    Очень удобно иметь всю документацию в одном файле с быстрым поиском. Такая документация есть, в формате windows help (.chm):
    http://www.php.net/distributions/manual/php_manual_en.chm
    Расширенный вариант документации в формате .chm, с очень ценными комментариями пользователей постоянно обновляется на этом сайте
    http://weblabor.hu/php-doc-chm

    Книги, журналы

    PHP. Сборник рецептов. Пeревод самой лучшей книги по PHP.

    Уникальная книга. Собрание конкретных ответов на конкретные вопросы. Как дату сложить, отнять, отформатировать. Как со строками работать, с файлами, с БД. Если сравнивать книги по количеству воды в них, то это будет пустыня Сахара. Книга разбита на 20 глав, каждая глава состоит из разделов вида: постановка проблемы - решение - объяснение.
    Немного устарела, в плане того, в чем пхп сильно ушел вперед - XML, обработка ошибок. Однако в базовых основах языка остается непревзойдённой. Может использоваться как в виде справочника для решения конкретных проблем, так и в виде учебника.


    Котеров Д., Костарев А., "PHP5 в подлиннике".

    Второе издание знаменитой книги Д.Котерова. Не имеет ничего общего с первым. Это не переработанная, это совершенно новая книга. Уникальна тем, что подходит как начинающему, так и профессионалу - в ней изложены все аспекты программирования на PHP. В отличие от всех прочих скороспелок по "PHP5", вышедших ДО выхода самой пятой версии, книга действительно написана на материале релиза 5 версии.

    Если совсем нету денег на книги, то можно почитать очень старый учебник Д.Гилмора "PHP - учебный курс": http://phpfaq.ru/txt/gilmor

    Первый русскоязычный онлайн журнал по PHP - PHPinside.RU

    Сборник черезвычайно полезных сведений от авторa РНР: www.lerdorf.com/tips.pdf (на английском языке)

    Документация и книги по MySQL
    Самую свежую версию официальной документации для своей версии MySQL можно посмотреть на сайте http://dev.mysql.com/doc/
    Раньше там был и русский перевод к версии 4.0, но поскольку он устарел и не поддерживается, то его с сайта убрали. Посмотреть его по можно по адресу http://www.mysql.ru/docs/man
    Книга М.Грабера Введение в SQL http://www.mysql.ru/docs/gruber/



    П.Дюбуа.
    MySQL. Сборник рецептов Книга из суперсерии "cookbook" (книга рецептов) от автора "Библии MySQL" Поля Дюбуа. книга, необходимая каждому разработчику, использующему MySQL. Собрание ответов на любые практические вопросы. По ссылке можно ознакомиться с одной из глав книги.


    П.Дюбуа.
    MySQL. 2-е издание . Второе издание "Библии MySQL", на которой училось не одно поколение девелоперов и администраторов. Объясняет секреты MySQL от "а" до "я", но при этом очень простым и понятным языком.



    В начало раздела Наверх


    Nothing works! Что делать??? Поиск ошибок и отладка.

    Быстрые рекомендации.
    Introduction. Очень важное.
    Сообщения об ошибках PHP.
    Отладка и поиск ошибок в своем алгоритме.
    Пример отладки.
    Самое важное - знать, что ты хочешь получить.
    Conclusion.
    Comments

    Быстрые рекомендации.
    1. Убедитесь, что вы видите сообщения об ошибках, если они возникают.
    Для этого надо добавить в начало скрипта 2 строчки
    ini_set ( 'display_errors' , 1 );
    error_reporting ( E_ALL );

    Хотя в некоторых случаях это всё равно не поможет. Тогда смотрите ошибки в логах веб-сервера.
    Ещё можно добавить в файл .htaccess строчку
    php_flag display_errors 1
    Обязательно убрать всех собак (@) из кода!
    Если апач выдаёт ошибку 500 - значит надо смотреть текст ошибки в логе ошибок веб-сервера.

    2. При проблемах с MySQL (supplied argument is not a valid MySQL result resource) под строкой, где произошла ошибка, обязательно надо вывести на экран mysql_error() и сам запрос - для визуального контроля и копирования на форум. повторяю - вывести надо ЗАПРОС! А не PHP-код, который его формирует.

    3. При работе с изображениями , чтобы увидеть сообщение об ошибке, обязательно надо догадаться отключить вывод заголовка, говорящего браузеру, что дальше идет картинка.
    И, естественно, обращаться к скрипту напрямую, а не через тег <img>!

    4. При проблемах в аплоаде в первую очередь смотрите массив $_FILES ( print_r ( $_FILES ); ). Описания ошибок из $_FILES['filename']['error'] есть в мануале .

    5. При проблемах во взаимодействии сервера и клиента (куки, сессии, запросы)- в обязательном порядке смотреть обмен HTTP заголовками

    6. И САМОЕ ВАЖНОЕ: запуская скрипт, смотрите не то, что показывает браузер, а ИСХОДНЫЙ HTML код! .

    Получив сообщение об ошибке, вы можете его прочитать и исправить.
    Если не справились - пишите на форум. При этом КОПИРУЙТЕ сообщение об ошибке, и КОПИРУЙТЕ небольшой - 3-5 строк - кусок кода, на который указывает ошибка. Повторяю - КОПИРУЙТЕ! никакой отсебятины!

    Если вы всё равно не нашли ошибку - читайте дальше:

    Introduction. Очень важное.
    Ты написал программу, а она не работает.
    Вариантов ты видишь немного - либо сидеть и пытаться умственным усилием обнаружить ошибку, в сотый раз просматривая код, либо пойти на форум и попросить, чтобы там тебе нашли ошибку.
    Самое интересное, что есть третий, в сто раз лучше первых двух.
    Этот способ называется "Отладка программы". По-английски - debug.
    Заключается он в том, чтобы заставить программу саму показать, где в ней ошибка.
    Это мало того, что получится быстрее, чем спрашивать на стороне - так зачастую это единственный способ решить проблему . Единственный.
    Я тебе сейчас открою страшный секрет. В мире НЕТ программистов, которые пишут код, как художники на Арбате - сел, наваял, отдал. Нету. И не будет.
    Процесс написания программы - циклический: Написал кусок кода - посмотрел, как работает. Если не работает - ищем ошибки. Работает - пишем дальше.
    The only way. Других вариантов нет.
    Больше того. В большинстве случаев совершенно бесполезно вываливать на форум свой код, и спрашивать - "В чём ошибка?" . На форуме не сидят волшебники вперемешку с телепатами. И гадалок с прорицателями - тоже нет. Поэтому отгадывать, в чём, теоретически, может быть ошибка, никто не будет. Ошибку найти может только хозяин программы. На своём сервере. Со своими настройками и опечатками. Поэтому локализовать ошибку - найти место, где она происходит, определить тип ошибки - можно только самостоятельно. А вот исправить её на форуме помогут. Если не получится самому.

    Те, кто приходит к веб-программированию от дизайна, или от игр, или от нечего делать, просто не знают этой страшной тайны: Основное время программиста уходит не на написание кода. Основное время программиста уходит на поиск ошибок и отладку. Это не шутка. It's true. И если вы решили заняться программированием, то вам придётся искать ошибки точно так же, как это делают все остальные.
    К сожалению, очень много людей приходит к PHP вообще без опыта программирования и, как следствие - никогда не слышали об отладке.
    А это и есть самое главное в программировании - умение искать ошибки.
    И мы с тобой сейчас будем учиться их искать.

    Программа не работает. Что можно сделать в этом случае?

    Сообщения об ошибках PHP.
    Самый твой большой помощник в деле отладки - это сам PHP. При возникновении любых проблем он сообщит тебе о них. То есть, в первую очередь ты должен убедиться в том, что если сообщение об ошибке есть - ты его увидишь.

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

    Это чудовищные заблуждения. Сообщения об ошибках - это ПОМОЩЬ! Это громадная помощь программисту. Как ей воспользоваться, мы рассмотрим ниже.


    Даже самый рабочий код, которому ты на 100% доверяешь, и который на соседней машине работает, как часы, может выдавать сообщение об ошибке. Причин тому может быть бесконечное множество. Это и такой распространенный случай, как отсутствие прав доступа к файлам, и такие экзотические, как запрещение провайдером выполнения самых распространенных и безобидных функций.

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

    Во-первых, надо выяснить, выводятся ошибки на экран или пишутся в лог. Обычно, домашний, или тестовый сервер настраивается так, чтобы ошибки выводились на экран. Рабочий же сервер, с сайтом в публичном доступе ОБЯЗАТЕЛЬНО должен быть настроен так, чтобы ошибки не выводились на экран (поскольку посетителю они все равно ничего не скажут, а программист их не увидит), а писались в лог, где программист их увидит.
    Если ты не уверен, и не знаешь, где посмотреть, а ошибку найти надо срочно, то напиши в самом начале скрипта две строчки
    ini_set ( 'display_errors' , 1 );
    error_reporting ( E_ALL ^ E_NOTICE );

    Эти две строки заставят выводить сообщения обо всех критических ошибках на экран.
    Если никаких ошибок не выведется, надо написать
    error_reporting ( E_ALL );
    Это очень сильно поможет , показав несуществующие переменные и другие мелкие ошибки, которые обычно игнорируются, но от которых может зависеть работоспособность программы.
    IMPORTANT! В случае ошибки синтаксиса, по очевидным причинам, установка с помощью ini_set не сработает.
    Поэтому лучше на неё не надеяться, а либо исправить в php.ini (или в .haccess добавить строчку php_flag display_errors 1 ), либо искать ошибку в логе.

    Во-вторых, убедись, что в коде отсутствуют символы '@' перед именами функций. Этот запрещает вывод сообщения об ошибке. Хорошенькое дело! Ты ошибку ищешь-ищещь, а сам же своей программе рот заткнул.

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

    При возникновении проблем с функциями mysql (supplied argument is not a valid MySQL result resource) под строкой, где произошла ошибка, обязательно надо вывести на экран mysql_error() и сам запрос - для визуального контроля и копирования на форум.

    При работе с изображениями , чтобы увидеть сообщение об ошибке, обязательно надо догадаться отключить вывод заголовка, говорящего браузеру, что дальше идет картинка.
    При аплоаде в первую очередь смотрите массив $_FILES.
    При проблемах во взаимодействии сервера и клиента - в обязательном порядке смотреть обмен HTTP заголовками
    И всегда смотрите не то, что показывает браузер, а ИСХОДНЫЙ HTML код!

    Допустим, сообщение об ошибке появляется, и ты его получил. Что делать дальше? Очень просто - прочесть и исправить. Если не хватает знания английского языка, то стоит либо воспользоваться переводчиком, либо взять значащую часть этого сообщения и запросить Google. 90% вероятности, что кто-то с такой ошибкой уже сталкивался, и ты тут же прочтешь ответ.
    Если же не нашел, то задай вопрос в форуме, точно скопировав небольшой (3-5 строк) кусок кода, в котором произошла ошибка, точно указав строку, о которой говорится в сообщении об ошибке, а так же - самое главное! - само сообщение об ошибке.
    Согласись, что с такой информацией тебе на форуме помогут гораздо скорее и качественней?

    Отладка и поиск ошибок в своем алгоритме.
    Но бывает так, что программа не вызывает ошибок, но все равно не работает, или работает не так, как надо.
    Тут уже виноват или алгоритм или какие-то внешние факторы.
    Однако и тут можно найти место, где происходит ошибка.
    Но только при одном условии.
    что ты четко представляешь, что делает твоя программа, каждая функция, каждая строка в ней. Потому, что если ты представляешь, то можешь предсказать, какие значения должны иметь переменные на каждом этапе выполнения.
    А дальше все ОЧЕНЬ просто!
    Во-первых, надо разделить программу на логические блоки.
    Допустим, скрипт выводит форму, получает ее, и записывает данные в базу. ТРИ шага! И в любом из них может быть ошибка, приводящая к тому, что данные в базу не записываются.
    Надо проконтролировать на каждом из участков - все ли переменные имеют то значение, которое ожидается.
    Программа ведь работает с переменными.
    Как проверить?
    Выводить все используемые переменные на экран! И визуально контролировать их содержимое.
    Всего-то лишь написать проблемных местах var_dump($var) и выяснится, что переменная-то пустая!
    И уже можешь пойти на форум не с вопросом "у меня вот код на 100 строк, где ошибка?", а "я написал функцию, но почему-то, когда обращаюсь в ней к переменным, они все пустые". или "из формы не передаются переменные".
    Между этими двумя способами задания вопросов - пропасть.
    Первый не может тебе помочь никак. Ты, собственно, и сам не знаешь, что у тебя за проблема. А при втором ты уже знаешь проблему, и, если сам не справился с ее решением, то можешь задать на форуме конкретный вопрос.

    Еще очень поможет избежать ошибок в программе выставление error_reporting в E_ALL с самого начала работы скрипта.
    Если при отлове критических ошибок сообщения о потенциальных ошибках могут нам помешать увидеть главную, то при разработке нам желательно видеть все - и потенциальные в том числе. Скажем, при E_ALL, при обращении к несуществующей переменной, PHP выдаст предупреждение. То есть, тебе не придется самому выводить переменную, чтобы выяснить, что ты не присвоил ей никакого значения - РНР тебя сам предупредит.

    Пример отладки.
    Из html формы передаются чекбоксы с именами c_1, c_1, c_3... c_10
    В скрипте мы пытаемся в цикле вывести
    for ( $i = 1 , $i < 11 , $i ++) {
    echo
    $_POST [ 'с_$i' ];
    }

    скрипт ничего не выводит.
    Начинаем отлаживать.
    Сначала смотрим в исходный код html страницы. соответствует ли она стандартам?
    Допустим, соответствует. Значит, проблема не в форме.
    Далее, проверяем в скрипте - а есть ли такая переменная, к которой мы обращаемся - массив $_POST?
    пишем

    echo '<pre>' ;
    var_dump ( $_POST );

    Убеждаемся в том, что массив есть и все элементы на месте. Значит, проблема не в передаче.
    Значит, мы как-то неправильно обращаемся к массиву.
    обращаемся мы к нему так: $_POST['с_$i']
    Надо проверить - а во что превращается 'с_$i'?
    делаем echo 'с_$i'; и видим... совсем не то, что ожидали увидеть.
    И вот теперь уже идем либо читать документацию про строки в пхп (что предпочтительнее), либо - на форум, с вопросом "почему у меня переменная не заместилась своим значением". Каковой вопрос будет гораздо лучше звучать, чем "у меня форма не работает".
    Clear?

    Следует понимать, что здесь приведён пример, Нереальный. Показан алгоритм действий.
    В реальности, при error_reporting(E_ALL); PHP сразу же показал бы, что индекс массива у вас неправильный.

    Самое важное - знать, что ты хочешь получить.
    Примерно половина вопросов на форумах вызвана тем, что человек делает что-то...НЕ ЗНАЯ, что именно!
    Самый гениальный вопрос всех времён и народов: "у меня база съела все переводы строк из текстарии".
    Человек просто не дал себе труд посмотреть, как будет выглядеть HTML, который он хочет получить, и решил, что переводы строк съела база.
    И так во всём.
    Непризнанный гений строит сложный SQL запрос, а когда его спрашивают, как запрос должен выглядеть - он только хлопает глазами. ВСЕГДА СНАЧАЛА составьте запрос руками и выполните в консоли или phpmyadmin! А после того, как получили нужный запрос и отладили - милости просим, составляйте его на пхп.
    Работа с удалённым хостом через сокет по протоколу HTTP - то же самое! Сначала научитесь выполнять все команды руками, посмотрите ответы сервера глазами, а потом моделируйте этот диалог в пхп.
    Работа с яваскриптом по тому же методу описана в факе "на танке"

    Запомните - на пхп вы работаете только со СТРОКАМИ! HTML страница, которую вы создаёте скриптом - это для пхп всего лишь набор строк! Ему без разницы, что в этом наборе - теги img, script или frame. Пхп за вас не сделает переводы строк, не нарисует яваскрипт. Если вы не знаете яваскрипта - то не пытайтесь создавать программу на нём с помощью PHP.
    Открывая соединение с удалённым хостом, вы посылаете строку в сокет, вы получаете строку из сокета. Пхп ничего не понимает в этих строках и за вас диалог вести не будет! Это ВЫ должны чётко понимать, что вы хотите послать в сокет, и что получить! Поэтому возьмите программу telnet, соединитесь с нужным хостом и пробуйте сначала САМИ сделать то, что хотите заставить сделать пхп.
    Если у вас не работает скрипт с сокетами - бегом в телнет смотреть, что происходит!
    SQL запрос - это СТРОКА. Вы должны себе чётко представлять, какой запрос получится в результате вашего хитроумного пхп-кода! Сервер БД не понимает конструкций intval, date, mktime и так далее! Это всё пхп-код. Результатом которого будет являться строка корректного SQL запроса. прежде, чем писать пхп код, вы должны ЧЁТКО СЕБЕ ПРЕДСТАВЛЯТЬ, КАК ДОЛЖЕН ВЫГЛЯДЕТЬ SQL ЗАПРОС В РЕЗУЛЬТАТЕ!
    Если у вас не выполняется SQL запрос 0 выводите его на экран и смотрите - что нагородили своим скриптом!

    Заключение.
    Отладка - главное занятие программиста.
    Отладка - единственный и самый мощный способ найти ошибку в программе.
    Отладка состоит из двух основных компонентов:
    1. Максимально упрощать пример. Если у вас не работает программа, которая рисует форму,получает данные, записывает данные формы в базу и выводит их снова, то разбейте программу на составляющие и выполняйте по очереди.
    Если у вас не работает сложная подпрограмма определения работоспособности кук - напишите сначала тест в две строчки чтобы убедиться, что вы хотя бы можете выставлять и читать куку.
    2. Вывод отладочной информации.
    Проверяйте значение КАЖДОЙ переменной! Каждого значения, возвращаемого функцией!
    Не работает локейшен? Выведите его на экран и скопируйте в браузер!
    В файл записывается пустая строка? проверяйте составляющие этой строки на каждом этапе ее создания и выводите на экран!
    Убедились, что на экран выводится? Тренируйтесь писать в файл, на тестовой строке! Забитой прямо в скрипт! Уменьшайте количество неизвестных!
    И всегда смотрите не то, что показывает браузер, а ИСХОДНЫЙ HTML код!

    Надеюсь, что я смог хотя бы немного объяснить принципы этого занятия.
    Удачной отладки.


    В начало раздела Наверх


    Решение проблемы "Cannot add header information - headers already sent"


    Ошибку эту исправить несложно.
    Часто такое же сообщение появляется при старте сессий, в немного другой формулировке:
    Warning: Cannot send session cookie - headers already sent
    Warning: Cannot send session cache limiter - headers already sent

    Для начала узнаем, как вообще общается броузер с сервером. Происходит это по специальному протоколу HTTP . К примеру, когда ты набраешь адрес, или нажимаешь на ссылку, броузер посылает HTTP запрос серверу. Сервер отвечает. Первыми в ответе ВСЕГДА идут HTTP заголовки. Хоть один. И только потом уже сервер посылает, а броузер принимает, текст, или картинку, или файл - в общем, что было запрошено. Cобственно, из-за этого правила - сначала заголовок, а потом информация, и происходит данная ошибка. РНР, для твоего удобства, посылает заголовки автоматически, как только скрипт начинает выдавать бровзеру информацию. Соответственно, если хоть один пробел был уже передан пользователю, заголовки уже ушли, и снова их послать уже никак не можно. А, как ты уже, наверное, догадался, команды header() , setcookie , session_start() , посылают HTTP заголовки.

    Разберем теперь это предупреждение.
    Warning: Cannot add header information - headers already sent by (output started at /www/script.php:5) on line 20

    Cannot add header information - headers already sent . Все ясно написано. "Не могу послать заголовок, поезд уже ушел" - пишет нам РНР. Дальше РНР сообщает, в каком скрипте и в какой его строке ( output started at /www/script.php:5 ) произошел вывод информации, вызвавший автоматическую посылку заголовков. Очень легко найти и исправить. Может быть, там html теги, может быть, echo, а может и просто незамеченая пустая строка или пробел перед первым тегом <? .
    Очень часто такую ошибку вызывает файл, подключаемый через include , в котором либо есть какой-то вывод, либо пустая строка после закрывающего PHP тега - обнаружить ее очень трудно.

    Для решения этой проблемы нужно функцию header () (или session_start (), setcookie ) и всю логику, которая ее вызывает, поместить ДО любого вывода в броузер. Просто перенести повыше в скрипте.
    Ведь вы всё равно перенаправляете браузер. То есть, никакой текст всё равно не будет выведен! Значит, и выводить что-то одновременно с заголовком Location нет смысла. Правильно планируйте структуру своего скрипта: блок, который обрабатывает POST, не должен ничего выводить в браузер.

    Иногда вы проверили ВСЁ - нигде ничего нет. Смените редактор. Посмотрите свой файл в другой программе. К примеру, Windows Блокнот при использовании кодировки Unicode добавляет в начало вашего файла служебный символ Byte Order Mark , никак при этом не ставя вас в известность. Откройте скрипт в другом редакторе и удалите посторонние символы. И смените Блокнот на другой редактор.

    Многочисленные вопросы на форуме заставляют меня сделать здесь важное замечание:
    Эта ошибка появляется не от того, что у вас в скрипте "что то написано выше". А от того, что РНР выводит что-то в браузер. Это не обязательно код. Это может быть сообщение об ошибке . может быть пробел или хтмл тег. Yes Yes. Для самых талантливых: речь идет о любом символе, отправленном в браузер, а не только о тех, которые браузер отображает неискушенному пользователю. У HTML страниц есть исходный текст. И именно он является результатом работы PHP скрипта, а не красивые буковки с картиночками, как думает очень большое количество людей.

    В начало раздела Наверх


    Variables are not passed! Проблема Undefined variable


    Во всех старых руководствах по РНР написано, что даные, полученные из формы, или переданные по ссылке, вот так: script.php?peremennaya=znachenie&variable=value
    автоматически становятся переменными PHP, $peremennaya и $variable
    Эта информация устарела.
    Дело в том, что в целях безопасности, начиная с версии 4.1, РНР настраивается по умолчанию так, чтобы переданные значения не назначались переменным.
    Получить же переданное значение можно обратившись к соответствующему массиву.
    For example:
    Если передаем методом GET, обратившись к скрипту по ссылке вида
    script.php?var=value
    или отправив форму, указав в ней method="GET",
    то все переменные содержатся в массиве $_GET .
    echo $_GET [ 'var' ] ; напечатает " value ".
    Если получаем данные из формы, отправленной методом POST, то все поля этой формы содержатся в массиве $_POST . Допустим, в форме был элемент
    <input type="hidden" name="var" value="1"> ,
    то в скрипте, который указан в action формы, можно написать echo $_POST [ 'var' ]; и будет выведена 1.

    Поэтому, если вы уверены, что переменная есть, но вы не можете ее найти - ищите ее в суперглобальных массивах.
    Подробнее о них можно почитать на русском языке в официальной документации
    http://ru2.php.net/variables.predefined

    То же касается и серверных переменных, таких, как $REMOTE_ADDR , $PHP_SELF . Получить их можно, обратившись к массивам $_SERVER , $_ENV или функцией getenv -
    getenv ( 'HTTP_REFERER' )
    ;

    Переменные, зарегистрированные в сессии, следует искать в массиве $_SESSION .
    Данные cookie содержатся в массиве $_COOKIE, сведения о закачанных файлах - в $_FILES
    Переменные окружения - в $_ENV, а так же, существует массив $_REQUEST, в котором собраны данные из GET, POST и cookie.

    Important! Весьма в поисках переменных помогает одна их главных функций PHP - phpinfo ()
    ее следует применять всякий раз, когда вы "потеряли" переменную, вызовите phpinfo ( 32 ); в скрипте, в котором не работает авторизация, в скрипте, который принимает файл при аплоаде - и все найдется!


    Все то же самое можно прочесть и в документации: http://www.php.net/manual/ru/security.globals.php

    В начало раздела Наверх


    Различие между абсолютными и относительными путями. In the file system and on the site.


    твой сайт существует в как бы в двух измерениях.
    Реальном и виртуальном.

    Для всех посетителей - это виртуальный веб-сервер. Который отличается, в числе прочего, тем, что на нем НЕ СУЩЕСТВУЕТ ФАЙЛОВ. если ты пишешь http://site.ru/file.html - это не файл. Это URI, виртуальный адрес. Никакого файла с именем file.html на сервере может вообще не быть. Это все виртуальные адреса, а не файлы.
    И браузер работает именно с адресами.

    Для разработчика же сайт - это программа, выполняющаяся на совершенно конкретном реальном компьютере. С совершенно конкретным жестким диском, каталогами и файлами. И скрипт, работая со своими данными, подгружая другие скрипты, работает именно с реальными ФАЙЛАМИ, на физическом ДИСКЕ.

    Вот в этом различии и кроются трудности, с которыми часто сталкиваются новички.
    Теряют файлы, путают ссылки с файлами, обращаются к локальным файлам по протоколу HTTP, или инклюдят файлы от корня веб-сервера.

    А всего-то надо четко понимать две вещи:
    1. Различать корень веб-сервера, как его видит браузер, и корень файловой системы на диске.
    2. Отличие относительных путей от абсолютных.

    Начнем со второго.
    It's very simple. Если путь указывается от корня системы, то это путь абсолютный. Это как почтовый адрес в реальной жизни - откуда бы ты не шел, но по точному адресу ты всегда точно найдешь нужное место.
    примеры абсолютных путей:
    /var/www/site/forum/index.php
    /img/frame.gif
    с:\windows\command.com

    В юникс-системах и на веб сайтах корень обозначается косой чертой - "/".
    It is important. Это не просто палочка, а самостоятельный АДРЕС, путь.
    В адресе http://www.site.ru/ последняя косая черта - не для красоты! Она обозначает вполне конкретный адрес - начало сайта.
    На диске в юникс системах так же можно набрать "cd /" и ты попадешь в корневой каталог.
    В виндоус системах файловая система разбивается по дискам, поэтому, в абсолютном адресе надо указывать имя диска. Абсолютного корня всей файловой системы в виндоус нет, у каждого диска - свой. Например, C:\ E:\
    поэтому, даже если путь в виндоус начинается с косой черты, то это не абсолютный путь, а относительный. Относительно текущего диска. А абсолютный начинается с буквы.

    Если в начале пути корень не указать, то этот путь будет относительным, и он достаивается от текущего положения. В реальной жизни это напоминает дорогу к винному магазину - "два квартала налево и там все время прямо". Дойти по такому пути можно только из конкретной точки. Из другой ты попадешь уже в совсем другое место.
    Самый простой пример относительного пути - это просто имя файла.
    Если файл находится в том же каталоге, с которым работает программа - она его найдет, добавив текущий путь к имени файла.
    примеры относительных путей:
    file.php (фал лежит в той же папке)
    ./file.php (фал лежит в той же папке. такая запись иногда требуется в некоторых юникс системах)
    images/picture.jpg (файл лежит в капке images, которая находится в текущей)
    ../file.php (файл лежит в папке, которая расположена на один уровень выше от текущей)
    ../../file.php (файл лежит в папке, которая расположена на два уровня выше от текущей)

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

    Теперь перейдём к первому пункту.
    Различие корня веб-сервера, как его видит браузер, и корень файловой системы на диске.
    В общем-то, из предыдущих объяснений уже все должно быть понятно.
    На диске путь к файлу скрипта может быть таким:
    /var/www/site/forum/index.php
    В то же время, виртуальный адрес этого скрипта при просмотре через браузер, будет:
    http://www.site.ru/forum/index.php
    На этом примере легко увидеть, где пересекаются два измерения: у этих двух адресов есть общая часть - /forum/index.php - и она-то и служит причиной путаницы.
    Для браузера это самый полный путь, который только может быть. Он начинается от корня сайта.
    Для скрипта же, исполняющегося на сервере - это всего лишь ЧАСТЬ пути.
    для скрипта путь /forum/index.php окажется несуществующим - в корне диска нет каталога forum!
    чтобы получить полный путь для того, что на сайте выглядит, как /forum/index.php , надо приставить слева к нему путь к папке, которая считается корневаой для всего веб сервера.
    в нашем примере - это
    /var/www/site
    Этот путь задается в кофигурации веб-сервера и именно он содержится в системной переменной PHP $_SERVER [ 'DOCUMENT_ROOT' ]

    В виртуальном же сервере - том, который видит пользователь - наоборот, нет никакого диска. Есть корень сайта. То есть, для того, чтобы любая ссылка гарантированно работала, независимо от того, из какого места сайта она вызывается, она должна быть абсолютной.
    Если у вас на сайте есть, допустим, два раздела:
    http://www.site.ru/about/info.php
    and
    http://www.site.ru/job/vacancy.php
    то, если в файле info.php сделать ссылку просто на vacancy.php , то браузер ее не найдет - он будет искать адрес http://www.site.ru/about/vacancy.php , достраивая путь от текущего каталога.
    Поэтому надо писать полный путь от корня сайта - /job/vacancy.php
    Всё это касается, естественно, не только тегов <a> но и <img> и любых других, где используются ссылки на другие файлы.

    Ссылки на локальные адреса следует писать без указания протокола и домена - только путь от корня сайта - /job/vacancy.php . Ссылки же на другие сайты следует писать полностью - http://www.site1.ru/job/vacancy.php .


    PHP предоставляет множество средств для работы с файлами, каталогами и URL-ами.

    Во-первых, это многочисленные предопределённые переменные, которые описаны в документации и значения которых в своём скрипте пможно посмотрев с помощью phpinfo() :

    Константа __FILE__ содержит имя текущего исполняемого файла.
    В отличие от PHP_SELF она содержит имя файла, исполяющегося в данный момент.
    очень полезной представляется конструкция dirname ( __FILE__ ) , на которую желательно заменить все вызовы файлов, лежащих в том же каталоге, что и вызывающий скрипт. For example:
    require dirname ( __FILE__ ). "/init.php"
    функция dirname() , наряду с basename() является одними из наиболее употребительных для работы с файлами и каталогами.




    В начало раздела Наверх


    Как разбить вывод из mysql постранично


    Как сделать постраничный вывод из mysql "как в яндексе"?
    по 10 записей на страницу, внизу - ссылки на остальные страницы?

    Сначала научимся получать из базы нужные записи.
    Их получение в mysql обеспечивается оператором LIMIT, который вызывается с двумя параметрами - с какой записи начинать, и сколько выводить (внимание! не по какую, а сколько!)
    SELECT * FROM table LIMIT 0,10
    этот запрос вернет записи с первой по 10, поскольку нумерация начинается с 0
    соответственно, запрос для третьей страницы будет выглядеть, как
    SELECT * FROM table LIMIT 20,10
    получается, что нам всего лишь надо передать в скрипт число, которое потом подставить в запрос.
    Этим будет заниматься код, который выводит ссылки на страницы.
    Естественно, в цикле.
    Для цикла нам понадобится количество записей, которое возвращает запрос без лимита.
    Это число можно получить двумя путями. Либо отдельным запросом, в котором отсутствует оператор LIMIT, а вместо перечисления полей после оператора SELECT запрашивается только count(*):
    $q = "SELECT count(*) FROM table" ;
    $res = mysql_query ( $q );
    $row = mysql_fetch_row ( $res );
    $total_rows = $row [ 0 ];


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

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

    Для начала определим, сколько всего получится страниц. Для этого надо поделить общее число записей на количество оных на одной странице и округлить результат в большую сторону. Таким округлением занимается в пхп функция ceil()
    $num_pages = ceil ( $total_rows / $per_page );
    В этом выражении участвует переменная $per_page , в которую мы положим количество выводимых на странице записей.
    Ведь, если это количество изменится, мы же не хотим ползать по всему коду и исправлять цифры? проще сделать это один раз в начале скрипта при объявлении переменной. В запрос, вторым параметром LIMIT, подставлять нужно, конечно же, тоже ее.

    Ну, а дальше, собственно, вывод ссылок.
    for( $i = 1 ; $i <= $num_pages ; $i ++) {
    echo
    '<a href="' . $_SERVER [ 'PHP_SELF' ]. '?num=' . $i * $per_page . '">' . $i . "</a>\n" ;
    }

    в цикле от 1 до $num_pages выводим ссылку с параметром num, равным числу, которое надо передать в LIMIT, а в тексте ссылки пишем номер страницы, поскольку людям понятнее видеть номер страницы, а не записи. На код это не влияет, а людям приятно.

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

    что у нас в результате получилось?

    // количество записей, выводимых на странице
    $per_page = 10 ;
    // получаем номер страницы
    if (isset( $_GET [ 'page' ])) $page =( $_GET [ 'page' ]- 1 ); else $page = 0 ;
    // вычисляем первый оператор для LIMIT
    $start = abs ( $page * $per_page );
    // составляем запрос и выводим записи
    // переменную $start используем, как нумератор записей.
    $q = "SELECT * FROM `table` ORDER BY field LIMIT $start,$per_page" ;
    $res = mysql_query ( $q );
    while(
    $row = mysql_fetch_array ( $res )) {
    echo ++
    $start . ". " . $row [ 'field' ]. "<br>\n" ;
    }

    // дальше выводим ссылки на страницы:
    $q = "SELECT count(*) FROM `table`" ;
    $res = mysql_query ( $q );
    $row = mysql_fetch_row ( $res );
    $total_rows = $row [ 0 ];

    $num_pages = ceil ( $total_rows / $per_page );

    for(
    $i = 1 ; $i <= $num_pages ; $i ++) {
    if (
    $i - 1 == $page ) {
    echo
    $i . " " ;
    } else {
    echo
    '<a href="' . $_SERVER [ 'PHP_SELF' ]. '?page=' . $i . '">' . $i . "</a> " ;
    }
    }


    Разумеется, вышеприведённый код подходит только как учебное пособие. С его помощью становится понятным принцип, но в реальных условиях мы сразу же столкнемся, как минимум, с двумя проблемами:
    Во-первых, кроме переменной $page нашему крипту явно будут переданы и другие переменные, да и адрес может совсем не совпадать с именем скрипта. А мы это при формировании ссылок не учитываем.
    Во-вторых, нормальный современный сайт немыслим без шаблонов. И такая ужасная лапша из SQL запросов, PHP кода и HTML тегов никуда не годится.

    Займемся решением этих проблем.
    Первая решается просто:
    $uri = strtok ( $_SERVER [ 'REQUEST_URI' ], "?" ). "?" ;
    if (
    count ( $_GET )) foreach ( $_GET as $k => $v ) if ( $k != "page" ) $uri .= urlencode ( $k ). "=" . urlencode ( $v ). "&" ;

    и полученную переменную $uri подставляем в код вместо $_SERVER['PHP_SELF']

    Вторая - тоже несложно. Шаблонизаторов много, но мы воспользуемся самым универсальным - PHP.
    Что же у нас получилось? А получился у нас - рефакторинг ! Переделка старого кода в соответствии с требованиями современности, плюс мелкое причесывание:

    <?
    //определим фрагмент запроса, который отвечает за то, какие записи мы запрашиваем
    $from_where = "FROM table WHERE filter=1" ;
    // и получим общее количество записей
    $res = mysql_query ( "SELECT count(id) " . $from_where );
    $row = mysql_fetch_row ( $res );
    $total_rows = $row [ 0 ];

    //дальше получаем номер страницы и значение для лимита
    if (isset( $_GET [ 'page' ])) $CUR_PAGE =( $_GET [ 'page' ]); else $CUR_PAGE = 1 ;
    $start = abs (( $CUR_PAGE - 1 )* $per_page );

    //выполняем запрос и получаем данные для вывода
    $query = "SELECT * $from_where ORDER BY date DESC LIMIT $start,$per_page" ;
    $res = mysql_query ( $query );
    while (
    $row = mysql_fetch_array ( $res )) $DATA [++ $start ]= $row ;

    //определяем адрес страницы без переменной page
    $uri = strtok ( $_SERVER [ 'REQUEST_URI' ], "?" ). "?" ;
    if (
    count ( $_GET )) {
    foreach (
    $_GET as $k => $v ) {
    if (
    $k != "page" ) $uri .= urlencode ( $k ). "=" . urlencode ( $v ). "&" ;
    }
    }

    //узнаем общее количество страниц и заполняем массив со ссылками
    $num_pages = ceil ( $total_rows / $per_page );
    for(
    $i = 1 ; $i <= $num_pages ; $i ++) $PAGES [ $i ]= $uri . 'page=' . $i ;

    //а дальше выводим в шаблоне днные и навигацию:
    ?>
    Найдено сообщений: <b> <?=$total_rows?> </b><br><br>
    <? foreach ( $DATA as $i => $row ): ?>
    <?=$i?>
    . <a href="?id= <?=$row [ 'id' ] ?> "> <?=$row [ 'title' ]) ?> </a><br>
    <? endforeach ?>

    <br>
    Страницы:
    <? foreach ( $PAGES as $i => $link ): ?>
    <?
    if ( $i == $CUR_PAGE ): ?>
    <b> <?=$i?> </b>
    <? else: ?>
    <a href=" <?=$link?> "> <?=$i?> </a>
    <? endif ?>
    <? endforeach ?>



    В начало раздела Наверх


    Определение IP адреса

    Беллетристика
    Theory
    Практика.
    Примечания.
    Comments

    Беллетристика
    Один из самых дремучих вопросов в околопхпешном вебе - это определение IP адреса.
    Такого количества неправильного кода не написано, наверное, ни для какой другой операции.

    Каждый, кто в один прекрасный день узнаёт о существовании переменной HTTP_X_FORWARDED_FOR, тут же воображает себя мегагуру, и заменяет ей REMOTE_ADDR. Потом приходит знание о других переменных (X_REAL_IP, VIA, и ещё вагон и маленькая тележка), изобретаются многослойные мегаконструкции, изобретатели хвастаются друг перед другом их многоэтажностью и сравнивают свои творения с "кодом из PHPbb!".

    При этом спроси любого из них - "какой именно адрес они хотят определить?" - ни один не ответит: понимание основ функционирования сети TCP/IP среди пхп-программистов традиционно слабое.
    А вот стремление к нахождению Идеального и Единственно Правильного Решения - традиционно сильное.
    В результате вместо IP адреса в логи пишется не пойми что.

    К примеру, возьмем, казалось бы, простой вопрос "какой именно IP адрес (из цепочки хостов, через которые идет запрос от компьютера клиента к серверу) мы хотим записать в лог?". После того, как выяснилось, что большинство пхп-программистов затрудняются на него ответить, я и решил написать эту заметку.

    А это, между прочим, очень важный вопрос. Не ответив на него, наряду с вопросом " Зачем нам нужен IP адрес?", приступать к самому определению бессмысленно.
    При том, что большинству читателей этого текста вопросы покажутся бессмысленными.
    Ну что ж, попробуем разобраться.

    Theory
    At first. Самые азы. Для тех, кто не знает.
    Все элементы массива $_SERVER, начинающиеся со слова "HTTP_" - это HTTP-заголовки.
    Как уже знают вдумчивые читатели фака на танке, HTTP заголовки присылает клиент. И прислать может любые.
    К примеру, заголовок X-All-Your-Base-Belongs-To-Us: Surrender!
    Или, как вы уже, наверное, догадались, заголовок X-Forwarded-For: admin durak
    Мне кажется, что записывать столь глубокомысленную строку вместо IP адреса - не самая лучшая идея.
    Как и вообще доверять любым переменным, начинающимся с HTTP. Это первое правило, которое надо запомнить с молоком матери: Любые элементы массива $_SERVER, начинающиеся с "HTTP_", можно использовать только в справочных целях! К примеру, HTTP_REFERER записываем, чтобы потом посмотреть. Но ни в коем случае не делаем на него Location.

    Во-вторых, определимся с тем, ДЛЯ ЧЕГО нам нужен IP адрес. Если мы хотим записать в лог, то пишем однозначно только REMOTE_ADDR. В этой переменной содержится реальный IP адрес реального хоста в интернете, который произвел соединение с нашим сервером. Единственный реальный адрес. Никаких других сервер не знает.
    Апач пишет в логи именно REMOTE_ADDR. Не надо считать авторов веб-сервера дурнее себя.

    Что значит - реальный IP адрес? А то и значит. Адрес хоста, который произвел соединение с нашим сервером. Этот адрес по определению может быть только один. Один, а не 5 по цепочке. Рассмотрим типичный пример:
    Есть пользовательский компьютер, который, который находится в офисной сети. IP компьютера 192.168.0.22
    Офисная сеть включена через роутер в сеть здания. IP роутера - 10.10.0.3
    Сеть здания, в свою очередь, подключена к интернету, через роутер. IP роутера - 77.88.22.11
    Пользователь заходит на сайт, через НТТР прокси. IP прокси - 212.121.0.8
    Так вот, сеть TCP/IP так устроена, что каждый следующий узел ничего не знает о предыдущих. Есть только пара хостов, которые соединяются друг с другом. В самих TCP/IP пакетах никакой информации о предыдущих хостах не предусмотрено.
    Поэтому, как это ни обидно, но реальным адресом мы можем считать только последний в цепочке - адрес HTTP прокси.

    Больше того. Ну допустим, узнали мы адрес компьютера пользователя (чем кичатся многие определители с помощью activeX и ява-апплетов). Этот адрес - 192.168.0.22. Он из приватной сети . Компьютеров с такими адресами в мире - миллионы. Найти компьютер по такому адресу - невозможно. Пользы от него - практически никакой. Практически, но не совсем. Why? Слушаем дальше:

    Поскольку в протоколе HTTP текстовые заголовки, то в них можно добавить свой. Что некоторые хосты и делают. В те самые X-Forwarded-For, Via и прочие.
    Можем мы их использовать? Можем. Если правильно понимать - для чего.
    Для определения "реального IP адреса", как мы уже убедились - нельзя. А для чего же можно? Например - справки. просто записать, на всякий случай. BUT! Только в том случае, если мы откажемся от дурацкой идеи найти один идеальный IP адрес. Если мы не будем писать вместо реального всякую лабуду, а будем записывать все похожее на IP адрес с реальным наряду, то почему нет?
    Итак, можно записать всё, похожее на IP адрес. Понадобится выявить злостного вредителя - возможно, какой-то интересный айпишник среди заголовков и проскочит.
    Для небольшого повышения надежности сессий - тоже можно. Писать в сессию не только реальный, но и все похожие. И все сверять. Хоть один не совпал - сессию рубим.
    И в других подобных случаях.
    Не забывая: реальный - отдельно, все похожее на IP адрес - отдельно.
    Не забывая: особо полагаться на все эти заголовки не стоит.

    Практика.
    So. Из всего вышеизложенного делается простой вывод.
    IP адрес в скрипте может быть только один. Лежит он в переменной REMOTE_ADDR.
    Следовательно, вожделенный код получания "идеального IP адреса" выглядит, как
    $ip = $_SERVER [ 'REMOTE_ADDR' ]
    Dot.

    Further. Если мы хотим воспользоваться "заголовками, похожими на IP адрес" (лучше всего, во избежание недоразумений, совсем не считать их адресами хостов, а HTTP заголовками особого формата. Тем более, что никакого стандарта на содержимое заголовков X-Forwarded-For, Via и прочих - нет. Там могут оказаться IP адреса чарез запятую, или доменные имена или не через запятую. Не говоря уже о подделках!), то нет смысла судорожно искать все имена заголовков, где может встретиться адрес. Проще искать сами адреса.
    Берем, пишем простой код, который в цикле перебирает массив $_SERVER, и регулярным выражением выцепляет все заголовки, в которых встречается подхдящая под шаблон IP адреса строка. Если встретилась, то весь заголовок - с именем и всем содержимым - добавляем в массив или в строку. Которая хранится отдельно от IP адреса, в текстовом виде.
    Соответственно, в нужном месте повторяем операцию, и сверяем. IP адрес с IP адресом, строку похожих заголовков - со строкой похожих заголовков.
    function get_all_ip () {
    $ip_pattern = "#(? :( ?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)#" ;
    $ret = "" ;
    foreach (
    $_SERVER as $k => $v ) {
    if (
    substr ( $k , 0 , 5 )== "HTTP_" AND preg_match ( $ip_pattern , $v )) $ret .= $k . ": " . $v . "\n" ;
    }
    return
    $ret ;
    }

    Такой вот, несложный код.
    Правда, нужда в нем, если задуматься, очень невелика. Разве что, для тех же сессий. А для справки, про запас... не проще ли писать вообще все HTTP заголовки, пришедшие в скрипт? И это поинформативнее будет, чем выцеплять какой-то один адрес из HTTP_X_REAL_IP.
    Да и для сессий следует применять с осторожностью - IP адрес может оказаться, к примеру, в реферере...

    Примечания.
    Недавно я выяснил удивительную вещь. Оказывается, на свете существуют криворукие хостеры, у которых на сервере нет REMOTE_ADDR (а точнее есть, но в нем лежит... адрес самого сервера!). И пихают они адрес удаленного хоста кому куда бог на душу положит. Некоторые - вы будете смеяться - в HTTP_X_FORWARDED_FOR. Говорят, в некоторых больших программных продуктах есть даже специальная настройка для таких случаев - "Получать IP-адреса из заголовка X_FORWARDED_FOR".
    Разумеется, этот курьёз не опровергает сказанного выше, и не стоит кидаться писать автоматические определители IP с его учетом. Все подобные случаи должны разбираться только в ручном режиме, самим программистом. Который сначала убедится - где именно в HTTP_X_FORWARDED_FOR лежит нужный адрес - в начале цепочки запятых или в конце, напишет правильный рег, и только потом в настройки сайта добавит код
    $_SERVER['REMOTE_ADDR']=get_ip_from_xff();

    Примечание для хостеров: mod_realip или mod_rpaf
    Примечание для пользователей: разумеется, таких хостеров надо избегать, как калёного железа. Наверняка ведь это не единственная их криворукость?

    В начало раздела Наверх


    Проблемы с кодировкой в MySQL версий 4.1+

    Быстрые рекомендации
    Подробные объяснения.
    Перекодировка
    Исправление БД, в таблицах которой неверно указана кодировка
    Если все равно ничего не получается
    Notes
    ОПС
    Comments

    Быстрые рекомендации
    Если из базы выводятся вопросики, то после соединения с сервером выполняем волшебный запрос
    set names кодировка
    Параметр "кодировка" должен соответствовать кодировке, в которой выводятся страницы на сайте.
    eg:
    set names utf8
    Если это не помогло, и всё равно идут вопросики или крокозябры - значит, криво настроена кодировка таблиц. В этом случае см. рис.1 п. "Исправление БД" ниже.
    Если выводится нормально, но сортировка хромает - см. туда же.

    Подробные объяснения.
    До версии 4.1 кодировку данных в MySQL можно было задать только одну на всех. Теоретически, ничто не мешало в одной таблице хранить данные в юникоде, а в другой - в KOI-8. Так все и поступали: в конце концов, для БД любые данные - это всего лишь набор цифр. Что в неё положил - то она тебе и вернёт. Но правильный поиск и сортировка работали только для данных, которые были в кодировке, совпадавшей с настройками MySQL.

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

    Во-первых, для каждого поля в таблице были введены два параметра: кодировка (CHARACTER SET) и правила сравнения (COLLATION).
    Кодировка - это понятно. Говорим базе, в какой кодировке лежат наши данные.
    Правила сравнения задают порядок сортировки и сравнения данных при поиске.
    COLLATION жестко привязан к CHARACTER SET-у и может быть задан только из поддерживаемых кодировкой. Проще говоря, начало названия COLLATION должно совпадать с CHARACTER SET. К примеру, для кодировки utf8 можно задать правила сравнения utf8_bin, но нельзя cp1251_bin.
    Обычно у каждой кодировки есть, как минимум, два набора правил сравнения - имякодировки_bin и имякодировки_general_ci . Первый сравнивает в лоб по кодам символов, а второй - регистронезависимо, учитывая совпадающие символы. COLLATION имякодировки_general_cs сравнивает регистрозависимо, отличаясь от _bin тем, что учитывает совпадающие символы ("е" и "ё" в русском языке), а также, при сортировке, ставит на место те символы, которые идут в кодировке не по порядку (например "ё" в 1251).
    Если для поля не указан COLLATION, то он берется по умолчанию. К примеру, для utf8 - utf8_general_ci. В большинстве случаев COLLATION по умолчанию устраивает пользователя, а это значит, что его задавать не нужно. То есть, достаточно указать только кодировку.
    Кодировка может быть задана для поля, таблицы, database и для всего сервера. Установки имеют характер умолчаний, и могут быть изменены на любом уровне:
    Кодировку (и сортировку) можно указывать для каждого отдельного поля. Если они не указаны, то при создании таблицы берутся из кодировки, указанной для этой таблицы. Если при создании таблицы кодировка не указывается, то она берется из параметров database. Так же и при создании database - либо задаются явно, либо берутся из параметров сервера.

    Во-вторых, появилась необходимость говорить базе данных, в какой кодировке мы записываем или хотим получить свои данные. То есть, появилось такое понятие, как кодировка клиента . Здесь и кроется ответ на вопрос - "откуда берутся "вопросики"? Они появляются, если кодировка таблицы не совпадает c кодировкой клиента.
    Соответственно, в MySQL появились две новые команды
    set character_set_client
    и
    set character_set_results

    Первая указывает, в какой кодировке приходят данные в базу, а вторая - в какой их выдавать. Поскольку чаще всего эти кодировки совпадают, то можно писать короче - один запрос "SET NAMES кодировка", который и устанавливает оба эти параметра.

    Из приведенных объяснений должно быть ясно, что для беспроблемной работы нам надо сделать всего две вещи:
    1. Указывать правильную кодировку клиента.
    Это можно сделать либо в настройках сервера в my.ini, либо тем самым запросом SET NAMES.
    2. Создавая таблицы, не забывать указывать правильную кодировку для них.
    Это можно сделать несколькими способами.
    Самое простое - это указывать кодировку и правила сравнения прямо в коде CREATE TABLE. Example:

    CREATE TABLE `chartest` (
    `name` varchar(10) default NULL
    ) ENGINE=MyISAM CHARACTER SET=utf8


    Но что, если у нас огромный дамп на сотни таблиц, сделанный в прошлой версии MySQL? Дописывать к каждой таблице вручную? Возможно, это и придется делать. Но сначала надо попробовать установить параметры по умолчанию.
    Как мы помним, при создании таблиц, если для них не указывается collation и charset, эти параметры берутся из настроек database.
    Следовательно, надо попытаться изменить эти настройки.
    сначала смотрим, какие они сейчас: заходим в консоль и пишем

    use `mydb`
    show variables like "character_set_database";

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

    alter database `mydb` character set utf8;

    Если запрос прошел успешно, то проверяем ещё раз, и, если все нормально, то начинаем создавать таблицы или заливать дамп.
    Если таким образом сделать не удалось (не хватает прав), то варианта только два - или обращаться к провайдеру, чтобы он сам поменял настройки, или дописывать COLLATION И CHARACTER SET ко всем создаваемым таблицам вручную.

    Перекодировка
    Как следует предыдущих объяснений, кодировка клиента должна соответствовать реальной кодировке поступающих данных. В этом случае, даже если данные лежат в другой, то всё равно никаких проблем не будет - MySQL автоматом перекодрует туда и обратно.
    Проведем эксперимент. Для него нам потребуется MySQL, установленная под Windows. Для тех, у кого другая ОС, я думаю, поменять кодировки в терминале проблемы не составит.
    Для демонстрации возможностей перекодировки воспользуемся тем фактом, что по умолчанию консоль windows настроена на старую кодировку DOS - 866. То есть, сначала мы создадим таблицу в этой кодировке и запишем в неё данные, а потом попробуем общаться с базой в другой кодировке.

    Сначала запустим командный интерпретатор cmd.exe и установим в свойствах окна шрифт Lucida Console.
    затем вызываем консоль mysql:
    C:\MySQL\bin\mysql.exe -uroot test
    В консоли пишем:

    set names cp866;
    CREATE TABLE ct (`name` varchar(10) default NULL)CHARACTER SET=cp866;
    insert into ct values ('Вова');
    select * from ct;


    Если мы все сделали правильно, то вывод будет таким:
    +------+
    | name |
    +------+
    | Вова |
    +------+


    дальше пишем exit , выходим из консоли, и пишем команду
    chcp 1251
    которая сменит кодировку окна консоли windows на 1251
    затем снова запускаем консоль mysql и пишем:

    set names cp1251;
    select * from ct;
    insert into ct values ('Вова');
    select * from ct;


    То же самое можно повторить и для кодировки utf8 (chcp 65001).
    В результате мы видим, что даже тогда, когда данные поступают не в той кодировке, в которой они хранятся в базе, работа с ними происходит совершенно корректно. При этом они продолжают лежать в базе в той же самой кодировке, в которой они были с самого начала - 866.

    Возможности перекодировки ограничиваются, разумеется, одним и тем же языком. То есть, из 1251 можно перекодировать в 866, в koi8r, в UTF8. В latin1 из 1251 перекодировать нельзя - появятся вопросики.

    Исправление БД, в таблицах которой неверно указана кодировка
    Или что делать, если буквы нормальные, а поиск и сортировка работают странно.

    Итак, у нас есть проблемы, которые не решаются запросом SET NAMES. Это значит, что в таблицах лежат данные в одной кодировке, а указана для этих таблиц - другая. В принципе, быстрое решение этой проблемы можно вывести из предыдущих объяснений - сделать запрос SET NAMES с кодировкой, которая указана в таблице. Посмотреть её можно запросом show create table `table` .
    Если там в последней строчке написано DEFAULT CHARSET=latin1, то выполняем запрос SET NAMES latin1
    В таблице не будет работать толком сортировка и поиск, но хотя бы сами данные будут отдаваться и записываться нормально (если кодировка html страницы соответствует фактической кодировке лежащих в базе данных). Но это, конечно, ненормальная ситуация, тем более, что исправить её совсем несложно.
    Что мы сейчас и проделаем.

    Для исправления существует два способа, которые описаны по ссылкам внизу.
    Я воспользуюсь вариантом из FAQ сайта linux.by, приведя только его содержательную часть. Но настоятельно рекомендую прочитать подробный вариант, с объяснением опций и исправлением возможных ошибок .

  • Узнаём кодировку таблиц (show create table `table`).
  • Делаем дамп БД с помощью mysqldump
    Допустим, мы выяснили, что таблицы были созданы по умолчанию в кодировке latin1, а фактически в них содержатся данные в utf8. В этом случае используем команду:
    mysqldump -uUSERNAME -pPASSWORD DB_NAME --allow-keywords --create-options --complete-insert --default-character-set=latin1 --add-drop-table > dump.sql
    Распространненая ошибка в таких случаях - когда в --default-character-set указывают фактическую кодировку данных, в данном случае - utf8. В дампе будет мусор. Указывать надо ту, которая установлена в таблицах. В результате MySQL не будет пытаться данные перекодировать, и отдаст как есть.
  • Просматриваем файл с дампом, чтобы убедиться, что в файле нормальные данные в кодировке utf8, а не мусор.
  • Целый и проверенный дамп копируем в сторону. Далеко в сторону.
  • В файле дампа поправляем операторы CREATE DATABASE и/или CREATE TABLE для создания таблиц в правильной кодировке. Либо меняем настройки database, как это было описано выше.
  • заливаем дамп обратно:
    mysql -uUSERNAME -pPASSWORD DB_NAME --default-character-set=utf8 < dump.sql
  • в код сайта, после функций mysql_connect и mysql_select_db добавляем строчку
    mysql_query("SET NAMES utf8") ;
  • Всё, можно работать!

    Если все равно ничего не получается
    Большое количество вопросов по кодировкам, не имеющих отношения к БД, побудило меня составить небольшое сводное руководство. Итак, кодировка нашего сайта складывается из 4 пунктов:
    1. Кодировка базы данных.
    • Задается при создании таблиц.
    • Может быть любая. Должна отражать реальную кодировку данных в таблице.
    • Например, если данные у нас будут лежать в кодировке Windows-1251, то создавая таблицу, пишем CREATE TABLE chartest (string text) DEFAULT CHARSET=cp1251;
    • Проверить текущую кодировку таблицы можно запросом SHOW CREATE TABLE tablename
    2. Кодировка клиента БД (клиентом в данном случае является скрипт, работающий с БД).
    • Задается сразу после соединения с БД, запросом SET NAMES кодировка
    • Должна совпадать с кодировкой выводимой HTML страницы.
    • Например, если страница у нас в utf-8, то в PHP пишем mysql_query ( "SET NAMES utf8" );
    • Проверить можно запросом show variables like '%char%'; (переменные character_set_client, character_set_connection и character_set_results должны иметь установленное нами значение)
    3. Кодировка сайта.
    • Задается HTTP заголовком Content-type.
    • Должна соответствовать кодировке данных на странице.
    • Например, если страница у нас в utf-8, то в PHP напишем header ( "Content-Type: text/html; charset=UTF-8" );
    • Проверить можно, выбрав в браузере пункт меню Вид - кодировка. Так же полезно посмотреть, какую кодировку шлет сервер в заголовке.
    4. Кодировка данных на странице.
    • Задается в редакторе.
    • Должна соответствовать той кодировке, которую мы хотим.
    • Особых рекомендаций здесь дать невозможно, но хотя бы минимальная компьютерная грамотность, чтобы выбрать в редакторе желаемую кодировку при сохранении, автору сайта необходима.

    NB: Обозначения кодировок в mysql могут не совпадать с общепринятыми. Внимательно смотрите примеры

    Примечания
  • Решая проблемы с кодировкой БД, не забывайте указывать правильную кодировку САЙТА!
    Во-первых, убедитесь, что сервер отдает правильную кодировку в HTTP заголовке и в meta теге.
    Во-вторых, если в коде присутствуют перекодировки, то убедитесь, что они работают, и работают правильно. А ещё лучше - вообще уберите их, и пользуйтесь средствами БД.
    И вообще - старайтесь отличать проблемы браузера от проблем с БД.

    ОПС
    Если бы я нашел этот текст чуть раньше, то эта статья не была бы написана. Все очень толково и подробно: http://www.linux.by/wiki/index.php/FAQ_PHP_MySQL_charset
    Еще один неплохой текст по кодировкам: http://mysqlfaq.wikispaces.com/Encoding
    Официальная документация, разумеется. Я, конечно, знаю, что ни один из читателей этого фака не пойдет читать документацию на английском... Но сам я брал информацию именно оттуда. http://dev.mysql.com/doc/refman/5.0/en/charset.html

    В начало раздела Наверх


    Пример кода, работающего с MySQL


    Самая классическая задача при работе с базой данных - это приложение вида форма-таблица.
    Таблица отображает записи, лежащие в БД, а форма служит для их добавледния/редактирования.

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


    <?
    mysql_connect
    ();
    mysql_select_db ( "new" );
    $table = "test" ;
    if(
    $_SERVER [ 'REQUEST_METHOD' ]== 'POST' ) {
    if (
    $id = intval ( $_POST [ 'id' ])) {
    $query = "UPDATE $table SET name='" . mysql_real_escape_string ( $_POST [ 'name' ]). "' WHERE id=$id" ;
    } else {
    $query = "INSERT INTO $table SET name='" . mysql_real_escape_string ( $_POST [ 'name' ]). "'" ;
    }
    mysql_query ( $query );
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'PHP_SELF' ]);
    exit;
    }
    if (!isset(
    $_GET [ 'id' ])) {
    $LIST =array();
    $query = "SELECT * FROM $table" ;
    $res = mysql_query ( $query );
    while(
    $row = mysql_fetch_assoc ( $res )) $LIST []= $row ;
    include
    'list.php' ;
    } else {
    if (
    $id = intval ( $_GET [ 'id' ])) {
    $query = "SELECT * FROM $table WHERE id=$id" ;
    $res = mysql_query ( $query );
    $row = mysql_fetch_assoc ( $res );
    foreach (
    $row as $k => $v ) $row [ $k ]= htmlspecialchars ( $v );
    } else {
    $row [ 'name' ]= '' ;
    $row [ 'id' ]= 0 ;
    }
    include
    'form.php' ;
    }
    ?>

    form.php
    <form method="POST">
    <input type="text" name="name" value=" <?=$row [ 'name' ] ?> "><br>
    <input type="hidden" name="id" value=" <?=$row [ 'id' ] ?> ">
    <input type="submit"><br>
    <a href="?">Return to the list</a>
    </form>

    list.php
    <a href="?id=0">Add item</a>
    <? foreach ( $LIST as $row ): ?>
    <li><a href="?id= <?=$row [ 'id' ] ?> "> <?=$row [ 'name' ] ?> </a>
    <? endforeach ?>


    В начало раздела Наверх


    Как писать музыку на PHP


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

    Нижеследующий текст призван ответить на можеcтво подобных вопросов разом.

    Ответ: Для большинства перечисленных задач в PHP встроенных функций нет. РНР - это язык обработки гипертекста. Или просто текста. Вот с текстом РНР работает великолепно. А если вы хотите работать с бинарными данными, то следует посмотреть в сторону специально предназначенных для этого утилит.
    Вся работа РНР сведется в этом случае к вызову внешней программы с помощью команды system() или её аналогов.

    Определить, существуют ли у РНР встроенные функции для решения той или иной задачи, можно с помощью документации. Просто посмотрев список функций . Если лень, то можно использовать тот же самый критерий: текстовый или бинарный у нас протокол. К примеру, HTTP, FTP, POP3, SMTP - это все текстовые протоколы. И РНР, в силу своей текстовой сущности, прекрасно с ними работает.
    Всякие картинки, видео, музыка - это бинарные данные. И для работы с ними стоит использовать внешние утилиты. Мне сейчас возразят, что с картинками PHP работать умеет. Ага, умеет. Полтора формата и imagestring. Граждане. Давно пора забыть про эту убогую библиотечку - в мире есть множество гораздо более продвинутых средств для работы с графикой. И все, что нужно для их освоения - это научиться работать с ними из командной строки, а потом аккуратно перенести эту строку в функцию system().

    For reference:
    Для работы с видео - ffmpeg
    Для работы с графикой - ImageMagick

    Так же к этому разделу можно отнести многочисленные вопросы, посвященные скачиванию. Чего угодно и откуда угодно. Файлов с русскими буквами, файлов с пробелами в имени, файлов с зарегистрированными в браузере расшинениями (например html). Как заставить скачиваться или наоборот - как заставить открыть.

    Ответ на все эти многочисленные вопросы один. И очень простой. Решается в два счёта:
    1. Находим сайт, который реализует нужный нам функционал. Заодно проверяем - а возможно ли это в принципе. Если не нашли, то, скорее всего, нельзя. Если нашли, то
    2. Берем любую программу просмотра НТТР заголовков, выполняем требуемую операцию на найденном сайте и смотрим, какие заголовки он формирует. После этого формируем у себя такие же.
    Задача решена.



    В начало раздела Наверх


    Пример системы управления сайтом

    См. статью http://phpfaq.ru/easy/


    В начало раздела Наверх


    Mail injection. Работа с e-mail средствами PHP.

    What it is?
    Формат e-mail
    Protection
    Comments

    Что это такое?
    Это когда форма обратной связи на сайте используется для рассылки спама.
    How? Cамый дурной вариант - когда скрипт позволяет пользователю подставить адрес получателя. Это уж совсем ни в какие ворота не лезет. И даже словом injection не назовешь - а просто головотяпство.
    Собственно injection - это когда адреса получателей подставляются в поля формы так, чтобы попасть в заголовки письма. К примеру, в заголовок.

    Говоря о mail injection, невозможно обойти вниманием сам принцип работы почтовых отправлений. Даже больше скажу: человек, который знает, как устроено электронное письмо, сразу понимает, в чем смысл таких инъекций, и как от них защищаться. Вообще, это та проблема, о которой я все время говорю: Давая новичку полное понимание предмета, ты отвечаешь ему на сто вопросов разом. А пичкать ответами на каждый отдельный вопрос - ну это же жутко непроизводительно! Не по-программистски!
    Что проще - посмотреть ответ в таблице умножения, или постоянно бегать к соседу с вопросами - сколько будет пятью пять, трижды семь?
    Что проще - один раз объяснить (а ещё лучше - отправить почитать) про формат письма или отвечать на сто вопросов про инъекции, кодировки, вложения файлов и так далее?

    Формат e-mail
    Итак, что собой представляет почтовое сообщение?
    Во-первых, это обычный текст. просто набор строк определенного формата. Которые можно читать и, при некотором навыке - понимать.
    Во-вторых, вы будете смеяться, но устроено оно точно по тому же принципу, что и наши веб-страницы.
    При запросе страницы по протоколу HTTP сначала идут заголовки, каждый на своей отдельной строке, а затем - тело ответа. Соответственно, заголовки от тела отделяются пустой строкой. Такой вот нехитрый формат. Сначала каждая строка воспринимается как заголовок, а как только встречается пустая - значит, дальше идет тело, которое надо показывать.

    В почтовом сообщении все устроено точно так же.
    Вообще, я считаю, что один раз посмотреть - это лучше, чем сто раз прочесть. А при работе с почтой исходный текст письма так же важен, как и исходник HTML страницы при разработке сайта на пхп.
    Здесь ещё одна проблема. Очень многие программисты просто не знают, что они хотят сделать. Я, говорит, хочу отправить письмо. Но ведь письмо - это ТЕКСТ! Казалось бы, чего проще - составил сначала текст, который хочешь получить, убедился, что этот текст работает - и генери точно такой же на пхп! But no. поскольку человек не знает, из каких элементов состоит исходный текст письма, и какой за что отвечает - то и тычется с вопросами: а почему у меня письмо крокозябрами? А почему у меня письмо нормальное, а заголовок крокозябрами - я ведь кодировку указал? А почему заголовок не "Вам письмо" а лабуда какая-то - =?koi8-r?B?98HNINDJ09jNzw==?= ???
    С SQL запросами и HTML текстом то же самое. Я, говорит, хочу меню на пхп. Милый - объясняешь ему - меню на пхп не бывает! Нарисуй какое хочешь меню на HTML, а потом пиши скрипт на пхп, который рисует такое же. Мне не надо - орет - на HTML! Подожду ответа более грамотного специалиста!

    Так же и здесь. Ну разберись один раз - что какой заголовок значит и как кодируется.
    В бате и в Outlook Express посмотреть исходник письма можно. В MS Outlook - только заголовки. В других клиентах не знаю. Но Outlook Express есть на любой виндовой машине, а пользователям других систем, я надеюсь, не нужно объяснять формат почтовых сообщений. Поэтому рекомендую создать письмо в OE, зайти в отправленные, нажать Свойства - Подробно - Исходное сообщение.
    Да, очень рекомендую перед этим в настройках выставить формат отправления, как "просто текст". иначе исходник будет гораздо сложнее для понимания. Что мы там увидим?
    From: "phorror" <[email protected]>
    To: =?koi8-r?B?98HNINDJ09jNzw==?= <[email protected]>
    Subject: =?koi8-r?B?98HNINDJ09jNzw==?=
    Date: Wed, 13 Jun 2007 10:08:32 +0400
    MIME-Version: 1.0
    Content-Type: text/plain;
    charset="koi8-r";
    Content-Transfer-Encoding: 7bit
    X-Priority: 3
    X-MSMail-Priority: Normal
    X-Mailer: Microsoft Outlook Express 6.00.2900.3028
    X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3028

    test
    test
    test


    на заголовки, начинающиеся с X, обращать внимание не надо - это необязательные.
    главное, что мы видим:
    1. Все, как я и рассказывал - сначала заголовки, потом пустая строка, потом текст
    2. Формат заголовка: начинается с новой строки, дальше идет ключевое слово, дальше двоеточие, пробел и значение (опять очень похоже на НТТР заголовки!)
    3. Формат указания e-mail адресов. Кроме имени, понятно без перевода. про имя ниже.
    4. формат указания заголовка. Ничё непонятно. Хотя, если не ужасаться, а сесть и немножко подумать, то можно сообразить, что, во-первых, такая лабуда применяется для форматирования строк в кодировках, отличных от latin1, а, во-вторых, формат-то у них совсем простой. Вопросительные знаки разделяют разные поля (точь-в точь как палочки | или пять троеточий в твоей первой гостевой книге). Первым полем идет явно кодировка языка. Вторым - можно догадаться - формат кодирования текста. Там может быть B или Q. (base64 и Q-encoding соответственно). А дальше - сам текст заголовка.
    Следовательно, понятен и алгоритм раскодирования: разбиваем по вопросам, раскодируем текст (функции есть в пхп, для Q подойдет от quoted-printable, как я понимаю), перекодируем язык если надо. All. Для сборки своего заголовка - обратная операция.
    Если бы сабжект был английский, то он выглядел бы просто:
    Subject: This is a test letter
    5. Поле Content-Type показывает нам, что заголовок может состоять не из одной строки, а может продолжаться на следующих - для этого дополнительные строки должны начинаться с пробельного символа. А так же, что она отвечает за кодировку текста письма, указывает формат - текст или html, и ведает ещё одной очень важной функцией: отвечает за структуру сложных, multipart сообщений, которые мы рассматривать здесь не будем.

    Из всего вышесказанного видно, что научиться отправлять собственные письма (те, которые формирует скрипт, а не из заполненной формы) очень просто:
    1. Пишем в аутлуке ровно такое же письмо, которое хотим отправлять скриптом.
    2. Смотрим заголовки.
    3. Пишем скрипт, который формирует точно такие же.
    4. Подсталяем эти заголовки в нужные поля функции mail
    5. Готово!
    И никаких проблем с кодировками самого письма, заголовоков, имени отправителя! слово "крокозяблы" пропадает из нашего лексикона!
    И с SQL запросами все ровно то же самое. новички часто пытаются сразу писать скрипт, который формирует сложный запрос, даже не представляя себе в точности, как этот запрос должен выглядеть! Запрос сначала надо написать и отладить в консоли или графическом клиенте. А потом писать пхп скрипт, который формирует точно такой же текст запроса.

    All. Теперь можно переходить к инъекциям.

    Protection
    Исходя из структуры письма можно сделать простой вывод: используя переводы строк, можно добавить любое количество новых заголовоков, в том числе - и адресов получателей.
    то есть, если мы напишем в своем коде
    $subject=$_POST['subject'], а в этом поле будет написано сто адресов получателей, разделенных переводами строк, то письмо отправится сотне жертв. просто, как два байта переслать.

    Отсюда следует, что и защита форм обратной связи такая же простая.
    Самое надёжное - помещать введенную пользователем информацию только в текст письма, и никуда больше. Сам я использую именно этот метод. Мне он нравится своей простотой и надежностью. А чем решение проще - тем оно мне милее.

    Если же ну прямо так уж хочется эмулировать отправку "настоящего" емейла - с заголовком, адресом отправителя и так далее, то все данные из формы, которые вставляются не в текст сообщения, следует обязательно проверить на наличие символов "\r" и "\n"!
    И при наличии таких символов письмо не отправлять.
    Адрес получателя, разумеется, дожен быть жестко прописан в скрипте (видал я некоторых уникумов, писавших адрес в скрытое поле в форме).

    All.

    В начало раздела Наверх


    Безопасность PHP скриптов

    Files
    SQL Injection
    Eval
    Mail Injection
    XSS
    CSRF, Cross-site Request Forging
    HTTP Injection
    Инициализация переменных
    Заливка файлов на сервер
    ОПС
    Comments

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

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

    Files
    В хорошем современном сайте уже редко можно встретить передачу имени файла в скрипт - данные хранятся в базе, а модули подключаются по более сложным алгоритмам, чем просто передача имени файла в адресной строке. Однако начинающие программисты просто без этого не могут. Доходит до анекдота - "хочу, чтобы было index.php?module=news потому что news.php - не солидно, сайт не выглядит крутым!". Ну, на что не пойдешь ради того, чтобы выглядеть круто. Надо только не забывать о безопасности. О том, что подсунуть для инклюда можно через адресную строку что угодно - от файла на другом сервере (с любым пхп-кодом!), до файла на своем - с настройками или паролями.

    Для борьбы с такими подстановками есть несколько методов. Можно проверять имя файла регулярками, но мне ближе всего функция basename() , которая отрезает от имени файла все лишнее.
    Если надо передавать файл вместе с путем к нему, к примеру, themes/green/balloons.php, то проверка будет сложнее. Поэтому я бы рекомендовал, все-таки, передавать только имя файла. Или не передавать ничего.

    SQL Injection
    Следующий тип атак довольно подробно рассмотрен в уже имеющейся статье на этом сайте, \"Кавычки \". Cоставление запросов, слеши, SQL Injection . Хочется лишь добавить, что защита от инъекций - не главное в соблюдении правильного синтаксиса, а побочный продукт, и синтаксис надо соблюдать всегда. Особенно - учитывая инъекции второго порядка, когда данные в запрос подставляются не от пользователя, а уже лежащие на сервере.

    Eval
    Удивительно, но многие программисты не осознают того факта, что применять eval к пользовательским данным - это все равно, что лично вручать вору ключ от сейфа. Этой функцией и так-то следует пользоваться очень осторожно, а на обработку ей введенных пользователем данных и вовсе должен быть жесткий запрет.
    При этом следует понимать, что пользовательские данные остаются таковыми и после того, как пользователь непосредственно отправил их в скрипт. Полученные из базы данных при последующих исполнениях скрипта, они не становятся менее опасными.

    Mail Injection
    Получившая распространение в последнее время атака так же уже рассмотрена на этом сайте, Mail injection. Работа с e-mail средствами PHP. . Коротко можно сказать, что нельзя допускать наличие переводов строки в данных, которые попадут в заголовки письма.

    XSS
    Тип атаки, так же получивший распространение сравнительно недавно. В отличие от всех предыдущих, которые атакуют непосредственно код скрипта, XSS атакует его функциональность: заставляет выполнять пользователя действия, нужные хакеру, или ворует его, пользователя, данные.
    Методы весьма разнообразны и многочисленны, однако наиболее распространенный и опасный - это внедрение javascript инструкций в безобидные теги - <img>, <a> и так далее.
    Наиболее действенным методом борьбы с этим является полный запрет на ввод пользователем какого бы то ни было HTML кода. Методов борьбы с этим достаточно много, но следует помнить, что функция strip_tags() не убирает опасные вставки из разрешенных тегов. поэтому лично я предпочитаю пользоваться функцией htmlspecialchars() , при выводе любых пользовательских данных.
    Но главная проблема при защите от XSS - не сам метод, а последовательность его применения. практически все известные атаки были произведены в тех частях сайта, где никто и подумать не мог об обработке пользовательских данных. But in vain. Обрабатывать их надо везде и всегда.

    Так же к этому разделу можно отнести атаки на сессии. Связанные с кражей или подстановкой идентификатора. подробнее можно почитать в разделе Сессии. Подробное описание работы и объяснение механизма.

    CSRF, Cross-site Request Forging
    Формирование на сайте хакера запроса, который будет выполнен браузером пользователя (и, как следствие - со всеми правами пользователя!). То есть, просто авторизация от этой атаки не спасает. Для защиты от этого типа атак следвет взять за правило добавлять ко всем важным формам скрытое поле, содержащее уникальную строку (сгенерированную случайным образом только для этой формы), записывать её в сессию и сравнивать при обработке формы. Хакер добавить такую строку в свою форму не сможет, и атака не сработает.

    HTTP Injection
    XSS уязвимость, похожая на mail injection, только объектом атаки являются не заголовки письма, а HTTP-заголовки. Атака экзотическая, но знать про неё надо. Хакеру ничего не стоит дописать к урлу код с яваскриптом, который благополучно через переменную PHP_SELF или REQUEST_URI программист сам вставит в свою страницу.

    Инициализация переменных
    Со времени появления PHP в нем есть одна очень неприятная функциональность: получить пользовательские данные в своем скрипте можно совсем того не ожидая. Да-да, речь идет о register_globals. Если эта директива включена, пользователь может создать в скрипте любую переменную с любым содержимым, если только она не была создана программистом. А программисты часто это делать забывают, к примеру, если мы имеем код
    if ( $login == "login" AND $password == "password" ) $admin = 1 ;
    и в дальнейшем проверяем переменную $admin, то любой может получить права админа, просто дописав в адресной строке index.php?admin=1.
    Лекарством от этого служит принудительная инициализация всех переменных перед использованием. В строгих языках, таких, как Паскаль, невозможно использовать переменную, предварительно не объявив. В PHP это возможно, но злоупотреблять этим не стоит - следует объявлять все переменные перед использованием. В качестве же временной заплатки можно порекомендовать register_globals=off.
    то же самое можно почитать на офсайте PHP, http://ru.php.net/manual/ru/security.globals.php

    Отдельной строкой следует упомянуть борьбу с register_globals. Попытки эмулировать её включение (если отключена) или нейтрализовать (если включена) приводят к обратным результатам. Многочисленные циклы с назначением или удалением переменных приводят к проблемам более худшим, если бы директива просто была установлена в желаемое значение. Единственный гарантированный способ избежать её влияния - инициализация переменных.

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

    ОПС
    Статья Сергея Круглова, http://www.captcha.ru/articles/antihack/


    В начало раздела Наверх


    Базовые понятия MySQL и отличия от текстовых файлов.

    FAQ. Порядок записей, первая и последняя.
    FAQ. ID.
    Язык SQL.
    FAQ. Перенос данных между разными серверами.
    Практическое использование.
    Comments

    FAQ. Порядок записей, первая и последняя.

    A list is a text file. Or, say, a list of students in a class journal.
    To get an ordered list, we must reorder the list when adding any record. При добавлени любой! А если мы хотим иметь два и больше видов упорядочивания? For example, the list of students is compiled in alphabetical order, but we are interested in seeing it in terms of academic performance. Start two lists? NO! Start a database.
    And to understand the first, most basic principle. Порядок у данных бывает только при выборке их из БД ! This is a very important fact. The data in the database is not stored as in a class journal - in order. And as in the hourglass - a bunch. Не задашь порядок вывода - будут выводиться от балды. The base itself does not remember which record was the first and which was the last. But it can sort by the attributes specified by the user when creating the table.

    Первое, что надо запомнить . There is no first and last record in the database. You can talk about the order only when displaying, if the user himself specified this order. For example, by sorting records alphabetically. Вот в такой выборке есть и первая и последняя.
    If we need to know the order in which records are written to the database, add a field with which to determine this.

    The next thing that a database differs from a file is random access. In a file, in order to refer to a line in the middle, you need to sort through all those going to it. When upgrading - even worse. In order to change the line in the string file - you need to rewrite it ALL! Whole. With the same database - everything is simple. We want to choose one line - choose! We want to change one line - we change. We want to delete two - delete!
    Примечание: На самом деле, БД - это не волшебный ящик, и хранит она данные не в нематериальном эфире, а в тех же самых файлах. И точно так же их переписывает. But it makes it completely transparent to the user.

    With arbitrary access and lack of internal numbering "first-second-third", we face the problem of identifying strings. Допустим, мы вывели строки из базы на экран, и теперь, кликнув по ссылке, хотим отредактировать одну из них. Как её запросить? By the contents of one of the fields? But what if this content coincides with a few more records? Нам нужен уникальный идентификатор! MySQL has an ingenious solution. One of the fields, usually called `id`, is autoincrement, that is, automatically increasing. When adding each new record, if the id is not specified or the value is zero, the database assigns a value to the id field, one more than the previous one. So we got a unique identifier - none of the lines in this table will have the same id. Now it's very easy to edit or output any entry - just specify its id.
    By the way, auto-incremental id has one more side property. Sorting the table for this field, you can get the very notorious first or last record Smile happy

    Однако, с этим полем связано множество ошибок и непониманий.

    FAQ. ID.
    Firstly, this field is mistaken for the numerator. As in the class journal - 1,2,3-th student ... And if the student was expelled from school? А если мы выбираем не всех учеников, а только мальчиков?
    Rule one: id to numbering has not the slightest relation! Firstly, because id can go not in order, and secondly, that all the same, we have the order is only for SAMPLE. Which can be anything, id in it can go completely different! If we want to number the results - please, in the output we add the code to PHP, which will do it. IN THIS PICK. It is necessary to number when outputting. It is because the sampling options can be an infinite number.
    Let's see why another beginner may need a numbering? Для определения количества выбранных записей. For this, there is a function - mysql_num_rows (). She will help us with ANY sample. While the numbering does not help us at all, for the reasons indicated above.
    Rule two: You can not change the record id, never in any way. First, it's just never necessary. Secondly, this is our UNIQUE ID. The id field only looks like numbers. In fact - this is the only way, and at any time to identify the record. Let's say we have a website with news. Someone put a link to the news with id = 1. Then we changed this id. As a result, a person will come to the wrong link. Если же вам необходимо id перенумеровать – значит, вам просто не нужно автоинкрементное поле.

    Язык SQL.
    Now a little bit about the SQL language. This, as I said, is a genius invention of mankind. Практически искусственный интеллект. Judge for yourself.
    In order to choose the names of all the pupils of the school, students in class 7A, sorting them alphabetically, you should write this query:
    SELECT lastname FROM school WHERE klass='7A' ORDER BY lastname
    This is an almost meaningful proposal in English!
    Let's try to make a translation:
    ВЫБРАТЬ фамилии ИЗ школы [такие,] ГДЕ класс равняется 7А, ОТСОРТИРОВАВ ФАМИЛИИ ПО АЛФАВИТУ
    Well, is not it bad? We are practically in the natural language telling the database what we need - and it gives us the right lines in the right order! И все это ОДНОЙ СТРОЧКОЙ! In this case, the complexity of the sample can be any. If we chose from a text file - we would have to write the program not on one sheet. And here - one line!
    Well, how? Do you still want to file files?

    FAQ. Transferring data between different servers.
    Data transfer via dump files containing INSERT requests.
    That is, on the server from which we want to transfer the database, we generate a file with INSERT requests for each row of each table in the database. And on the remote server, we simply execute these requests.

    The most correct and direct way to get a dump is to use the command line utility mysqldump
    Go in DOS or in the shell and write
    mysqldump -u<логин> -p<пароль> база > dump.sql
    получаем файл dump.sql, который следует перенести на удалённый сервер и исполнить там.
    To do this, call mysql shell like this:
    mysql -u<логин> -p<пароль> база < dump.sql
    Everything, the database is moved.
    If you do not have access to the shell, you can use the PHP script Sypex Dumper

    Practical use.
    A very good selection of materials that teach you how to work with the database is on the site "PHP in details".
    Это уже упоминавшаяся здесь статья Вадима Ткаченко
  • "Вступление в PHP и MySQL" .

  • А так же статьи Дмитрия Лебедева:
  • Работа с базами данных. Start.

  • Работа с MySQL: Подробнее

  • Работа с MySQL. Новостная лента для странички

  • Так же, при работе с MySQL необходимо учитывать требования, изложенные в разделе этого FAQ, посвященном составлению запросов и использованию кавычек

    Остальные материалы, посвященные MySQL:


    В начало раздела Наверх


    Рисование календаря


    Функция выводит календарь на месяц и принимает аргументом массив с датами в формате ГГГГ-ММ-ДД.
    Если дата совпадает в выводимой в календаре, она оформляется ссылкой
    Example of use:
    <?
    if (isset( $_GET [ 'date' ])) echo "выбрана дата " . $_GET [ 'date' ];
    my_calendar (array( '2004-09-01' ));
    ?>

    Code:
    <?
    function my_calendar ( $fill = '' ) {
    $month_names =array( "январь" , "февраль" , "март" , "апрель" , "май" , "июнь" ,
    "июль" , "август" , "сентябрь" , "октябрь" , "ноябрь" , "декабрь" );
    if (isset(
    $_GET [ 'y' ])) $y = $_GET [ 'y' ];
    if (isset(
    $_GET [ 'm' ])) $m = $_GET [ 'm' ];
    if (isset(
    $_GET [ 'date' ]) AND strstr ( $_GET [ 'date' ], "-" )) list( $y , $m )= explode ( "-" , $_GET [ 'date' ]);
    if (!isset(
    $y ) OR $y < 1970 OR $y > 2037 ) $y = date ( "Y" );
    if (!isset(
    $m ) OR $m < 1 OR $m > 12 ) $m = date ( "m" );

    $month_stamp = mktime ( 0 , 0 , 0 , $m , 1 , $y );
    $day_count = date ( "t" , $month_stamp );
    $weekday = date ( "w" , $month_stamp );
    if (
    $weekday == 0 ) $weekday = 7 ;
    $start =-( $weekday - 2 );
    $last =( $day_count + $weekday - 1 ) % 7 ;
    if (
    $last == 0 ) $end = $day_count ; else $end = $day_count + 7 - $last ;
    $today = date ( "Ymd" );
    $prev = date ( '?\m=m&\y=Y' , mktime ( 0 , 0 , 0 , $m - 1 , 1 , $y ));
    $next = date ( '?\m=m&\y=Y' , mktime ( 0 , 0 , 0 , $m + 1 , 1 , $y ));
    $i = 0 ;
    ?>
    <table border=1 cellspacing=0 cellpadding=2>
    <tr>
    <td colspan=7>
    <table width="100%" border=0 cellspacing=0 cellpadding=0>
    <tr>
    <td align="left"><a href=" <? echo $prev ?> ">&lt;&lt;&lt;</a></td>
    <td align="center"> <? echo $month_names [ $m - 1 ], " " , $y ?> </td>
    <td align="right"><a href=" <? echo $next ?> ">&gt;&gt;&gt;</a></td>
    </tr>
    </table>
    </td>
    </tr>
    <tr><td>Пн</td><td>Вт</td><td>Ср</td><td>Чт</td><td>Пт</td><td>Сб</td><td>Вс</td><tr>
    <?
    for( $d = $start ; $d <= $end ; $d ++) {
    if (!(
    $i ++ % 7 )) echo " <tr>\n" ;
    echo
    ' <td align="center">' ;
    if (
    $d < 1 OR $d > $day_count ) {
    echo
    "&nbsp" ;
    } else {
    $now = "$y-$m-" . sprintf ( "%02d" , $d );
    if (
    is_array ( $fill ) AND in_array ( $now , $fill )) {
    echo
    '<b><a href="' . $_SERVER [ 'PHP_SELF' ]. '?date=' . $now . '">' . $d . '</a></b>' ;
    } else {
    echo
    $d ;
    }
    }
    echo
    "</td>\n" ;
    if (!(
    $i % 7 )) echo " </tr>\n" ;
    }
    ?>
    </table>
    <? } ?>


    В начало раздела Наверх


    Обработка ошибок, часть 1. Общие принципы.


    Большинство начинающих пхп-программистов путаются в обработке ошибок.
    Причём путаница происходит оттого, что они смешивают несколько понятий. Namely:
    1. Факт ошибки.
    2. Сообщение системы об ошибке.
    3. Обработка ошибки
    4. Информирование пользователя об ошибке.

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

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

    Системное сообщение об ошибке - не твой враг, а твой друг. Избавляться от него не надо! Наоборот - надо стремиться получить его всеми силами - оно поможет исправить тебе ошибку .
    Не надо просто путать программиста с пользователем.
    Если ты разрабатываешь сайт, и пользователь - ты сам, то удобнее смотреть ошибки на экране.
    поэтому делаем в настройках сервера
    display_errors= on

    Если сайт уже работает, и на него заходит куча пользователей, то ситуация меняется в корне.
    Во-первых, системные сообщения об ошибках пользователь видеть не должен.
    Во-вторых, их как-то должен видеть программист, причём не только когда он сам обращается к сайту, но и те ошибки, которые происходят у других пользователей.
    Первая задача решается уже знакомой нам директивой
    display_errors= off
    вторая - настройкой, которая заставит пхп все ошибки писать в лог, где их потом может увидеть программист.
    log_errors= on

    С самописными функциями всё просто.
    главное - никаких die(mysql_error())!!!
    это хорошо на этапе обучения, но никуда не годится на посещаемом сайте!
    во-первых, ПОЛЬЗОВАТЕЛЮ эта mysql_error() ничего не скажет.
    во-вторых, программист её не увидит.
    В-третьих, негоже вообще обрывать вывод сайта на середине.
    Поэтому делаем проверку вместо die надо использовать trigger_error()
    В результате у нас должно получиться
    $query = "Select * FROM table" ;
    $res = mysql_query ( $query ) or trigger_error ( mysql_error (). $query )
    ;
    Таким образом, сообщение об ошибке выведется туда же, куда выводятся все остальные ошибки, в зависимости от установок, рассмотренных выше.

    Из написанного выше становится ясно, что собака не бывает нужна в принципе никогда.
    Во-первых, расставить собак во всех местах вероятного появления ошибки просто нереально.
    Во-вторых, и самое главное - собака делает НЕ ТО, ЧТО ВАМ НУЖНО! Вы просто путаете вывод сообщения пользователю и информирование программиста об ошибке.
    Вам нужно запретить вывод ошибок пользователю? Excellent! ОДИН раз написать display_errors гораздо проще, чем лазить по коду, расставляя собак.
    Надо посмотреть сообщение об ошибке? Excellent! Лезем в лог или включаем display_errors, вместо того, чтобы сидеть и гадать на кофейной гуще - где ошибка. Вывод же сообщений мы собакой подавили!

    All. С программистом закончили.
    Теперь осталось проинформировать пользователя об ошибке - ведь до сих пор мы заботились только о том, чтобы пользователь не увидел ошибку.
    Теперь подумаем, как сделать так, чтобы пользователь увидел дружественное сообщение об ошибке, да ещё и желательно не в разорванном дизайне.
    Проще всего это делается с использованием шаблонов.
    Ведь при их использовании сначала исполняется весь нужный код, по завершении которого можно проконтролировать успешность его выполнения, а потом выводится шаблон, который, в случае ошибки, можно заменить шаблоном стандартного сообщения об ошибке.
    Подробнее об этом можно прочитать в следующей главе: Обработка ошибок, часть 2. Разбор примера. Исключения.

    В начало раздела Наверх


    Обработка ошибок, часть 2. Разбор примера. Исключения.

    Пример из жизни.
    Использование исключений.
    Comments

    Пример из жизни.
    Вот яркий образчик "обработки ошибок", который можно встретить практически в любом скрипте
    if ( is_writable ( $file ) {
    $handle = fopen ( $file , 'w' ) || die( 'error opening' );
    fwrite ( $handle , $text ) || die( 'error writing' );
    fclose ( $handle );
    } else die(
    "not writable" );

    Почему это неправильно, было рассказано в первой части.
    Попробуем переписать этот код по-другому.

    $handle = fopen ( $file , 'w' );
    $written = fwrite ( $handle , $text );
    fclose ( $handle );
    if (
    $written === FALSE ) {
    $error = "Извините, произошла ошибка. Попробуйте повторить позднее"
    }


    Функция is_writable имеет смысл только в том случае, если планируется как-то реагировать на невозможность записи. А если для пользователя, как это часто бывает, существует только два состояния - "операция прошла успешно" и "произошла ошибка", то и дополнительная проверка ничем не поможет. А вот запись в лог конкретной причины невозможности записи - очень поможет программисту.
    поэтому мы убираем проверку is_writable, чтобы ошибки при открытии и записи файла пошли в лог, а для сообщения пользователю проверяем только самый конечный результат - запись в файл.

    однако это решение подходит не для всех случаев. иногда от наличия ошибки зависят очень большие куски кода, которые гарантированно будут выдавать ошибки, которые ничего не добавят к самой первой. К примеру, если на месте fopen будет mysql_connect, за которым идет десяток запросов, то после нужной программисту ошибки соединения в логе будет еще куча ошибок запросов.
    В таких случаях нужно обрабатывать эти "ключевые точки":
    if ( $handle = fopen ( $file , 'w' )) {
    $written = fwrite ( $handle , $text );
    fclose ( $handle );
    }
    if (!
    $handle OR $written === FALSE ) {
    show_error_page ();
    }


    Смысл здесь в чем?
    Как описано в первой части, мы должны разделять саму ошибку, сообщение об ошибке, информирование о ней пользователя и программиста .
    саму ошибку обрабатывает первый if - если файл не открылся, то записи не будет.
    сообщение об ошибке, как положено, не пойдет на экран, а пойдет в лог.
    программист будет проинформирован из лога же.
    А пользователь, которому неважно, какие у нас там были ошибки, а интересует только одно: записалось-не записалось - получает свое сообщение. Для этого мы проверяем то, что интересует пользователя - произошла ли, собственно, запись в файл.

    Использование исключений.
    В PHP5 появился стандартный для большинства языков механизм исключений.
    почитать про него можно в документации , а здесь мы просто посмотрим, как будет выглядеть код с применением этого механизма:
    try {
    $handle = fopen ( $file , "a" );
    if (!
    $handle ) throw new Exception ( "open" );
    fwrite ( $handle , $text );
    fclose ( $handle );
    }
    catch (
    Exception $e ) {
    show_error_page ();
    }


    Можно провести такую аналогию, что throw является аналогом die, но не для всего скрипта, а только для канарейки - кода, заключенного между try {}. Что позволяет, с одной стороны, не выполнять код, который все равно не сработает, а с другой - не обрывать работу всего скрипта, а завершить его корректно.

    Вообще, механизм исключений предоставляет просто неограниченные возможности по управлению ошибками.
    Взять, например, вот такой код:
    function exceptions_error_handler ( $severity , $message , $filename , $lineno ) {
    throw new
    ErrorException ( $message , 0 , $severity , $filename , $lineno );
    }
    set_error_handler ( 'exceptions_error_handler' );


    при его использовании наш тестовый пример становится совсем удивительным:
    try {
    $handle = fopen ( $file , 'w' );
    fwrite ( $handle , $text );
    fclose ( $handle );
    }
    catch (
    Exception $e ) {
    show_error_page ();
    }

    Видишь обработку ошибок? И я нет. А она есть!
    fwrite($handle, $text); не выполнится, если fopen отработала с ошибкой.

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

    В начало раздела Наверх


    Что такое PHP?


    Как ни странно, но все 5 лет, которые существует этот сайт, здесь не было такого раздела Smile happy
    попробуем восполнить этот недостаток.
    Данный раздел предназначается тем, кто собирается изучать PHP или просто интересуется - что это такое.

    PHP - это язык программирования .
    Язак программирования, предназначенный для создания сайтов. Или, другими словами, PHP позволяет автоматизировать работу с сайтом.

    Для чего вообще может понадобиться язык программирования при создании сайта? Кому он пригодится, а кому - не очень? Давайте посмотрим.

    Условно говоря, применение PHP можно разделить на три группы:

    1. Самое простое (но очень мощное) применение - это использование PHP, как аналога SSI. Записать все неизменяемые части сайта в отдельные файлы (меню, шапку, подвал) и вместо того, чтобы писать одно и то же на каждой странице, просто вызывать
    include "menu.php" ;
    Это уже значительно облегчит работу с сайтом. К примеру, если добавится новый пункт в меню...

    2. Следующий этап - создание отдельных небольших программок. Это может быть голосование, гостевая книга, вывод текущей даты, рисование календаря... Хотя последние две задачи, в отличие от первых, не обязательно делать на PHP - можно и на Яваскрипте. PHP применяется тогда, когда нужно какую-то информацию хранить на сервере. К примеру, гостевую книгу на Яваскрипте написать множно, но вот... увидит её сообщения только тот, кто их добавлял Smile happy

    3. Победное шествие PHP па планете началось в тот миг, когда кому-то пришла в голову замечательная идея: а почему бы не добавлять материалы на сайт не обычным способом, закачивая HTML файлы по FTP, а забивая текст в форму, как сообщение в гостевую книгу?
    Таким образом, пишется две программы, одна из которых позволет администратору добавить информацию на сайт, а вторая - показывает эту информацию посетителям. Таким образом устроены практически все современные сайты.

    С чем чаще всего путают PHP? С готовыми программами, написанными на PHP. Если вам нужна гостевая книга, портал или электронный магазин - вам не нужно учить PHP. Вам нужно найти готовую программу.

    Если же вы решили изучать этот язык, то следует знать, что ожидает впереди. Для того, чтобы стать даже начинающим веб-программистом, надо знать:
    - три языка программирования - HTML, PHP и SQL.
    - Иметь очень хорошее представление о протоколе передачи гипертекста HTTP и о том, как взаимодействует компьютер пользователя с веб-сервером.
    - иметь базовые представления об операционных системах (в частности понития файл, каталог, знать основные отличия windows от unix)
    - иметь базовые представления о сетевом протоколе TCP/IP (как соединяются компьютеры, что такое IP адрес, DNS и прочее)
    - обязательно придется познакомиться с понятием отладки своих программ

    Если весь этот объем не пугает, то добро пожаловать! Начните с раздела " Хочу изучать PHP "

    В начало раздела Наверх


    Как делать UPLOAD файлов на сервер

    См. статью http://phpclub.ru/detail/article/upload


    В начало раздела Наверх


    Как сделать уменьшенную копию картинки?

    См. статью http://phpfaq.ru/links#resize


    В начало раздела Наверх


    Templates


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

    Самое простое и очевидное заблуждение состоит в том, что новички называют шаблоном вынесенный в отдельный файл "дизайн" - обший html для всех страниц сайта. И на этом успокаиваются. Динамическую информацию, ничтоже сумняшеся, выводя старым добрым echo Smile happy
    На самом же деле, шаблонизатор в основном занимается выводом изменяющегося содержимого страниц сайта. А вывод "дизайна" - задача второстепенная.

    Фантазий главных две:
    1. Шаблоны нужны "дизайнеру", чтобы он мог их править, не разбираясь в PHP.
    2. Следовательно, шаблоны служат для отделения PHP от HTML.

    Давайте попробуем задуматься над первым утверждением. Кто такой дизайнер? Это человек, который работает в фотошопе. HTML он чаще всего не знает вообще. А над шаблоном работает либо специальный верстальщик или - чаще всего... сам программист! Смешно, правда?
    Теперь следствие, про отделение PHP от HTML. Excellent. Перед нами стоит святая цель отделить. Поэтому мы придумываем Смарти и пишем:
    {foreach key=cid item=con from=$contacts}
    <a href="contact.php?contact_id={$cid}">{$con.name} - {$con.nick}</a><br />
    {/foreach}

    Ещё смешнее.
    "Дизайнер", ради которого все затевалось, падает в обморок от счастья.

    Получается, наши причины, по которым мы решили пользоваться шаблонами, гроша выеденного не стоят. И что, не нужны, выходит, шабоны вообще? Нужны. Но сначала надо ответить себе на вопрос - зачем. Для чего нужны шаблоны. И проверить ответ практикой. Я много раз задавал людям этот вопрос. Но почти никто не может на него ответить. Зачем ему нужны шаблоны. Получается, люди делают что-то, не зная зачем.
    Это - самое смешное.

    За время своей деятельности в качестве веб-программиста я сформулировал для себя три причины, по которым нужны шаблоны лично мне. По сути, их две. А сводятся, в конечном счете, к одной:

    Один код - несколько представлений .

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

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

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

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

    В начало раздела Наверх