How to correctly configure PHP/Appache to self-host a Kanban server?

i am having issues with setting up a Vhost hosting Kanboard

It is the first time i use shell, php and apache. I wanted to install ‘Kanboard’ on MacOS Sonomoa to use it for a personnal purpose. I want to be able to access it from a browser at the adress : “kanboard.local” without having to do anything in the terminal when starting my computer.

I was not able to connect to my virtual host.

I did the following :

  • Installed Homebrew with the .pkg from their site

  • Installed php with homebrew

  • Installed MySQL and SQLite with homebrew

  • Installed HTTPD/Apache with homebrew

  • Downloaded the Kanboard master.zip from GitHub and unzipped it

Then :

I went to the HTTPD configuration file (httpd.conf) and added a line to load the php module:

LoadModule php_module lib/httpd/modules/libphp.so

I’ve checked which Directory has been declared, I’ve put : Require granted all on this directory ( …./var/www )

I added the line:

DirectoryIndex index.php index.html 

on the advice of ChatGPT

In the declared directory, I created an ‘html’ folder in which I put the kanboard folder I had unzipped Then, I went to my hosts database (/etc/hosts) and added the line:

127.0.0.0.1 kanboard.local

Finally, I went to the virtual hosts configuration file ( …/etc/extra/httpd-vhosts.conf) And I configured a virtual host like this:

Listen 8080 
<VirtualHost *:8080> 
DocumentRoot ‘/usr/local/var/www/html/kanboard’ 
ServerName kanboard.local
<Directory ‘/usr/local/var/www/html/kanboard’> 
Options Indexes FollowSymLinks MultiViews 
AllowOverride All 
Require all granted 
</Directory> 

I started php then httpd, then restarted them. I checked the ‘localhost:8080’ page it works correctly. I can use this page to browse kanboard files but the application itself is not built at all. When I try to connect to ‘kanboard.local’, I get a message “connection failed”. Can you help me / do you have any idea what is missing or causing the problem?

Date Format with PHP using Datetime

I have two dates I need to show it in format

29th and 30th November 2024
or
28th to 30th November 2024
or 
28th October to 30th November 2024
or
30th November and 1st December2024

What I tried so for is the code below.

                $date1 = new DateTime('2024-09-29');
                $month1 = $date1->format('m');
                $date2 = new DateTime('2024-09-30');
                $month2 = $date2->format('m');
                $interval = $date1->diff($date2);
                if($month1 == $month2){
                    if($interval->days == 1){
                        $result_date = getDayWithSuffix($date1);
                        $result_date .= ' and ';
                        $result_date .= getDayWithSuffix($date2);
                        $result_date .= ' '.$date1->format('F');
                        $result_date .= ' '.$date1->format('Y');
                    } else {
                        $result_date = getDayWithSuffix($date1);
                        $result_date .= ' to ';
                        $result_date .= getDayWithSuffix($date2);
                        $result_date .= ' '.$date1->format('F');
                        $result_date .= ' '.$date1->format('Y');
                    }
                } else {
                    if($interval->days == 1){
                        $result_date = getDayWithSuffix($date1);
                        $result_date .= ' '.$date1->format('F');
                        $result_date .= ' and ';
                        $result_date .= getDayWithSuffix($date2);
                        $result_date .= ' '.$date2->format('F');
                        $result_date .= ' '.$date1->format('Y');
                    } else {
                        $result_date = getDayWithSuffix($date1);
                        $result_date .= ' '.$date1->format('F');
                        $result_date .= ' to ';
                        $result_date .= getDayWithSuffix($date2);
                        $result_date .= ' '.$date2->format('F');
                        $result_date .= ' '.$date1->format('Y');
                    }
                }

Is there a better way to achieve this?

php password and user doesn’t after a new post call [closed]

I created a simple log-in form in php to access an archive with user and password (encrypted in MD5 without hash or salt).
I then decided to insert a filter in the archive page (to filter only results of a specific date).
When I run the filter the page no longer finds the user and password pair and redirects me to the login page.
The file is Archivio.php

Code without filter:

<?php
$utente = $_POST["Utente"];
$password = $_POST["Password"];
$password = md5($password);

$id=
$sql=mysqli_query($mysqli, "select * from Utenti_Archivio where Utente='".$utente."' and          Password='".$password."'");
    if (mysqli_num_rows($sql) == 1){
    }
    else{
        header( "Location: Login.html" );
        die;
    }

$querySQL = mysqli_query($mysqli, "select Nome,Cognome,Ospitante,Sede,`Ora entrata`,`Ora uscita`,Giorno from  `Archivio`");
?>

Code with filter:

 <div class="filtro">
            <form method="POST">
                <div class="textfiltro">
                    Filtra per giorno:
                </div>
                <div class="field">
                    <input type="text" name="data" id="data">
                </div>   
                <div>
                    <button class="filtrobutton">FILTRA</button>
                </div>        
            </form>
        </div>
<?php
$utente = $_POST["Utente"];
$password = $_POST["Password"];
$password = md5($password);

$id=
$sql=mysqli_query($mysqli, "select * from Utenti_Archivio where Utente='".$utente."' and          Password='".$password."'");
    if (mysqli_num_rows($sql) == 1){
    }
    else{
        header( "Location: Login.html" );
        die;
    }

$data = $_POST["data"];
if (!empty($data)) {
    $querySQL = mysqli_query($mysqli, "SELECT Nome,Cognome,Ospitante,Sede,`Ora entrata`,`Ora uscita`,Giorno FROM `Archivio` WHERE Giorno = '$data'");
} else {
    $querySQL = mysqli_query($mysqli, "SELECT Nome,Cognome,Ospitante,Sede,`Ora entrata`,`Ora uscita`,Giorno FROM `Archivio`");
}
?>

Login html file

   <body>
      <div class="login-form">
         <form id="host" action="Archivio.php" method="post">
            <div class="field">
               <input type="text" name="Utente" placeholder="Utente" required>
            </div>
            <div class="field">
               <input type="password" name="Password" placeholder="Password" required>   
            </div>
            <button type="submit">ENTRA</button>
         </form>
         <button onclick="document.location='Home.html'">INDIETRO</button>
         <button onclick="location.reload()">RIPRISTINA</button>
      </div>
    </body>

Can you help me?

I have tried moving the credential verification query after the filter and using the get method for the filter but it does not solve the problem.

Convert encoding from ISO 8859-1 to UTF-8 in PHP project

I have an old PHP project written in PHP 5.2, and am updating it to PHP 8.2

It extracts data from a SQL Server database, and on specific button presses it fills out this data into existing Excel templates.

Since PHPExcel does not exist any more I have used composer to replace it with phpspreadsheet across my project. The project is using UTF-8 for encoding (Header and meta included in my PHP and HTML), and also when connecting to the database.

PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8

I have also changed the default charset = UTF-8 in my php.ini file

On my webpage all the German umlauts are showing without any problem, but the Excel files are not being filled out properly. It seems there is an encoding mismatch somewhere. In my Excel file the word Prüfung"shows like this: prüfung

The collation of my SQL Server database is Latin1_General_CI_AS.

Any ideas? I am expecting the German umlaut characters to be displayed and printed correctly.

With Nesbot Carbon diffForHumans(), display 19 months instead of 1 year when less than 26 months

I display a diffForHumans with one part

echo Carbon::create('1 hour 59 minutes ago')->diffForHumans(); // 1 hour ago
echo Carbon::create('1 day 23 hours ago')->diffForHumans(); // 1 day ago
echo Carbon::create('1 month 23 days ago')->diffForHumans(); // 1 month ago
echo Carbon::create('1 year 10 months ago')->diffForHumans(); // 1 year ago

For up to 25 months, I’d prefer to see months before it switches to year: e.g. to see 23 months instead of 1 year.

More generaly, I would like to keep the lower unit (like month) when the higher unit (like year) is 1 or 2.

I would also like the display to be as compact as possible.

How do I do that?

Woocommerce not using the right templates

I’m having some template hierarchy issues trying to add woocommerce to a custom theme I made for a client. The site isn’t using the templates it should be. The shop page the plugin generates is using ‘page.php’ instead of ‘archive-product.php’ and products are using ‘single.php’ instead of ‘single-product.php’. I created a folder called woocommerce in my theme folder and copied those two WC template files over, but they’re not being used. I used a function to force using the right templates, which worked but now the products don’t appear on the shop page like they did when the wrong templates were being used. I’ve never worked with WC before, so is there something basic I’ve missed?

Things I’ve tried:

  • switching to a default theme
  • turning off all plugins
  • double checking the folder structure (the files are in /mytheme/woocommerce/)
  • double checking the products are published
  • adding a debug line to the template echo

    Products found!

    which echoes that they are being found (just not showing)

  • checked the console for CSS or JS Issues
  • confirmed that the correct page is assigned as the Shop Page

Uncaught PHP Exception Error: “Typed property PimcoreModelDocumentDocType::$name must not be accessed before initialization” at DocType.php

I’ve been using Pimcore for a month, working on my localhost machine. Yesterday, I uploaded my Pimcore project to my server. I copied all the files and added the necessary permissions to the folders, but I keep getting the following error when I log in or refresh the page.

Would anyone be able to help?

request.CRITICAL: Uncaught PHP Exception Error: “Typed property PimcoreModelDocumentDocType::$name must not be accessed before initialization” at DocType.php line 134 {“exception”:”[object] (Error(code: 0): Typed property PimcoreModelDocumentDocType::$name must not be accessed before initialization at httpdocs/vendor/pimcore/pimcore/models/Document/DocType.php:134)”} []

I have tried looking inside every single file mention in the error log (If anybody needs the error log screenshot I will send it to you).
The only temporary solution is editing the following file:

httpdocs/vendor/pimcore/pimcore/models/Document/DocType/Listing/Dao.php
on line 60

    public static function sortByPriority(DocType $a, DocType $b): int
    {
        $a->getPriority();
        if ($a->getPriority() === $b->getPriority()) {
            return strcasecmp($a->getGroup() . $a->getName(), $b->getGroup() . $b-      >getName());
        }

        return $a->getPriority() <=> $b->getPriority();
    }

Overriding the following code by commenting the code and returning 0 the error doesn’t take place.
This doesn’t make sense for two main reasons:

  1. Is in the vendor folder so it gets frequently updated;
  2. In my local machine this error is not present, and I copied every folder 1to1 on my server.

Set Python Environment and Execute Python Code in PHP Script

I am trying to convert the vcd file to wavedrom json file using the python library

https://github.com/Toroid-io/vcd2wavedrom

when I run on my terminal the below code works fine

 source /var/www/xxx/vcd2wavedrom/verilog-env/bin/activate && 
 python3 /var/www/xxx/vcd2wavedrom/vcd2wavedrom/vcd2wavedrom.py 
 -i /var/www/xxx/uuids/abcd/dump.vcd 
 -o /var/www/xxx/uuids/abcd/dump.json

When i execute the same code in my php script as

$env = 'source /var/www/xxx/vcd2wavedrom/verilog-env/bin/activate';

cmd = "python3 /var/www/xxx/vcd2wavedrom/vcd2wavedrom/vcd2wavedrom.py -i ".$uuid_dir."/dump.vcd -o ".$uuid_dir."/dump.json";

#!/bin/sh

shell_exec($env ." && ". $cmd);

I am getting sh: 1: source: not found error.

How can set the venv from php script.

I am not a python developer. Please elaborate your answer as much as possible for me to understand better.

I tried to remove the source, but I got

 sh: 1: /var/www/xxx/vcd2wavedrom/verilog-env/bin/activate: Permission denied
 Traceback (most recent call last):
 File "/var/www/xxx/vcd2wavedrom/vcd2wavedrom/vcd2wavedrom.py", line 9, in <module>
 from vcdvcd.vcdvcd import VCDVCD
 ModuleNotFoundError: No module named 'vcdvcd'

I set verilog-env folder to 777 but still the same error persists

The problem of moving a variable to another page [duplicate]

Hi I’m using this code in my page to move to the same page but with account access, I’m learning how to use various languages so I didn’t want to use Javascript.

<label for="toggleBanner" class="open-btn">Log In</label>

      <!-- Overlay del banner -->
      <div class="overlay">
        <div class="overlay-content">
          <!-- Etichetta per chiudere il banner -->
          <label for="toggleBanner" class="close">&times;</label>
          <h2>Inserisci credenziali</h2>
          <form method="post" action="LogIn.php?CODICE=<?php echo htmlspecialchars($codice, ENT_QUOTES, 'UTF-8'); ?>">
            <label for="name">Nome:</label>
            <input type="text" id="name" name="name" required><br><br>
            
            <label for="password">Password:</label>
            <input type="password" id="password" name="password" required><br><br>
            
            <input type="submit" value="Invia">
          </form>
        </div>
      </div>

The problem that occurs is that the $codice that passes from one page to another is not received and is always interpreted 0.
Thanks in advance for any advice, and also for tips on other things

Despite having checked several times that before pressing send the page knows which code it refers to.

Determining Entropy in PHP

I am using the following code in my code to send a password reset token to a user.

$token = md5($user_id . time());

Why this is considered as a bad approach being cited as it has a weak entropy? The above code would generate a scary-looking 32 bit token that an attacker cannot decipher at all.

Suppose md5 reverse engineering is not possible (Although it is).

My question is why this is a bad approach? How do I say it has a weak entropy? Is there a way I can calculate its entropy?

codeigniter 3 merge 2 table with pagination

I have 2 tables products and api_products i want to merge these 2 tables with pagination. I am getting issues with pagination when merging .

$internalProduct = $this->Product_model->listprodcts($limit, $offset);

$apiProduct = $this->Product_model->get_api_products_from_db($car_make_url, $car_model_url, $car_year_url);

php CodeIgnitor dynamic content passing to Views

I am working on a model to generate responses for the selected AImodels and send them to views at the end simultaneously as you can see in my process method below.

<?php

namespace AppControllers;

use AppModelsExamModel;
use AppModelsVendorModel;
use AppModelsExamTopicModel;
use AppModelsCertificationModel;
use AppModelsAIResponseModel;
use ReactPromisePromise;

class OptionController extends BaseController
{
    public function index()
    {
        // Display the form view
        return view('options_form');
    }
public function process()
{
    $db = ConfigDatabase::connect();

    $examModel = new ExamModel();
    $examTopicModel = new ExamTopicModel();
    $certificationModel = new CertificationModel();
    $vendorModel = new VendorModel();
    $responseModel = new AIResponseModel();

    // Get form inputs
    $exam_codes_raw = $this->request->getPost('examCodes'); // The raw input string of exam codes
    $aiModels = $this->request->getPost('aiModel'); // Array of selected AI models
    $prompt_template = $this->request->getPost('prompt'); // User-provided prompt
    $project_name = $this->request->getPost('projectName'); // User-provided project name

    // Ensure $exam_codes_raw is treated as a string
    if (is_array($exam_codes_raw)) {
        $exam_codes_raw = implode("n", $exam_codes_raw); // Convert array to string
    }

    // Split the raw exam codes string into an array
    $exam_codes = preg_split('/[rn,]+/', trim($exam_codes_raw));

    $responses = [];
    $insertedId = null; // Initialize variable to avoid "undefined variable" errors

    // Determine if headings should be applied based on keywords in the prompt template
    $keywords = ['information', 'info', 'article', 'articles', 'post', 'something', 'tell', 'tell me', 'artical', 'arrticle', 'poost', 'say', 'illuminate', 'shed'];
    $applyHeadings = false;
    foreach ($keywords as $keyword) {
        if (stripos($prompt_template, $keyword) !== false) {
            $applyHeadings = true;
            break;
        }
    }

    // Loop through each exam code
    foreach ($exam_codes as $exam_code) {
        // Trim any extra whitespace for each exam code
        $exam_code = trim($exam_code);

        // Fetch exam details from the database
        $exam = $examModel->where('exam_code', $exam_code)->first();
        if ($exam === null) {    
            $responses[$exam_code] = [
                'error' => 'Could not find data for exam code ' . $exam_code,
                'model' => []
            ];
            continue;
        }

        $certification = $certificationModel->find($exam['certification_ids']);
        if ($certification === null) {
            $responses[$exam_code] = [
                'error' => 'Could not find certification data for exam code ' . $exam_code,
                'model' => []
            ];
            continue;
        }

        $vendor = $vendorModel->find($exam['vendor_id']);
        if ($vendor === null) {
            $responses[$exam_code] = [
                'error' => 'Could not find vendor data for exam code ' . $exam_code,
                'model' => []
            ];
            continue;
        }

        $topics = $examTopicModel->where('exam_code', $exam_code)->findAll();
        $topicsSections = array_map(function($topic) {
            return $topic['description'];
        }, $topics);

        // Process the prompt
        $parsed_prompt = $this->processPrompt(
            $exam_code,
            $prompt_template,
            $exam['name'],
            $exam['exam_shortname'],
            $certification['detail_name'],
            $certification['cert_shortname'],
            $vendor['name'],
            $topicsSections // Pass the array of topics
        );

        // Check if there was an error in processing the prompt
        if (strpos($parsed_prompt, 'Error:') !== false) {
            // Handle the error by skipping the AI generation for this exam code
            $responses[$exam_code] = [
                'error' => $parsed_prompt,
                'model' => []
            ];
        }

        // Call the respective AI models for the parsed prompt
        foreach ($aiModels as $aiModel) {
            $response = '';
            $modelName = ucfirst($aiModel); // Example: 'openai' becomes 'OpenAI'
            try {
                switch ($aiModel) {
                    // Handle AI model requests as needed
                    case 'openai-gpt-3.5-Turbo':
                        $apiKey = '';
                        $response = $this->generateTextOpenAI($parsed_prompt, $apiKey);
                        break;
                    case 'cohere':
                        $apiKey = '';
                        $response = $this->generateTextCohere($parsed_prompt, $apiKey, $exam_code);
                        break;
                    case 'claude-3-Opus':
                        $apiKey = '';
                        $response = $this->generateTextClaudeOpus($parsed_prompt, $apiKey);
                        break;
                    case 'openai-gpt-4o-mini':
                        $apiKey = '';
                        $response = $this->generateTextOpenAIGPT4omini($parsed_prompt, $apiKey);
                        break;
                    case 'claude-3-Haiku':
                        $apiKey = '';
                        $response = $this->(generateTextClaudeHaiku$parsed_prompt, $apiKey);
                        break;
                    case 'claude-3.5-Sonnet':
                        $apiKey = '';
                        $response = $this->generateTextClaudeSonnet($parsed_prompt, $apiKey);
                        break;
                    case 'openai-gpt-4-turbo':
                        $apiKey = '';
                        $response = $this->generateTextOpenAIGPT4turbo($parsed_prompt, $apiKey);
                        break;
                }

                $responses[$exam_code]['model'][$aiModel] = $response;

                // Store the AI model name and its response
                $responseDatetime = date('Y-m-d H:i:s'); // Current date and time

                // Insert data into the table
                $builder = $db->table('responses');

                // Prepare the data to insert
                $data = [
                    'ai_model' => $modelName,
                    'created_at' => $responseDatetime,
                    'prompt' => $parsed_prompt, // Store the actual parsed prompt
                    'response' => $response, // Store the AI generated response
                    'project_name' => $project_name, // Store the project name
                ];
                $builder->insert($data);
                $insertedId = $db->insertID();

            } catch (Exception $e) {
                // Display error message if something goes wrong
                $responses[$exam_code]['model'][$modelName] = "Error: " . $e->getMessage();
            }
        }
    }

    return view('response_view', [
        'insertedId' => $insertedId,
        'responses' => $responses
    ]);
}

The response is being sent to the views as whole. I have to wait for all my responses to be completed then appear on the views all at once.

**

The problem is I want the dynamic responses on my views. The moment an
AiModel that was selected is done with generation it should appear on
the views instead of waiting for all of them.

**

Below is my views code:

    <body>
<div class="container">
<div class="wrapper">
        <!-- Sidebar  -->
        <nav id="sidebar">
            <div class="sidebar-header">
                <h3>AI Prompt Model</h3>
            </div>
            <ul class="list-unstyled components">
                <li class="active">
                    <ul class="collapse list-unstyled" id="homeSubmenu">
                        <li>
                            <a onclick="window.location.href='<?= base_url('dashboard'); ?>'">Go to Dashboard</a>
                        </li>
                        <li>
                            <a onclick="window.location.href='<?= base_url('homepage'); ?>'">Go to Home Page</a>
                        </li>
                    </ul>
                </li>
            </ul>
            </nav>
    <div class="main-content">
        <h1>AI Model Responses</h1>
        <div class="exam-section">
            <button id="compareButton">Compare Selected</button>
            <table>
                <thead>
                <tr>
                    <th>Select</th>
                    <th>Exam Code</th>
                    <th>AI Model</th>
                    <th>Response</th>
                    <th>Action</th> <!-- Add this column -->
                </tr>
                </thead>
                <tbody>
                <?php
                $exam_responses = [];

                foreach ($responses as $exam_code => $data):
                    if (isset($data['model']) && !empty($data['model'])):
                        foreach ($data['model'] as $model => $response):
                            $normalized_model = strtolower($model);

                            if (!isset($exam_responses[$exam_code])) {
                                $exam_responses[$exam_code] = [];
                            }

                            // Ensure the response is a string
                            if (is_array($response)) {
                                // If response is an array, assume it's the first item in the array
                                $response = isset($response['response']) ? $response['response'] : '';
                            }

                            $response = preg_replace('/**(.+?)**/', '<strong>$1</strong>', $response);
                            $response = preg_replace('/^#### (.+)$/m', '<h4>$1</h4>', $response);
                            $response = preg_replace('/^### (.+)$/m', '<h3>$1</h3>', $response);
                            $response = preg_replace('/^## (.+)$/m', '<h2>$1</h2>', $response);
                            if (!isset($exam_responses[$exam_code][$normalized_model])) {
                                $exam_responses[$exam_code][$normalized_model] = $response;
                            }
                        endforeach;
                    endif;
                endforeach;
                ?>

                <?php foreach ($exam_responses as $exam_code => $models):
                    foreach ($models as $model => $response):
                        $decoded_response = is_string($response) ? htmlspecialchars_decode($response) : '';
                        ?>
                        <tr>
                            <td>
                                <input type="checkbox" class="response-checkbox"
                                       data-exam-code="<?= htmlspecialchars($exam_code) ?>"
                                       data-response="<?= htmlspecialchars($decoded_response) ?>"
                                       data-model="<?= htmlspecialchars(ucwords($model)) ?>">
                            </td>
                            <td><?= htmlspecialchars($exam_code) ?></td>
                            <td><?= htmlspecialchars(ucwords($model)) ?></td>
                            <td>
                                <button class="response-btn" onclick="toggleResponse(this)">View Response</button>
                                <div class="response-box"><?= htmlspecialchars_decode($decoded_response) ?></div>
                            </td>
                            <td>
                                <button class="delete-btn" data-id="<?= htmlspecialchars($insertedId) ?>"
                                        onclick="deleteRow(this)">Delete
                                </button>
                            </td>
                        </tr>
                    <?php
                    endforeach;
                endforeach;
                ?>
                </tbody>
            </table>
        </div>
        <a href="<?= base_url('optioncontroller'); ?>">Go back</a>
    </div>
</div>

<!-- Copy Notification -->
<div class="copy-notification" id="copyNotification">Response copied to clipboard</div>

<!-- Compare Modal -->
<div id="compareModal" class="modal">
    <div class="modal-content">
        <h2>Compare Responses</h2>
        <div id="compareResults"></div>
    </div>
</div>

Any help would be appreciated!

mysql update column sequence inconsequent [closed]

OK, so i have a weired issue with a simple update query.
We believe this happpened when we switched servers and thus a new MariaDB version.

We currently use MariaDB 10.11, php 8.3 and PDO to execute a query. On an AlmaLinux 9 server
Before we were on CentOs 7 with MariaDB 10.6, and same php 8.3

The query in question is quite simple and updates 2 datetime columns
First, last run date to the value of the scheduled next run date, and then next run date to when this job finished.

$query = $this->db->prepare("UPDATE `{$this->Config->sql_prefix}_jobs` SET `last_at` = `next_at`, `next_at` = :next_at WHERE `id` = :id");

Now, mysql should work of the columns in sequence as they are defined. But this not always works.
like 8 out of 10 days, first the next_at gets updated and then last_at, so the wrong order.
Making both columns the same value.

I can ( and i do ) work around this issue by simply passing a calculated value as named parameter for last_at column, but i have other queries where this also happens and its not possible there due sheer amount of data, which i first would need to query and process in php.

EDIT: I should also mention, that when i execute this query directly within mysql / phpmyadmin, the issue does not appear.
So i have a feeling this might be a php/pdo issue.

Does anyone have some kind of clue?

Composer unable to resolve a package from subdirectory

I have a laravel project which is supposed to use a package from /packages directory. I keep getting error when I run composer update or composer install (I even tried clearing composer’s cache):

Your requirements could not be resolved to an installable set of
packages.

Problem 1
– Root composer.json requires gata/gata-webhooks, it could not be found in any version, there may be a typo in the package name.

Potential causes:

Following is the directory structure of my main project and the sub package. The main project is on the “main” git branch.

enter image description here

I added the following code in my main project’s composer file:

 "require": {
    "php": "^8.2",
    "laravel/framework": "^11.9",
    "laravel/sanctum": "^4.0",
    "laravel/tinker": "^2.9",
    "gata/gata-webhooks": "*"
},
...
"autoload": {
    "psr-4": {
        "App\": "app/",
        "Database\Factories\": "database/factories/",
        "Database\Seeders\": "database/seeders/",
        "Gata\GataWebhooks\": "packages/gata-webhooks/src/"

    }
}
...
 "extra": {
    "laravel": {
        "dont-discover": [],
        "providers": [
            "Gata\GataWebhooks\GataWebhooksServiceProvider"
        ]
    },
    "repositories": [
        {
            "type": "path",
            "url": "./packages/*",
            "options": {
                "symlink": true
            }
        }
    ]
},
...
},
"minimum-stability": "dev",
"prefer-stable": true

And my package has the following in it’s composer.json

{
"name": "gata/gata-webhooks",
"description": "Webhooks to receive payloads from e-commerce systems",
"version": "1.0.0",
"type": "library",
"license": "MIT",
"autoload": {
    "psr-4": {
        "Gata\GataWebhooks\": "src/"
    }
},
"require": {
},
"minimum-stability": "dev",
"prefer-stable": true

}

Can someone please tell me what I am doing wrong?

Multi processing in php

i have a problem with this cronjob function its related to a chatbot The problem is when user start broadcast campaigns other users have to wait until next one can start

public function subscriber_broadcaster($api_key="") // braodcast_message
    {
        // $this->api_key_check($api_key);
        $broadcaster_number_of_message_to_be_sent_in_try=$this->config->item("broadcaster_number_of_message_to_be_sent_in_try");
        if($broadcaster_number_of_message_to_be_sent_in_try==0) $broadcaster_number_of_message_to_be_sent_in_try="";
        $broadcaster_update_report_after_time=$this->config->item("broadcaster_update_report_after_time"); 
        if($broadcaster_update_report_after_time=="" || $broadcaster_update_report_after_time==0) $broadcaster_update_report_after_time=10;
        $number_of_campaign_to_be_processed = 1; // max number of campaign that can be processed by this cron job
        // $number_of_message_tob_be_sent = 50000;  // max number of message that can be sent in an hour

        $subscriber_broadcaster_hold_after_number_of_errors=$this->config->item("subscriber_broadcaster_hold_after_number_of_errors");
        if($subscriber_broadcaster_hold_after_number_of_errors=="" || $subscriber_broadcaster_hold_after_number_of_errors==0) 
            $subscriber_broadcaster_hold_after_number_of_errors=30; // default 10

        /****** Get all campaign from database where status=0 means pending ******/
        $where_str = " (posting_status='0' OR is_try_again='1') AND posting_status!='3'";
        $this->db->where($where_str);
        $join = array('users'=>'messenger_bot_broadcast_serial.user_id=users.id,left');
        $campaign_info= $this->basic->get_data("messenger_bot_broadcast_serial",$where='',$select=array("messenger_bot_broadcast_serial.*","users.deleted as user_deleted","users.status as user_status","users.user_type as user_type"),$join,$limit=50, $start=0, $order_by='schedule_time ASC');  

        $page_ids_names=array();
        $access_token_database_database=array();
        $facebook_rx_fb_user_info_id_database=array();
        $campaign_id_array=array();  // all selected campaign id array
        $campaign_info_fildered = array(); // valid for process, campign info array

        $valid_campaign_count = 1;
        foreach($campaign_info as $info)
        {
            if($this->is_demo=='1' && $info['user_type']=="Admin")
            {
                $this->db->where("id",$info['id']);
                $this->db->update("messenger_bot_broadcast_serial",array("posting_status"=>"1","is_try_again"=>"0"));
                continue;
            }
            if($info['user_deleted'] == '1' || $info['user_status']=="0")
            {
                $this->db->where("id",$info['id']);
                $this->db->update("messenger_bot_broadcast_serial",array("posting_status"=>"1","is_try_again"=>"0"));
                continue;
            }

            $campaign_id= $info['id'];
            $time_zone= $info['timezone'];
            $schedule_time= $info['schedule_time']; 
            $total_thread = $info["total_thread"];
            $page_id =$info["page_id"]; // auto ids
            $fb_page_id =$info["fb_page_id"]; 
            $user_id = $info["user_id"];                  

            if($time_zone) date_default_timezone_set($time_zone);            
            $now_time = date("Y-m-d H:i:s");

            if((strtotime($now_time) < strtotime($schedule_time)) && $time_zone!="") continue; 
            if($valid_campaign_count > $number_of_campaign_to_be_processed) break; 

            // get access token and fb user id
            $token_info =  $this->basic->get_data('facebook_rx_fb_page_info',array("where"=>array('id'=>$page_id,'user_id'=>$user_id)));
            foreach ($token_info as $key => $value) 
            {
                $access_token_database_database[$campaign_id][$value["id"]] = $value['page_access_token'];
                $facebook_rx_fb_user_info_id = $value["facebook_rx_fb_user_info_id"];
                $facebook_rx_fb_user_info_id_database[$campaign_id] = $facebook_rx_fb_user_info_id;
                $page_ids_names[$value["id"]] = $value["page_name"];
            }

            // valid campaign info and campig ids
            $campaign_info_fildered[] = $info;
            $campaign_id_array[] = $info['id']; 
            $valid_campaign_count++;      
        }

        if(count($campaign_id_array)==0) exit();        

        $this->db->where_in("id",$campaign_id_array);
        $this->db->update("messenger_bot_broadcast_serial",array("posting_status"=>"1","is_try_again"=>"0"));

        // get config id
        $getdata= $this->basic->get_data("facebook_rx_fb_user_info",array("where_in"=>array("id"=>$facebook_rx_fb_user_info_id_database)),array("id","facebook_rx_config_id"));
        foreach ($getdata as $key => $value) 
        {
            $facebook_rx_config_id_database[$value["id"]] = $value["facebook_rx_config_id"];
        } 

        $this->load->library("fb_rx_login"); 

        // send message
        foreach($campaign_info_fildered as $info)
        {
            $campaign_id= $info['id'];            
            $user_id = $info["user_id"];           
            $catch_error_count=$info["last_try_error_count"];
            $successfully_sent=$info["successfully_sent"];
            $successfully_delivered=$info["successfully_delivered"];
 
            $fb_rx_fb_user_info_id = $facebook_rx_fb_user_info_id_database[$campaign_id]; // find gb user id for this campaign
            $this->fb_rx_login->app_initialize($facebook_rx_config_id_database[$fb_rx_fb_user_info_id]);

            $i=0;
        
            $campaign_lead=$this->basic->get_data("messenger_bot_broadcast_serial_send",array("where"=>array("campaign_id"=>$campaign_id,"processed"=>"0")),'','',$broadcaster_number_of_message_to_be_sent_in_try);

            foreach($campaign_lead as $key => $value) 
            {
                if($catch_error_count>$subscriber_broadcaster_hold_after_number_of_errors)  // if 30 catch block error then stop sending, mark as complete
                {
                    $this->basic->update_data("messenger_bot_broadcast_serial",array("id"=>$campaign_id),array("posting_status"=>'4','successfully_sent'=>$successfully_sent,'completed_at'=>date("Y-m-d H:i:s"),"error_message"=>$error_msg,"is_try_again"=>"0","last_try_error_count"=>$catch_error_count));
                    break;
                }
                $campaign_message_send=$info["message"];
                $page_id_send  = $value["page_id"];
                $send_table_id = $value['id'];
                $subscribe_id = $value['subscribe_id'];
                $subscribeauto_id = $value['subscriber_auto_id'];
                $client_first_name = $value['subscriber_name'];
                $client_last_name = $value['subscriber_lastname'];
                $client_otn_token=$value['otn_token']; // if OTN Campaign
                
                $error_msg="";
                $message_error_code = "";
                $message_sent_id = "";

                if(!isset($access_token_database_database[$campaign_id][$page_id_send])) continue;
                $page_access_token_send = $access_token_database_database[$campaign_id][$page_id_send]; // get access toke from our access token database

                //  generating message
                $campaign_message_send = str_replace('{{first_name}}',$client_first_name,$campaign_message_send);
                $campaign_message_send = str_replace('{{last_name}}',$client_last_name,$campaign_message_send);
                $replace_search=array('PUT_SUBSCRIBER_ID','#SUBSCRIBER_ID_REPLACE#');
                $campaign_message_send=str_replace($replace_search, $subscribe_id, $campaign_message_send);

                if($client_otn_token!="")
                    $campaign_message_send = str_replace('PUT_OTN_TOKEN',$client_otn_token,$campaign_message_send);


                // print_r($campaign_message_send); continue;

                $message_sent_id="";
                $now_sent_time=date("Y-m-d H:i:s");  
                $deliveryTime=''; 
                $isDelivered='0';  
                $successfully_sent++;    
                try
                {
                    // $campaign_message_send = spintax_process($campaign_message_send);
                    $response = $this->fb_rx_login->send_non_promotional_message_subscription($campaign_message_send,$page_access_token_send);

                    if(isset($response['message_id']))
                    {
                        $message_sent_id = $response['message_id']; 
                        $successfully_delivered++;
                        $deliveryTime=date("Y-m-d H:i:s");
                        $isDelivered="1";
                    }
                    else 
                    {
                        if(isset($response["error"]["message"])) $message_sent_id = $response["error"]["message"];  
                        if(isset($response["error"]["code"])) $message_error_code = $response["error"]["code"];

                        if($message_error_code=="551") // unvalilable user
                        {
                            $this->basic->update_data("messenger_bot_subscriber",array("id"=>$subscribeauto_id),array("unavailable"=>"1","last_error_message"=>$message_sent_id));
                        }
                        else
                        {
                            $error_msg = $message_sent_id;
                            $catch_error_count++;
                        }                    
                    }              
                    
                }

                catch(Exception $e) 
                {
                  $error_msg = $e->getMessage();
                  $catch_error_count++;
                }

                // generating new report with send message info

                $i++;  
                // after 10 send update report in database
                if($i%$broadcaster_update_report_after_time==0)
                {
                    $this->basic->update_data("messenger_bot_broadcast_serial",array("id"=>$campaign_id),array('successfully_sent'=>$successfully_sent,"successfully_delivered"=>$successfully_delivered,"error_message"=>$error_msg,"last_try_error_count"=>$catch_error_count));
                }
       
                // updating a lead, marked as processed
                $this->basic->update_data("messenger_bot_broadcast_serial_send",array("id"=>$send_table_id),array('processed'=>'1',"sent_time"=>$now_sent_time,"delivered"=>$isDelivered,"delivery_time"=>$deliveryTime,"message_sent_id"=>$message_sent_id));
            }

            // one campaign completed, now update database finally
            if((count($campaign_lead)<$broadcaster_number_of_message_to_be_sent_in_try) || $broadcaster_number_of_message_to_be_sent_in_try=="" || $catch_error_count>$subscriber_broadcaster_hold_after_number_of_errors)
            { 
                $new_posting_status = ($catch_error_count>$subscriber_broadcaster_hold_after_number_of_errors) ? '4' : '2';
                $complete_update=array("posting_status"=>$new_posting_status,'successfully_sent'=>$successfully_sent,"successfully_delivered"=>$successfully_delivered,'completed_at'=>date("Y-m-d H:i:s"),"is_try_again"=>"0","last_try_error_count"=>$catch_error_count);
                if(isset($error_msg))
                $complete_update["error_message"]=$error_msg;
                $this->basic->update_data("messenger_bot_broadcast_serial",array("id"=>$campaign_id),$complete_update);
            }
            else // suppose broadcaster_update_report_after_time=20 but there are 19 message to sent, need to update report in that case
            {
                $this->basic->update_data("messenger_bot_broadcast_serial",array("id"=>$campaign_id),array('successfully_sent'=>$successfully_sent,"successfully_delivered"=>$successfully_delivered,"is_try_again"=>"1"));
            }

            $this->basic->update_data('otn_postback',['id'=>$info['otn_postback_id']],['rcn_last_sent_time'=>date("Y-m-d H:i:s")]);

        }        
   
    }

i want to run all campaigns once without waiting to running campaign to finish.

i tried this modification but not working for me to running it into multitask

public function subscriber_broadcaster($api_key = "") // broadcast_message
{
    // $this->api_key_check($api_key);
    $broadcaster_number_of_message_to_be_sent_in_try = $this->config->item("broadcaster_number_of_message_to_be_sent_in_try");
    if ($broadcaster_number_of_message_to_be_sent_in_try == 0) $broadcaster_number_of_message_to_be_sent_in_try = "";
    $broadcaster_update_report_after_time = $this->config->item("broadcaster_update_report_after_time");
    if ($broadcaster_update_report_after_time == "" || $broadcaster_update_report_after_time == 0) $broadcaster_update_report_after_time = 10;
    $number_of_campaign_to_be_processed = 20; // max number of campaign that can be processed by this cron job

    $subscriber_broadcaster_hold_after_number_of_errors = $this->config->item("subscriber_broadcaster_hold_after_number_of_errors");
    if ($subscriber_broadcaster_hold_after_number_of_errors == "" || $subscriber_broadcaster_hold_after_number_of_errors == 0)
        $subscriber_broadcaster_hold_after_number_of_errors = 30; // default 10

    /****** Get all campaign from database where status=0 means pending ******/
    $where_str = " (posting_status='0' OR is_try_again='1') AND posting_status!='3'";
    $this->db->where($where_str);
    $join = array('users' => 'messenger_bot_broadcast_serial.user_id = users.id, left');
    $campaign_info = $this->basic->get_data("messenger_bot_broadcast_serial", '', array(
        "messenger_bot_broadcast_serial.*",
        "users.deleted as user_deleted",
        "users.status as user_status",
        "users.user_type as user_type"
    ), $join, $limit = $number_of_campaign_to_be_processed, $start = 0, $order_by = 'schedule_time ASC');

    if (count($campaign_info) == 0) exit();

    // Initialize arrays for handling multi-cURL
    $multiHandle = curl_multi_init();
    $curlHandles = [];
    $campaignHandlesMap = []; // Map to track which curl handle corresponds to which campaign

    foreach ($campaign_info as $info) {
        // Skip invalid campaigns
        if ($info['user_deleted'] == '1' || $info['user_status'] == "0") {
            $this->db->where("id", $info['id']);
            $this->db->update("messenger_bot_broadcast_serial", array("posting_status" => "4", "is_try_again" => "0"));
            continue;
        }

        // Prepare data for each campaign
        $campaign_id = $info['id'];
        $time_zone = $info['timezone'];
        $schedule_time = $info['schedule_time'];
        $user_id = $info['user_id'];

        if ($time_zone) date_default_timezone_set($time_zone);
        $now_time = date("Y-m-d H:i:s");

        // Skip campaigns scheduled for the future
        if ((strtotime($now_time) < strtotime($schedule_time)) && $time_zone != "") continue;

        // Get access tokens and prepare messages
        $token_info = $this->basic->get_data('facebook_rx_fb_page_info', array("where" => array('id' => $info["page_id"], 'user_id' => $user_id)));
        foreach ($token_info as $key => $value) {
            $access_token_database[$campaign_id][$value["id"]] = $value['page_access_token'];
        }

        // Fetch leads to process for the current campaign
        $campaign_lead = $this->basic->get_data("messenger_bot_broadcast_serial_send", array("where" => array("campaign_id" => $campaign_id, "processed" => "0")), '', '', $broadcaster_number_of_message_to_be_sent_in_try);

        foreach ($campaign_lead as $key => $value) {
            $campaign_message_send = $info["message"];
            $page_id_send = $value["page_id"];
            $client_first_name = $value['subscriber_name'];
            $client_last_name = $value['subscriber_lastname'];

            $campaign_message_send = str_replace('{{first_name}}', $client_first_name, $campaign_message_send);
            $campaign_message_send = str_replace('{{last_name}}', $client_last_name, $campaign_message_send);

            if (!isset($access_token_database[$campaign_id][$page_id_send])) continue;

            $page_access_token_send = $access_token_database[$campaign_id][$page_id_send];

            // Prepare CURL request for each lead
            $url = "https://graph.facebook.com/v4.0/me/messages?access_token={$page_access_token_send}";
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($campaign_message_send));
            curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));

            // Add handle to multi-cURL
            curl_multi_add_handle($multiHandle, $curl);

            // Track the handles and campaign info
            $curlHandles[] = $curl;
            $campaignHandlesMap[(string)$curl] = [
                'campaign_id' => $campaign_id,
                'send_table_id' => $value['id']
            ];
        }

        // Set the campaign to "in progress"
        $this->db->where("id", $campaign_id);
        $this->db->update("messenger_bot_broadcast_serial", array("posting_status" => "1", "is_try_again" => "0"));
    }

    // Execute multi-cURL to process all requests concurrently
    $running = null;
    do {
        curl_multi_exec($multiHandle, $running);
        curl_multi_select($multiHandle);
    } while ($running > 0);

    // Process each result
    foreach ($curlHandles as $curl) {
        $response_data = curl_multi_getcontent($curl);
        $response_data = json_decode($response_data, true);
        $campaign_data = $campaignHandlesMap[(string)$curl];
        $campaign_id = $campaign_data['campaign_id'];
        $send_table_id = $campaign_data['send_table_id'];

        // Handle response for each request
        if (isset($response_data['message_id'])) {
            // Message sent successfully
            $this->basic->update_data("messenger_bot_broadcast_serial_send", array("id" => $send_table_id), array('processed' => '1'));
        } else {
            // Handle error
            $error_msg = isset($response_data["error"]["message"]) ? $response_data["error"]["message"] : "Unknown error";
            $this->basic->update_data("messenger_bot_broadcast_serial_send", array("id" => $send_table_id), array('processed' => '0', "error_message" => $error_msg));
        }

        // Remove handle
        curl_multi_remove_handle($multiHandle, $curl);
        curl_close($curl);
    }

    // Close the multi-handle
    curl_multi_close($multiHandle);

    // Finalize each campaign's status based on success/failure
    foreach ($campaign_info as $info) {
        $campaign_id = $info['id'];
        $catch_error_count = $info["last_try_error_count"];
        $successfully_sent = $info["successfully_sent"];
        $successfully_delivered = $info["successfully_delivered"];
        $error_msg = null;

        // Check if all leads are processed for this campaign, if so, mark it as completed
        $remaining_leads = $this->basic->get_data("messenger_bot_broadcast_serial_send", array("where" => array("campaign_id" => $campaign_id, "processed" => "0")));
        if (count($remaining_leads) == 0 || $catch_error_count > $subscriber_broadcaster_hold_after_number_of_errors) {
            $new_posting_status = ($catch_error_count > $subscriber_broadcaster_hold_after_number_of_errors) ? '4' : '2';
            $complete_update = array(
                "posting_status" => $new_posting_status,
                'successfully_sent' => $successfully_sent,
                "successfully_delivered" => $successfully_delivered,
                'completed_at' => date("Y-m-d H:i:s"),
                "is_try_again" => "0",
                "last_try_error_count" => $catch_error_count
            );
            if (isset($error_msg)) {
                $complete_update["error_message"] = $error_msg;
            }
            $this->basic->update_data("messenger_bot_broadcast_serial", array("id" => $campaign_id), $complete_update);
        } else {
            $this->basic->update_data("messenger_bot_broadcast_serial", array("id" => $campaign_id), array(
                'successfully_sent' => $successfully_sent,
                "successfully_delivered" => $successfully_delivered,
                "is_try_again" => "1"
            ));
        }

        $this->basic->update_data('otn_postback', ['id' => $info['otn_postback_id']], ['rcn_last_sent_time' => date("Y-m-d H:i:s")]);
    }
}