Creating Agent and Running every 60 sec. in bitrix24 - php

I have added one function in timeman mosule as follows.
class CTimeManReport extends CAllTimeManReport
{
function testAgent()
{
mail('t#demo.co.in','agent','agent');
return "testAgent();";
}
}
Now I want to run this function every 60 sec.I have added agent though control panel in agent section,but its running only once.

After running the agent bitrix added to database value that agent function returns. So in your case, it must be return "CTimeManReport::testAgent();" because your function is a class method.
Also, it must be a class method, not an instance method, so add public static before your function
So, try to do something like that:
class CTimeManReport extends CAllTimeManReport
{
public static function testAgent()
{
mail('t#demo.co.in','agent','agent');
return "CTimeManReport::testAgent();";
}
}
If this advice doesn't help, you can add cron support for your agents, because by default they work on pages reloads. Follow this link for instructions
P.S. don't add your own code into the bitrix module's code. Because you may lose your code after bitrix updates. Add your own code or include your classes into the local/php_interface/init.php

Related

Laravel multi-tenant jobs

Running into a problem where jobs cannot connect to the database.
Invalid catalog name: 1046 No database selected
I need to set the account in the job so I have an extended class to make sure the account is sent with the job so that I can ensure the database can connect to the correct database.
<?php
namespace App\Jobs;
use Illuminate\Support\Facades\DB;
abstract class Job
{
protected $account;
public function start()
{
// runs when creating the job, so the config holds the correct value
$this->account = config('database.connections.tenant.database');
}
public function handle()
{
// since the handle function runs outside of setting the job
// the database is no longer set in the config
config()->set('database.connections.tenant.database', $this->account);
// try to force it to reconnect incase it already did for some reason.
DB::reconnect();
}
}
This is my current version I am playing around with, variations seem to not affect it. I basically run start in the constructor and then make sure it runs the parent handle in the job so that it bootstraps the proper db configuration.
The end result I am looking for is it to set the tenant database as account and when its running the job it uses that database for all queries.
Found out a way around this. Its not pretty but based on what I can see laravels queues just don't really handle this sort of thing well.
First I removed the override for the handle function, all I really needed was to make sure the account the queue needed to run on was available on the Job class.
abstract class Job
{
protected $account;
public function start()
{
// runs when creating the job, so the config holds the correct value
$this->account = config('database.connections.tenant.database');
}
}
Next I moved the switch to the correct tenant database to the AppServiceProvider in the boot method.
Event::listen(JobProcessing::class, function ($event) {
if ($payload = $event->job->payload()) {
preg_match('/"account";s:[0-9]+:"(.*?)"/', $payload['data']['command'], $matches);
if (count($matches)) {
if (isset($matches[1])) {
config()->set('database.connections.tenant.database', $matches[1]);
config()->set('database.default', 'tenant');
}
}
}
});
What i did here is look into the serialize object for the account with some regex. Might be improvements to be made here but so far works in testing. It then sets the correct database once it confirms the account.
The reason I had to go this far is that the Job does queries when the job itself is serialized, so in order to pass the account it needed to be done before its serialized.

Call exit function from child class in PHP

I have a PHP class where I have to invoke PHP header function to show a webpage. As pointed out from this post, header should be followed by exit callback. Since this situation is very common in my classes, I've defined a method in parent class:
class ParentClass
{
protected function header($url)
{
header('Location:'.$url);
exit;
}
}
I would like to invoke this method from children classes:
class ChildClass extends ParentClass
{
public function someFunc()
{
$this->header($some_url);
}
}
PHP documentation says that exit terminates the current script. My question is: does the exit function terminate child script even if it is contained in parent class?
EDIT
In my specific situation, I am using a MVC design pattern and ChildClass is a controller. Inside it, sometimes I need to show a view, sometimes I need to redirect to another url. Let me explain it with a practical example.
Suppose to have a website with a login section. When login page is displayed to users (login data not submitted), login controller should show login view. This view contains a form with an action like action="login.html". When data is submitted, login controller is invoked and checks login data: if login is successful, user is redirected to his admin section.
class UsersController extends BaseController
{
public function login()
{
try
{
if(isset($_POST['submit']))
{
// check login data, throw exception in case of error
// if success, redirect to admin section
$this->header('admin.html');
}
else
{
// show login view
}
}
catch(Exception $e)
{
// show login view (with error)
}
}
}
class BaseController
{
protected function header($url)
{
header('Location:'.$url);
exit;
}
}
Since this situation is quite common in my controllers, I've preferred to define a header method in BaseController instead of typing everytime
header('Location:someURL.html');
exit;
In my OP, I only wanted to be sure that $this->header('admin.html'); callback would terminate current login method, even if it is defined in BaseController script.
Hope it's a little clearer now.
As already descripted in the comment, exit will terminate everything, i.e. the webpage immediately stops executing, including clean up functions and finally blocks.
So, you should consider using exit very carefully because many things might happen: data doesn't get written to the database when you're not using auto-commit (unless you commit the data before calling exit). Auto-commit is not enabled by default in PHP's MySQL module (as far as I know).
Here is an example:
class B extends A {
public function someFunc() {
# you might wanna use partent instead as
# pointed out by Ding in the comments, but
# maybe someFunc does more that just doing
# the redirect.
$this->header("http://google.com");
}
}
try {
print("Ok...");
$obj = new B();
$obj->someFunc();
print("Nahh..."); # doesn't get called/
} finally {
print("Cleaning up."); # doesn't get called, either.
}
Instead of calling the exit method, you should rather implement a clear MVC design pattern. Here is a very quick example:
<?php
class Response {
# use private here and use appropriate
# getters and setters.
public $status_code = 200;
public $content = "";
public $headers = array();
}
class HomeView extends View {
# called when a get request is made.
public function get() {
$response = new Response();
$response->content = "Hello world."
}
}
class RedirectionView {
public function get() {
$response = new Response();
$response->status_code = 301; # use 302 if moved only temporarily.
$response->headers["Location"] = "http://google.com";
}
}
function processRequest() {
# load appropriate view programatically
$view = new RedirectionView();
$response = $view->get();
http_response_code($response->status_code);
foreach ($response->headers as $headerName => $headerValue) {
header(sprintf("%s: %s", $headerName, $headerValue));
}
print($view->content)
}
?>
Note that this is not really a MVC design pattern (the model is missing and, well, it's not clear what the controller, however, that's what django (a Pyhton framework) uses, too). You might wanna check out PHP MVC frameworks (a quick google search will do the trick) or implement your own (my example might be a good start).
Edit 1:
Using exit is probably ok (but I wouldn't use it because I believe it is bad practice). If you're able to edit the design approach you're using, I'd make your View BaseClass get/post method (or whatever you're the method that returns response object. Note: if you're using prints to show the response to the user, try to add a Response which contains all the data you're request needs to show. This is better than having prints (or echos) everywhere in your code).
Using the response object, your code would then be able to either just set the location header (if it's a redirect) or show the message. So you don't need to "kill" any part of the webpage and it will terminal when the execution ends. But again: you're probably good to go with an exit call in your method (unless you're encountering any issues with your code (database transactions that aren't committed, statistic data that is not updated (because its after the exit statement). Exit will simply terminate your script completely.

Sharing PHP singleton object between different users

I am working on a web application that lets users login and give an exam scheduled by the admin. I have an "accumulate" function that should run automatically once all the users have finished giving the test and update the database (according to some logic).
I know I can use the database to keep a store of users who are giving the test right now and accordingly run the logic but I am interested in knowing if this can be done by a singleton class.
My code:
class ScoreBoard{
var $current;
private static $board=NULL;
// private static $current=0;
private function __construct() {
$this->current=0;
}
static function scoreboard(){
if(!self::$board){
self::$board= new ScoreBoard();
$this->log("Created new");
// return self::$board;
}
return self::$board;
}
function add(){
$this->current+=1;
$this->log("add ".$this->current);
}
function subtract(){
$this->current-=1;
$this->log("subtract ".$this->current);
// file_put_contents("scoreboard.txt",self::$current);
if($this->current<0)
$this->current=0;
if($this->current<=0)
{
$this->accumulate();
self::$board=NULL;
}
}
}
And in the startExam.php file I am calling this function as :
$scoreboard= ScoreBoard::scoreboard();
$scoreboard->add();
And doing
$scoreboard= ScoreBoard::scoreboard();
$scoreboard->subtract();
when the exam ends. Thus, when each user starts the exam the singleton objects add function should be called and when he ends it the subtract function should be called. But this doesn't seem to work for some reason and the $current never increases beyond 1.
Kindly let me know if what I am trying to do is possible or not. And if there are any better ways to achieve what I want to do.Thank you.
You could use Memcached to achieve this -
Your constructor function would then look something like this:
$this->memcache = new Memcached();
$this->memcache->addServer("127.0.0.1", "11211") // I think that's the right port for default.
$this->current = $this->memcache->get("current");
But you could do the same logic via storing the value in a file - and that would be easier...

Execute a Function in Silverstripe via Cronjob

Hi I want to execute a function via cronjob to start an csv import. At the moment the import is triggered by accessing a controller in the browser tld.de/Update
The controller has this code http://pastie.org/8351266
How can I execute the function init() via Cronjob?
Thx!
In SilverStripe you can access any route that is accessible via HTTP also by running cli-script.php in the command line
There also is sake which is just a bash wrapper around cli-script.php (but sake needs to be installed)
so, from your project directory, you can run both commands which will perform the same action (in this case, run a dev/build):
php framework/cli-script.php dev/build
sake dev/build
see the docs for commandline ussage of silverstripe: http://doc.silverstripe.org/framework/en/topics/commandline
the 2nd part of your question (how to call a method from a controller) is actually more a question of routing in silverstripe and has nothing to do with how it is called (cronjob)
I assume that your controller is a Page_Controller or a subclass of that (so bound to a SiteTree Model), then routing is done for you (it takes the URLs you set in the CMS).
so lets see some example code and lets assume you have a page with the URLSegment about:
class Page_Controller extends ContentController {
private static $allowed_actions = array('something');
public function init() {
// the init method will run before every action
// this means this code will run, no matter if you visit /about or /about/something
}
public function index() {
// this is the default action (this code is optional and can be removed),
// and will be called if you visit website.com/about
return $this;
}
public function something() {
// this is the somethingaction,
// and will be called if you visit website.com/about/something
// do something here
return $this;
}
}
you can then call run to get the result of the index():
php framework/cli-script.php about
and this to get the result of something():
php framework/cli-script.php about/something
NOTE: the init method itself is not accessable via URL, it is the "setup" that runs before an action
NOTE: all actions other than index() have to be allowed by adding them to $allowed_actions (also note that you need to ?flush=1 after adding to $allowed_actions to reload the config cache)
EDIT: this was actually the response to your first question, after seeing your code example, this addition:
for standalone controllers it works the same way, just that you have to define the routes, and make sure that you have $Action in the route so that something() can be called
You could do this without Silverstripe sake. Install curl and call the URL via a cronjob, ie:
0 0 * * * curl --silent http://tld.de/Update
The proper way to do this would be to write a Silverstripe task, and invoke your controller from within the task. I haven't tested this code but it would go something like this:
class YourTask extends BuildTask {
public $description = "...";
//...
public function run($request) {
YourController::init();
}
}
You can invoke it over sake using:
0 0 * * * /path/to/framework/sake dev/tasks/YourTask
why not create a build task ? which is specially designed for such requirements (at-least that's how I consider build tasks)
<?php
class ArticleCsvUpdateTask extends BuildTask {
protected $title = 'Article Csv Update';
protected $description = 'Build task for article Csv update';
public function run($request) {
$loader = new ArticleCsvBulkLoader('Color');
if($loader->load('import-new.csv')) {
$loader->load('import-new.csv');
}
}
}
Which can be assess both from browser using "yoursite/dev/tasks/ArticleCsvUpdateTask" and from command line using "php framework/cli-script.php dev/tasks/ArticleCsvUpdateTask" OR using "sake dev/tasks/ArticleCsvUpdateTask" (if you have sake installed).
May be I am not getting your exact requirement but I believe this is much cleaner and nicer way of running a cron job with silverstripe.
See Zauberfisch's answer for a complete solution
I'm not familiar with Silverstripe, but if I understand correctly, this controller init function can be called with a HTTP request.
As the silverstripe docs say, you can call any url from the command line:
php framework/cli-script.php Update/init
More information is available here, and consider using sake for this task.
I think the right way do this is create a php file console like:
#!/usr/bin/env php
<?php
require_once "/path/to/your/class/Update.php";
$class = new Update();
$class->init();
Add right perms to this file
chmod 755 consolefile
And finally run this script with cronjob

Prevent invocation of instance method X from context != Y

This is a tricky one.
I am "emulating" ZF Bootstrapping (surface appearance). Don't ask me why, call it academic interest. So I have a Bootstrap Abstract with a method "run" that iterates over itself to locate any methods prefixed with "init".
The application looks for a user defined class which extends this class, in which the user can define any number of methods in this way. However, I want to prevent the user from being able to execute the "run" command of it's parent class, while still exposing the same command for the client code.
class Bootstrap_Abstract{
protected final function run(){
// if method exists that starts 'init' - execute the method
}
}
class Bootstrap extends Bootstrap_Abstract(){
public function initSomething(){
//do something
}
//PREVENT THIS
public function initRun(){
$this->run();
}
}
//application code, not exposed to user - changes in behaviour require changes in this code directly
class Application(){
$Bootstrap = new Bootstrap();//load user bootstrap
$Bootstrap->run();
}
To determine "what" called a particular method, look into debug_backtrace
Post Script:
the problem with my original code was an error in design. The responsibility for iterating through the Bootstrap methods should have been given to the invoking class, not the target class itself.
I solved this problem by moving the function out to the invoker. Funny how obvious/simple refactoring is in hindsight...

Categories