Home > Articles > Programming > PHP

This chapter is from the book

Interfacing the Network

As we mentioned earlier, IRC requires some processing overhead. Hacking a complete protocol handler for interfacing with IRC is a bit of a complex task, but we favored IRC instead of the database-backed solution because an API already exists that does this work for us.

Know the market! It's essential for every programming project to know which parts have already been done by other people and which still need to be done. Never reinvent the wheel! Especially for commercial projects, it can pay off tremendously to buy foreign bulletproof solutions for specific tasks, rather than design and develop one yourself. The latter is sometimes more expensive and much more time-consuming. On top of that, external solutions are usually constantly being improved—a process that's totally independent of the progress of your own project. By receiving an upgrade from an external company, you simply replace a part of your application with a newer version. This way, you can upgrade certain parts of your application without having to put your own work into the changes. Plus, when using existing libraries, you automatically agree to build your project on common, standardized APIs, which is always a great benefit.

On the other hand, binding yourself to foreign products can prove to be a negative decision if the producer fails to improve the product or keep it up to date, as well as if bugs in it aren't corrected.

In our experience, Open Source products have been the most successful external parts to be integrated. Open Source products are being improved and extended extremely rapidly and are usually oriented at common and open high-potential standards.

Exercise for the Reader

Search for applications/libraries written in PHP that make use of IRC and compare them in terms of design, flexibility, and ease of use. Of course, the implementation is also interesting (but shouldn't be your main focus). The design is always the most crucial part of development; after the design is finished, the actual implementation is usually straightforward and easy to do (even though a lot of programmers think differently).

The library we've chosen for this project is phpIRC (http://www.phpwizard.net/phpIRC), for these reasons:

  • It's easy to use.

  • It's a powerful, complete API.

  • It uses event-based processing.

The use of event-based processing is particularly interesting here. This is a technique usually implemented in traditional applications; for example, all Windows programs are event-based. Event-based programs run in an endless loop, waiting for something (an event) to happen. Events can include user input, mouse movements, network events (incoming packets), etc. As soon as an event is signaled, the program breaks out of its main loop and searches for a procedure that handles this event. All procedures that want to handle the event that just occurred are called with the specific parameters of the event (for example, packet data of incoming network traffic).

Concretely, using "traditional" programming, an incoming ping would be handled as shown in Listing 3.1:

Listing 3.1 Pseudocode for handling a ping.

again:

wait_for_network_data();

if(incoming_data == ping)
{
    send_pong();
    update_traffic_counter();
}

goto again;

This code waits until it receives data from the network, then tries to find out whether the data was a ping. If so, the code sends a pong back and updates a traffic counter for statistical reasons. After that, it just jumps back to where it began. Imagine this with hundreds of events, some of which might depend on others, some not, some only under certain circumstances…A pain!

However, event-based programming makes it significantly easier, as shown in Listing 3.2:

Listing 3.2 Event-based pseudocode for handling a ping.

event_handler ping()
{

    send_pong();
}

event_handler incoming_data()
{

    update_traffic_counter();

    case of ping: handle_event(ping);
}

while(not_done())

    wait_for_event();
    case of network_data: handle_event(incoming_data);

}

The code looks bigger, but also much clearer. The main loop waits for an event to happen. If it finds that an event happened and that it was triggered due to incoming network data, it dispatches this event using the central procedure handle_event(). This function then determines a handler for the event and calls it. The handler in turn updates the traffic counter and launches another event if the first event was a ping. After dispatching the event using handle_event() again, a pong is sent.

Alternatively, both ping() and incoming_data() could register themselves to the event "incoming_data". However, creating two different events gives a greater variety of events and thus allows for much more detailed, target-oriented processing.

It's a bit strange at first getting used to event-based processing of information (it works similarly to a finite-state machine), but it has many advantages:

  • A modular structure is forced on the application. Each module works independently of the other modules and can easily be changed, exchanged, or extended.

  • Any part of the program can trigger any kind of event and thus enforce any type of reaction in the application (in other words, you can control any part of your code from any other part of your code).

  • From one central point of the program, all data can be dispatched to all recipients transparently. You don't have to worry about manually copying and transforming structures; each event handler takes care of receiving its data on its own.

  • New code can be plugged into the application extremely easily, just by creating a procedure that registers itself to the appropriate event.

Thus, once the main event-dispatcher framework is created, the whole application can be created by writing handlers, handlers, and more handlers.

Get familiar with the techniques used to implement finite-state machines. These are elemental in programming and information processing in general.

Luckily, the event-dispatcher framework is already contained in phpIRC, so we won't need to do that programming for this project.

Interface Structure

phpIRC forms the IRC client part of the application and is responsible for all network communication. This means that it also needs to be in control all the time to be able to react to network messages in a timely manner. If phpIRC's message-processing functions were activated only occasionally, safe, secure, and speedy communication couldn't be guaranteed. For this reason, phpIRC forces a special program layout, as shown in Figure 3.6.

03fig06.gifFigure 3.6. phpIRC's forced application layout.

After doing initialization and setup, the application has to surrender control to phpIRC. phpIRC then enters its main event loop and waits for something to happen. During setup, the application has to register callbacks for each event it wants to process (for example, incoming private messages, incoming server messages, and so on). These callbacks are the only possibility for the application to regain control. phpIRC then dispatches all events to all functions that have registered themselves with the library. These functions can in turn enter another idle loop in phpIRC to wait for another event to happen, or they can use phpIRC's API to perform certain actions on the network (send private messages, join/leave channels, and so on).

This very basic layout already allows for downstream communication, which means that phpIRC is able to receive messages from other users. People could actually "talk" to your script.

Note: Downstream means from the network to the user. Upstream is the opposite, from the user to the network.

Exercise for the Reader

Structure a downstream interface that makes use of phpIRC's features. Implement it on paper to become familiar with phpIRC's API. Then build a simple downstream interface that logs onto IRC and displays all messages from a specified channel.

Downstream Communication

Since chatting is a real-time task, meaning that it happens as you do it and causes instant replies, we don't want to introduce latency into the interface. Latency describes the reaction time of the interface; for example, the time from the point when the reader presses the Enter key to submit a message until it shows up in the chat window. Even though a latency of less than a second might objectively be a very short wait, it seems extremely long and annoying to the user. Ergo, incoming messages must be displayed at once (or at least as soon as possible). HTTP is a stateless protocol, however, and doesn't allow instant updates of pages without reloading a complete document. Of course, there are multipart documents and automatic refreshes, but these options introduce a very nasty flicker each time the page loads again, require database buffering for output, and introduce lag because of constant reconnects and data transfer from the Web server.

One solution is "streaming HTML," something that's not officially supported anywhere, but works nevertheless: The script that does the interface output simply idles in an endless loop and doesn't terminate the HTML page the browser is receiving. When something has to be sent to the user, it's printed and immediately flushed from the server's buffers. This way, the browser is constantly rendering and always displays the most up-to-date data. One problem persists in this approach, however; no complex HTML entities can be rendered on the fly. For example, you can't output the rows of a table one by one, because the browser requires all rows and columns of the table to be present completely to determine the final size of the table. As long as you restrict yourself to outputting text lines one after another, and only use tables when you can print them all at once, everything works fine.

Quirks such as streaming HTML are common tricks that you should know. Always keep yourself informed about such things.

Streaming HTML also has one implication that some see as drawback and some as advantage: Since the client connection stays open, there must always be one server process handling it. This means that every client requires at least one Web server process to be running only for that client. The advantage is that no overhead "per hit" occurs. Usually, when the client requests a document, a new process has to be spawned; the script generating that document has to be loaded, parsed, and executed; and finally, the data has to be sent. Since the server process now remains in memory, however, spawning, script loading, and interpretation only have to be done once per client. On sites that would otherwise have hundreds of hits per second, this might be a definite advantage. However, each process now stays resident in memory and demands RAM for itself—on Intel x86 systems equipped with Linux, Apache, and PHP 4.0, such processes tend to be as big as 2MB each. Consequently, a small server with minimal RAM on board would soon start to run from swap—and that means death.

Note: Swap memory is virtual memory that's meant to extend the RAM—the physical amount of memory on a computer. Swap memory is stored on a hard disk, which is extremely slow. When physical memory is all used up, modern operating systems start allocating new memory in the slow swap memory. If a chat server gets hit by a lot of clients at once, which eat up all physical memory and start running in swap memory, the operating system will constantly have to exchange parts of the RAM with parts of the swap memory (since programs can't be executed from swap memory), and this starts a "cycle of death": The operating system notices that a process in swap needs to be run and loads it into RAM, but has to put another running process from RAM into swap. It executes the process in RAM but finds the old process (now residing in swap) has to be run, so it swaps it back into RAM, and so on and so on. You can quickly kill a server this way, forcing it to be reset or taken off the net. By the way, this is also a common "denial of service" attack, a bit similar to the ones that Yahoo! and others were exposed to earlier this year.

Would you have thought about the implications of resident processes? If not, make sure you do next time! Keep evaluating every situation fully.

Upstream Communication

Upstream communication—that is, accepting user input and sending it to the network—is the next stage to consider.

Here's the hard part: We can't send data to the IRC network from just any process. Why not? Because IRC is a state-sensitive protocol, communication is bound to a specific client connection. PHP doesn't allow taking over foreign sockets from other processes; thus, the main process that also handles downstream communication (the process that acts as IRC client) runs isolated from all other processes. The question now is how we can open a door to pass data into the main client.

How would you implement upstream communication? Make at least a theoretical approach. Draw the dataflow on paper. If you haven't done so already, write down at least three possibilities for runtime data exchange.

The downstream process must keep running and may not be terminated. We can't simply reinvoke it using a POST or a GET for passing data, since that would mean launching another process, with the need to re-login, re-setup, etc. Using such an approach would result in constant login/quit sequences that would be extremely disturbing in a chat. And it would result in data loss, since during the time between a logout and a login, lots of messages could be transmitted (which would be invisible to the newly logging-in client).

The chat could be based on a single bot that stayed online all the time and recorded all messages for all users into a database. The user interface would then only need to extract all meaningful data from the database. However, two problems stand against this possibility: a) The chat would be mainly database-backed (something we wanted to avoid); and b) It wouldn't make the clients visible to other IRC clients, as the bot would be the only "real" client on the network. This would make usage of the IRC network ridiculous.

Thus, we need at least two independent processes: one that handles the IRC communication and can't be interrupted, and another to accept incoming messages from the user. Some sort of "container" must then be used to interface between the two processes. Figure 3.7 illustrates this problem.

03fig07.gifFigure 3.7. Upstream communication.

The situation can be compared to a car race. The driver racing on the track is the "main client" and the racing team in the pit is the user input field. The driver is bound to the race he's in; he can't just leave the track and stop to see what's going on. Whenever the racing team flags him in for a pit stop, they "interface" to him—giving him a signal to make a break after the next lap.

What's being done is (leaving radio communication aside) to signal every time the driver passes the finish line. This signal works as the "interface" to the driver. Basically this is what we need to do, too—signal to our main process. Since the main process is event-based, we frequently get the chance to take control over the application and do what we want to do. This means that we can install a handler that "looks" frequently for a signal from the outside. The method to periodically stop and check for incoming data is called polling and will be the preferred method for phpChat. phpIRC features idle callbacks, which get invoked every time phpIRC has nothing to do and simply waits for something to happen on the network. Tagging a handler to this event enables us to watch out for a signal. Now, how are we going to signal something? This is actually pretty easy, using one of the following methods:

  • Set a flag in a database.

  • Create a lockfile in the file system.

  • Use semaphores.

  • Set a flag in shared memory.

These are basically the methods that we have with PHP to "leave a message." The following sections describe each method.

Pipes can't be used for interprocess communication here, because a pipe requires two processes to be running at the same time. Our situation requires interfacing one constantly running process from other, short-term processes.

Note: Of course, more exotic methods are available, such as sending emails between processes. We've seen people doing this, but we won't go into that option here, as the disadvantages should be clear to the reader.

Setting a Flag in a Database

Setting a flag in a database is probably the de facto standard method for PHP users: Connect to a database, leave some data in it, let it be processed further by other processes. This method is extremely easy to implement and is available on all systems, but has a disadvantage. Can you tell what the disadvantage is?

The disadvantage doesn't come from the database stuffer (the process that inserts user messages) but rather from the database reader (the main process that retrieves all user messages from the database). To achieve a good "chat feeling," we need as little latency as possible—and thus a very good response time. The response time is crucial for Web-based chat, as this is how the user will actually feel "integrated" into the action. When the messages come slower and slower, users quickly become frustrated and quit. Our testing showed that a latency of more than a second is too much. To stay below this value, the poll frequency in which the main process has to read messages from the database must be very short; the default value in phpChat is 0.5 seconds (two checks within a second). Now, as soon as a lot of clients have to be handled by the chat system, the database gets quite busy and takes up more and more resources. At about 40-50 queries per second, our test server spent about one third of its processing time simply executing database queries. Even if this was a disqualifying benchmark for the database system (it should have been able to process many more queries), some optimization is obviously necessary, and this isn't the ideal setup.

Creating Lockfiles

Our next idea was that, if the database took up too many resources when handling interprocess communication, a file system might be more efficient.

But the file system clearly lost the race. Again, the stuffer wasn't the problem—creation of the lockfiles worked smoothly. To detect whether a lock was set, however, lots of calls to clearstatcache() had to be done in order to correctly determine whether a lockfile had been deleted or was still present. clearstatcache() had such a hard impact on the system performance that we didn't try to look further into this option; the chat only worked at a quarter of the performance it reached using the database-backed approach.

Create your own benchmarks. Make test scripts accessing the database and the file system at high frequency. Write down your results and compare them. This is always a good idea when evaluating data-exchange methods—never trust theoretical descriptions of what the systems can be capable of! In practice, most things will look different.

Using Semaphores

Of course, the reasons for the poor performance of the former approaches are easily recognized.

What are the reasons? Try to find and write them down. Try to find the critical points—this is crucial when having to optimize later on. "A chain is only as strong as its weakest link," and software is only as fast as its slowest inner loop. The process of finding these bottlenecks is called profiling and is extremely important.

When using a database, the bottleneck is the database: the time required to invoke the database, let it execute the (relatively small) query, retrieve the result, and determine what to do next (called the overhead) is pretty long compared to the result we're getting. In other words, we're using a huge software system designed for complex data storage to exchange simple, Boolean values—if there's something a database was not designed for, it's this. No wonder it didn't perform optimally; the bottleneck is the overhead, the time required for setup and deinitialization.

The file system performed badly because it was not designed for this usage, and because of other limitations: PHP doesn't include optimal file-system access methods. Determining the existence of a file requires constant cache invalidations and recaching—again, large overhead for a trivial task.

So why not use something completely different? We're surely not the first people having to deal with interprocess communication; others must have come up with good solutions for this already. And so we reach the next possibility: semaphores.

Semaphores do exactly what we want to do: They work as signals. Semaphores are counters stored in shared memory. You can "acquire" a semaphore and thus increase its counter, and you can "release" a semaphore, decreasing its counter. Additionally, there's the possibility of waiting for a semaphore to become free, meaning that its counter falls back to zero. This option has one drawback, however: Semaphores were meant to lock resources, to create some kind of scheduling mechanism allowing many processes to wait for available time on a device, or something similar. Whenever you're waiting for a semaphore to become free, the process that's waiting is put to sleep and cannot perform other tasks. If the main process was waiting for the user-input field to signal a new message, it would sleep and couldn't process the incoming network traffic.

No reason to give up yet; people have come up with still other solutions.

Setting Flags in Shared Memory

Shared memory is similar to semaphores, but a bit more versatile; shared memory is memory that's available to every process in a system. Multitasking systems are usually designed in such a way that each process is running completely isolated from other processes for security reasons. Different processes can share data by setting up and connecting to special memory blocks, namely shared memory blocks. These blocks can then contain variables (or any other kind of data, but PHP only supports storage of concrete variables).

This is exactly what we want: the ability to store a Boolean value in a place in memory where every process can look at it. Since shared memory works (as the name suggests) only in RAM, it's extremely fast and requires almost no overhead. With this option, every chat process looks for its own variable in shared memory and only issues a query to the database whenever it finds that variable set by the user-input field.

Why is the data exchange still based on a database at the very end? Try to find some answers.

The database is still being used for one main reason. Shared memory is not supported by default in PHP; you need to specifically compile support for it into PHP. However, many people with access to a PHP-enabled server don't have the option of recompiling PHP because they only rented space on the server, because they don't have sufficient rights, or maybe because others depend on a certain setup of PHP. Leaving the database in as the final data-exchange path makes use of shared memory as an optional optimization. People who can't use it can simply disable it and still have a fully working version of the chat server—operating at suboptimal performance, but operating.

When creating an application designed for widespread distribution, keep in mind that not everyone will have the same setup as you—and probably not the possibility of re-creating your very special setup. Even though PHP is 99% system-independent, some things do depend on the system. Carefully calculate whether enforcing certain circumstances is worth a potentially huge loss of customers.

Interface to the User

Now that we moved all the tricky parts with the data exchange out of the way, the actual HTML interface to the user is trivial. We know how to accept input from the user and how to deal with network communication. The last "problem" is packaging the generated output for the user in a convenient way. HTML offers only one way to have different windows act independently in one browser view: framesets. The interface typically consists of the user-input field; the chat output field; a nickname list (or just nick list), which shows other participating clients in the same room; and an action panel to allow one-click control over the chat for actions such as nickname changes, joins, parts, quits, and so on. These activities can all be handled by single processes whose output will be integrated into a frameset.

The main process, also responsible for the chat-output streaming, will keep state information updated in a database that all other interface components can access, retrieve, and display in a suitable fashion (see Figure 3.8).

03fig08.gifFigure 3.8. The final application layout.

Interface to the Developer

An interface for developers? What does this have to do with chat? And how is it supposed to work? Typically, most applications suffer from the disability of being "solid," meaning being either completely unmodifiable or difficult to modify by foreign developers. In terms of end-user-oriented software (for example, desktop environments such as Windows, KDE, MacOS, etc.), hardly anyone will ever find the ideal solution. Similar to a chat system, most people who download it say, "Hey, great, but it lacks this and that," or "Cool, but I don't like the way it does xyz."

Without an easy, clearly exposed path for modification by anyone using it, most applications end up in the trash. Most people won't even try to work on a program they didn't develop themselves if the ease of doing so doesn't hit them right in the face.

This means that for the chat application to consistently enforce independence of code and interface layout (allowing an interface to HTML developers) and to consistently enforce independence of data-processing steps, we need to create a solid application core (the part of the application that nobody should ever need to change) which interfaces to a distributed set of plug-ins (the part of the application that most people will want to change somehow).

Think again about the importance of these enforcements. Would you like an application to be designed like this? Would you even need it? Think about how this could be realized.

Interface to HTML Developers

In terms of the HTML interface, abstraction of code and layout is done using templates. This is the easiest possibility for tweaking an application to your needs, yet it's also the most powerful. Within seconds, you can change the look and feel—without having to modify a single line of code. Everyone with basic HTML knowledge could completely restructure the way the application would show itself to a user. As this method is discussed elsewhere in this book, we won't go deeper into it here. To find more details about using templates, please read Chapter 5, "Basic Web Application Strategies."

Interface to Code Developers

Providing an interface to other developers is usually associated with the term API (Application Programming Interface). APIs are normally provided by libraries (such as phpIRC), but not by complete applications. But applications that have the capacity to be extended by a programmer are much more successful than applications that must be used "as is." Of course, in terms of PHP applications, anyone can modify the source code, but many people refrain from analyzing a complex system and applying modifications to it. Thus, the application itself needs to expose certain ways of being extended.

Note: We're differentiating here between applications and libraries. Libraries are meant to be used by applications, cannot be run stand-alone, and are generally much easier to extend than applications. Applications consist of a full, closed system.

Try to find out how common applications can be extended. For example, for your favorite text-processing tool, see whether the developers provided the capacity to extend the tool's functionality.

Two primary possibilities of extending applications have evolved: Either the application provides scripting capabilities (similar to macros), or the application is able to use plug-ins. As for PHP, implementing a script language in a time-critical part of a system…we don't need to think any further. On top of that, the complexity of creating a full-fledged parser is way too much to ask. But plug-ins are much easier to implement and have many advantages. A plug-in is a little piece of code that can register itself with the application and catch certain events from it, get access to internal data, and so forth. While integrating seamlessly with the main system, plug-ins still remain isolated files that can be detached and spread separately. They can be attached to the system without having to modify a line of code, which allows a system administrator without any knowledge of PHP to extend the application by using foreign code. Concretely, this is realized as shown in Figure 3.9.

03fig09.gifFigure 3.9. Chat system with plug-ins.

Design your own plug-in-system, at least theoretically. Create a minimal application that's able to register plug-ins with itself and execute them.

When starting up, phpChat includes an include file, which in turn includes all wanted plug-ins. Listing 3.3 shows how this include file works:

Listing 3.3 The plug-in includer.

//////////////////////////////////////////////////////////////////////////////
//
// Plug-in Integrator
//
//////////////////////////////////////////////////////////////////////////////

include("chat_plugin_out_htmlspecialchars.php3");

include("chat_plugin_out_link_transform.php3");

include("chat_plugin_out_colorcodes.php3");

include("chat_plugin_clock.php3");

include("chat_plugin_cmd_basic.php3");
include("chat_plugin_out_basic.php3");

//////////////////////////////////////////////////////////////////////////////

Each of the plug-ins is built up in the same way, consisting of a main part and an event part. The main part calls two functions in phpChat, with the following names: chat_register_plugin_init() and chat_register_plugin_deinit(). Each function takes as a parameter the name of another function, which should be called for plug-in initialization and plug-in deinitialization, respectively.

phpChat adds these function names to an internal table. Upon initialization of the chat, as soon as phpChat is fully set up, it makes a run through the initialization table and calls the initialization function of every plug-in that registered itself. Similarly, upon shutdown, it runs through the deinitialization table. This method allows signaling the plug-ins to activate and deactivate themselves.

To be useful in the application, phpChat offers a set of events to which each plug-in can attach itself. During plug-in initialization, each plug-in tells phpChat to send a set of desired events. Events might include the chat being idle, the user submitting a new message, the user clicking on a nickname in the nick list, an incoming message from the network, and so on.

At runtime, the plug-ins can intercept these events and perform certain tasks. The clock plug-in, for example, registers itself to the "idle" event and checks the current system time frequently. After a predefined number of minutes, it announces the time to the user.

For most events, phpChat also sends parameters (such as the message texts for incoming messages), which the plug-ins are allowed to change. For example, the list of plug-ins in Listing 3.3 includes plug-ins named htmlspecialchars and link_transform. These plug-ins change the output of messages; htmlspecialchars applies a call to htmlspecialchars() to all printed text (for security reasons, so that no one can insert malicious HTML code into the chat), and the link transformer detects all URLs and email addresses and prefixes them with <a href=""></a> or mailto:, respectively, so that users can click links right in the chat window (see Figure 3.10).

03fig10.gifFigure 3.10. The plug-ins at work.

As you can see, plug-ins offer an extremely powerful way of extending a complex system. Consequently, phpChat has abstracted most of its own internals into plug-ins as well. The complete command interpreter has been moved into a plug-in, as well as the complete set of text formatting/printing procedures. This means that there is only a solid kernel that doesn't have to be changed because there's simply nothing in there that would require changing—the rest can be freely modified, extended, even removed, without any impact on system performance or operability. Have you ever seen an application that doesn't complain about someone deleting its files? Using this technique, an application won't complain—and will even dynamically adapt to it.

Plug-ins can be used in many ways, not just for chat programs. For example, you could also build a portal site consisting of the traditional news page, an email interface, etc. Using plug-ins, you can design a "site kernel" that handles all basic issues such as providing page layout, database back end, sessioning, and so on. Based on the site kernel, you can then create plug-ins for displaying news, sending and receiving email, even for providing different methods of logging in. Even if it's quite an effort, we encourage you to create a plug-in-based application as an exercise. It will be worth the work.

Listing 3.4 shows a plug-in template implementing a "dummy" plug-in as code base for new plug-ins.

Listing 3.4 A plug-in template.

<?

//
// Use these variables to tell the plug-in installer how you named your
// initialization and deinitialization functions. This is done to eliminate
// the need for changing the installer code, which would ask for errors.
//
$plugin_init_function = "myplugin_init";
$plugin_deinit_function = "myplugin_deinit";
//
//
//

//////////////////////////////////////////////////////////////////////////////
//
// myplugin_idle_callback(int code, mixed parameter) - example callback
//
//////////////////////////////////////////////////////////////////////////////
//
// This is an example for a callback function. See below on how to register
// and remove it from the call chain.
//
// $code specifies the reason for invocation, $parameter contains all callback
// information.
//
// The return value should always consist of a modified or unmodified version
// of the input parameter $parameter. The return value is used as input
// parameter for the next callback. This allows for multi-stage message
// processing and such.
//
//////////////////////////////////////////////////////////////////////////////

function myplugin_idle_callback($code, $parameter)
{

    return($parameter);

}

//////////////////////////////////////////////////////////////////////////////
//
// myplugin_init() - initializes this plug-in
//
//////////////////////////////////////////////////////////////////////////////
//
// Put all your initialization code in here. This code will be called as soon
// as the main bot is all set up with connecting and callback installation;
// thus, you can rely on a safe environment.
//
// Although the return value is currently not used, "0" should indicate
// initialization failure and "1" initialization success. This might be used
// later on to enable plug-ins to stop the current chat session right after
// login.
//
//////////////////////////////////////////////////////////////////////////////
//
// Return value:
// 0 - error
// 1 - success
//
//////////////////////////////////////////////////////////////////////////////

function myplugin_init()
{

    // register callbacks here
    chat_register_callback(CHATCB_IDLE,
"myplugin_idle_callback");
    
    return(1);
}

//////////////////////////////////////////////////////////////////////////////
//
// myplugin_deinit() - deinitializes this plug-in
//
//////////////////////////////////////////////////////////////////////////////
//
// All deinitialization code should go here. This function is called before
// the bot goes down; thus, all network connections are still active.
//
// Although the return value is currently not used, "0" should indicate
// deinitialization failure and "1" deinitialization success. This might be
// used later on to force delayed shutdowns.
//
//////////////////////////////////////////////////////////////////////////////

function myplugin_deinit()
{

    // remove callbacks here
    chat_remove_callback(CHATCB_IDLE, "myplugin_idle_callback");
   
    return(1);

}

//////////////////////////////////////////////////////////////////////////////
//
// NOTE: DO NOT CHANGE ANYTHING BELOW THIS POINT!
//
//////////////////////////////////////////////////////////////////////////////

// installer code starts here

// register initialization function
chat_register_plugin_init($plugin_init_function);

// register deinitialization function
chat_register_plugin_deinit($plugin_deinit_function);

// installer code done

//////////////////////////////////////////////////////////////////////////////

?>

The main code registers the initialization and deinitialization routines for this plug- in. The plug-in initializer then installs the callbacks this plug-in wants to intercept, and the deinitializer removes them.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020